diff --git a/.github/workflows/static_analysis.yml b/.github/workflows/static_analysis.yml index 51f991f7..4615946a 100644 --- a/.github/workflows/static_analysis.yml +++ b/.github/workflows/static_analysis.yml @@ -26,4 +26,4 @@ jobs: use_cmake: false # (Optional) cppcheck args - cppcheck_args: -i$GITHUB_WORKSPACE/components/gbc/gnuboy -i$GITHUB_WORKSPACE/components/nes/nofrendo -i$GITHUB_WORKSPACE/components/msx/fmsx -i$GITHUB_WORKSPACE/components/doom/prboom -i$GITHUB_WORKSPACE/components/sms/smsplus -i$GITHUB_WORKSPACE/components/genesis/gwenesis -i$GITHUB_WORKSPACE/components/darkforces/tfe -i$GITHUB_WORKSPACE/components/gui/generated -i$GITHUB_WORKSPACE/components/menu/generated -i$GITHUB_WORKSPACE/components/jpegdec --check-level=exhaustive --force --enable=all --inline-suppr --inconclusive --platform=mips32 --std=c++17 --suppressions-list=$GITHUB_WORKSPACE/suppressions.txt + cppcheck_args: -i$GITHUB_WORKSPACE/components/gbc/gnuboy -i$GITHUB_WORKSPACE/components/nes/nofrendo -i$GITHUB_WORKSPACE/components/msx/fmsx -i$GITHUB_WORKSPACE/components/doom/prboom -i$GITHUB_WORKSPACE/components/sms/smsplus -i$GITHUB_WORKSPACE/components/genesis/gwenesis -i$GITHUB_WORKSPACE/components/darkforces/tfe -i$GITHUB_WORKSPACE/components/gui/generated -i$GITHUB_WORKSPACE/components/menu/generated -i$GITHUB_WORKSPACE/components/jpegdec -i$GITHUB_WORKSPACE/components/mgs/mgs -i$GITHUB_WORKSPACE/components/mgs/psyz -i$GITHUB_WORKSPACE/components/mgs/psyq --check-level=exhaustive --force --enable=all --inline-suppr --inconclusive --platform=mips32 --std=c++17 --suppressions-list=$GITHUB_WORKSPACE/suppressions.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 2e884187..5030aa86 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,11 @@ set(DOOM_COMPONENTS "doom") ### DARK FORCES ### set(DARKFORCES_COMPONENTS "darkforces") +### METAL GEAR SOLID ### +# Native port of the FoxdieTeam decompilation (components/mgs); needs the +# 16MB-PSRAM board (ESP32-S3-BOX-3). +set(MGS_COMPONENTS "mgs") + ### SNES ### # set(SNES_COMPONENTS "snes") @@ -78,6 +83,11 @@ if(DARKFORCES_COMPONENTS) add_compile_definitions(ENABLE_DARKFORCES) endif() +# if MGS_COMPONENTS is set, add compile definitions for METAL GEAR SOLID +if(MGS_COMPONENTS) + add_compile_definitions(ENABLE_MGS) +endif() + # make the components list for the emulators we want set(EMULATOR_COMPONENTS ${NES_COMPONENTS} @@ -88,6 +98,7 @@ set(EMULATOR_COMPONENTS ${GENESIS_COMPONENTS} ${DOOM_COMPONENTS} ${DARKFORCES_COMPONENTS} + ${MGS_COMPONENTS} ) set( diff --git a/README.md b/README.md index a3251b34..eb6a1fe2 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ that removes the frame-rate cap for maximum speed. | MSX 1 / 2 | [fmsx](https://fms.komkon.org/fMSX/) | — | D-Pad / A / B / Start / Select | | Doom | [prboom](https://prboom.sourceforge.net/) | — | Full speed with audio **and haptic feedback** | | Dark Forces | [The Force Engine](https://theforceengine.github.io/) (via [BSzili's Amiga port](https://github.com/BSzili/TheForceEngine/tree/amiga)) | — | Software renderer, iMuse sound effects and OPL3 music; see [Dark Forces setup](#dark-forces-setup) | +| Metal Gear Solid | [mgs_reversing](https://github.com/FoxdieTeam/mgs_reversing) decompilation, native via [psyz](https://github.com/Xeeynamo/sotn-decomp/tree/master/tools/psyz) (after [davidmonterocrespo24's ESP32-S3 port](https://velxio.dev/blog/posts/metal-gear-solid-on-esp32-s3/)) | — | Experimental, BOX-3 only; software-rasterized 3D at ~10 fps, no sound or codec yet; see [Metal Gear Solid setup](#metal-gear-solid-setup) | > 🧬 **Full-speed Genesis on the S3.** The Genesis core ships with a custom > **dual-core** modification of gwenesis that spreads the workload across both of @@ -252,6 +253,42 @@ darkforces/DARK.GOB, boxart/darkforces.jpg, Star Wars: Dark Forces Settings (`settings.ini`) and agent / save files are written to the same folder. +### Metal Gear Solid setup + +Metal Gear Solid is a native build of the decompiled game (not an emulator) and +needs the files from your own disc image (US disc 1, `SLUS-00594`). Extract +them with the script in `components/mgs/tools/`: + +```bash +python components/mgs/tools/extract_disc.py "Metal Gear Solid (USA) (Disc 1).bin" /Volumes/SDCARD +``` + +The extractor creates an `MGS/` folder under the directory it is given, so +pointing it at the card root writes the game's files to `MGS/` on the card: + +``` +MGS/STAGE.DIR (71 MB: every stage) +MGS/RADIO.DAT +MGS/FACE.DAT +MGS/VOX.DAT +MGS/DEMO.DAT +MGS/BRF.DAT +MGS/ZMOVIE.STR (optional, full-motion video; not played) +``` + +Then reference `STAGE.DIR` from `metadata.csv`: + +```csv +MGS/STAGE.DIR, boxart/mgs.jpg, Metal Gear Solid +``` + +The game boots straight into the dock (`s00a`, where the game begins); +`MGS_START_STAGE` in `components/mgs/CMakeLists.txt` picks another stage. It +needs the 16MB-PSRAM board (ESP32-S3-BOX-3). Controls: d-pad, A = circle, +B = cross, X = square, Y = triangle, START, SELECT; SELECT + A/B/X/Y = L1/R1/L2/R2. +Save states are not supported (the port has no memory card yet); see +`components/mgs/VENDOR.md` for what works and what does not. + ### metadata.csv format Each line maps a ROM to its boxart and display name: diff --git a/components/mgs/CMakeLists.txt b/components/mgs/CMakeLists.txt new file mode 100644 index 00000000..aa5b11ab --- /dev/null +++ b/components/mgs/CMakeLists.txt @@ -0,0 +1,119 @@ +# Metal Gear Solid for esp-box-emu. +# +# A native build of the FoxdieTeam decompilation (mgs_reversing), using the +# ESP32-S3 port by davidmonterocrespo24: the PlayStation SDK is replaced by psyz +# (a C reimplementation with a software GPU rasterizer and the GTE) plus the +# PSY-Q library decompilation, and the PSX kernel's threads and vblank run on +# FreeRTOS. See VENDOR.md for what is vendored and what was changed. +# +# Only one stage can be linked statically (every file in source/stage/ defines +# its own _StageCharacterEntries[]); port/stage_union.c is the merged table +# the ESP32 port generates for all the stages that can be packed. +# COMPONENT_DIR, not CMAKE_CURRENT_SOURCE_DIR: IDF also evaluates this file in +# its early requirements pass, from the build directory +set(MGS_ROOT ${COMPONENT_DIR}/mgs) +set(PSYZ_ROOT ${COMPONENT_DIR}/psyz) +set(PSYQ_ROOT ${COMPONENT_DIR}/psyq) + +# Where the game drops you when it starts (any stage of port/stage_union.c +# works, given the full STAGE.DIR). s00a is the dock, where the game begins; +# "title" is the game's own boot chain, which the upstream port cannot run yet +# (memory card and CD streaming are missing). +set(MGS_START_STAGE "s00a" CACHE STRING "stage Metal Gear Solid boots into") + +# ---- the game (overlays and stage tables are curated by the vendoring) ---- +file(GLOB_RECURSE SRC_GAME ${MGS_ROOT}/source/*.c) +list(APPEND SRC_GAME ${MGS_ROOT}/port/stage_union.c) + +# ---- the port layer: PSX kernel / SDK pieces on FreeRTOS ------------------- +set(SRC_PORT + ${MGS_ROOT}/port/psyz_port.c + ${MGS_ROOT}/port/psyq_compat.c + ${MGS_ROOT}/port/soft_render.c + ${MGS_ROOT}/port/platform_headless.c + ${MGS_ROOT}/port/stage_stubs.c + ${MGS_ROOT}/port/esp32_threads.c + ${MGS_ROOT}/port/esp32_vblank.c + ${MGS_ROOT}/port/virtual_cd.c) + +# ---- psyz (minus the pieces MGS supplies itself) --------------------------- +file(GLOB SRC_PSYZ ${PSYZ_ROOT}/src/psyz/*.c) + +# ---- the PSY-Q decompilation subset (psyq/SOURCES.txt) ---------------------- +file(STRINGS ${PSYQ_ROOT}/SOURCES.txt PSYQ_FILES) +set(SRC_PSYQ "") +foreach(f ${PSYQ_FILES}) + list(APPEND SRC_PSYQ ${PSYQ_ROOT}/${f}) +endforeach() + +# ---- esp-box-emu glue ------------------------------------------------------- +set(SRC_GLUE + src/mgs.cpp + src/platform/mgs_input.c + src/platform/mgs_present.c) + +idf_component_register( + INCLUDE_DIRS "include" + SRCS ${SRC_GLUE} ${SRC_GAME} ${SRC_PORT} ${SRC_PSYZ} ${SRC_PSYQ} + PRIV_INCLUDE_DIRS + "src/platform" + "${MGS_ROOT}/source" # MUST precede psyz: both have a common.h + "${MGS_ROOT}/source/include" + "shim" + "${PSYZ_ROOT}/include" + "${PSYZ_ROOT}/src" + "${PSYQ_ROOT}/src" # psyz's libspu/libspu_private.h + "${MGS_ROOT}/port/include" + REQUIRES box-emu statistics pool_allocator esp_timer heap esp_system + LDFRAGMENTS "linker.lf" + ) + +# the game's build configuration: C only (INTEGRAL clashes with a template +# parameter in libfmt, which the C++ glue uses) +target_compile_definitions(${COMPONENT_LIB} PRIVATE + $<$:INTEGRAL> + $<$:__psyz> + $<$:VERSION_PC> + $<$:NON_MATCHING> + $<$:PERMUTER> + $<$:HARD_LINK> + $<$:_internal_version_us> + MGS_ESPBOX=1 + MGS_START_STAGE="${MGS_START_STAGE}") + +# 1998 PSY-Q C: gnu17 (GCC 14's gnu23 default rejects it), int/long/pointer +# mixing that was harmless on MIPS, and type punning everywhere (strict aliasing +# removed the stores of CompMatrix and detached every limb from every model). +# -O2: the rasterizer and the GTE are the frame budget. +target_compile_options(${COMPONENT_LIB} PRIVATE + -O2 + -w + -Wno-error + -fno-strict-aliasing + $<$:-std=gnu17> + $<$:-Wno-incompatible-pointer-types> + $<$:-Wno-int-conversion> + $<$:-Wno-implicit-function-declaration> + $<$:-Wno-builtin-declaration-mismatch> + $<$:-Wno-return-mismatch> + # SHELL: keeps CMake from merging the repeated -include flags + "$<$:SHELL:-include string.h>" + "$<$:SHELL:-include gtemac.h>" + # MGS_WHERE breadcrumbs (the printf redirection inside is for another board) + "$<$:SHELL:-include ${MGS_ROOT}/port/mgs_printf.h>") + +# Global names the game shares with other emulator cores in this firmware +# (gnuboy's sound_off, fMSX's ClearImage); rename them throughout libmgs.a. +target_compile_definitions(${COMPONENT_LIB} PRIVATE + $<$:sound_off=mgs_sound_off> + $<$:ClearImage=mgs_ClearImage>) + +# The PSY-Q decompilation's is psyz's (INCLUDE_ASM compiles to +# nothing there); the game's own common.h shadows it on the include path, so +# force psyz's in first for these files. +set_source_files_properties(${SRC_PSYQ} PROPERTIES + COMPILE_OPTIONS "-include;${PSYZ_ROOT}/include/common.h") + +# the game's main() is started from the emulator (src/mgs.cpp) +set_source_files_properties(${MGS_ROOT}/source/main/main.c PROPERTIES + COMPILE_DEFINITIONS "main=mgs_main") diff --git a/components/mgs/VENDOR.md b/components/mgs/VENDOR.md new file mode 100644 index 00000000..1d53253c --- /dev/null +++ b/components/mgs/VENDOR.md @@ -0,0 +1,80 @@ +# Vendored sources + +Metal Gear Solid here is not emulated: it is the FoxdieTeam decompilation of +the PlayStation game compiled natively for the ESP32-S3, following +davidmonterocrespo24's port (see +https://velxio.dev/blog/posts/metal-gear-solid-on-esp32-s3/). Three trees are +vendored; the emulator-side glue lives in `src/` and `include/`. + +## `mgs/` — the game and the ESP32 port layer + +- Upstream: https://github.com/FoxdieTeam/mgs_reversing (no license file; the + decompiled game code is Konami's) +- Branch used: https://github.com/davidmonterocrespo24/mgs_reversing/tree/esp32-port +- Commit: `5a2c7ee2be477bdde465654b5bf52de1275e1096` + +`mgs/source/` is the subset the ESP32 port links (`esp32/main/CMakeLists.txt` +upstream): everything except `contrib/`, `stagevr/`, `snake_vr/`, the per-stage +`stage/*.c` tables (replaced by `port/stage_union.c`, the merged table for the +stages that are 100% C) and the overlays other than `s07a s11e s11i d18ar s08br +s19br`. `mgs/port/` is the port layer (software GPU, FreeRTOS threads and +vblank, virtual CD over files, headless platform), `tools/` the disc extraction +scripts, `mgs/ESP32_README.md` and `mgs/PORT_STATUS.md` the upstream notes on +what works. + +## `psyz/` — the PlayStation SDK reimplementation + +- Upstream: https://github.com/Xeeynamo/sotn-decomp/tree/master/tools/psyz (MPL-2.0) +- Fork used: https://github.com/davidmonterocrespo24/psyz/tree/esp32 +- Commit: `97b064c28a28d79d80655e120b336e70097328ae` + +`psyz/include` (the SDK headers; unlicensed upstream), `psyz/src/psyz/*.c` +(minus `libgte_hw_sqrt.c` and `libsn.c`, which MGS supplies itself; MPL-2.0), +`psyz/src/{internal,draw}.h` and the software rasterizer +`psyz/src/platform/soft_raster.inc.c` (MPL-2.0, `psyz/src/platform/LICENSE`). + +## `psyq/` — the PSY-Q library decompilation + +- Upstream: the `decomp/` tree of the same psyz repository (MIT, `psyq/LICENSE`) +- Commit: `97b064c28a28d79d80655e120b336e70097328ae` + +Only the translation units listed in `psyq/SOURCES.txt` (the set the SOTN ESP32 +port proved to compile plus the libspu units MGS needs; `libpress/dec.c` is not +in the upstream tree) and the private headers next to them. + +## Changes made for esp-box-emu + +All guarded by `MGS_ESPBOX` where they sit in vendored code: + +- `shim/`: the PSY-Q headers the port expected from a private directory + (`libpad.h` constants, `mgrex.h`, `game.h`). +- The three big static buffers are pointers filled from the emulator's 4MB ROM + block: `mgs_main_ram` (`libgv/libgv.h`, `libgv/resident.c`, `game/strctrl.c`, + `sound/sd_main.c`, `port/psyz_port.c`), `mgs_face_group` (`port/psyz_port.c`, + `menu/radiomes.c`) and `g_RawVram` (`port/soft_render.c`, `libdg/display.c`, + `port/esp32_vblank.c`, `psyz/src/platform/soft_raster.inc.c`). + `GV_ResidentAreaBottom` is computed at init instead of being a static + initializer of the array's address. +- `port/esp32_threads.c`: per-slot stack sizes from the measured high-water + marks; `Mgs_ThreadsPause/Resume/StopAll()`. +- `port/esp32_vblank.c`: `Mgs_PauseVblank/ResumeVblank/StopVblank()`. +- `port/virtual_cd.c`: files are read from the data root directly; + `Mgs_CdDeinit()`. +- `port/platform_headless.c`: `Mgs_GetDataRoot()`. +- `port/psyz_port.c`: `psyz_CompMatrix()` (the name `port/include/gtemac.h` + routes `gte_CompMatrix` to). +- `source/libsio/dummy.c`: the mts idle task's console poll yields a tick + (`Mgs_IdleYield()`), so the emulator's own tasks on core 0 keep running. +- `psyz/src/psyz/{libcd,libsnd,libspu,psyz_spu}.c`: `libspu_private.h` is found + through the include path instead of a `../../decomp/` relative path. +- `linker.lf`: every static of `libmgs.a` goes to PSRAM (.bss) or is bracketed + by `_mgs_{bss,common,data}_start/_end`, which `src/mgs.cpp` uses to reset the + game's state between launches (the PSX booted from scratch each time). +- `sound_off` and `ClearImage` are renamed with `-D` to avoid the other cores' + globals. + +## Status + +What the upstream port reports (`mgs/PORT_STATUS.md`): boots into any packed +stage, Snake and the guards work, 3D with correct skeletons; no audio, no codec +calls, no cutscenes/streaming, no memory card, 8-15 fps. diff --git a/components/mgs/include/mgs.hpp b/components/mgs/include/mgs.hpp new file mode 100644 index 00000000..a463703d --- /dev/null +++ b/components/mgs/include/mgs.hpp @@ -0,0 +1,30 @@ +#pragma once + +#include +#include +#include +#include + +// Metal Gear Solid (The FoxdieTeam decompilation, built natively through psyz) +// for esp-box-emu. The game runs in its own FreeRTOS tasks: run_mgs_rom() only +// feeds it the gamepad and paces the emulator loop. +// +// `rom_filename` is the path of STAGE.DIR; every other game file (RADIO.DAT, +// FACE.DAT, ...) is expected in the same directory. +void init_mgs(const std::string& rom_filename, uint8_t* romdata, size_t rom_data_size); +void reset_mgs(); +void run_mgs_rom(); +void deinit_mgs(); + +void pause_mgs_tasks(); +void resume_mgs_tasks(); + +// true once the game has ended (or failed to start); the cart then returns to +// the emulator menu +bool mgs_quit_requested(); + +// Save states are not supported (the port has no memory card yet). +void load_mgs(std::string_view save_path, int save_slot); +void save_mgs(std::string_view save_path, int save_slot); + +std::span get_mgs_video_buffer(); diff --git a/components/mgs/linker.lf b/components/mgs/linker.lf new file mode 100644 index 00000000..bf2063c6 --- /dev/null +++ b/components/mgs/linker.lf @@ -0,0 +1,24 @@ +# Metal Gear Solid: the game's statics. +# +# Everything the decompiled game, psyz and the PSY-Q libraries keep in .bss +# lives in PSRAM (requires CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY). The +# SURROUND symbols (_mgs_bss_start/_end, _mgs_common_start/_end, +# _mgs_data_start/_end) let the emulator reset the game's static state between +# launches: the PSX booted from scratch every time, the decompiled code assumes +# zeroed .bss and fresh .data, and this core must be fully stoppable. +[scheme:mgs_statics] +entries: + bss -> extern_ram + common -> extern_ram + data -> dram0_data + +[mapping:mgs] +archive: libmgs.a +entries: + * (mgs_statics); + bss -> extern_ram SURROUND(mgs_bss), + common -> extern_ram SURROUND(mgs_common), + data -> dram0_data SURROUND(mgs_data) + # the emulator-side glue owns the reset itself (the .data snapshot, the + # lifecycle flags) and must survive it: keep it in the default sections + mgs (default) diff --git a/components/mgs/mgs/ESP32_README.md b/components/mgs/mgs/ESP32_README.md new file mode 100644 index 00000000..17c63817 --- /dev/null +++ b/components/mgs/mgs/ESP32_README.md @@ -0,0 +1,85 @@ +# Metal Gear Solid on the ESP32-S3 + +Gameplay on the XIAO ESP32S3 handheld + +Gameplay on the handheld: [watch it on YouTube](https://youtube.com/shorts/XTUJKQWMhi0). + +A native port — not an emulator — of the Metal Gear Solid decompilation to an +ESP32-S3 microcontroller: 240 MHz dual-core Xtensa, 512 KB of internal SRAM, +8 MB of PSRAM. The whole engine is compiled to Xtensa machine code; the +PlayStation SDK is replaced by [psyz](https://github.com/davidmonterocrespo24/psyz) +(including the GTE geometry macros MGS needs) and a software rasterizer. + +Two targets: + +| | Waveshare ESP32-S3-Touch-LCD-2 | DIY handheld (XIAO ESP32S3 Sense) | +|---|---|---| +| Build | `build.ps1` | `build.ps1 xiao` | +| Panel | ST7789 | ILI9341, 320×240 native | +| Game data | FAT partition in flash (trimmed `STAGE.DIR`) or microSD | microSD (full 71 MB `STAGE.DIR`) | +| Frame rate | ~15 fps in the dock with enemies | similar | + +The handheld's wiring, parts list and button map are in +[`hardware/HARDWARE.md`](hardware/HARDWARE.md). It is the same console that +runs the Castlevania: Symphony of the Night port. + +## You need your own disc + +Nothing derived from the game is in this repository. Extract the files from +**your own** disc image with `port/extract_disc.py` and copy them to the +microSD under `/MGS/` (`STAGE.DIR`, `RADIO.DAT`, …). For the Waveshare board's +8 MB flash partition, `port/pack_stagedir.py` rebuilds a `STAGE.DIR` holding +only the stages that fit. + +## Status + +**Works:** boot straight into gameplay at the dock (`s00a`); Snake, guards, +searchlights, cameras, alerts and gunfire; 3D models with correct skeletons; +no panics over long play sessions. + +**Not yet:** +- **Codec calls** (radio dialogues). The data reaches the parser but the index + into `RADIO.DAT` is wrong; see [`port/STATUS.md`](../port/STATUS.md). +- **The opening** (swim-in and briefing) needs memory card, CD streaming and + the codec together. +- **The elevator**: its actor is never requested by the stage script — + most likely gated behind story progress that only advances through a codec + call. +- CD audio/video streaming; performance beyond ~15 fps. + +## Bugs worth knowing about + +This port shook out several problems that a PlayStation silently tolerates and +modern compilers or real memory protection do not — the full journal is +[`port/STATUS.md`](../port/STATUS.md): + +- **Strict aliasing deleted a whole matrix rotation.** 1998 PSY-Q C type-puns + freely; GCC 14 at `-O2` removed stores in `CompMatrix` it "proved" dead, and + every skeleton came out deformed. Found by disassembly: the function had zero + multiply instructions. `-fno-strict-aliasing` fixes the class. +- **The PSX GPU refuses polygons larger than 1023×511**, and the game relies on + it: projections behind the camera saturate at the GTE clamp and produce + screen-sized quads that the console simply drops. A software rasterizer draws + them — as a white sheet over the screen during alerts. +- **The OT tag is two words in psyz, one on the PSX.** `SetDrawStp` and + `DG_MakePrim` wrote the packed PSX layout into the length field. +- **`printf` over native USB blocks when nobody is listening**, which froze the + cooperative task scheduler inside the game's own debug output. + +## Building + +ESP-IDF v5.5, plus a checkout of the companion SOTN port, which provides psyz +(the PlayStation SDK reimplementation both games share) and a few headers: + +``` +git clone -b esp32-port --recurse-submodules https://github.com/davidmonterocrespo24/sotn-decomp +``` + +Point the build at it with the `SOTN` CMake cache variable +(`esp32/main/CMakeLists.txt`) or the `SOTN` environment variable for +`port/build.sh`. Then: + +``` +powershell -File esp32/build.ps1 xiao +powershell -File esp32/flash.ps1 COM9 -Xiao +``` diff --git a/components/mgs/mgs/PORT_STATUS.md b/components/mgs/mgs/PORT_STATUS.md new file mode 100644 index 00000000..b37a7276 --- /dev/null +++ b/components/mgs/mgs/PORT_STATUS.md @@ -0,0 +1,528 @@ +# Metal Gear Solid on ESP32-S3 — port status + +Last updated: 5-Aug-2026. +Board: Waveshare ESP32-S3-Touch-LCD-2 (240 MHz, 512 KB SRAM, 8 MB PSRAM +octal @120 MHz, 16 MB flash, ST7789 320x240, microSD on SPI2). + +--- + +## Where we are + +**The game boots, loads levels from the full disc and is playable.** Snake walks, +turns, swims, crosses camera volumes and collides with the world. The radar draws. +Water exists. It can boot into any of 56 stages. + +What is **still missing before it can truly be called playable**: + +| | status | +|---|---| +| character skeleton | fixed (strict aliasing in `CompMatrix`; see below) | +| codec calls / elevator | not working yet (see PENDING) | +| cutscenes | do not play (data missing from the card) | +| audio | not implemented | +| performance | 8-15 fps depending on scene (target 30) | +| memcard / saving | not implemented | + +--- + +## How to build and flash + +```powershell +E:\Hardware\mgs\mgs_reversing\esp32\build.ps1 # build +E:\Hardware\mgs\mgs_reversing\esp32\flash.ps1 COM6 # firmware + flash data +``` + +Firmware only (the usual case, much faster): + +```powershell +& "C:\Espressif\python_env\idf4.4_py3.10_env\Scripts\python.exe" ` + "C:\Espressif5.5\frameworks\esp-idf-v5.5.4\components\esptool_py\esptool\esptool.py" ` + --chip esp32s3 --port COM6 --baud 921600 write_flash ` + 0x10000 "E:\Hardware\mgs\mgs_reversing\esp32\build\mgs_esp32s3.bin" +``` + +Play with the PC keyboard: + +``` +python E:\Hardware\mgs\mgs_reversing\port\play.py COM6 +``` + +W/A/S/D move, E action, X crouch, Z punch, Q first person, 1-4 triggers, +ENTER pause, ESC quit. **Close play.py before flashing**: it holds COM6. + +### Choosing which level to boot into + +`esp32/main/CMakeLists.txt`: + +```cmake +set(MGS_START_STAGE "s01a" CACHE STRING "stage to boot into") +``` + +Any name from `Mgs_PlayableStages` (in `port/stage_union.c`) works, as long as the +microSD carries the full STAGE.DIR. Useful ones: `s00a` the dock (where the +game starts), `s01a` heliport (guards, snow), `s02a` tank hangar, `s03a` +armory building. + +--- + +## Game data + +It is read from a **microSD**, not from flash. Layout, at the root of the +card: + +``` +MGS\ + STAGE.DIR 68.6 MB all 96 stages on the disc <- copied + RADIO.DAT 1.7 MB codec <- copied + FACE.DAT 3.3 MB codec faces <- copied + BRF.DAT 5.5 MB briefings <- copied + DEMO.DAT 258 MB IN-ENGINE CUTSCENES <- PENDING + VOX.DAT 196 MB voices <- PENDING + ZMOVIE.STR 47 MB full-screen video <- PENDING +``` + +All seven are prepared in `E:\Hardware\mgs\sdcard\MGS\`. **Copying the three +pending ones is the next concrete step**: without them the cutscenes run +empty (see "Pending" below). + +**Why microSD and not flash**: reading from the card does NOT disable the +instruction cache (it is an SPI peripheral, not the XIP flash), so files are +read on demand without spending PSRAM. The flash partition only holds a +trimmed copy of 9 stages, as a fallback. + +**TRAP**: when the card does not mount, the board falls back to that flash copy and +load times drop from 693 to 21 vblanks — because it reads from PSRAM. **It looks like +a 35x improvement and it is a failure in disguise.** Before trusting any load +measurement, check the log for: +`mgs: microSD mounted` and `[dir] table_size 1152 -> 96 stages`. + +--- + +## How the port is built + +It compiles the MGS decomp (`github.com/FoxdieTeam/mgs_reversing`) against **psyz** +(a reimplementation of the PSX SDK, in `E:\Hardware\sotn\sotn-decomp\tools\psyz`) +instead of the original PSY-Q, with a software rasterizer shared with the SOTN +port. All port-specific code lives under `#ifdef __psyz`. + +Port-owned pieces, in `port/`: + +| file | what it does | +|---|---| +| `virtual_cd.c` | virtual CD: libfs asks for absolute sectors, this translates them to files | +| `esp32_threads.c` | the mts threads on top of FreeRTOS tasks, all pinned to core 0 | +| `esp32_vblank.c` | the vblank the PSX provided + LCD scanout on core 1 | +| `soft_render.c` | links the software rasterizer to the 1 MB VRAM in PSRAM | +| `psyq_compat.c` | PSY-Q functions that psyz does not provide | +| `stage_union.c` | **generated** by `gen_stage_union.py` — single actor table | +| `platform_headless.c` | translation of `cdrom:\...` paths and files | +| `play.py` | keyboard controller over the serial port | + +--- + +## Fixed bugs worth not stepping on again + +The most expensive ones to find, each with its root cause: + +**`Square0` was shifting 12 bits it should not.** The numeric suffix in PSY-Q is the +shift amount: `Square0` does NOT shift, `Square12` does. Since all of MGS +measures distances with `Square0 -> sum -> sqrt`, **every length in the game came +out divided by 64**. It broke movement, collisions, enemy range, bullets and +lighting. The clue came from the user: "the world moves fine, the player doesn't". + +**Five GTE MVMVA codes copied from the wrong row** of the game's own table +(`source/include/inline_n.h`). `Psyz_GteRtv0/Rtv1/Rtv2/Ll/Llv0` +added the GTE's stale translation vector to what should have been pure +directions. Once fixed, the per-frame step went from a pinned `0,1` to a sustained `27,0` +and Snake stopped falling out of the world. **`Psyz_GteRt` IS correct — do not touch it.** + +**`VSync(-1)` truncated to 16 bits** a counter the PSX returns as 32 bits. At +65,536 fields (**17.6 minutes**) the clock wrapped to zero while a task was +waiting for a larger mark, and the game froze forever. This also affects +the SOTN port. + +**`PSX_W` was 256, SOTN's resolution.** MGS runs at 320x240, so the +scanout copied 256 of 320 columns and centered the crop: **one fifth of +the image lost** and a black band on each side. + +**Division by zero in libhzd.** On MIPS it is harmless (undefined result, execution +continues); **on Xtensa it resets the board**. `HZD_DIV` was added and applied to +the 7 divisions with a runtime-computed divisor. General rule for this port: +any `/` with a variable divisor is a bomb. + +**The LCD and the microSD share SPI2 and the driver does not arbitrate them.** The LCD is +asynchronous (one frame is 30 queued transfers), so reading the card in the +middle of an image fires an assert and resets. Fix: `Mgs_SpiBusTake/Give`, with +**draining of in-flight transfers before releasing the bus**. + +**Stage scripts are TWO-PASS.** s00a is +`if (var[5] < 6) { set up the Colonel's call } else { build the dock }`, and +that call's callback does `restart`. **The world is built on the second +pass, and answering the codec is what triggers it.** Since the codec was disabled (a +mask I added myself to dodge a FACE.DAT crash), the else branch never +ran and the dock came up empty. Fix: `MENU_RadioDrainHeadless` in +`menu/radio.c`, which answers the call and dispatches its procedure without opening the +codec screen. + +### The family that has cost the most: the 8-byte header + +These four are the same misunderstanding with four faces, and deserve to be read +together because **there are almost certainly more left**. + +The PSX packs the pointer to the next primitive and its length into ONE 32-bit +word: 24 bits of address, 8 of length. PSY-Z splits them into two +words, because a pointer on this chip does not fit in 24 bits. **Everything that +wrote, initialized or assumed the old format ended up shifted by one word.** + +And there is an aggravating factor that makes it especially hard to see: on the console +`addPrim()` rewrote that shared word on every insertion, so +forgetting to set the length was **invisible**. Here "forgotten" comes to +mean "never written". + +**`SetDrawStp` wrote the GP0 command on top of the length field.** +`((u_long*)p)[1] = 0xE6000000 | stp` — word 1 is the length; the command +goes in `code[0]`, word 2. The reader saw a packet of 3.8 billion +words, discarded it, and resumed **in the middle of a primitive**, reading vertices and +pointers as draw commands. It fired in combat because that function +configures semi-transparency, which is what muzzle flashes and smoke turn on. +PSY-Z had already suffered this in its own `setDrawTPage` and fixed it there; the +constructor in MGS's compatibility layer was left unreviewed. +**When PSY-Z fixes a header mismatch, sweep for the equivalents on the +game side.** + +**`DG_MakePrim` did not initialize the packet length.** `GV_ZeroMemory` +only clears the `DG_PRIM` header; the packet area arrives with whatever +was in the block. Each user must write its own length and almost all of them +do. Fixed in `DG_MakePrim` itself — one place instead of chasing +every user — deriving `len = psize/4 - 2` from the size table and +checking it against the PSY-Z macros: FT4 -> 9, GT4 -> 12, TILE -> 3. + +**The `DG_PrimInfos` table had the console's offsets.** First vertex at ++8 instead of +12, and every primitive 4 bytes shorter. Each transformed vertex +landed on the primitive's command word and the entire world reached +the GPU as code-0 garbage. + +**The `POLY_GT4` offset**: 56 bytes, not 52, with `x0` at +12 and not at ++8. The stride between vertices does NOT change. + +### A hardware rule was missing, not a line of code + +**The PSX GPU REFUSES to draw polygons larger than 1023x511 pixels.** +It looks like a limitation and it is a **protection**: when a polygon crosses +behind the camera its projection overflows and the GTE pins the offending +vertices at its +/-1024 limit, producing a quad stretched from corner +to corner. The console drops them. Our rasterizer painted them whole — three +full screens of texels per frame and 142 ms of rasterization — and that +was the dock's spotlights covering everything. + +Implemented in `soft_raster.inc.c` **before** clipping to the draw area: +clipping would shrink each one to screen size and hide exactly the property +being tested. Counter `psyz_tri_huge`, published as `oversize` in `[tri]`. + +**Reimplementing a GPU is not just drawing what it draws: it is also NOT drawing +what it refuses to draw.** + +### The compiler deleting code without warning + +**`CompMatrix` lost its entire rotation to strict aliasing.** It wrote the +composed translation through a `(VECTOR*)` cast over a `long[3]`; GCC 14 +at `-O2` deduced that nobody reads those writes and **eliminated the call**. The +disassembly did not contain a single multiplication: it computed `m2->t = m0->t + m1->t`. + +Effect: each bone's translation was never rotated by the parent's matrix. +Each bone rotated correctly around a joint that never moved — arms +detached from the torso, knees backwards, feet pointing in different directions. + +Fixed with plain `long long` arithmetic, **plus `-fno-strict-aliasing` for +the whole game**. There was at least a second identical case in the camera +computation (`libdg/display.c:318`). This is 1998 PsyQ C and does type punning +everywhere; the flag is the protection, not the one-off patch. + +**When source and behavior disagree, disassemble.** `nm` for the +address, `objdump -d --start-address`, `addr2line` to confirm which +file it came from (there may be two definitions, and the linker's is the one that runs). +There is a script for this: `~/.claude/skills/retro-port-measure/scripts/`. + +### Sentinel values and out-of-range accesses + +**`GM_CameraList[-1]`.** The loop in `camera.c` decrements the index down to -1 when +no camera claims the change, and the following lines index the array. On +PSX that reads the preceding 16 bytes — deterministic, and the game shipped with +them. Here it is whatever the linker leaves there. + +**But `-1` is NOT an error: it is the sentinel for "there is no fixed camera here"**, which +is how the game hands the shot back to the one following the player. `ChangeCamera` +handles it explicitly. My first fix treated it as a failure and pinned the previous +camera: it killed one bug and created another — the camera stayed stuck when leaving the +zone. **Before treating a value as invalid, check whether its consumer has +a branch for it.** + +### Boot race + +The vblank tick called `Mgs_PadsUpdate` -> `usb_serial_jtag_read_bytes` +before `PadInitDirect` had installed the driver: `LoadProhibited` in +roughly **1 of every 3 boots** (measured: 6 boots, 2 panics). +Flag `sSerialReady` in `esp32/main/esp_input.c`. + +### Bounding-box culling + +My own test treated two opposite cases the same way: a box that **crosses** the +near plane (meaningless projection -> draw and let the rasterizer decide) and +one that is **entirely behind** (nothing can appear -> discard). Both have +at least one corner with `z <= H/2`, so both were drawn. It is not a +rare case: an overhead shot leaves out all the floor the player has just +walked, box by box. Measured: `kept-saturated 16090 -> 4947`, +`models drawn 25093 -> 14054`. + +--- + +## How all of this was measured + +Five hypotheses died by measurement before the causes were found. None was +far-fetched; all were false. The method is distilled in the +`retro-port-measure` skill, and the essentials are: + +**Design the measurement so that it CAN fail.** The `d2 == want` test compared +`|R.t|` against `|t|`, which are equal for any rotation. It could never +fail, and that is why the deformed skeleton survived for weeks behind a green +check. What did catch it: printing the positions in **two different +frames** and seeing that the parent-child offset did not change while the parent rotated +19 degrees. + +**A counter that nobody increments invents a finding.** `[trap] ... 0 entered` +was on its way to becoming "the trigger system is dead"; the +counter did not have a single `++`. Once fixed, the triggers were firing from the +first second. + +**Split counters by cause, not by outcome.** A "65% corrupt" was four +different things in one bucket, and three of them were normal. + +**Print the two numbers you are going to compare together.** The camera was solved in +a single capture by placing its probe right next to the saturation probe, after three attempts +comparing them across different runs. + +**Check where a field is WRITTEN before interpreting it.** `eye_inv.t` +looks like a position and is an already-rotated direction vector; misreading it produced a +detailed and false story about the camera flying upward. + +**Packet formats vary within the same range of codes.** Three +misreadings in a row of the GPU stream, with impossible coordinates that were +PSRAM pointers. Bits: `0x10` gouraud, `0x08` quad, `0x04` texture, `0x02` +semi-transparent; rectangles (0x60-0x7F) are a whole different format. + +**Do not narrow the probe on a hunch.** I filtered `[cover]` to untextured +primitives reasoning that "a flash has no texture". A light cone does +have one: I excluded exactly what I was looking for and lost a whole round. + +**Correlation tells you where to look, not what to fix.** + +--- + +## Handheld console (built and running) + +XIAO ESP32S3 Sense + ILI9341 320×240 SPI display + analog stick (drone +gimbal) + 8 buttons, powered over USB. The game data lives on the Sense +board's own microSD. The console was first planned around a 3.5" ILI9488; +the panel actually fitted turned out to be an ILI9341, which also happens to +be the game's native 320×240 — no centering, two bytes per pixel. + +**The complete wiring diagram is in `esp32/hardware/HARDWARE.md`** — +a schematic (`wiring.svg`), the pin table, the resistor ladder values, the +button map for both games, and the mistakes made during the build. Key +decisions it captures: + +- All 11 XIAO pins in use; touch is left out (T_CS tied to 3V3, + mandatory or it corrupts the microSD). Ladder on D2 (ADC is only available on D0-D5), + display DC on D6. +- 6 buttons via the resistor ladder + 2 direct (the ones held down + at the same time: fire and aim). A 74HC165 alternative was documented + in the chat but not chosen. +- RESET on its own pin (D4): with RESET tied to 3V3 the panel did not start + reliably. The display's GND wire is mandatory — without it the panel + back-powers through its data pins and stays white. + +**Console software (done)**: `build.ps1 xiao` — 8 MB partition table with a +3 MB app and no data partition, ILI9341 driver from the component registry, +ladder + stick sampled on their own task (never in the vblank tick), and a +non-blocking USB console. See `esp32/main/board_pins_xiao.h`. + +## PENDING + +### 1. The deformed skeleton — FIXED (7-Aug-2026) + +Symptom: head and arms displaced, none attached to the torso, knee backwards, +feet pointing in different directions. The world looked fine; only the characters. + +**Root cause: the compiler deleted a rotation.** `CompMatrix` in +`port/psyz_port.c` wrote the composed translation like this: + +```c +ApplyMatrixLV(m0, (VECTOR*)&tmp.t[0], (VECTOR*)&tmp.t[0]); /* WRONG */ +``` + +`MATRIX::t` is `long[3]`. Writing it through a `VECTOR*` violates strict +aliasing, and GCC 14 at `-O2` deduced that nobody reads those writes and eliminated +the whole call. The disassembly of `CompMatrix` in the ELF **did not contain a single +multiplication**: it computed `m2->t = m0->t + m1->t`, a bare addition. + +Effect: each bone's translation was never rotated by the parent's matrix. +Each bone rotated correctly around a joint that never moved. + +Fixed with `long long` arithmetic without type punning, plus +**`-fno-strict-aliasing` for the whole game** in `esp32/main/CMakeLists.txt`. +This is 1998 PsyQ C and does punning everywhere; there was at least a +second identical case in `libdg/display.c:318` (the camera computation). The +flag is the protection, not the one-off patch. + +Confirmed on hardware and by the user: *"now it looks perfect, it's properly +rigged"*. + +**Why it survived weeks of measurement — the lesson worth more than the bug:** + +| measurement | why it did not catch it | +|---|---| +| rest pose (M1) | with all angles at zero the rotation is the identity: rotating or not makes no difference | +| `d2 == want` | `\|R.t\| = \|t\|` for any rotation. **It is a tautology, it cannot fail.** | +| `det == 4096` | the 3x3 part was fine; the bug was only the translation | +| vertex seams | downstream symptom; measured at zero effect | + +What did catch it: **printing the position of the 16 joints in two different +frames.** The parent-child offset came out identical byte for byte while +the parent's rotation changed by 19 degrees. An offset that does not move when the +parent rotates is impossible if it is being rotated. + +**Design the measurement so that it CAN fail.** + +**Tools left in the tree for next time:** + +- `MGS_TURNTABLE` (CMake, default 0). Presents each skeleton standing and + facing the level's camera, spinning around the screen vertical: + `WORLD.R = EYE^T . Ry(angle)`. Since `EYE` is orthonormal its transpose is its + inverse, so it cancels the camera and works in any level — necessary + because s11e looks down on its room from above and a spin around the model's own axis only shows + the top of the head. Careful: `EYE` carries the `DG_AdjustOverscan` scale, so with + the turntable active `det` drops to ~3705 and lengths shrink by 0.4%; + it is an artifact of the view, not of the rig, and disappears when turned off. +- Automatic rig audit in `libdg/screen.c` (`[rig]`). It checks EVERY + skeleton the engine assembles, without needing to have it in view: `det > 0` (no + reflections), `det` equal across all bones, and `d2/want` constant along + the rig. **It compares the RATIO, not the value**, so it is scale + invariant: the game scales characters legitimately and in animation (det + 4814 and 5659 were measured on the same rig in different frames, with all bones + in agreement). A single bone that disagrees is a real defect and is printed by + name. Silence = clean. This is what answers "are the enemies properly + rigged?" — they use the same 16-bone rig and the same code, so they get + audited on their own as soon as they appear on screen. + +### 2. Cutscenes and the codec — blocker located + +The Colonel's call when using the elevator **does happen**: the log shows +`[trap] NEW trigger 'liftcall'`, `change camera 3` and +`[codec] answered: proc ...`. It is not visible because `MENU_RadioDrainHeadless` +answers it silently, and the real codec module is turned off +(`MGS_CODEC_SCREEN` in `source/menu/menuman.c`; setting it to 1 turns it on). + +**What is solved**: the 417792 bytes of the face group. The reservation +`mgs_face_group[0x80000]` in PSRAM (`port/psyz_port.c`) holds them, and +`radiomes.c` uses it when `GV_PACKET_MEMORY0` says no. **Careful**: +`menu_radio_codec_helper_helper7_80048080` must NOT pass that reservation to +`GV_FreeMemory` — freeing a static array corrupts the pool's free list far +away and much later. It is already guarded with a pointer comparison. + +**What is missing, and it is a RACE**: `face data num 0`, because +`sectorAndSize` arrives as a fill pattern (`0xCCEEEEAE`). In `radiomes.c`: + + line 510: mts_start_task(MTSID_CD_READ, menu_radio_codec_task_proc_80047AA0, ...) + line 600: FS_LoadFileRequest(1, startSector, size, radioDatFragment); + +**The parser starts as its own task BEFORE the read request is +issued.** It reads the buffer before anyone fills it. That explains why the value +comes out byte-for-byte identical across different builds: nobody writes there. + +Already ruled out (by reading code, without flashing): the file id is correct +(`cd_names[1]` is RADIO.DAT), the position table is filled correctly +(`finfo[i].pos = i * SLOT_SECTORS`), and adding the `FS_LoadFileSync` wait in the +loader did not change the value by a single bit — consistent with the race, not with a +slow read. + +Next step: synchronize. Either issue the read before starting the task, or +make the task wait until the fragment is ready. + +### 2b. Cutscenes run without data + +`s01a` opens with the `d01a` cutscene (the helicopter one), which requests `DEMO.DAT` and +`VOX.DAT`. Since they are not on the card, it runs empty, **does not place Snake** (the +script literally prints `Where Is Snake ????`) and ends up crashing in +`DG_AllocPacks` walking uninitialized pointers. + +**Fix: copy the three pending files to the microSD.** A minor +follow-up: even once they are there, the stream guard should be reviewed so that a missing +file never leaves half-built objects. + +### 3. The microSD mounts intermittently + +Same hardware and firmware: sometimes on the first try, sometimes all 6 attempts fail +with `ESP_ERR_TIMEOUT` or `ESP_ERR_INVALID_CRC`. `Mgs_SdCardInit` already waits 250 ms +before the first attempt and retries 6 times at 300 ms. Reseating the card +fixes it, so it points to physical contact. **40 MHz does not work on this board** +(always invalid CRC); 20 MHz. + +### 4. Slow loading: 12 seconds of black screen per level + +Reading 1.1 MB from the card takes ~11.5 s (≈95 KB/s, when the bus can deliver ten times +more). Raising the burst from 16 to 128 sectors gave only 7%, so that is not +the bottleneck. Skipping the redundant `fseek` on sequential reads was added +(walking the FAT of a 68 MB file on every sector), **not yet measured because the +card did not mount**. If that is not enough, look at the FATFS block size. + +### 5. Performance: 8-15 fps + +Dominant cost: **207 cycles per textured pixel**, almost all of it PSRAM +latency. SOTN reaches 60 fps with the same rasterizer because it is 2D and walks the +texture in a straight line (one cache read serves 128 pixels); MGS is 3D and in +a rotated triangle **every pixel is a cache miss**. The data cache is already +at its maximum (64 KB). + +**The identified path**: move the hot texture pages into internal RAM, +40 times faster. It needs the ~90 KB trapped in the mts stacks, which +request 20 KB x 8 = 160 KB and use (measured with `uxTaskGetStackHighWaterMark`) +2 KB... except one task that uses **14,484 B** and where 20 KB was not excessive. The +correct sizing is that big one at 24 KB and the rest at 6 KB. + +### 6. No audio, no saving + +Not implemented. The SOTN port has a software SPU with 44.1 kHz pull that +could be reused. + +### 7. Debug probes to remove + +There are quite a few `printf` under `#ifdef __psyz` (bound.c, trans.c, screen.c, +control.c, chara.c, trap.c, wt_area.c, wt_view.c, level.c, near.c, virtual_cd.c, +esp32_vblank.c, display.c). Useful now, noise later. + +### 8. 28 stages cannot be visited + +Their actor tables still have raw MIPS addresses (`0x800dxxxx`) because +that code is not decompiled. `Mgs_ResolveStage` rejects them by name. +`gen_stage_union.py` regenerates the list; `s11d` is additionally excluded because it links +against an overlay that is not compiled. + +--- + +## Method: what worked and what did not + +Three lessons that were expensive to learn: + +**Decode the data before suspecting the code.** The theory that "the +GCL interpreter only executes one command" was false: the block has 19 nodes and +only one prints. Four independent decodings of the retail bytecode +solved it in minutes, after two sessions of suspicion. + +**Measure the board, do not reason about the code.** The skeleton investigation +ran out of suspects by reasoning, and **had never measured the hardware**. A single +measurement ruled out at once everything we had been looking at. + +**A temporary stub needs something that forces it to be revisited.** Two of the three +big blockers of one session were my own shortcuts with comments that were no longer +true — the codec one said "the game plays fine without the motion +detector" and had the story engine turned off. diff --git a/components/mgs/mgs/port/esp32_threads.c b/components/mgs/mgs/port/esp32_threads.c new file mode 100644 index 00000000..6c840451 --- /dev/null +++ b/components/mgs/mgs/port/esp32_threads.c @@ -0,0 +1,391 @@ +/* The PSX kernel's thread primitives, on FreeRTOS. + * + * MGS's mts layer is a cooperative scheduler: it decides which task should run + * and calls ChangeTh() to switch to it. On the PSX ChangeTh does not return -- + * execution resumes inside the target thread, and control comes back only when + * somebody switches back. + * + * That maps onto FreeRTOS exactly: one task per PSX thread, created suspended, + * and ChangeTh becomes "resume the target, suspend myself". The cooperative + * semantics are preserved because at most one of these tasks is ever runnable. + * + * What is deliberately NOT preserved: the stack pointer MGS passes to OpenTh. + * It allocates its own stacks out of the game heap, which FreeRTOS cannot adopt + * -- each task gets its own instead, sized from the largest MGS asks for. + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include "esp_heap_caps.h" + +#define MGS_MAX_THREADS 8 +/* MGS declares 2 KB stacks (GAME_STACK_SIZE in main.c) because the PSX's libc + * was tiny. Ours is not: one newlib printf can eat a couple of KB on its own, + * and Main()'s call chain goes several frames deep before the first output. An + * overflow here is silent -- the task just stops -- so be generous. */ +/* 20480 was a guess made before anything could be measured. The high-water + * marks from a long session say what it actually needs: + * + * t0=2100 t1=1944 t2=3164 t3=14484 t4=2080 t5=2124 + * + * One task uses its stack; the other five never pass 3.2 KB of their 20. These + * live in internal SRAM -- they have to, because a task whose stack is in PSRAM + * loses access to it the instant the flash driver disables the cache, and the + * chip resets before it can even print. So this number is spent from the + * scarcest memory on the board, and it is the same memory the rasterizer wants + * for hot texture pages. + * + * 16 KB keeps 1.9 KB over the worst mark observed and returns 24 KB. Deliberately + * conservative: the mark is an observation of one session, not a proof, and a + * stack overflow here would corrupt a neighbour rather than fault cleanly. The + * real prize is per-task sizing -- five of these could run on 4 KB and free + * ~80 KB -- but that needs the slot-to-thread mapping to be known at creation + * time, which it is not yet. */ +#define MGS_THREAD_STACK 16384 +#ifdef MGS_ESPBOX +/* esp-box-emu shares its internal RAM with the emulator menu and the display + * path, so the slots are sized from the measured high-water marks above (slot + * 3 is the game's main loop, the others never passed 3.2KB) rather than all + * getting 16KB. */ +static const unsigned mgs_thread_stack_size[MGS_MAX_THREADS] = { + 6144, 6144, 6144, 20480, 6144, 6144, 6144, 6144 }; +#define MGS_STACK_FOR(i) (mgs_thread_stack_size[(i)]) +#else +#define MGS_STACK_FOR(i) ((unsigned)MGS_THREAD_STACK) +#endif + +typedef struct { + TaskHandle_t handle; + void (*entry)(void); + void* stack; /* PSRAM */ + StaticTask_t* tcb; /* must stay in internal RAM */ + int in_use; + /* The PSX kept the interrupt mask in each task's SR register, restored on + * every context switch: a task that transfers away inside a critical + * section does NOT mask interrupts for whoever runs next. A single global + * flag wedged the whole system the moment that happened; so the flag is + * per-thread, swapped on every switch. */ + int crit; +} MgsThread; + +extern volatile int psyz_critical_depth; /* psyz libapi.c: the running task's "SR" */ + +static MgsThread threads[MGS_MAX_THREADS]; +static int current_thread = -1; + +/* The PSX's ChangeTh was a syscall and ran with interrupts masked; nothing + * could observe it half-done. Here the vblank tick preempts at any instruction + * and once read current_thread mid-update -- it then resumed a task that was + * not parked and left two mts tasks runnable, which wedges the cooperative + * scheduler without ever crashing. While a cooperative switch is in flight the + * tick DEFERS its preemption to the next frame instead (ChangeThFromISR + * returns 0 and mts reverts its bookkeeping). */ +static volatile int change_in_flight; + +static void thread_trampoline(void* arg) { + MgsThread* t = (MgsThread*)arg; + /* Park BEFORE running anything. Creating the task and then suspending it + * from the creator is a race: on a dual-core chip FreeRTOS can start it on + * the other core immediately, so the PSX thread would run before the code + * that created it had finished filling in its task control block. */ + vTaskSuspend(NULL); + if (t->entry) { + t->entry(); + } + /* a PSX thread that returns simply stops being scheduled */ + t->in_use = 0; + vTaskSuspend(NULL); + for (;;) { + } +} + +unsigned long OpenTh(unsigned long (*func)(), unsigned long sp, + unsigned long gp) { + int i; + (void)sp; /* MGS's own stack; FreeRTOS supplies ours */ + (void)gp; + for (i = 0; i < MGS_MAX_THREADS; i++) { + if (!threads[i].in_use) { + break; + } + } + if (i == MGS_MAX_THREADS) { + printf("[thread] OpenTh: out of slots (max %d)\n", MGS_MAX_THREADS); + return (unsigned long)-1; + } + printf("[thread] OpenTh slot %d func %p\n", i, (void*)func); + threads[i].entry = (void (*)(void))func; + threads[i].in_use = 1; + + /* Stacks go to PSRAM. FreeRTOS takes them from internal RAM by default, + * and internal RAM is this chip's scarce resource -- with three tasks + * already up, the fourth xTaskCreate() failed, OpenTh returned -1, and + * every later ChangeTh() to that task was rejected. That is what stalled + * the boot: not a scheduling deadlock, just no room. + * + * The task control block itself must stay internal (FreeRTOS touches it + * from contexts where PSRAM may be unreachable); only the stack moves. */ + /* ...but they cannot LIVE there. PSRAM is reached through the same cache + * the flash driver switches off while it reads, so the moment a task with a + * PSRAM stack calls fopen() the CPU loses access to its own stack and the + * chip resets instantly -- too early even to print a panic. That is exactly + * what happened at the first CD read. Internal RAM it is; the stacks are + * smaller to make them fit, which is affordable because MGS itself only + * asked for 2 KB. There is no PSRAM fallback: a task with a PSRAM stack + * resets the chip on its first file read, so failing the OpenTh is the + * safer outcome. + * + * A slot keeps its stack and TCB across CloseTh/OpenTh (the sizes are + * per slot): a task that exits and is re-created reuses them, and the + * storage is released only by Mgs_ThreadsStopAll. Reusing the TCB right + * after a task deleted itself would race the idle task's clean-up, so + * let that run first. */ + if (threads[i].stack || threads[i].tcb) { + vTaskDelay(1); + } + if (!threads[i].stack) { + threads[i].stack = heap_caps_malloc(MGS_STACK_FOR(i), MALLOC_CAP_INTERNAL); + } + if (!threads[i].tcb) { + threads[i].tcb = heap_caps_malloc(sizeof(StaticTask_t), MALLOC_CAP_INTERNAL); + } + if (!threads[i].stack || !threads[i].tcb) { + printf("[thread] OpenTh: no internal RAM for slot %d (stack %p tcb %p)\n", i, + threads[i].stack, (void*)threads[i].tcb); + free(threads[i].stack); + free(threads[i].tcb); + threads[i].stack = NULL; + threads[i].tcb = NULL; + threads[i].in_use = 0; + return (unsigned long)-1; + } + /* Pinned to core 0, without exception. The vblank tick preempts the game + * on this same core (esp32_vblank.c); letting FreeRTOS float an mts task + * to core 1 re-creates the cross-core race where ChangeThFromISR reads a + * stale current_thread and resumes a task parked mid-ChangeTh -- which is + * how a second copy of Main() once started running interleaved with the + * first. */ + threads[i].handle = xTaskCreateStaticPinnedToCore( + thread_trampoline, "mgs_th", MGS_STACK_FOR(i), &threads[i], 5, + (StackType_t*)threads[i].stack, threads[i].tcb, 0); + if (!threads[i].handle) { + printf("[thread] OpenTh: xTaskCreateStatic failed for slot %d\n", i); + threads[i].in_use = 0; + return (unsigned long)-1; + } + /* the task parks itself on entry (see thread_trampoline); nothing to do + * here but hand back the slot as the PSX thread id */ + return (unsigned long)i; +} + +/* Called from the tick every ~48 s. uxTaskGetStackHighWaterMark reports the + * smallest free margin a task has ever had, in words, so the deepest the game + * has actually gone is MGS_THREAD_STACK minus that -- the number the stack + * size should be sized from, plus headroom for a path not exercised yet. */ +void Mgs_ReportThreadStacks(void) { + int i; + unsigned total_free = 0; + printf("[stack] used of %d B:", MGS_THREAD_STACK); + for (i = 0; i < MGS_MAX_THREADS; i++) { + if (threads[i].in_use && threads[i].handle) { + unsigned free_b = + (unsigned)uxTaskGetStackHighWaterMark(threads[i].handle) * + sizeof(StackType_t); + printf(" t%d=%u/%u", i, (unsigned)MGS_STACK_FOR(i) - free_b, (unsigned)MGS_STACK_FOR(i)); + total_free += free_b; + } + } + printf(" | reclaimable %u B\n", total_free); +} + +long CloseTh(unsigned long thread) { + int i = (int)thread; + if (i < 0 || i >= MGS_MAX_THREADS || !threads[i].in_use) { + return 0; + } + threads[i].in_use = 0; + if (threads[i].handle) { + vTaskDelete(threads[i].handle); + threads[i].handle = 0; + } + return 1; +} + +long ChangeTh(unsigned long thread) { + int target = (int)thread; + int self; + + /* FIRST, before anything -- even the validation and its printf. Any window + * where the tick can preempt while this function has read current_thread + * but not finished the switch lets the vblank act on half-truth: it once + * fired inside the printf below, saw the OLD current, "resumed" the very + * task that was mid-switch, and the mts bookkeeping skewed one task over + * -- the ready bit of the woken task was consumed by the wrong task and + * the whole scheduler starved without crashing. */ + change_in_flight = 1; + self = current_thread; + + if (target < 0 || target >= MGS_MAX_THREADS || !threads[target].in_use) { + change_in_flight = 0; + printf("[thread] ChangeTh: bad tid %d\n", target); + return 0; + } + if (target == self) { + change_in_flight = 0; + return 1; + } + + /* ChangeTh must NEVER return to its caller until somebody switches back: + * mts_receive() sets up its wait and then yields through here, so a + * ChangeTh that returns early makes the receive look like it completed + * with no sender -- which is exactly the "RECV ?? SRC -2" loop. + * + * The very first call comes from whatever task bootstrapped the game + * (app_main's), which is not yet one of ours. Adopt it into a slot so it + * can be parked and resumed like any other. */ + if (self < 0) { + int i; + for (i = 0; i < MGS_MAX_THREADS; i++) { + if (!threads[i].in_use) { + break; + } + } + if (i < MGS_MAX_THREADS) { + threads[i].handle = xTaskGetCurrentTaskHandle(); + threads[i].entry = 0; + threads[i].in_use = 1; + self = i; + } + } + +#ifndef MGS_ESPBOX /* once per frame in steady state: noise on the emulator's console */ + printf("[thread] ChangeTh %d -> %d\n", self, target); +#endif + if (self >= 0 && self < MGS_MAX_THREADS) { + threads[self].crit = psyz_critical_depth; + } + psyz_critical_depth = threads[target].crit; + current_thread = target; + vTaskResume(threads[target].handle); + + if (self >= 0 && self < MGS_MAX_THREADS && threads[self].handle) { + /* cleared just before parking: from here on current_thread is + * consistent and the tick may preempt again */ + change_in_flight = 0; + vTaskSuspend(NULL); + } else { + change_in_flight = 0; + /* nothing to park: spin the scheduler so the target actually runs */ + vTaskDelay(1); + } + return 1; +} + +/* the scheduler asks who is running */ +int Mgs_CurrentThread(void) { return current_thread; } + +/* Resume a PSX thread WITHOUT parking the caller. + * + * On the console, waking a task from the vblank interrupt was a side effect of + * writing the new TCB into the kernel header: the interrupt return then landed + * in the woken task. There is no equivalent here, and plain ChangeTh() is wrong + * because it suspends whoever calls it -- and the caller is the vblank tick, + * which must keep running. So: make the target runnable and return. The next + * cooperative ChangeTh() from game code will settle who actually holds the CPU. + */ +long ChangeThFromISR(unsigned long thread) { + int target = (int)thread; + static int noisy = 12; + if (change_in_flight) { + /* a cooperative ChangeTh is mid-update; preempting on a half-written + * current_thread is how two tasks once ended up runnable. Skip this + * frame; the caller reverts its bookkeeping and the next vblank + * retries. */ + return 0; + } + if (target < 0 || target >= MGS_MAX_THREADS || !threads[target].in_use) { + if (noisy > 0) { + noisy--; + printf("[isr] ChangeThFromISR: bad tid %d\n", target); + } + return 0; + } + if (noisy > 0) { + noisy--; + printf("[isr] resume tid %d (was %d)\n", target, current_thread); + } + /* mts is cooperative: exactly one of its tasks may be runnable. Resuming + * the target without parking the outgoing one leaves two of them runnable + * on a preemptive RTOS, they trample each other's scheduler state, and + * nothing progresses. The vblank tick is not an mts task, so it is free to + * suspend the outgoing one on their behalf. */ + if (current_thread >= 0 && current_thread < MGS_MAX_THREADS && + current_thread != target && threads[current_thread].handle) { + vTaskSuspend(threads[current_thread].handle); + /* swap the per-task interrupt mask exactly like ChangeTh does */ + threads[current_thread].crit = psyz_critical_depth; + } + psyz_critical_depth = threads[target].crit; + current_thread = target; + vTaskResume(threads[target].handle); + return 1; +} + +/* a parking spot for game tasks the port retires (e.g. the SIO console) */ +void Mgs_TaskSleepForever(void) { + for (;;) { + vTaskDelay(pdMS_TO_TICKS(1000)); + } +} + +#ifdef MGS_ESPBOX +/* esp-box-emu: pause / resume / stop --------------------------------------- + * The emulator menu suspends the game: only the running PSX thread is live + * (the others park themselves in ChangeTh), so that is the one to suspend and + * resume. Stop deletes every task the scheduler created and forgets the slots; + * the task that bootstrapped the game (adopted in ChangeTh, entry == 0) is + * owned by the caller and is skipped. */ +static int paused_thread = -1; + +void Mgs_ThreadsPause(void) { + paused_thread = current_thread; + if (paused_thread >= 0 && paused_thread < MGS_MAX_THREADS && threads[paused_thread].handle) { + vTaskSuspend(threads[paused_thread].handle); + } +} + +void Mgs_ThreadsResume(void) { + if (paused_thread >= 0 && paused_thread < MGS_MAX_THREADS && threads[paused_thread].handle && + threads[paused_thread].in_use) { + vTaskResume(threads[paused_thread].handle); + } + paused_thread = -1; +} + +void Mgs_ThreadsStopAll(void) { + int i; + for (i = 0; i < MGS_MAX_THREADS; i++) { + if (threads[i].handle && threads[i].entry) { + vTaskDelete(threads[i].handle); + } + threads[i].handle = 0; + threads[i].entry = 0; + threads[i].in_use = 0; + threads[i].crit = 0; + } + /* the storage goes back only once FreeRTOS has retired the deleted + * tasks (the idle task does that), never in the same breath */ + vTaskDelay(pdMS_TO_TICKS(5)); + for (i = 0; i < MGS_MAX_THREADS; i++) { + free(threads[i].stack); + free(threads[i].tcb); + threads[i].stack = NULL; + threads[i].tcb = NULL; + } + current_thread = -1; + change_in_flight = 0; + paused_thread = -1; +} +#endif /* MGS_ESPBOX */ diff --git a/components/mgs/mgs/port/esp32_vblank.c b/components/mgs/mgs/port/esp32_vblank.c new file mode 100644 index 00000000..5f6e40b1 --- /dev/null +++ b/components/mgs/mgs/port/esp32_vblank.c @@ -0,0 +1,305 @@ +/* The vertical blank, which the PSX gave games for free. + * + * On the console a vblank interrupt fires 60 times a second and the BIOS calls + * whatever the game registered through VSyncCallback(). MGS's mts scheduler is + * built on it: mts_wait_vbl() parks a task until the callback has advanced the + * frame counter, and the whole boot sequence blocks on that. Without a tick + * here the game reaches mts_init_vsync() and never comes back -- which is + * exactly the black screen. + * + * WHERE the tick runs matters as much as that it runs. On the console the + * vblank interrupted THE SAME CPU the game was executing on, so the scheduler + * work inside the callback could never race the task it was preempting. The + * first version of this file ran the tick on core 1 while the game ran on + * core 0, and ChangeThFromISR's read of current_thread raced the ChangeTh in + * flight on the other core -- once in a while it revived the parked boot + * thread mid-stack, and a SECOND copy of Main() started interleaving with the + * first. So: every mts task is pinned to core 0 (esp32_threads.c) and this + * tick is pinned to core 0 at higher priority. Preemption on one core is + * exactly an interrupt, and the race cannot exist. + * + * The LCD scanout stays on core 1: it only reads VRAM, and at 80 MHz SPI it + * would otherwise eat a third of the game's frame budget. + */ + +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#ifdef MGS_HUNT_ROGUE_WRITER +#include "esp_cpu.h" +#endif +#include +#include + +extern void (*g_VsyncCallback)(void); /* psyz/src/psyz/libetc.c */ + +/* NTSC field rate. The tick is 1 ms, so 16 ms is the closest whole number; + * that is 62.5 Hz rather than 59.94, which is near enough for pacing and can + * be swapped for a hardware timer once frames are actually being drawn. */ +#define VBLANK_PERIOD_MS 16 + +/* mts game tasks run at priority 5 (esp32_threads.c); the tick must preempt + * them the way the hardware interrupt did */ +#define VBLANK_TICK_PRIO 10 + +unsigned mgs_vblank_count; + +static void vblank_tick_task(void* arg) { + TickType_t next = xTaskGetTickCount(); + (void)arg; + for (;;) { + vTaskDelayUntil(&next, pdMS_TO_TICKS(VBLANK_PERIOD_MS)); + mgs_vblank_count++; +#ifdef MGS_HUNT_ROGUE_WRITER + /* One known-corrupted pack keeps landing at the same heap address + * every boot. Arm a store watchpoint on its command word AFTER the + * stage load settles: the shade pass never touches this obj, so the + * next writer to trip it IS the rogue -- its PC comes out in the + * panic backtrace. Core 0, where every mts task runs. */ + /* Trap the load-time writer red-handed. Watch word 3 of the known + * pack (its xy0): the legitimate initializer only touches len/code + * and the four rgb words, and this obj's per-frame transform never + * runs -- the ONLY store to xy0 is the rogue planting its color + * pointer. Armed early, first trip names the culprit. */ + if (mgs_vblank_count == 60u) { + esp_cpu_set_watchpoint(0, (void*)0x3c1cd4d4, 4, + ESP_CPU_WATCHPOINT_STORE); + printf("[hunt] watchpoint armed on xy0 (0x3c1cd4d4)\n"); + } +#endif + { + /* sample the buttons first: the BIOS also read pads on vsync, + * before the game's callback saw the frame */ + extern void Mgs_PadsUpdate(void); + Mgs_PadsUpdate(); + } + /* Deliver scheduler wakes ONLY while mts sits in its IDLE task. + * + * The idle task is Konami's own design: when every real task waits, + * mts transfers into task 11 (the SIO console), whose polling spin is + * the one piece of code that touches no scheduler state -- on the + * console it ran with interrupts enabled precisely so the vblank + * could preempt it. Any OTHER task may be mid-way through editing + * ready masks or wait chains, and preempting those is what produced + * the intermittent boot wedges: every fix that narrowed a window + * (change_in_flight, critical sections) shrank the failure rate + * without reaching zero. Restricting the preemptive switch to the + * idle spin removes the race by construction, and costs nothing: a + * frame-paced game passes through idle every frame, so wakes are at + * most a fraction of a frame late -- exactly a masked interrupt. */ + { + extern volatile int psyz_critical_depth; + extern int mts_active_task_800C0DB0; + static unsigned t_all, t_idle, t_crit, t_fired; + t_all++; + if (mts_active_task_800C0DB0 == 11) t_idle++; + if (psyz_critical_depth == 0) t_crit++; + if (g_VsyncCallback && psyz_critical_depth == 0 && + mts_active_task_800C0DB0 == 11 /* MTS_TASK_IDLE */) { + t_fired++; + g_VsyncCallback(); + } +#ifdef MGS_BOARD_XIAO + /* The controls get their own, much faster cadence. + * + * The scheduler summary below reports once per 3000 ticks -- 48 + * seconds -- and a stick is pushed and released in a fraction of + * that. Reporting the controls at that rate showed them at rest + * every single time and read as a dead axis. Two seconds, and the + * deflection is the largest seen since the last line rather than + * the value at the instant of printing. */ + if ((t_all % 120u) == 0u) { + extern int mgs_stick_dbg[8]; + extern int mgs_adc_loops; + printf("[stick] raw %4d,%4d center %4d,%4d " + "max-dev %4d,%4d ladder %4d pad %04X loops %d\n", + mgs_stick_dbg[0], mgs_stick_dbg[1], + mgs_stick_dbg[2], mgs_stick_dbg[3], + mgs_stick_dbg[4], mgs_stick_dbg[5], + mgs_stick_dbg[6], (unsigned)mgs_stick_dbg[7], + mgs_adc_loops); + mgs_stick_dbg[4] = mgs_stick_dbg[5] = mgs_stick_dbg[7] = 0; + } +#endif + if ((t_all % 1800u) == 0u) { + extern void mts_dbg_dump(void); + mts_dbg_dump(); + } + if ((t_all % 3000u) == 0u) { + /* mgs_where is the last MGS_WHERE breadcrumb any task set. It + * rides along on this line because this line is the one that + * still gets out when a wedged game has filled the console + * buffer with its own printfs -- which is exactly when knowing + * where a task stopped is worth anything. */ + { + extern const char *mgs_where; + printf("[tick] all %u idle %u crit0 %u fired %u active %d" + " | ultimo: %s\n", + t_all, t_idle, t_crit, t_fired, + mts_active_task_800C0DB0, mgs_where); + } + { + /* How much of each 20 KB mts stack is actually used? + * + * Eight slots at 20 KB is 160 KB against roughly 160 KB of + * free internal SRAM -- the reservation is the single + * biggest consumer of the memory that would otherwise hold + * the hot texture pages, which is where the frame rate is + * stuck (207 cycles per textured pixel, nearly all of it + * PSRAM latency). 20 KB was a guess made when a stack + * overflow would have been impossible to diagnose; this + * says what it can safely become. */ + extern void Mgs_ReportThreadStacks(void); + Mgs_ReportThreadStacks(); + } + } + } + } +} + +/* The PSX scans out continuously: whatever is in VRAM reaches the screen every + * field, whether or not the game is drawing. Presenting only when the game + * calls VSync(0) means a boot that has not reached its frame loop shows + * nothing at all -- and a black panel then says "the LCD path is broken" when + * really nothing has been drawn yet. */ +/* Present when the picture CHANGES, not on a timer. + * + * Each present reads ~120 KB out of PSRAM strided across rows -- twice the + * data cache -- so a redundant one does not merely waste SPI time, it evicts + * the texture and CLUT working set the rasterizer is using on the other core. + * A fixed period cannot win: the game's frame time moves with the scene, from + * two vblanks in a corridor to four in an open room, so any constant either + * sends the same buffer twice or holds a finished one back. DG_SwapFrame bumps + * mgs_frame_seq when it flips, which is exactly the moment there is something + * new to send. + * + * The poll still runs every vblank so a new frame waits at most 16 ms, and the + * fallback below keeps the PSX's "whatever is in VRAM reaches the screen" + * behaviour: a boot that has not reached its frame loop -- or a game that has + * wedged -- still shows what is in the buffer instead of a black panel that + * wrongly reads as a broken LCD path. */ +#define SCANOUT_POLL_MS VBLANK_PERIOD_MS +#define SCANOUT_IDLE_MS 250 + +unsigned mgs_frame_seq; + +static void scanout_task(void* arg) { + TickType_t next = xTaskGetTickCount(); + extern unsigned short* g_RawVram; + void lcd_present(const unsigned short* src); + extern DISPENV g_dispenv; + unsigned last_seq = 0; + int idle_ms = 0; + (void)arg; + for (;;) { + vTaskDelayUntil(&next, pdMS_TO_TICKS(SCANOUT_POLL_MS)); + if (mgs_frame_seq == last_seq) { + idle_ms += SCANOUT_POLL_MS; + if (idle_ms < SCANOUT_IDLE_MS) { + continue; + } + } + last_seq = mgs_frame_seq; + idle_ms = 0; + lcd_present(&g_RawVram[(unsigned)g_dispenv.disp.y * 1024u + + (unsigned)g_dispenv.disp.x]); + +#ifdef MGS_VRAM_MAP + /* Did the game DRAW, and is anything IN the window we scan out? + * Decides between "renders but wrong origin" and "never draws" + * without touching the game. Off by default now that it does: the + * sweep itself reads PSRAM strided, and it buries the game's own + * console output. */ + { + static unsigned beat; + if (++beat >= 300u) { + extern unsigned sotn_push_count, sotn_exeque_count, + sotn_pkt_drops; + unsigned nz = 0, y, x; + const unsigned short* base = + &g_RawVram[(unsigned)g_dispenv.disp.y * 1024u + + (unsigned)g_dispenv.disp.x]; + for (y = 0; y < 240; y += 8) { + for (x = 0; x < 320; x += 4) { + if (base[y * 1024u + x]) { + nz++; + } + } + } + { + /* Where in VRAM is the picture actually landing? An 8x4 + * coarse map of the whole 1024x512 framebuffer memory: + * each cell is the count of non-black samples. If the + * game draws somewhere other than the two 320-wide + * buffers we scan out, this shows it immediately. */ + unsigned cy, cx, yy, xx, cell; + printf("[vram] disp %d,%d exeq %u map:\n", + g_dispenv.disp.x, g_dispenv.disp.y, + sotn_exeque_count); + for (cy = 0; cy < 4; cy++) { + printf("[vram] "); + for (cx = 0; cx < 8; cx++) { + cell = 0; + for (yy = cy * 128; yy < (cy + 1) * 128; yy += 8) { + for (xx = cx * 128; xx < (cx + 1) * 128; + xx += 4) { + if (g_RawVram[yy * 1024u + xx]) { + cell++; + } + } + } + printf("%5u", cell); + } + printf("\n"); + } + (void)nz; (void)sotn_push_count; (void)sotn_pkt_drops; + } + beat = 0; + } + } +#endif /* MGS_VRAM_MAP */ + } +} + +static int started; +static TaskHandle_t tick_task, lcd_task; + +void Mgs_StartVblank(void) { + if (started) { + return; + } + started = 1; + if (xTaskCreatePinnedToCore(vblank_tick_task, "mgs_vbl", 4096, NULL, + VBLANK_TICK_PRIO, &tick_task, 0) != pdPASS) { + printf("[vblank] could not start the tick task\n"); + started = 0; + return; + } + if (xTaskCreatePinnedToCore(scanout_task, "mgs_lcd", 4096, NULL, 6, &lcd_task, + 1) != pdPASS) { + printf("[vblank] could not start the scanout task\n"); + } + printf("[vblank] %d ms tick on core 0 (prio %d), scanout on core 1 " + "(present on frame change)\n", + VBLANK_PERIOD_MS, VBLANK_TICK_PRIO); +} + +#ifdef MGS_ESPBOX +/* esp-box-emu: the emulator menu freezes the game by holding the vblank tick + * (nothing wakes an mts task without it) and the scanout. */ +void Mgs_PauseVblank(void) { + if (tick_task) vTaskSuspend(tick_task); + if (lcd_task) vTaskSuspend(lcd_task); +} + +void Mgs_ResumeVblank(void) { + if (lcd_task) vTaskResume(lcd_task); + if (tick_task) vTaskResume(tick_task); +} + +void Mgs_StopVblank(void) { + if (tick_task) { vTaskDelete(tick_task); tick_task = NULL; } + if (lcd_task) { vTaskDelete(lcd_task); lcd_task = NULL; } + started = 0; +} +#endif /* MGS_ESPBOX */ diff --git a/components/mgs/mgs/port/include/gtemac.h b/components/mgs/mgs/port/include/gtemac.h new file mode 100644 index 00000000..adee3e35 --- /dev/null +++ b/components/mgs/mgs/port/include/gtemac.h @@ -0,0 +1,18 @@ +#ifndef GTEMAC_SHIM_H +#define GTEMAC_SHIM_H +#include +#include +/* PSY-Q's rand() returns 0..0x7fff and the game scales its results by 15 + * bits; the hosted libc's is 31-bit. Every C file of the component sees this + * (the header is force-included); psyz_port.c undefines it to reach libc. */ +int psyz_rand(void); +#define rand psyz_rand +/* PSX 1KiB scratchpad at 0x1F800000: a plain array on any other target */ +extern unsigned long psyz_scratchpad[256]; +#define getScratchAddr(n) ((unsigned long*)&psyz_scratchpad[(n)]) +/* PsyQ inline forms that differ from psyz's plain-C prototypes */ +#define gte_NormalClip(a, b, c, out) (*(int*)(out) = NormalClip((a), (b), (c))) +MATRIX* psyz_CompMatrix(MATRIX* m0, MATRIX* m1, MATRIX* m2); +#define gte_CompMatrix(a, b, c) psyz_CompMatrix((MATRIX*)(a), (MATRIX*)(b), (MATRIX*)(c)) +VECTOR* ApplyMatrixLV(MATRIX* m, VECTOR* v0, VECTOR* v1); +#endif diff --git a/components/mgs/mgs/port/include/libpress.h b/components/mgs/mgs/port/include/libpress.h new file mode 100644 index 00000000..cae1fff4 --- /dev/null +++ b/components/mgs/mgs/port/include/libpress.h @@ -0,0 +1,3 @@ +#ifndef LIBPRESS_H +#define LIBPRESS_H +#endif diff --git a/components/mgs/mgs/port/include/libsn.h b/components/mgs/mgs/port/include/libsn.h new file mode 100644 index 00000000..7b655b56 --- /dev/null +++ b/components/mgs/mgs/port/include/libsn.h @@ -0,0 +1,3 @@ +#ifndef LIBSN_H +#define LIBSN_H +#endif diff --git a/components/mgs/mgs/port/mgs_printf.h b/components/mgs/mgs/port/mgs_printf.h new file mode 100644 index 00000000..d7c0d404 --- /dev/null +++ b/components/mgs/mgs/port/mgs_printf.h @@ -0,0 +1,54 @@ +/* Telemetry that never waits for a listener. + * + * The Waveshare board talks over a UART: characters go down the wire whether + * anyone is attached or not, and a write never blocks for long. The handheld + * talks over the chip's native USB, and that is a different contract -- when no + * host is draining the endpoint the buffer fills and the write BLOCKS. + * + * This game prints enormously: the decompiled sources narrate their own + * progress, and the port adds per-frame telemetry on top. On USB that turns + * into a cooperative scheduler stopped dead inside a printf, with every mts + * task frozen behind it -- the board booted, mounted the card, found all 96 + * stages, started the sound task and then went silent forever. Nothing + * crashed, and from the outside it looked like a hang in the sound code. + * + * So printing is best-effort: format into a small buffer and hand it to the + * USB driver with a zero timeout. With a terminal attached everything appears + * as before; with nobody listening the bytes are dropped and the game runs at + * full speed, which is what a console should do anyway. + * + * Included into every game translation unit through -include, so the game's own + * printf calls are covered without editing 700 files. + */ +#ifndef MGS_PRINTF_H +#define MGS_PRINTF_H + +#ifdef MGS_BOARD_XIAO + +#include +#include + +int Mgs_Printf(const char* fmt, ...); + +/* The game calls printf; route it here. Undefined first because some headers + * in this tree have their own ideas about the name. */ +#undef printf +#define printf Mgs_Printf + +#endif /* MGS_BOARD_XIAO */ + +/* A breadcrumb that survives a saturated console. + * + * When the game is wedged, its own printfs are exactly what cannot get out -- + * the buffer is full of them. This writes a pointer to a string literal into + * one global, costing a single store, and the vblank tick prints it with its + * own once-per-3000-ticks line. That line does get through, so the last place + * a stuck task reached is always visible. */ +#ifdef __psyz +extern const char *mgs_where; +#define MGS_WHERE(s) (mgs_where = (s)) +#else +#define MGS_WHERE(s) ((void)0) +#endif + +#endif /* MGS_PRINTF_H */ diff --git a/components/mgs/mgs/port/platform_headless.c b/components/mgs/mgs/port/platform_headless.c new file mode 100644 index 00000000..fc6bfda1 --- /dev/null +++ b/components/mgs/mgs/port/platform_headless.c @@ -0,0 +1,217 @@ +/* The platform layer PSY-Z expects the host to supply, in its headless form. + * + * These are the same seventeen hooks the SOTN port implements in + * esp32/main/esp_shim.c and esp_fileapi.c; this file is the MGS equivalent, + * written so a link succeeds and a boot can be traced over the console before + * any display, pad or audio hardware is wired up. Swapping in the real ESP-IDF + * implementations later is a file substitution, not a redesign. + * + * Deliberately NOT included here: anything that touches the LCD, the GPIO + * buttons or I2S. Those belong in an esp32/ component next to this one, so the + * portable core stays buildable on a PC for A/B comparison. + */ + +#include +#include +#include +#include +#include +#include +#include + +/* -------------------------------------------------------------------------- + * Video: the vsync hook PSY-Z's VSync() sits on + * + * Returns elapsed time in units of the PSX's ~16.7 ms field, which is what the + * game's frame pacing reads. Headless, so it just counts. + * ------------------------------------------------------------------------ */ + +unsigned int mgs_frame_count; /* readable from a debugger / telemetry */ + +int Psyz_VideoVSync(int mode) { + if (mode < 0) { + /* VSync(-1) asks how many vertical blanks have elapsed since boot. On + * the PSX that counter is driven by the hardware and runs whether or + * not the game is drawing -- mts_VSyncCallback reads it to decide + * which sleeping tasks are due. Returning the number of PRESENTED + * frames instead means the clock never moves when nothing is being + * drawn, so every mts_wait_vbl() sleeps forever: the boot stops dead + * at the first daemon that waits for a frame. */ + extern unsigned mgs_vblank_count; + return (int)mgs_vblank_count; + } + if (mode == 0) { + mgs_frame_count++; + if ((mgs_frame_count % 60u) == 1u) { + extern DISPENV g_dispenv; + printf("[vsync] frame %u disp %d,%d %dx%d\n", mgs_frame_count, + g_dispenv.disp.x, g_dispenv.disp.y, + g_dispenv.disp.w, g_dispenv.disp.h); + } + } + return (int)mgs_frame_count; +} + +/* -------------------------------------------------------------------------- + * Pad + * ------------------------------------------------------------------------ */ + +/* Filled by whatever input source exists: serial keys during bring-up, GPIO + * buttons on the board. Zero means "nothing pressed", which is what a headless + * boot wants. */ +unsigned short mgs_pad_state[2]; + +void Psyz_PadsPoll(void) { + /* no input source yet; the pad buffers keep whatever mgs_pad_state holds */ +} + +/* -------------------------------------------------------------------------- + * Audio + * ------------------------------------------------------------------------ */ + +int Psyz_AudioInit(void) { + return 0; /* no mixer yet; the SPU emulation runs but produces no output */ +} + +void Psyz_AudioLock(void) {} +void Psyz_AudioUnlock(void) {} + +/* -------------------------------------------------------------------------- + * Logging + * ------------------------------------------------------------------------ */ + +LOG_LEVEL psyz_logLevel = LOG_LEVEL_W; + +void psyz_log(unsigned int level, const char* file, unsigned int line, + const char* func, const char* fmt, ...) { + static const char tag[] = "DIWE"; + va_list ap; + (void)file; + printf("[%c] %s:%u: ", level < sizeof(tag) - 1 ? tag[level] : '?', + func ? func : "?", line); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); + printf("\n"); +} + +/* -------------------------------------------------------------------------- + * Low-level file API + * + * PSY-Z macro-renames open/read/write/lseek/close, which is why these live in + * their own translation unit in the SOTN port: any file that sees those macros + * gets both the libc declarations and the wrapper bodies rewritten. Here the + * bodies are self-contained, so there is nothing to knot. + * ------------------------------------------------------------------------ */ + +/* The game asks for "cdrom:\MGS\NAME;1"; map that onto a plain directory so a + * PC run and a microSD run take the same path. */ +static const char* mgs_data_root = "."; + +void Mgs_SetDataRoot(const char* path) { + if (path) mgs_data_root = path; +} +const char* Mgs_GetDataRoot(void) { return mgs_data_root; } + +void Mgs_DataPath(const char* devname, char* out, unsigned n); + +static void translate(const char* devname, char* out, size_t n) { + const char* p = devname; + size_t i = 0; + if (!p) { + out[0] = 0; + return; + } + /* strip the "cdrom:" prefix and the ";1" version suffix, and turn the + * PSX's backslashes into ordinary separators */ + if (!strncmp(p, "cdrom:", 6)) p += 6; + if (n == 0) return; + i = (size_t)snprintf(out, n, "%s/", mgs_data_root); + if (i >= n) { + /* the data root alone does not fit: snprintf returned the length it + * wanted, not what it wrote, so it is not an index into out */ + out[n - 1] = 0; + return; + } + for (; *p && i + 1 < n; p++) { + if (*p == ';') break; + out[i++] = (*p == '\\') ? '/' : *p; + } + out[i] = 0; +} + +/* the virtual CD needs the same cdrom: -> /flash mapping */ +void Mgs_DataPath(const char* devname, char* out, unsigned n) { + translate(devname, out, (size_t)n); +} + +int psyz_open(const char* devname, int flag) { + char path[256]; + FILE* f; + translate(devname, path, sizeof(path)); + f = fopen(path, (flag & 1) ? "r+b" : "rb"); + if (!f) { + printf("[fs] open failed: %s (from %s)\n", path, + devname ? devname : "(null)"); + return -1; + } + return (int)(size_t)f; +} + +int psyz_close(int fd) { + if (fd <= 0) return -1; + return fclose((FILE*)(size_t)fd); +} + +long psyz_read(long fd, void* buf, long n) { + if (fd <= 0) return -1; + return (long)fread(buf, 1, (size_t)n, (FILE*)(size_t)fd); +} + +long psyz_write(long fd, void* buf, long n) { + if (fd <= 0) return -1; + return (long)fwrite(buf, 1, (size_t)n, (FILE*)(size_t)fd); +} + +long psyz_ioctl(long fd, long com, long arg) { + (void)fd; (void)com; (void)arg; + return 0; +} + +long psyz_lseek(long fd, long offset, long flag) { + if (fd <= 0) return -1; + if (fseek((FILE*)(size_t)fd, offset, (int)flag) != 0) return -1; + return ftell((FILE*)(size_t)fd); +} + +/* -------------------------------------------------------------------------- + * Memory-card style file helpers + * ------------------------------------------------------------------------ */ + +struct DIRENTRY; + +struct DIRENTRY* my_firstfile(const char* pattern, struct DIRENTRY* entry) { + (void)pattern; + (void)entry; + return 0; /* no card enumeration yet */ +} + +struct DIRENTRY* my_nextfile(struct DIRENTRY* entry) { + (void)entry; + return 0; +} + +long my_erase(const char* path) { + char full[256]; + translate(path, full, sizeof(full)); + return remove(full) == 0 ? 1 : 0; +} + +long my_format(const char* fs) { + (void)fs; + return 1; +} + +/* Budget for mts assertions: see the note in source/mts/mts_new.h. Enough to + * see the first failures, few enough to leave the boot log readable. */ +int mts_assert_budget = 12; diff --git a/components/mgs/mgs/port/psyq_compat.c b/components/mgs/mgs/port/psyq_compat.c new file mode 100644 index 00000000..72ac4d32 --- /dev/null +++ b/components/mgs/mgs/port/psyq_compat.c @@ -0,0 +1,316 @@ +/* PSY-Q SDK entry points that PSY-Z does not provide, implemented for the + * portable build of Metal Gear Solid. + * + * Split by confidence: + * - the geometry helpers are real implementations, transcribed from the + * documented PSY-Q semantics and using PSY-Z's own fixed-point primitives + * - the kernel thread/exec API is stubbed and LOUD: MGS's mts layer sits on + * top of it, so the right time to implement it for real is when a boot + * actually reaches one of these, and the abort message says which + */ + +#include +#include +#include +#include +#include + +/* -------------------------------------------------------------------------- + * Geometry + * ------------------------------------------------------------------------ */ + +/* m2 = m0 x m1 (rotation part only; PSY-Q leaves the translation alone) */ +MATRIX* MulMatrix0(MATRIX* m0, MATRIX* m1, MATRIX* m2) { + MATRIX r; + int i, j; + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + int acc = m0->m[i][0] * m1->m[0][j] + m0->m[i][1] * m1->m[1][j] + + m0->m[i][2] * m1->m[2][j]; + r.m[i][j] = (short)(acc >> 12); + } + } + for (i = 0; i < 3; i++) { + for (j = 0; j < 3; j++) { + m2->m[i][j] = r.m[i][j]; + } + } + return m2; +} + +/* m1 = m0 x m1, in place */ +MATRIX* MulMatrix2(MATRIX* m0, MATRIX* m1) { return MulMatrix0(m0, m1, m1); } + +/* current GTE rotation matrix = itself x m0 + * + * PSY-Q's reference says m0 = R_gte x m0 with the GTE left alone, so this is + * suspect. But changing it to that -- together with dropping SetRotMatrix from + * the _gte builders, which the analysis said had to be done as a pair -- made + * every character skeleton fall apart on the board. Reverted, and left as it + * was until the limb transform chain in libdg/screen.c and libdg/pos.c is read + * properly rather than patched from a reference manual. */ +MATRIX* MulRotMatrix(MATRIX* m0) { + MATRIX cur; + ReadRotMatrix(&cur); + MulMatrix0(&cur, m0, &cur); + SetRotMatrix(&cur); + return m0; +} + +MATRIX* ReadRotMatrix(MATRIX* m) { + Psyz_GteReadRotMatrix(m); + return m; +} + +/* cross product; the "12" form keeps the 20.12 fixed-point scale */ +VECTOR* OuterProduct0(VECTOR* v0, VECTOR* v1, VECTOR* v2) { + long x = v0->vy * v1->vz - v0->vz * v1->vy; + long y = v0->vz * v1->vx - v0->vx * v1->vz; + long z = v0->vx * v1->vy - v0->vy * v1->vx; + v2->vx = x; + v2->vy = y; + v2->vz = z; + return v2; +} + +VECTOR* OuterProduct12(VECTOR* v0, VECTOR* v1, VECTOR* v2) { + long x = v0->vy * v1->vz - v0->vz * v1->vy; + long y = v0->vz * v1->vx - v0->vx * v1->vz; + long z = v0->vx * v1->vy - v0->vy * v1->vx; + v2->vx = x >> 12; + v2->vy = y >> 12; + v2->vz = z >> 12; + return v2; +} + +/* Componentwise square. The trailing 0 is PSY-Q's shift factor, and it means + * NO shift -- exactly like OuterProduct0 above, and unlike the *12 spellings + * which are the ones that come back >>12. + * + * This shifted, so every square was 4096x too small. Everything that measures + * a distance in this game goes Square0 -> sum -> SquareRoot0, so every length + * came back divided by 64 (sqrt of 4096). GV_LenVec3 then scales a vector by + * len2/len1 with that len1 as the divisor, which made the movement step 64x + * too large: Snake advanced three frames, overshot into a wall, and the + * collision response threw him back -- a sawtooth that never made progress. + * The same wrong lengths also reached enemy sight ranges, bullet hits and the + * lighting attenuation in libdg/light.c. */ +VECTOR* Square0(VECTOR* v0, VECTOR* v1) { + v1->vx = v0->vx * v0->vx; + v1->vy = v0->vy * v0->vy; + v1->vz = v0->vz * v0->vz; + return v1; +} + +VECTOR* Square12(VECTOR* v0, VECTOR* v1) { + v1->vx = (v0->vx * v0->vx) >> 12; + v1->vy = (v0->vy * v0->vy) >> 12; + v1->vz = (v0->vz * v0->vz) >> 12; + return v1; +} + +/* The *_gte spellings build the matrix and load it into the GTE in one go. + * Rotation order follows the name; RotMatrix is Z*Y*X like PSY-Q's default. */ +static void build_rot(SVECTOR* r, MATRIX* m, int order_yxz) { + int sx = rsin(r->vx), cx = rcos(r->vx); + int sy = rsin(r->vy), cy = rcos(r->vy); + int sz = rsin(r->vz), cz = rcos(r->vz); + MATRIX mx, my, mz, t; + + mx.m[0][0] = 4096; mx.m[0][1] = 0; mx.m[0][2] = 0; + mx.m[1][0] = 0; mx.m[1][1] = cx; mx.m[1][2] = -sx; + mx.m[2][0] = 0; mx.m[2][1] = sx; mx.m[2][2] = cx; + + my.m[0][0] = cy; my.m[0][1] = 0; my.m[0][2] = sy; + my.m[1][0] = 0; my.m[1][1] = 4096; my.m[1][2] = 0; + my.m[2][0] = -sy; my.m[2][1] = 0; my.m[2][2] = cy; + + mz.m[0][0] = cz; mz.m[0][1] = -sz; mz.m[0][2] = 0; + mz.m[1][0] = sz; mz.m[1][1] = cz; mz.m[1][2] = 0; + mz.m[2][0] = 0; mz.m[2][1] = 0; mz.m[2][2] = 4096; + + if (order_yxz) { + MulMatrix0(&my, &mx, &t); + MulMatrix0(&t, &mz, m); + } else { + MulMatrix0(&mz, &my, &t); + MulMatrix0(&t, &mx, m); + } +} + +/* The _gte spellings build the matrix USING the GTE, and PSY-Q leaves the + * result sitting in the GTE's rotation register as a documented side effect -- + * that is the whole point of the variant, and callers on the character path + * (libdg/screen.c) rely on it. Removing the SetRotMatrix here was tried and + * scrambled every skeleton on screen: legs and arms reversed, head inside the + * chest. Leave it. */ +MATRIX* RotMatrix_gte(SVECTOR* r, MATRIX* m) { + build_rot(r, m, 0); + SetRotMatrix(m); + return m; +} + +MATRIX* RotMatrixZYX_gte(SVECTOR* r, MATRIX* m) { + build_rot(r, m, 0); + SetRotMatrix(m); + return m; +} + +MATRIX* RotMatrixYXZ_gte(SVECTOR* r, MATRIX* m) { + build_rot(r, m, 1); + SetRotMatrix(m); + return m; +} + +/* -------------------------------------------------------------------------- + * GPU odds and ends + * ------------------------------------------------------------------------ */ + +/* GPU_printf is defined by the PSY-Q decomp's libgpu/sys.c */ + +/* the 2-suffixed forms are the non-blocking variants; with a software GPU the + * transfer is synchronous either way */ +int LoadImage2(RECT* rect, u_long* p) { + LoadImage(rect, p); + return 0; +} + +int StoreImage2(RECT* rect, u_long* p) { + StoreImage(rect, p); + return 0; +} + +void SetDrawStp(DR_STP* p, int stp) { + /* Build the mask-bit primitive; the rasterizer already honours DR_STP. + * + * The GP0 word goes in code[0], NOT at raw word index 1. On the console a + * DR_STP is one packed tag followed by the command, so [1] was right; PSY-Z + * splits the tag into { u_long tag; u_long len }, which makes [1] the + * LENGTH field. Writing 0xE6000xxx there told the queue reader the packet + * was 3.8 billion words long -- it dropped the packet and then resumed + * mid-primitive, reading vertices and pointers as commands. That is the + * screen going white, and it fires on combat because muzzle flashes and + * smoke are what turn semi-transparency on. + * + * psyz hit exactly this in its own setDrawTPage and fixed it there (see the + * [2] and the comment in libgpu.h); SetDrawStp lives in MGS's compat layer + * so it never got the same treatment. Use the struct member rather than an + * index, so it cannot drift again if the header changes. */ + setlen(p, 1); + p->code[0] = 0xE6000000 | (stp & 3); +} + +u_long* BreakDraw(void) { + return 0; /* abort the in-flight DMA; nothing is in flight here */ +} + +void StUnSetRing(void) { /* CD streaming ring teardown; no ring yet */ } + +/* -------------------------------------------------------------------------- + * Pad + * ------------------------------------------------------------------------ */ + +/* PadInitDirect lives in esp32/main/esp_input.c: it registers the game's + * receive buffers so the board's buttons can be written into them */ + + +void PadStopCom(void) {} + +int PadInfoAct(int port, int actno, int term) { + (void)port; + (void)actno; + (void)term; + return 0; /* no actuators (rumble) modelled */ +} + +int SafetyCheck(void) { return 0; } + +/* -------------------------------------------------------------------------- + * Kernel: threads and executable control + * + * MGS's mts task layer is built on these. They are deliberately loud rather + * than silently wrong: whichever one a boot reaches first is the next thing to + * implement, and a silent stub here would produce a hang with no clue. + * ------------------------------------------------------------------------ */ + +static void unimplemented(const char* what) { + printf("\n*** PSX kernel call not implemented yet: %s()\n", what); + printf("*** MGS's mts task layer needs this. Implement it before the " + "boot can proceed past here.\n"); + abort(); +} + +/* OpenTh / CloseTh / ChangeTh now live in port/esp32_threads.c, + * implemented on FreeRTOS tasks. */ + +unsigned long GetGp(void) { return 0; } + +unsigned long GetSp(void) { + int here; + return (unsigned long)(size_t)&here; +} + +long SetConf(unsigned long ev, unsigned long tcb, unsigned long sp) { + (void)ev; + (void)tcb; + (void)sp; + return 0; /* kernel table sizing; ours are fixed */ +} + + + +void SetMem(unsigned long n) { (void)n; /* 2 or 8 MB RAM declaration */ } + +void LoadExec(char* name, unsigned long s_addr, unsigned long s_size) { + (void)s_addr; + (void)s_size; + printf("\n*** LoadExec(\"%s\"): the game asked to chain-load another " + "executable.\n", name ? name : "(null)"); + printf("*** On the PSX this is how discs swap; here it means the boot " + "path reached a disc change.\n"); + abort(); +} + +/* On the PSX these masked interrupts; mts wraps every piece of scheduler + * bookkeeping in them. Off-console the "interrupt" is the vblank tick task, + * which checks this depth and defers its callback while any critical section + * is open -- leaving these as no-ops let the tick preempt mts mid-update and + * the cooperative state tore itself apart, wedging the boot at random points. + * A counter, not a flag: the sections nest. */ +extern volatile int psyz_critical_depth; /* psyz/src/psyz/libapi.c */ + +void SwEnterCriticalSection(void) { psyz_critical_depth = 1; } +void SwExitCriticalSection(void) { psyz_critical_depth = 0; } + +/* linker-provided markers the startup code references */ +unsigned char _bss_orgend[1]; +void _96_init(void) {} + +/* -------------------------------------------------------------------------- + * Odds and ends the link still asks for + * ------------------------------------------------------------------------ */ + +/* Root counters: the PSX's three hardware timers. MGS reads RCnt1 to measure + * how long the GPU took; a monotonic tick is enough until the ESP-IDF timer is + * wired in. */ +long GetRCnt(unsigned long spec) { + extern unsigned int mgs_frame_count; + (void)spec; + return (long)(mgs_frame_count * 1000u); +} + +/* MGS's own pad bring-up hook; the platform layer owns the actual polling. */ +void MyPadInit(void) {} + +/* PC* host-file access comes from the PSY-Q decomp's libapi/ */ + +/* MGS implements PCinit/PCopen/PCread/PCclose itself in libfs/select.c but not + * these three; they are the write side of the PSY-Q host-file link, unused off + * a development PSX. */ +int PCcreat(const char* name, int perm) { (void)name; (void)perm; return -1; } +int PCwrite(int fd, char* buf, int len) { + (void)fd; (void)buf; (void)len; return -1; +} +int PClseek(int fd, int offset, int flag) { + (void)fd; (void)offset; (void)flag; return -1; +} diff --git a/components/mgs/mgs/port/psyz_port.c b/components/mgs/mgs/port/psyz_port.c new file mode 100644 index 00000000..1d7eb65b --- /dev/null +++ b/components/mgs/mgs/port/psyz_port.c @@ -0,0 +1,181 @@ +/* Support definitions for building MGS against PSY-Z instead of the PSY-Q SDK. + * Kept in one translation unit so nothing here depends on engine headers. */ + +#include +#include + +/* The PSX's 1 KiB scratchpad at 0x1f800000. The engine addresses it through + * SCRPAD_ADDR (see source/include/psxdefs.h), so relocating it is just this + * array. Aligned generously: the engine casts it to several structs. */ +unsigned long psyz_scratchpad[256] __attribute__((aligned(16))); + +/* PSY-Q's CompMatrix multiplies three matrices; PSY-Z declares a different + * signature under the same name, so MGS reaches this wrapper instead. + * + * m2 = m0 o m1, which for the translation means m0's ROTATION applied to m1's + * offset, then m0's own offset added. Getting that rotation is the whole point: + * this is what walks a skeleton down its chain, so if the offset is added + * unrotated every bone still spins correctly about a joint that never moves -- + * limbs detach from the torso and knees bend the wrong way, while the pose + * looks perfect the moment every angle is zero. + * + * That is exactly what happened here. The first version wrote the translation + * through a (VECTOR*) cast onto MATRIX::t, which is a strict-aliasing + * violation; at -O2 GCC is entitled to assume those stores cannot be read back + * through the long[3] member, and it deleted the rotation outright. The + * disassembly showed a CompMatrix with no multiply anywhere in it. Keep the + * arithmetic here in plain longs with no type punning so it cannot happen + * again. The accumulator is 64-bit to mirror the GTE's 44-bit MACs -- a world + * offset of 100k units times 4096, summed three times, does not fit in 32. + */ +MATRIX* CompMatrix(MATRIX* m0, MATRIX* m1, MATRIX* m2) +{ + MATRIX tmp; + long long x = m1->t[0], y = m1->t[1], z = m1->t[2]; + + MulMatrix0(m0, m1, &tmp); + + tmp.t[0] = (long)(((m0->m[0][0] * x + m0->m[0][1] * y + m0->m[0][2] * z) >> + 12) + m0->t[0]); + tmp.t[1] = (long)(((m0->m[1][0] * x + m0->m[1][1] * y + m0->m[1][2] * z) >> + 12) + m0->t[1]); + tmp.t[2] = (long)(((m0->m[2][0] * x + m0->m[2][1] * y + m0->m[2][2] * z) >> + 12) + m0->t[2]); + + *m2 = tmp; + return m2; +} + +#ifdef MGS_ESPBOX +/* PSY-Q's 15-bit rand() on top of the libc one (see gtemac.h) */ +#undef rand +int psyz_rand(void) +{ + return rand() & 0x7fff; +} + +/* port/include/gtemac.h routes gte_CompMatrix() to this name */ +MATRIX* psyz_CompMatrix(MATRIX* m0, MATRIX* m1, MATRIX* m2) +{ + return CompMatrix(m0, m1, m2); +} +#endif + +/* Long-vector form of ApplyMatrix: absent from PSY-Z. */ +VECTOR* ApplyMatrixLV(MATRIX* m, VECTOR* v0, VECTOR* v1) +{ + long x = v0->vx, y = v0->vy, z = v0->vz; + v1->vx = (m->m[0][0] * x + m->m[0][1] * y + m->m[0][2] * z) >> 12; + v1->vy = (m->m[1][0] * x + m->m[1][1] * y + m->m[1][2] * z) >> 12; + v1->vz = (m->m[2][0] * x + m->m[2][1] * y + m->m[2][2] * z) >> 12; + return v1; +} + +/* PC* host-file stubs moved to psyq_compat.c */ + +/* -------------------------------------------------------------------------- + * The 804 KB of PSX main RAM the game lays its heaps out in. + * + * A plain array rather than a heap block, because RESIDENT_BOTTOM is used as a + * static initializer in libgv/resident.c -- the address has to be a link-time + * constant. EXT_RAM_BSS_ATTR puts it in PSRAM, which is right for the biggest + * single reservation in the port: internal RAM is what the thread stacks need, + * and this block is only ever data, never code and never a stack. + * ------------------------------------------------------------------------ */ + +#include "esp_attr.h" + +#ifdef MGS_ESPBOX +/* esp-box-emu: allocated from the emulator's 4MB ROM block while the game runs + * (src/mgs.cpp); size MGS_MAIN_RAM_BYTES. */ +unsigned char* mgs_main_ram; +#else +EXT_RAM_BSS_ATTR unsigned char mgs_main_ram[0x80000 + 0xC9000 + 0x20000]; +#endif + +/* Skeleton measurement switches, read by libdg/screen.c's DG_ApplyRots. + * mgs_dbg_bindpose: force every joint angle to zero so the assembled pose is + * a pure sum of the model's own offsets, checkable against the retail data. + * mgs_dbg_skel: how many skeletal objects still to dump. */ +/* M1 answered: with the pose frozen, all sixteen joints landed exactly where + * the retail data says, det was 4096 everywhere and every joint sat exactly + * its own bone-length from its parent. The assembly chain is proven correct on + * this board, so the deformation has to come from the ANGLES. M2 asks the same + * three questions with the real animation running. */ +int mgs_dbg_bindpose = 0; +int mgs_dbg_skel = 0; + +/* Turntable angle, in the GTE's 4096-per-revolution units. Advanced once per + * frame by libdg/display.c and read by libdg/screen.c when MGS_TURNTABLE is + * defined; inert otherwise. */ +int mgs_dbg_spin = 0; + +/* How many level triggers have actually been crossed. Counted at the hit site + * in libhzd/trap.c: the first version printed a local that nothing ever + * incremented, so it reported zero all session and read as hard evidence that + * the trigger system was dead. A statistic nobody increments is worse than no + * statistic -- it invents a finding. */ +int mgs_dbg_trap_hits = 0; + +/* Somewhere for the codec's face group to live. + * + * The Colonel's calls draw from FACE.DAT, and the group the game asks for is + * 417792 bytes -- measured on the board, and nearly three times what the + * decompiled code's own comment suggests. GV_PACKET_MEMORY0 cannot find that, + * so the allocation returned NULL, the loader read over a null pointer and the + * parser dereferenced it: that crash is the whole reason the codec screen was + * switched off and no conversation was ever seen. + * + * The PSX had no other pool to offer. This board does: 8 MB of PSRAM, of which + * the game's own heaps use about 1.2. Half a megabyte reserved here is cheap, + * and unlike a heap block it cannot fail at the worst moment. It is data the + * renderer only reads, so PSRAM latency is the right trade. + * + * radiomes.c uses this as the fallback when the pool says no, and must not + * hand it back to GV_FreeMemory -- freeing a static buffer would corrupt the + * allocator's free list far away from here and long after. */ +#ifdef MGS_ESPBOX +unsigned char* mgs_face_group; /* esp-box-emu: from the ROM block, MGS_FACE_GROUP_BYTES */ +const unsigned mgs_face_group_size = 0x80000; +#else +EXT_RAM_BSS_ATTR unsigned char mgs_face_group[0x80000]; +const unsigned mgs_face_group_size = sizeof(mgs_face_group); +#endif + +/* Who last blanked the screen. libdg/display.c prints it when DG_SwapFrame + * takes its skip branch; the four sites that park DG_UnDrawFrameCount at + * 0x7fff0000 stamp it. Guessing which one it was cost two flashes. */ +const char *mgs_dbg_undraw_src = "(none)"; + +/* Where the sound task last got to. + * + * SOUND_INT spins and never yields, and its own printfs cannot say where it is + * because the console drops them once the buffer fills. This is read and + * printed by the vblank tick instead, whose one line per 3000 ticks does get + * through -- a breadcrumb that survives a saturated console. */ +const char *mgs_where = "(nada)"; + +#ifdef MGS_BOARD_XIAO +/* See port/mgs_printf.h for why this exists. Kept here rather than in a file + * of its own because it must NOT have the printf macro applied to itself. */ +#undef printf +#include +#include "driver/usb_serial_jtag.h" + +int Mgs_Printf(const char* fmt, ...) { + /* One shared buffer, and that is deliberate: this is called from several + * mts tasks, but they are cooperative and run one at a time on core 0, so + * they cannot interleave inside it. A per-call stack buffer would be + * 512 bytes of the mts stacks, which are the scarcest memory here. */ + static char line[512]; + va_list ap; + va_start(ap, fmt); + int n = vsnprintf(line, sizeof line, fmt, ap); + va_end(ap); + if (n <= 0) return n; + if (n > (int)sizeof line - 1) n = (int)sizeof line - 1; + /* Zero timeout: write what fits, drop the rest, never wait. */ + usb_serial_jtag_write_bytes(line, n, 0); + return n; +} +#endif diff --git a/components/mgs/mgs/port/soft_render.c b/components/mgs/mgs/port/soft_render.c new file mode 100644 index 00000000..186e3cfc --- /dev/null +++ b/components/mgs/mgs/port/soft_render.c @@ -0,0 +1,94 @@ +/* The software GPU backend for the portable Metal Gear Solid build. + * + * This compiles the SAME portable core the SOTN port and the PC harness use + * (soft_raster.inc.c, standalone flavour: it brings its own GP0 packet parser + * and the Draw_* state machine), so a divergence between desktop and board is + * always the platform layer and never the raster maths. + * + * VRAM is the PSX's 1 MB frame buffer as 512 rows of 1024 RGB5551 halfwords. + * On the ESP32-S3 this wants EXT_RAM_BSS_ATTR to land in PSRAM; that attribute + * is applied by the ESP-IDF build, not here, so this file stays target-neutral. + */ + +#include +#include +#include +#include +#include + +#include "internal.h" +#include "draw.h" + +/* The frame buffer the rasterizer writes into: 512 x 1024 halfwords = 1 MB. + * Far too big for the S3's internal SRAM, so it goes to PSRAM. Needs + * CONFIG_SPIRAM_ALLOW_BSS_SEG_EXTERNAL_MEMORY. */ +#if defined(MGS_ESPBOX) +#include "esp_attr.h" +u16* g_RawVram; /* esp-box-emu: from the ROM block, VRAM_W * VRAM_H halfwords */ +#elif defined(ESP_PLATFORM) +#include "esp_attr.h" +EXT_RAM_BSS_ATTR u16 g_RawVram[VRAM_H * VRAM_W]; +#else +u16 g_RawVram[VRAM_H * VRAM_W]; +#endif + +#define WARNF(fmt, ...) printf("[soft] " fmt "\n", ##__VA_ARGS__) +#define SOFT_RASTER_STANDALONE +#define SOFT_RASTER_NO_FALLBACK +#ifndef SOFT_RASTER_IRAM +/* The per-pixel loops must not execute from flash. Left empty they run XIP + * through the instruction cache, and a triangle filler is exactly the shape + * that thrashes it: a tight loop competing with the texel and framebuffer + * traffic it is generating. The SOTN port on this same board sets IRAM_ATTR + * here and reaches 60 fps; MGS had it empty. */ +#define SOFT_RASTER_IRAM IRAM_ATTR +#endif +#define SOFT_CLUT_CACHE +#include "platform/soft_raster.inc.c" + +/* The scanout asks which rows of VRAM the drawing area covers, so it only + * copies what the game actually draws. */ +void Soft_VisibleRows(int* y0, int* y1) { + *y0 = st.y0; + *y1 = st.y1; +} + +/* Where scanout should read from. MGS drives the display through libdg's own + * double buffer, so this is filled in when the display path is wired up; until + * then the top-left of VRAM is a safe answer. */ +void Mgs_DisplayOrigin(int* x, int* y) { + if (x) *x = 0; + if (y) *y = 0; +} + +/* Bind the rasterizer to VRAM and open the drawing area to the whole frame + * buffer. + * + * psyz calls this from its own ResetGraph, but MGS goes through the PSY-Q + * decomp's ResetGraph instead, which never reaches it -- so the first + * ClearImage faulted on a NULL `vram` (EXCVADDR 0x00000000, StoreProhibited). + * Bind at constructor time as well, exactly as the SOTN port had to. */ +__attribute__((constructor)) static void bind_vram_early(void) { + void Draw_Reset(void); + Draw_Reset(); +} + +void Draw_Reset(void) { + vram = g_RawVram; + st.x0 = 0; + st.y0 = 0; + st.x1 = VRAM_W - 1; + st.y1 = VRAM_H - 1; + st.ox = st.oy = 0; +} + +/* Frame telemetry psyz's libgpu.c references (SOTN defines these in its own + * backend shell). Readable over the console to see what the GPU stream did. */ +unsigned sotn_prim_cycles; +unsigned sotn_prim_census[8]; +unsigned sotn_prim_cycles_by[8]; +unsigned sotn_poly_kind[4]; +unsigned sotn_move_count; +unsigned sotn_exeque_count; +unsigned sotn_push_count; +unsigned sotn_pkt_drops; diff --git a/components/mgs/mgs/port/stage_stubs.c b/components/mgs/mgs/port/stage_stubs.c new file mode 100644 index 00000000..f9900feb --- /dev/null +++ b/components/mgs/mgs/port/stage_stubs.c @@ -0,0 +1,84 @@ +/* Stubs for code that lives in stages we are not linking yet. + * + * Three kinds, all distinguishable at runtime: + * - actor constructors from overlays that still contain assembly. A boot + * reaching one is trying to spawn something from a stage we did not + * link; it says so and carries on rather than faulting. + * - SPU internals: audio, not wired up yet. + * - MDEC: the movie decoder, likewise. + */ + +#include + +static void missing_actor(const char* name) { + static int warned; + if (warned < 20) { + printf("[stub] actor %s is from an unlinked overlay\n", name); + warned++; + } +} + +void* NewCamera_800CF388(void) { missing_actor("NewCamera_800CF388"); return 0; } +void* NewCape(void) { missing_actor("NewCape"); return 0; } +void* NewCountdownGcl(void) { missing_actor("NewCountdownGcl"); return 0; } +void* NewCrow(void) { missing_actor("NewCrow"); return 0; } +void* NewDog(void) { missing_actor("NewDog"); return 0; } +void* NewFog(void) { missing_actor("NewFog"); return 0; } +void* NewHind(void) { missing_actor("NewHind"); return 0; } +void* NewHindBoss(void) { missing_actor("NewHindBoss"); return 0; } +void* NewJeepDrum(void) { missing_actor("NewJeepDrum"); return 0; } +void* NewJeepEnemy(void) { missing_actor("NewJeepEnemy"); return 0; } +void* NewJeepScroll(void) { missing_actor("NewJeepScroll"); return 0; } +void* NewNinjaBoss(void) { missing_actor("NewNinjaBoss"); return 0; } +void* NewOcelotBoss(void) { missing_actor("NewOcelotBoss"); return 0; } +void* NewRope(void) { missing_actor("NewRope"); return 0; } +void* NewStage11GDemo(void) { missing_actor("NewStage11GDemo"); return 0; } +void* NewStage11Objects(void) { missing_actor("NewStage11Objects"); return 0; } +void* NewWire(void) { missing_actor("NewWire"); return 0; } + +/* audio: the SPU emulation is not wired up yet */ +int SpuGetKeyStatus(void) { return 0; } +int SpuReserveReverbWorkArea(void) { return 0; } +int SpuSetIRQ(void) { return 0; } +int SpuSetIRQAddr(void) { return 0; } +int SpuSetPitchLFOVoice(void) { return 0; } +int SpuSetReverbDepth(void) { return 0; } +int SpuSetReverbVoice(void) { return 0; } +int _SpuIsInAllocateArea_(void) { return 0; } +int _spu_FiDMA(void) { return 0; } +int _spu_FsetRXX(void) { return 0; } +int _spu_FsetRXXa(void) { return 0; } +int _spu_Fw(void) { return 0; } + +int _spu_inTransfer; +int _spu_init(void) { return 0; } +int _spu_mem_mode_plus; +int _spu_mem_mode_unitM; +int _spu_transMode; +/* These two are NOT functions: psyz declares them as callback POINTERS + * (libspu_private.h:190). Stubbing them as functions put them in read-only + * flash, so the first `_spu_transferCallback = 0` inside SpuClearReverbWorkArea + * stored into .text and faulted the cache. Kind matters as much as name. */ +void (* volatile _spu_transferCallback)(); +void (* volatile _spu_IRQCallback)(); +int _spu_tsa; + +/* MDEC: full-motion video */ +int DecDCToutSync(void) { return 0; } +int DecDCTvlc2(void) { return 0; } +int DecDCTvlcBuild(void) { return 0; } + +/* per-stage data referenced by tables in the main executable */ +int AN_DamageSmoke1; +int d18a_snake18_800D4E94; +int s15c_dyncon_800D8C9C; + +/* Sound-driver internals (libsnd). The SPU emulation exists but the sequenced + * music driver is not wired up; these keep the link honest until it is. */ +int VBLANK_MINUS; +int _snd_ev_flag; +int _snd_openflag; +int _spu_Fr; +int _svm_sreg; +void _SsMarkCallback(void) {} +void _SsVmInit(void) {} diff --git a/components/mgs/mgs/port/stage_union.c b/components/mgs/mgs/port/stage_union.c new file mode 100644 index 00000000..c12f24ea --- /dev/null +++ b/components/mgs/mgs/port/stage_union.c @@ -0,0 +1,275 @@ +/* One actor table for every stage this build can run, and the list of which + * stages those are. GENERATED -- edit port/gen_stage_union.py, not this file. + * + * On the console each stage overlay carried its own _StageCharacterEntries[] + * and only one overlay was resident, so the same symbol name never collided. + * Natively there are no overlays: exactly one table can link, and whichever + * stage it belonged to was the only one whose actors could spawn -- with + * s07a's table linked, select1's script asked for the stage-select actor, + * found nothing, and ended having drawn nothing. That was the black screen + * after "end scenario". + * + * The per-stage split only existed to save PSX memory. Here the union is a + * couple of hundred pointers, so link them all; GM_GetCharaID scans to + * CHARA_END and finds whichever class the running stage asks for. + * + * Stages left out must never be entered: their tables still hold raw MIPS + * addresses, and calling one jumps into whatever happens to sit at + * 0x800dxxxx. Mgs_ResolveStage refuses them by name using the list at the + * bottom. While only a handful of stages were packed into flash the missing + * file was refusal enough, but a board reading the full 68.6 MB STAGE.DIR off + * a card can reach all 96 and needs the explicit answer. + */ + +#define DECLARE_NEWCHARA_PROTOS +#include "charalst.h" + +CHARA _StageCharacterEntries[] = { + /* select1 */ + CHARA_STAGESELECT, + + /* demosel */ + CHARA_DEMOSEL, + + /* roll */ + CHARA_ENDINGROLL, + CHARA_MOVIE, + CHARA_ED_TELOP, + CHARA_B757_ED_TELOP, + + /* s07a */ + CHARA_ASIOTOKUN, + CHARA_FADEIO, + CHARA_COMMANDER, + CHARA_WATCHER, + CHARA_MERYL7, + CHARA_DOOR, + CHARA_WALL, + CHARA_MIRROR, + CHARA_ELEVPANEL, + CHARA_DYNWALL, + CHARA_TEXTURE, + CHARA_CINEMA, + CHARA_CAT_IN, + + /* s00a */ + CHARA_RIPPLES, + CHARA_PADCONTROL, + CHARA_PADVIBRATE, + CHARA_SNEBREATH, + CHARA_ENV_SOUND, + CHARA_CAMERASHAKE, + CHARA_PADDEMO, + CHARA_ASIATOKUN, + CHARA_SHAKEMODEL, + CHARA_PATOLAMP, + CHARA_WT_AREA, + CHARA_SMOKE, + CHARA_EMITTER, + CHARA_ELEVATOR, + CHARA_MOUSE, + CHARA_RSURFACE, + CHARA_TELOP, + CHARA_BUBBLE, + CHARA_FEWDAMAGE, + + /* s01a */ + CHARA_TOBCNT, + CHARA_MOTIONSE, + CHARA_DEMOCANCEL, + CHARA_GASEFFECT, + CHARA_DEMODOLL, + CHARA_TRUCKTRAP, + CHARA_CAMERA, + CHARA_SPHERE, + CHARA_BLINKTEX, + CHARA_OBJECT, + CHARA_SEARCHLIGHT, + CHARA_SNOW, + + /* s02a */ + CHARA_INTRUDECAM, + CHARA_BUB_D_SN, + CHARA_PUT_OBJECT, + CHARA_DUCTMOUSE, + + /* abst */ + CHARA_LOAD_DATA, + CHARA_ABST, + CHARA_ABST_DEMO1, + CHARA_ABST_DEMO2, + CHARA_AB_CH, + + /* camera */ + CHARA_JPEG, + + /* change */ + CHARA_CDCHANGE, + + /* d00a */ + CHARA_WT_VIEW, + + /* d03a */ + CHARA_UJI, + + /* d18a */ + CHARA_SNAKE18, + CHARA_SMKTRGT, + CHARA_WAKE, + + /* opening */ + CHARA_TEXSCROLL, + + /* option */ + CHARA_OPT, + + /* preope */ + CHARA_PREOPE, + + /* s02b */ + CHARA_SHUTTER, + + /* s02c */ + CHARA_KIKENKUN, + CHARA_WSURFACE, + CHARA_CENSOR, + CHARA_GASDAMAGE, + + /* s03a */ + CHARA_FINDTRAP, + CHARA_MOSAIC, + CHARA_CAMERAGUN, + CHARA_HIYOKO, + CHARA_RADARPOINT, + + /* s03b */ + CHARA_TORTURE, + CHARA_TR_BED, + CHARA_TR_OCELOT, + CHARA_BOXALL, + + /* s03c */ + CHARA_JOHNNY, + CHARA_PRISONOTACON, + CHARA_PRISONNINJA, + CHARA_PRISONSNAKE, + CHARA_PRISONSNAKE2, + + /* s04a */ + CHARA_DMYWALL, + CHARA_DMYFLOOR, + + /* s04c */ + CHARA_REVOLVER04, + CHARA_C4WIRE, + CHARA_AT, + CHARA_CAPE, + CHARA_VOICESYS, + CHARA_CLAYMORE, + CHARA_LIFEUP, + + /* s07c */ + CHARA_MERYL72, + CHARA_9FFD_2ND, + + /* s08a */ + CHARA_PANEL, + CHARA_PILOTLAMP, + CHARA_ELECDAMAGE, + CHARA_ELECFLOOR, + CHARA_BREAK_OBJECT, + CHARA_PLASMA, + CHARA_DEATHSPARK, + CHARA_REDALERT, + CHARA_GLASS, + CHARA_FLR_SPA, + + /* s08c */ + CHARA_WALLSPARK, + CHARA_BLOOD_POOL, + CHARA_BLOOD_POOL2, + + /* s11c */ + CHARA_DYNFLOOR, + CHARA_SCN_MARK, + CHARA_FOG, + CHARA_RASEN, + CHARA_RASEN_EL, + + /* s11e */ + CHARA_ZAKO11ECOM, + CHARA_ZAKO11E, + + /* s11g */ + CHARA_1787_HIND, + CHARA_S11_OBJS, + CHARA_11G_DEMO, + + /* s11i */ + CHARA_SNOWSTORM, + CHARA_ZAKO11FCOM, + CHARA_ZAKO11F, + CHARA_HIND2, + + /* s12a */ + CHARA_WOLF, + + /* s12c */ + CHARA_DOG, + + /* s13a */ + CHARA_CRANE, + CHARA_LIFT, + CHARA_DOOR2, + CHARA_FURNACE, + + /* s15b */ + CHARA_FALLSPLASH, + + /* s15c */ + CHARA_CONTAINER, + CHARA_CROW, + + /* s16a */ + CHARA_KEY_ITEM, + CHARA_WT_AREA2, + CHARA_ITEM_DOT, + + /* s16d */ + CHARA_BELONG, + + /* s19b */ + CHARA_COUNTDOWN2, + CHARA_JEEPSCROLL, + CHARA_JEEPDRUM, + CHARA_JEEP_EMY, + + /* select */ + CHARA_VIBEDITOR, + + /* sound */ + CHARA_SOUNDTEST, + + /* title */ + CHARA_OPEN, + CHARA_VRWINDOW, + CHARA_SAFETY, + CHARA_FONTTEXT, + CHARA_END +}; + +/* The stages whose actor table is represented above. "init" is not here: it is + * the boot stage and spawns no actors of its own, so it is always allowed. */ +const char *const Mgs_PlayableStages[] = { + "abst", "camera", "change", "d00a", "d01a", "d03a", + "d11c", "d16e", "d18a", "demosel", "ending", "opening", + "option", "preope", "roll", "s00a", "s01a", "s02a", + "s02b", "s02c", "s02d", "s03a", "s03b", "s03c", + "s03e", "s04a", "s04c", "s06a", "s07a", "s07c", + "s08a", "s08c", "s08cr", "s11c", "s11e", "s11g", + "s11i", "s12a", "s12c", "s13a", "s15b", "s15c", + "s16a", "s16b", "s16c", "s16d", "s19b", "s20a", + "select", "select1", "select2", "select3", "select4", "selectd", + "sound", "title", + 0 +}; diff --git a/components/mgs/mgs/port/virtual_cd.c b/components/mgs/mgs/port/virtual_cd.c new file mode 100644 index 00000000..c1903890 --- /dev/null +++ b/components/mgs/mgs/port/virtual_cd.c @@ -0,0 +1,341 @@ +/* A virtual CD over ordinary files. + * + * MGS's libfs does not open files by name. It reads the disc's ISO9660 + * directory once to learn which SECTOR each file starts at, and from then on + * everything -- STAGE.DIR's stage table, the movie index, streamed audio -- + * addresses data as absolute sector numbers. There is no CD here and no ISO + * directory, so that first read found garbage and took the board down. + * + * Rather than teach the whole of libfs about filenames, give it the disc it + * expects: hand out a synthetic sector range per file, and translate every + * sector read back into a seek on the corresponding file. Everything above + * this layer then works unmodified, including the trimmed STAGE.DIR that + * port/pack_stagedir.py builds -- its stage offsets are already sectors + * relative to the start of the file, which is exactly what the game assumes. + */ + +#include +#include + +#include "esp_heap_caps.h" + +#include "libfs/libfs.h" +#include "libfs/cdbios.h" + +#define CD_SECTOR 2048 + +/* Ranges are spaced far enough apart that no real file can ever overlap the + * next one; the retail disc's biggest file is DEMO.DAT at ~258 MB = 126k + * sectors, so a million sectors per slot leaves plenty of headroom. */ +#define SLOT_SECTORS 1000000u + +static const char* const cd_names[FS_MAX_FILEID] = { + "STAGE.DIR", "RADIO.DAT", "FACE.DAT", "ZMOVIE.STR", + "VOX.DAT", "DEMO.DAT", "BRF.DAT", +}; + +/* Where the disc data is read from, and why it differs per medium. + * + * Flash partition: copied into PSRAM at boot. Reading sectors straight out of + * it worked, but every read is a flash operation, and a flash operation on this + * chip switches the instruction cache off on both cores. Any other core + * executing from flash at that instant takes a cache fault -- which is what + * killed the boot inside the SPU init, with the sound task running while a CD + * read was in flight. Copying once, before a single game task exists, means + * there is never a flash access again afterwards, and a sector read becomes a + * memcpy. The cost is PSRAM: the partition holds at most 8 MB and the copy has + * to fit alongside everything else the game allocates. + * + * microSD: left on the card and read on demand. The card is a SPI peripheral, + * not the execute-in-place flash, so a read touches no cache and needs no copy + * -- which lifts the size limit entirely. That is the only way the full 68.6 MB + * STAGE.DIR, and with it every stage the game can ask for, fits at all. + * + * Whichever medium a file comes from, everything above this layer is unchanged: + * cd_fetch serves a sector from the copy or from the file. */ +static unsigned char* cd_data[FS_MAX_FILEID]; +static FILE* cd_files[FS_MAX_FILEID]; +static long cd_sizes[FS_MAX_FILEID]; +/* where each file-backed slot's handle currently sits, so a sequential run + * does not re-seek; -1 means unknown */ +static long cd_pos[FS_MAX_FILEID]; +static int cd_ready; + +/* the platform layer knows where the data lives */ +extern void Mgs_DataPath(const char* name, char* out, unsigned n); +extern const char* Mgs_GetDataRoot(void); + +/* The card shares SPI2 with the panel and the driver cannot arbitrate the two + * on its own -- see the note in esp32/main/lcd_esp32.c. Every access to a + * file-backed slot is bracketed by this; a PSRAM-backed one is a memcpy and + * needs nothing. */ +extern void Mgs_SpiBusTake(void); +extern void Mgs_SpiBusGive(void); + +/* the task structure the real CD BIOS drives; the callback contract is + * written in terms of it, so the virtual drive fills in the same fields */ +extern CDBIOS_TASK cd_bios_task_800B4E58; + +void Mgs_CdInit(void) { + int i; + if (cd_ready) { + return; + } + cd_ready = 1; + /* the scanout task is already running by now, so even opening files has to + * take the bus */ + Mgs_SpiBusTake(); + for (i = 0; i < FS_MAX_FILEID; i++) { + char path[160]; + char devname[64]; + FILE* f; + + /* the card wins when it carries the file: it is both bigger and + * cheaper to read from, so a board with a card ignores whatever was + * packed into flash for the same name */ + /* esp-box-emu: the files live directly in the data root (/sdcard/mgs) */ + snprintf(path, sizeof(path), "%s/%s", Mgs_GetDataRoot(), cd_names[i]); + f = fopen(path, "rb"); + if (f) { + fseek(f, 0, SEEK_END); + cd_sizes[i] = ftell(f); + fseek(f, 0, SEEK_SET); + cd_files[i] = f; + cd_pos[i] = 0; + printf("[vcd] %-10s %8ld bytes on microSD, sector %u\n", + cd_names[i], cd_sizes[i], (unsigned)(i * SLOT_SECTORS)); + continue; + } + + snprintf(devname, sizeof(devname), "cdrom:\\MGS\\%s;1", cd_names[i]); + Mgs_DataPath(devname, path, sizeof(path)); + f = fopen(path, "rb"); + if (!f) { + cd_sizes[i] = 0; + printf("[vcd] %-10s ABSENT (not on /sd or %s)\n", cd_names[i], path); + continue; + } + fseek(f, 0, SEEK_END); + cd_sizes[i] = ftell(f); + fseek(f, 0, SEEK_SET); + + cd_data[i] = heap_caps_malloc((size_t)cd_sizes[i], MALLOC_CAP_SPIRAM); + if (cd_data[i]) { + size_t got = fread(cd_data[i], 1, (size_t)cd_sizes[i], f); + fclose(f); + printf("[vcd] %-10s %8ld bytes -> PSRAM, sector %u\n", cd_names[i], + (long)got, (unsigned)(i * SLOT_SECTORS)); + } else { + /* no room: keep it on flash and accept the cache hazard for this + * one -- still better than the file being missing outright */ + cd_files[i] = f; + cd_pos[i] = 0; + printf("[vcd] %-10s %8ld bytes stays on flash, sector %u\n", + cd_names[i], cd_sizes[i], (unsigned)(i * SLOT_SECTORS)); + } + } + Mgs_SpiBusGive(); +} + +static void cd_open_all(void) { Mgs_CdInit(); } + +/* esp-box-emu: release everything Mgs_CdInit() opened so the game can be + * started again later. */ +void Mgs_CdDeinit(void) { + int i; + for (i = 0; i < FS_MAX_FILEID; i++) { + if (cd_files[i]) { fclose(cd_files[i]); cd_files[i] = NULL; } + if (cd_data[i]) { free(cd_data[i]); cd_data[i] = NULL; } + cd_sizes[i] = 0; + cd_pos[i] = 0; + } + cd_ready = 0; +} + +/* Not every file on the retail disc is on the board -- ZMOVIE.STR alone is + * 250 MB of full-motion video. Callers need to be able to ask, because libfs + * happily parses whatever a missing file returns and acts on the result. */ +int Mgs_CdFilePresent(int fileid) { + cd_open_all(); + if (fileid < 0 || fileid >= FS_MAX_FILEID) { + return 0; + } + return cd_data[fileid] != 0 || cd_files[fileid] != 0; +} + +/* Build the position table libfs would otherwise read off the disc. */ +int FS_CdMakePositionTable(char* buffer, FS_FILE_INFO* finfo) { + int i; + (void)buffer; + cd_open_all(); + for (i = 0; i < FS_MAX_FILEID; i++) { + finfo[i].name = cd_names[i]; + finfo[i].pos = (u_int)(i * SLOT_SECTORS); + } + /* disc 1; the game uses this to pick its executable name */ + return 0; +} + +/* -------------------------------------------------------------------------- + * The CD BIOS itself + * ------------------------------------------------------------------------ */ + +static int cd_last_result; + +int CDBIOS_Reset(void) { + cd_open_all(); + return 0; +} + +/* Copy one sector's worth of words out of the virtual disc. + * Returns 0 when the request falls past the end of the file. */ +static int cd_fetch(void* dst, unsigned sector, unsigned words) { + unsigned slot = sector / SLOT_SECTORS; + long offset = (long)(sector % SLOT_SECTORS) * CD_SECTOR; + unsigned bytes = words * 4u; + + if (slot >= FS_MAX_FILEID || offset >= cd_sizes[slot]) { + return 0; + } + if (offset + (long)bytes > cd_sizes[slot]) { + unsigned have = (unsigned)(cd_sizes[slot] - offset); + memset((char*)dst + have, 0, bytes - have); + bytes = have; + } + if (cd_data[slot]) { + memcpy(dst, cd_data[slot] + offset, bytes); + } else if (cd_files[slot]) { + int ok; + Mgs_SpiBusTake(); + /* Seek only when the read is not already where we left off. + * + * A stage load is one long sequential run of ~550 sectors, and FATFS + * resolves a byte offset by walking the file's cluster chain -- on a + * 68 MB STAGE.DIR that is a long walk, repeated for every sector, and + * it dominated the load: 1.1 MB took 11.5 s, about 95 KB/s, when the + * bus itself can do more than ten times that. Tracking the position + * and skipping the redundant seek costs one long per file. */ + ok = 1; + if (cd_pos[slot] != offset) { + ok = fseek(cd_files[slot], offset, SEEK_SET) == 0; + cd_pos[slot] = ok ? offset : -1; + } + if (ok) { + ok = fread(dst, 1, bytes, cd_files[slot]) == bytes; + cd_pos[slot] = ok ? offset + (long)bytes : -1; + } + Mgs_SpiBusGive(); + if (!ok) { + return 0; + } + } else { + return 0; + } + return 1; +} + +/* The real CD BIOS is asynchronous, and that is LOAD-BEARING, not an + * implementation detail. The stage loader is a pair of co-routines: the drive + * delivers sectors into a shared window while the FS daemon parses and copies + * them out between deliveries, and when a file finishes, its callback points + * the SAME window at the next file. Delivering the whole 275-sector stage in + * one synchronous burst let file 2 overwrite file 1 before the parser had seen + * a byte of it -- the "ntag size 0" garbage was our own second file on top of + * the first. + * + * So: ReadRequest only records the request, and CDBIOS_ReadSync -- the exact + * point where libfs polls "has more data arrived?" -- delivers it. One burst + * per poll, stopped early whenever the callback returns 2, because that is the + * "I redirected the buffer" signal: the parser must be given the CPU before + * anything lands on the redirected window. Callbacks also rewrite + * task->size/remaining mid-flight (that is how the config sector extends the + * read to the whole stage block), so both are re-read every iteration. Sizes + * are in 32-bit words, 512 to a sector, the way the task structure counts. */ +void CDBIOS_ReadRequest(void* buffer, unsigned int sector, unsigned int size, + void* callback) { + CDBIOS_TASK* task = &cd_bios_task_800B4E58; + + task->buffer = buffer; + if (size == 0) { + size = 0x7fff0000; /* "read until the callback says stop" */ + } + task->remaining = (int)((size + 3) >> 2); + task->size = (int)((size + 3) >> 2); + task->sector = (int)sector; + task->callback = (cdbios_task_pfn)callback; + task->sectors_delivered = 0; + task->ticks = 0; + task->state = CDBIOS_STATE_READ; + cd_last_result = 0; +} + +/* deliver sectors for the outstanding request; returns 1 while incomplete */ +static int cd_pump(void) { + CDBIOS_TASK* task = &cd_bios_task_800B4E58; + int burst; + + static int pump_log = 40; + if (task->state != CDBIOS_STATE_READ) { + return 0; + } + + /* A sector out of PSRAM is a memcpy, so the whole request can be delivered + * in one poll and the early-stops below do the real pacing. A sector off + * the card is a real SPI transfer, and this runs on the loader actor, so a + * big burst stalls the frame loop. + * + * That stall was worth avoiding when the concern was a hitch mid-game, but + * a stage load is not mid-game: the screen is black and nothing is being + * drawn until it finishes. Pacing it at 16 sectors a frame just made the + * black screen last longer -- the heliport took 12 seconds, which reads as + * a hang rather than a load. Take bigger bites; the only thing being kept + * responsive during a load is a picture nobody can see. */ + { + unsigned slot = (unsigned)task->sector / SLOT_SECTORS; + int from_file = slot < FS_MAX_FILEID && cd_data[slot] == 0; + burst = from_file ? 128 : 512; + } + if (pump_log > 0) { + pump_log--; + printf("[vcd] pump sector %d remaining %d buf %p\n", task->sector, + task->remaining, task->buffer); + } + while (task->remaining > 0 && burst-- > 0) { + unsigned words = task->remaining <= 512 ? (unsigned)task->remaining : 512u; + + if (task->buffer && !cd_fetch(task->buffer, (unsigned)task->sector, words)) { + task->state = CDBIOS_STATE_IDLE; /* off the end of the file */ + return 0; + } + task->remaining -= (int)words; + task->buffer_size = (int)words; + + if (task->callback) { + int status = task->callback(task); + if (status == 0) { + task->state = CDBIOS_STATE_IDLE; /* callback ended the read */ + return 0; + } + if (status == 2) { + /* buffer redirected: let the parser run before more arrives */ + task->sector++; + task->sectors_delivered++; + return 1; + } + } + if (task->buffer) { + task->buffer = (int*)task->buffer + words; + } + task->sector++; + task->sectors_delivered++; + } + if (task->remaining <= 0) { + task->state = CDBIOS_STATE_IDLE; + return 0; + } + return 1; +} + +int CDBIOS_ReadSync(void) { + return cd_pump(); +} diff --git a/components/mgs/mgs/source/animal/doll/demodoll.c b/components/mgs/mgs/source/animal/doll/demodoll.c new file mode 100644 index 00000000..41ae27d7 --- /dev/null +++ b/components/mgs/mgs/source/animal/doll/demodoll.c @@ -0,0 +1,548 @@ +#include "doll.h" + +#include +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "libhzd/libhzd.h" +#include "chara/snake/sna_init.h" +#include "game/jimctrl.h" + +int s01a_dword_800C3D04[] = { + 0x00000000, 0x00000001, 0x00000002, 0x00000004, + 0x00000008, 0x00000010, 0x00000020, 0x00000040, + 0x00000080, 0x00000100, 0x00000200, 0x00000400, + 0x00000800, 0x00001000, 0x00000000, 0x0000000F, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x0000001D, 0x0000001E, 0x0000001F, +}; + +void Demodoll_800DDF4C(DollWork *work) +{ + work->fBB4 = work->fBA8; + work->fBA0 = work->fB98; + work->fBB8 = work->fBAC; +} + +void Demodoll_800DDF84(DollWork *work) +{ + int fB94; + int fBA6; + + fB94 = work->fB94 + 1; + if (fB94 >= work->fA90) + { + fB94 = 0; + } + + work->fB94 = fB94; + work->fBA0 = work->fA94[work->fB94]; + work->fBB4 = HZD_GetAddress(work->control.map->hzd, &work->fBA0, -1); + work->fBB8 = work->fBAC; + + fBA6 = work->fBA0.pad; + if ((fBA6 & 0x1F) == 0x1F) + { + GV_DestroyActor(&work->actor); + } +} + +int Demodoll_800DE024(DollWork *work, int size) +{ + SVECTOR diff; + int dx, dz; + + dx = work->fBA0.vx - work->control.mov.vx; + diff.vx = dx; + + dz = work->fBA0.vz - work->control.mov.vz; + diff.vz = dz; + + if ((dx > -size) && (dx < size) && (dz > -size) && (dz < size)) + { + return 1; + } + + work->fC0A = GV_VecDir2(&diff); + work->fC1C++; + + return 0; +} + +int Demodoll_800DE0AC(DollWork *work) +{ + SVECTOR sp10; + SVECTOR sp18; + SVECTOR sp20; + SVECTOR sp28; + CONTROL *control; + int dx, dz; + int yaw; + int size; + int dist; + + control = &work->control; + + sp18 = control->mov; + sp18.vy = 0; + + sp20 = work->fBA0; + sp20.vy = 0; + + dx = sp20.vx - sp18.vx; + sp10.vx = dx; + + dz = sp20.vz - sp18.vz; + sp10.vz = dz; + + yaw = GV_VecDir2(&sp10); + + size = 250; + if ((dx > -size) && (dx < size) && (dz > -size) && (dz < size)) + { + dist = GV_DiffVec3(&sp18, &sp20); + GV_AddVec3(&sp18, &control->step, &sp28); + + if (dist < GV_DiffVec3(&sp28, &sp18)) + { + control->mov = work->fBA0; + control->step = DG_ZeroVector; + return 1; + } + } + + if (GV_DiffDirAbs(yaw, control->rot.vy) > 64) + { + control->turn.vy = yaw; + } + else + { + work->fC0A = yaw; + } + + work->fC1C++; + + return 0; +} + +int Demodoll_800DE25C(DollWork *work) +{ + return 0; +} + +void Demodoll_800DE264(DollWork *work, int index) +{ + printf(" %d = %d \n", index - 16, work->fE18[index - 16]); + + if (GM_StreamStatus() == -1) + { + work->fE38++; + GM_VoxStream(work->fE18[work->fE38], 0); + } + else + { + GM_StreamPlayStart(); + } +} + +void Demodoll_800DE2E8(DollWork *work, int index) +{ + int proc; + + proc = work->fE60[index]; + if (proc >= 0) + { + GCL_ExecProc(proc, NULL); + } +} + +int Demodoll_800DE320(DollWork *work) +{ + CONTROL *control; + GV_MSG *msg; + int n_msgs; + + control = &work->control; + control->n_msg = GV_ReceiveMessage(control->name, &control->msg); + + msg = control->msg; + n_msgs = control->n_msg; + + while (n_msgs > 0) + { + if (msg->message[0] == work->fC0E) + { + return 1; + } + + n_msgs--; + msg++; + } + + return 0; +} + +int Demodoll_800DE390(DollWork *work) +{ + int temp_v1; + int voice; + int temp_a1; + int proc; + +loop: + temp_v1 = work->fBA0.pad; + + voice = temp_v1 & 0x1F; + temp_a1 = (temp_v1 & 0x300) >> 8; + proc = (temp_v1 & 0xFC00) >> 10; + + work->fC08 = work->fC30[(temp_v1 & 0xE0) >> 5]; + + if (work->fC08 == 32001) + { + work->fC0E = proc; + } + else if (work->fC08 == 32000) + { + work->fC0E = proc; + work->fE44 = 0; + work->fE40 = 0; + } + + if (voice >= 16 && voice <= 25) + { + printf(" doll voice[%d]\n", voice); + Demodoll_800DE264(work, voice); + + work->fC08 = 0; + work->fC0A = work->control.rot.vy; + + Demodoll_800DDF84(work); + + if (!Demodoll_800DE024(work, 350)) + { + return 0; + } + + goto loop; + } + else if (voice == 30) + { + printf(" doll proc[%d]\n", proc); + Demodoll_800DE2E8(work, proc); + + work->fC08 = 0; + work->fC0A = work->control.rot.vy; + + Demodoll_800DDF84(work); + + if (!Demodoll_800DE024(work, 350)) + { + return 0; + } + + goto loop; + } + + work->fC04 = s01a_dword_800C3D04[voice]; + + if (work->fC04 == 29) + { + if (work->fDFC == 0) + { + work->fDFC = 1; + } + else + { + work->fDFC = 0; + } + + work->fC08 = 0; + Demodoll_800DDF84(work); + return 0; + } + + if ((work->fC04 == 0) && (work->fC08 == 31000)) + { + work->fC08 = 0; + work->fC0A = work->control.rot.vy; + Demodoll_800DDF84(work); + return 0; + } + + if (work->fC04 == 15) + { + work->fC08 = 0; + work->fC0A = work->control.rot.vy; + return 0; + } + + work->fC0A = work->fC40[temp_a1]; + return 1; +} + +int Demodoll_800DE550(DollWork *work) +{ + if (work->fC08 == 32001) + { + if (Demodoll_800DE320(work)) + { + work->fC0E = 0; + work->fC08 = 0; + return 1; + } + + work->fBFC |= work->fC04; + return 0; + } + else if (work->fC08 == 31000) + { + if (work->fBE4 != 0) + { + work->fC08 = 0; + return 1; + } + + work->fBFC |= work->fC04; + return 0; + } + else if (work->fC08 == 32000) + { + if (work->fC0E == 0) + { + if (GM_StreamStatus() == -1) + { + work->fC08 = 0; + return 1; + } + + work->fBFC |= work->fC04; + return 0; + } + + if ((work->fE48[work->fC0E - 1] <= jimctrl_80038688()) || (GM_StreamStatus() == -1)) + { + work->fC08 = 0; + return 1; + } + + work->fBFC |= work->fC04; + return 0; + } + else if (work->fC08 > 0) + { + work->fBFC |= work->fC04; + work->fC08--; + return 0; + } + + return 1; +} + +int Demodoll_800DE670(DollWork *work) +{ + if (work->fC08 > 0) + { + work->fBFC |= work->fC04; + work->fC08--; + } + + return Demodoll_800DE024(work, 350) != 0; +} + +int Demodoll_800DE6C4(DollWork *work) +{ + if (Demodoll_800DE0AC(work)) + { + return 1; + } + + work->fC1C++; + return 0; +} + +void Demodoll_800DE700(DollWork *work) +{ + work->fC1C++; +} + +void Demodoll_800DE714(DollWork *work) +{ + if (work->fC1C >= 16) + { + work->fC18 = 3; + work->fC1C = 0; + return; + } + + if (work->fC1C == 0 || work->fC1C == 3 || work->fC1C == 6) + { + work->fBFC |= 0x2; + } + + work->fBFC |= 0x1; + work->fC0A = work->fE08; + work->fC1C++; +} + +void Demodoll_800DE784(DollWork *work) +{ + switch (work->fC18) + { + case 0: + if (!Demodoll_800DE670(work)) + { + break; + } + + if (Demodoll_800DE25C(work)) + { + Demodoll_800DDF4C(work); + work->fB94 = work->fA90 - 1; + break; + } + + if (Demodoll_800DE390(work)) + { + work->fBFC |= work->fC04; + + if (work->fC0A >= 0) + { + work->control.turn.vy = work->fC0A; + } + + work->fC18 = 2; + } + else if (work->fC04 == 15) + { + work->fC18 = 1; + } + + work->fC1C = 0; + break; + + case 1: + if (!Demodoll_800DE6C4(work)) + { + break; + } + + Demodoll_800DDF84(work); + + if (!Demodoll_800DE024(work, 350)) + { + work->fC18 = 0; + work->fC1C = 0; + break; + } + + if (Demodoll_800DE390(work)) + { + work->fBFC |= work->fC04; + + if (work->fC0A >= 0) + { + work->control.turn.vy = work->fC0A; + } + + work->fC18 = 2; + } + else if (work->fC04 != 15) + { + work->fC18 = 0; + } + + work->fC1C = 0; + break; + + case 2: + if (!Demodoll_800DE550(work)) + { + break; + } + + Demodoll_800DDF84(work); + + if (Demodoll_800DE024(work, 350)) + { + if (Demodoll_800DE390(work)) + { + work->fBFC |= work->fC04; + + if (work->fC0A >= 0) + { + work->control.turn.vy = work->fC0A; + work->fC0A = -1; + } + } + else if (work->fC04 != 15) + { + work->fC18 = 0; + } + else + { + work->fC18 = 1; + } + } + else + { + work->fC18 = 0; + } + + work->fC1C = 0; + break; + } +} + +void Demodoll_800DE964(DollWork *work) +{ + switch (work->fC18) + { + case 3: + Demodoll_800DE700(work); + break; + + case 4: + Demodoll_800DE714(work); + break; + } +} + +void Demodoll_800DE9B4(DollWork *work) +{ + switch (work->fC14) + { + case 0: + Demodoll_800DE784(work); + break; + + case 1: + Demodoll_800DE964(work); + break; + } +} + +void Demodoll_800DEA04(DollWork *work) +{ + work->fC0A = -1; + work->fBFC = 0; + + if (work->fC10 == 0) + { + Demodoll_800DE9B4(work); + } + + work->fC20++; + + if (work->fDF8 != 0 && GM_StreamStatus() == -1) + { + if (work->fE18[work->fE38 + 1] != 0) + { + work->fE38++; + GM_VoxStream(work->fE18[work->fE38], 0x40000000); + } + } +} diff --git a/components/mgs/mgs/source/animal/doll/demodoll0.c b/components/mgs/mgs/source/animal/doll/demodoll0.c new file mode 100644 index 00000000..9412dfab --- /dev/null +++ b/components/mgs/mgs/source/animal/doll/demodoll0.c @@ -0,0 +1,438 @@ +#include "doll.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "chara/snake/sna_init.h" +#include "game/jimctrl.h" + +short s01a_dword_800C3CE4[] = { + 0x0000, 0x0001, 0x0002, 0x0003, + 0x0004, 0x0005, 0x0006, 0x0007, + 0x0008, 0x0009, 0x000A, 0x000B, + 0x000C, 0x000D, 0x000E, +}; + +void AN_Breath(MATRIX *world); + +void Demodoll_800DD860(DollWork *work, int); +void Demodoll_800DDB18(DollWork *work, int); +void Demodoll_800DDD14(DollWork *work, int); +void Demodoll_800DEA04(DollWork *work); + +int Demodoll_800DD6A8(DollWork *work) +{ + if ((work->fBFC & 0x1FFF) != 0) + { + work->fBD0 = Demodoll_800DDB18; + work->fBD8 = 0; + + work->control.turn.vz = 0; + work->control.turn.vx = 0; + + return 1; + } + + return 0; +} + +void Demodoll_800DD6E0(DollWork *work) +{ + int dy; + int ang; + + dy = GM_PlayerBody->objs->objs[6].world.t[1] - work->body.objs->objs[6].world.t[1]; + ang = (ratan2(work->fE0C, dy) & 0xFFF) - 1024; + + work->adjust[2].vx = ang; + work->adjust[6].vx = ang; + + if (ang < 0) + { + work->adjust[7].vx = ang * 3; + } + else + { + work->adjust[7].vx = (ang * 3) / 2; + } +} + +void Demodoll_800DD75C(DollWork *work) +{ +} + +void Demodoll_800DD764(DollWork *work) +{ + sna_act_helper2_helper2_80033054(work->control.name, &work->adjust[work->fE3C]); +} + +void Demodoll_800DD798(DollWork *work, int arg1) +{ + CONTROL *control; + + control = &work->control; + + if (arg1 == 0) + { + work->fBE0 = 0; + GM_ConfigObjectAction(&work->body, s01a_dword_800C3CE4[0], 0, 4); + } + + if (Demodoll_800DD6A8(work)) + { + if (work->fC0A >= 0) + { + control->turn.vy = work->fC0A; + } + } + else + { + if (work->fC0A >= 0) + { + work->fBD0 = Demodoll_800DD860; + work->fBD8 = 0; + + work->control.turn.vz = 0; + work->control.turn.vx = 0; + + control->turn.vy = work->fC0A; + } + + work->control.step = DG_ZeroVector; + } +} + +void Demodoll_800DD860(DollWork *work, int arg1) +{ + CONTROL *control; + int saved_vy; + int action; + DollMotion *motion; + int i; + + control = &work->control; + saved_vy = work->fC0A; + + if (arg1 == 0) + { + if (work->fDFC == 0) + { + work->fBE0 = 1; + action = s01a_dword_800C3CE4[work->fBE0]; + GM_ConfigObjectAction(&work->body, action, 0, 4); + } + else if (work->fDFE < 0) + { + work->fBE0 = 1; + action = s01a_dword_800C3CE4[work->fBE0]; + GM_ConfigObjectAction(&work->body, action, 0, 4); + } + else + { + work->fBE0 = work->fDFE + 1; + action = s01a_dword_800C3CE4[work->fBE0]; + GM_ConfigObjectAction(&work->body, action, 0, 4); + } + } + + if (work->fBE0 == 1) + { + if (work->fDFC != 0) + { + work->fBE0 = work->fDFE + 1; + action = s01a_dword_800C3CE4[work->fBE0]; + GM_ConfigObjectAction(&work->body, action, 0, 4); + } + } + else if (work->fDFC == 0) + { + work->fBE0 = 1; + action = s01a_dword_800C3CE4[work->fBE0]; + GM_ConfigObjectAction(&work->body, action, 0, 4); + } + + if (saved_vy < 0) + { + work->fBD0 = Demodoll_800DD798; + work->fBD8 = 0; + + work->control.turn.vz = 0; + work->control.turn.vx = 0; + return; + } + + control->turn.vy = saved_vy; + + motion = &work->fC48[work->fBE0]; + for (i = 0; i < motion->index; i++) + { + if (motion->entries[i].vx == work->m_ctrl.info1.frame) + { + if (work->fE00[1] != 0) + { + GM_SeSetMode(&work->control.mov, motion->entries[i].vy, motion->entries[i].vz); + } + + if (motion->entries[i].pad != 0) + { + work->fE00[0] = motion->entries[i].pad; + } + } + } +} + +int Demodoll_800DDA40(int arg0) +{ + if (arg0 & 0x1) + { + return 2; + } + + if (arg0 & 0x2) + { + return 3; + } + + if (arg0 & 0x4) + { + return 4; + } + + if (arg0 & 0x8) + { + return 5; + } + + if (arg0 & 0x10) + { + return 6; + } + + if (arg0 & 0x20) + { + return 7; + } + + if (arg0 & 0x40) + { + return 8; + } + + if (arg0 & 0x80) + { + return 9; + } + + if (arg0 & 0x100) + { + return 10; + } + + if (arg0 & 0x200) + { + return 11; + } + + if (arg0 & 0x400) + { + return 12; + } + + if (arg0 & 0x800) + { + return 13; + } + + if (arg0 & 0x1000) + { + return 14; + } + + return -1; +} + +void Demodoll_800DDB18(DollWork *work, int arg1) +{ + int fBFC; + int index; + int action; + DollMotion *motion; + int i; + + fBFC = work->fBFC; + + index = Demodoll_800DDA40(fBFC); + if (index > 0 && index != work->fBE0) + { + work->fBE0 = index; + action = s01a_dword_800C3CE4[work->fBE0]; + GM_ConfigObjectAction(&work->body, action, 0, 4); + } + + if ((fBFC & 0x1FFF) == 0) + { + work->fBE0 = 0; + action = s01a_dword_800C3CE4[work->fBE0]; + GM_ConfigObjectAction(&work->body, action, 0, 4); + + work->fBD0 = Demodoll_800DD798; + work->fBD8 = 0; + + work->control.turn.vz = 0; + work->control.turn.vx = 0; + } + + if (work->body.is_end != 0) + { + work->fBE4 = 1; + return; + } + + motion = &work->fC48[work->fBE0]; + for (i = 0; i < motion->index; i++) + { + if (motion->entries[i].vx == work->m_ctrl.info1.frame) + { + if (work->fE00[1] != 0) + { + GM_SeSetMode(&work->control.mov, motion->entries[i].vy, motion->entries[i].vz); + } + + if (motion->entries[i].pad != 0) + { + work->fE00[0] = motion->entries[i].pad; + } + } + } +} + +void Demodoll_800DDC7C(DollWork *work, int arg1) +{ + int fBFC; + int action; + + fBFC = work->fBFC; + + if (arg1 == 0) + { + work->fBE0 = 0; + action = s01a_dword_800C3CE4[work->fBE0]; + GM_ConfigObjectAction(&work->body, action, 0, 4); + } + + Demodoll_800DD6E0(work); + + if (!(fBFC & 0x1)) + { + work->fBD0 = Demodoll_800DD798; + work->fBD8 = 0; + + work->control.turn.vz = 0; + work->control.turn.vx = 0; + } + else if (fBFC & 0x2) + { + work->fBD0 = Demodoll_800DDD14; + work->fBD8 = 0; + + work->control.turn.vz = 0; + work->control.turn.vx = 0; + } + else + { + work->control.turn.vy = work->fE08; + } +} + +void Demodoll_800DDD14(DollWork *work, int arg1) +{ + int action; + + if (arg1 == 0) + { + work->fBE0 = 0; + action = s01a_dword_800C3CE4[work->fBE0]; + GM_ConfigObjectAction(&work->body, action, 0, 4); + + Demodoll_800DD75C(work); + } + + Demodoll_800DD6E0(work); + + work->fBD0 = Demodoll_800DDC7C; + work->fBD8 = 0; + + work->control.turn.vz = 0; + work->control.turn.vx = 0; + work->control.turn.vy = work->fE08; +} + +void Demodoll_800DDD84(DollWork *work) +{ + CONTROL *control; + OBJECT *body; + DollFunc cb; + int arg; + + control = &work->control; + body = &work->body; + + cb = work->fBD0; + arg = work->fBD8++; + + work->fBE4 = 0; + work->fE00[0] = 0; + + if (cb == NULL) + { + cb = Demodoll_800DD798; + work->fBD0 = Demodoll_800DD798; + } + + cb(work, arg); + + Demodoll_800DD764(work); + + if ((work->fE3E != 0) && ((GV_Time % 64) == (work->fE3E * 16))) + { + AN_Breath(&body->objs->objs[work->fE3C].world); + } + + control->height = body->height; + control->r_sphere = -1; + + if (work->fA78 < 0 && control->grounded != 0) + { + work->fA78 = 0; + } + + work->fA78 -= 16; + control->step.vy = work->fA78; +} + +void Demodoll_800DDEAC(DollWork *work) +{ + work->fBD0 = Demodoll_800DDB18; + work->fBD8 = 0; + + work->control.turn.vz = 0; + work->control.turn.vx = 0; +} + +void Demodoll_800DDEC8(DollWork *work) +{ + SVECTOR diff; + + GV_SubVec3(&GM_PlayerPosition, &work->control.mov, &diff); + diff.vy = 0; + + work->fE08 = GV_VecDir2(&diff); + work->fE0C = GV_VecLen3(&diff); +} + +void Demodoll_800DDF18(DollWork *work) +{ + Demodoll_800DDEC8(work); + Demodoll_800DEA04(work); + Demodoll_800DDD84(work); +} diff --git a/components/mgs/mgs/source/animal/doll/doll.c b/components/mgs/mgs/source/animal/doll/doll.c new file mode 100644 index 00000000..cd3efa74 --- /dev/null +++ b/components/mgs/mgs/source/animal/doll/doll.c @@ -0,0 +1,760 @@ +#include "doll.h" + +#include +#include "common.h" +#include "mts/mts.h" // for fprintf +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "libhzd/libhzd.h" +#include "strcode.h" + +#include "chara/snake/shadow.h" // for NewShadow +#include "enemy/asiato2.h" // for NewAsiato2 +#include "enemy/glight.h" // for NewGunLight + +short s01a_word_800C3CD4[8] = {31000, 15, 30, 60, 90, 32000, 32001, 30000}; + +void Demodoll_800DDEAC(DollWork *); +void Demodoll_800DDF18(DollWork *); + +#define EXEC_LEVEL GV_ACTOR_PREV +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_GBOUND | DG_FLAG_SHADE | DG_FLAG_AMBIENT | DG_FLAG_IRTEXTURE) +#define BODY_FLAG2 ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_GBOUND | DG_FLAG_SHADE) +#define WEAPON_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_GBOUND | DG_FLAG_SHADE | DG_FLAG_ONEPIECE ) + +void s01a_doll_800DBE0C(DollWork *work) +{ + CONTROL *control; + GV_MSG *msg; + int n_msgs; + + control = &work->control; + control->n_msg = GV_ReceiveMessage(control->name, &control->msg); + + msg = control->msg; + n_msgs = control->n_msg; + + while (n_msgs > 0) + { + switch (msg->message[0]) + { + case HASH_SOUND_ON: + work->fE00[1] = 1; + break; + + case HASH_SOUND_OFF: + work->fE00[1] = 0; + break; + } + + n_msgs--; + msg++; + } +} + +void DollAct_800DBE9C(DollWork *work) +{ + CONTROL *control; + + control = &work->control; + + if (GM_CheckMessage(&work->actor, control->name, HASH_KILL)) + { + GV_DestroyActor(&work->actor); + return; + } + + s01a_doll_800DBE0C(work); + + GM_ActControl(control); + GM_ActObject2(&work->body); + + Demodoll_800DDF18(work); + + DG_GetLightMatrix2(&control->mov, work->light); + + if (GM_CheckMessage(&work->actor, control->name, HASH_KILL)) + { + GV_DestroyActor(&work->actor); + } +} + +int s01a_doll_800DBF28(DollWork *work) +{ + HZD_HDL *hzd; + HZD_PAT *route; + HZD_PTP *point; + int i; + + hzd = work->control.map->hzd; + route = hzd->def->routes; + route += work->fA8C[work->fA86]; + + work->fA90 = route->n_points; + if (work->fA90 <= 0) + { + return -1; + } + + point = route->points; + for (i = 0; i < work->fA90; i++) + { + work->fA94[i].vx = point->x; + work->fA94[i].vy = point->y; + work->fA94[i].vz = point->z; + work->fA94[i].pad = point->command; + point++; + } + + return 0; +} + +int s01a_doll_800DBFC4(char *opt, char *out) +{ + int count; + char *str; + + count = 0; + + while ((str = GCL_NextStr()) != NULL) + { + *out++ = GCL_StrToInt(str); + count++; + } + + return count; +} + +int s01a_doll_800DC01C(char *opt, short *out) +{ + int count; + char *str; + + count = 0; + + while ((str = GCL_NextStr()) != NULL) + { + *out++ = GCL_StrToInt(str); + count++; + } + + return count; +} + +int s01a_doll_800DC074(char *opt, int *out) +{ + int count; + char *str; + + count = 0; + + while ((str = GCL_NextStr()) != NULL) + { + *out++ = GCL_StrToInt(str); + count++; + } + + return count; +} + +int s01a_doll_800DC0CC(DollWork *work) +{ + char *opt; + + work->fA8C[0] = 0; + work->fA86 = 0; + + opt = GCL_GetOption('r'); + if (opt != NULL && s01a_doll_800DBFC4(opt, work->fA8C) > 3) + { + fprintf(0, "Err Err Err Change Root Num Over !!\n"); + return -1; + } + + return 0; +} + +int s01a_doll_800DC134(DollWork *work) +{ + int i; + char *opt; + + for (i = 0; i < 5; i++) + { + work->fBE8[i] = 0; + } + + opt = GCL_GetOption('s'); + if (opt != NULL && s01a_doll_800DC074(opt, work->fBE8) > 4) + { + fprintf(0, "Err Err Err Sound Buff Over !!\n"); + return -1; + } + + return 0; +} + +int s01a_doll_800DC1AC(DollWork *work, int name, int map) +{ + char *opt; + int model; + int motion; + int config; + int radar_atr; + CONTROL *control; + char *pos; + char *dir; + OBJECT *body; + int flag; + int i; + SVECTOR indices; + int weapon; + + work->fE58 = 0; + + opt = GCL_GetOption('m'); + if (opt == NULL) + { + return -1; + } + + model = GCL_StrToInt(opt); + + opt = GCL_GetOption('o'); + if (opt == NULL) + { + return -1; + } + + motion = GCL_StrToInt(opt); + + opt = GCL_GetOption('c'); + if (opt != NULL) + { + config = GCL_StrToInt(opt); + } + else + { + config = 0; + } + + switch (config) + { + case 0: + radar_atr = RADAR_OFF; + break; + + case 1: + radar_atr = 0x1000; + break; + + case 2: + radar_atr = RADAR_NOISE; + break; + + default: + radar_atr = -1; + break; + } + + control = &work->control; + + if (GM_InitControl(control, name, map) < 0) + { + return -1; + } + + pos = GCL_GetOption('p'); + dir = GCL_GetOption('d'); + GM_ConfigControlString(control, (char *)pos, (char *)dir); + + if (radar_atr >= 0) + { + GM_ConfigControlAttribute(control, radar_atr | RADAR_SIGHT | RADAR_VISIBLE); + } + + control->r_sphere = -1; + GM_ConfigControlInterp(control, 4); + GM_ConfigControlTrapCheck(control); + control->seg_flag = 1; + + work->fE58 |= 0x1; + + body = &work->body; + + opt = GCL_GetOption('g'); + if (opt != NULL) + { + flag = GCL_StrToInt(opt); + } + else + { + flag = 1; + } + + if (flag != 0) + { + GM_InitObject(body, model, BODY_FLAG, motion); + } + else + { + GM_InitObject(body, model, BODY_FLAG2, motion); + } + + GM_ConfigObjectJoint(body); + GM_ConfigMotionControl(body, &work->m_ctrl, motion, &work->oars[0], &work->oars[21], control, work->rots); + body->objs->waist_rot = NULL; + GM_ConfigObjectLight(body, work->light); + + work->fE58 |= 0x2; + + GM_ConfigMotionAdjust(&work->body, work->adjust); + + work->fE38 = -1; + + opt = GCL_GetOption('s'); + if (opt != NULL) + { + work->fE18[0] = GCL_StrToInt(opt); + + i = 1; + while ((opt = GCL_NextStr()) != NULL) + { + if (i >= 8) + { + printf("Max Voice Over!! \n"); + return -1; + } + + work->fE18[i] = GCL_StrToInt(opt); + i++; + } + } + else + { + for (i = 0; i < 8; i++) + { + work->fE18[i] = 0; + } + } + + work->fDF8 = 1; + + opt = GCL_GetOption('b'); + if (opt != NULL) + { + work->fDF8 = GCL_StrToInt(opt); + } + + indices.vx = 0; + indices.vy = 6; + indices.vz = 12; + indices.pad = 15; + + work->shadow = NewShadow(control, body, indices); + if (work->shadow == NULL) + { + return -1; + } + + work->fE58 |= 0x4; + + opt = GCL_GetOption('w'); + if (opt == NULL) + { + return 0; + } + + weapon = GCL_StrToInt(opt); + + GM_InitObject(&work->weapon, weapon, WEAPON_FLAG, 0); + GM_ConfigObjectLight(&work->weapon, work->light); + GM_ConfigObjectRoot(&work->weapon, body, 4); + + work->fE58 |= 0x8; + + if (weapon == KMD_FAMAS) + { + work->glight = NewGunLight(&body->objs->objs[4].world, NULL); + if (work->glight == NULL) + { + return -1; + } + + work->fE58 |= 0x10; + } + + return 0; +} + +int s01a_doll_800DC570(DollWork *work) +{ + int i; + char *opt; + + for (i = 0; i < 8; i++) + { + work->fC30[i] = s01a_word_800C3CD4[i]; + } + + opt = GCL_GetOption('f'); + if (opt != NULL && s01a_doll_800DC01C(opt, &work->fC30[1]) > 4) + { + printf("Err Err Err Set time Over\n"); + return -1; + } + + for (i = 0; i < 4; i++) + { + work->fC40[i] = i * 1024; + } + + opt = GCL_GetOption('v'); + if (opt != NULL && s01a_doll_800DC01C(opt, work->fC40) > 4) + { + printf("Err Err Err Set Dir Over\n"); + return -1; + } + + return 0; +} + +int s01a_doll_800DC648(char *opt, DollWork *work) +{ + SVECTOR entry; + DollMotion *motion; + + while ((opt = GCL_NextStr()) != NULL) + { + GCL_StrToSV(opt, (short *)&entry); + + motion = &work->fC48[entry.vx]; + + if (motion->index >= 4) + { + return -1; + } + + motion->entries[motion->index].vx = entry.vy; + motion->entries[motion->index].vy = entry.vz & 0xFF; + motion->entries[motion->index].vz = (entry.vz >> 8) & 0x3; + motion->entries[motion->index].pad = (entry.vz >> 10) & 0x3; + + printf(" motion[%d] flam[%d] se[%d] ", entry.vx, entry.vy, motion->entries[motion->index].vy); + printf(" mode[%d] foot[%d]\n", motion->entries[motion->index].vz, motion->entries[motion->index].pad); + + motion->index++; + } + + return 0; +} + +int s01a_doll_800DC774(DollWork *work) +{ + int i; + char *opt; + + for (i = 0; i < 8; i++) + { + work->fE60[i] = -1; + } + + i = 0; + opt = GCL_GetOption('a'); + + while (opt != NULL) + { + if (i >= 8) + { + return -1; + } + + work->fE60[i] = GCL_StrToInt(opt); + + opt = GCL_NextStr(); + i++; + } + + return i; +} + +int s01a_doll_800DC7FC(DollWork *work) +{ + int i; + char *opt; + + for (i = 0; i < 16; i++) + { + work->fE48[i] = 0; + } + + i = 0; + opt = GCL_GetOption('n'); + + while (opt != NULL) + { + if (i >= 16) + { + return -1; + } + + work->fE48[i] = GCL_StrToInt(opt); + + opt = GCL_NextStr(); + i++; + } + + return i; +} + +int s01a_doll_800DC884(DollWork *work) +{ + int i; + char *opt; + int ret; + + for (i = 0; i < 12; i++) + { + work->fC48[i].index = 0; + } + + opt = GCL_GetOption('x'); + if (opt != NULL) + { + ret = s01a_doll_800DC648(opt, work); + if (ret < 0) + { + printf("Err Err Err Set SE Over\n"); + return -1; + } + } + else + { + ret = 0; + } + + return ret; +} + +void DollDie_800DC8F0(DollWork *work) +{ + if (work->fE58 & 0x1) + { + GM_FreeControl(&work->control); + } + + if (work->fE58 & 0x2) + { + GM_FreeObject(&work->body); + } + + if (work->fE58 & 0x8) + { + GM_FreeObject(&work->weapon); + } + + if (work->fE58 & 0x4) + { + GV_DestroyOtherActor(work->shadow); + } + + if (work->fE58 & 0x10) + { + GV_DestroyOtherActor(work->glight); + } + + if (work->fE58 & 0x20) + { + GV_DestroyOtherActor(work->fE04); + } + + if (work->fE5C >= 0) + { + if (work->fE80 != 0) + { + GCL_ExecProc(work->fE5C, NULL); + } + else + { + GCL_ForceExecProc(work->fE5C, NULL); + } + } +} + +void s01a_doll_800DC9FC(DollWork *work) +{ + int act; + + act = work->fA94[0].pad & 0x1F; + printf("doll act=%d \n", act); + + if (act > 0 && act < 16) + { + GM_ConfigObjectAction(&work->body, act + 1, 0, 0); + + work->fBE0 = act + 1; + work->fC04 = act; + work->fC08 = 1; + + Demodoll_800DDEAC(work); + } + else + { + GM_ConfigObjectAction(&work->body, 0, 0, 0); + } + + work->control.height = work->body.height; +} + +int DollGetResources_800DCAA4(DollWork *work, int name, int map) +{ + char *opt; + int hzd; + short ry; + + if (s01a_doll_800DC1AC(work, name, map) < 0) + { + return -1; + } + + s01a_doll_800DC134(work); + + if (s01a_doll_800DC0CC(work) < 0) + { + return -1; + } + + if (s01a_doll_800DBF28(work) < 0) + { + fprintf(1, "demodoll.c : action point Err\n"); + return -1; + } + + if (s01a_doll_800DC570(work) < 0) + { + return -1; + } + + if (s01a_doll_800DC884(work) < 0) + { + return -1; + } + + if (s01a_doll_800DC774(work) < 0) + { + return -1; + } + + if (s01a_doll_800DC7FC(work) < 0) + { + return -1; + } + + opt = GCL_GetOption('e'); + if (opt != NULL) + { + work->fE5C = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->fE5C = -1; + } + + work->fE80 = 0; + + opt = GCL_GetOption('z'); + if (opt != NULL) + { + work->fE80 = GCL_StrToInt(GCL_NextStr()); + } + + opt = GCL_GetOption('k'); + if (opt != NULL) + { + work->fE3C = GCL_StrToInt(opt); + } + else + { + work->fE3C = 6; + } + + opt = GCL_GetOption('h'); + if (opt != NULL) + { + work->fE3E = GCL_StrToInt(opt); + } + else + { + work->fE3E = 0; + } + + opt = GCL_GetOption('i'); + if (opt != NULL) + { + work->fDFE = GCL_StrToInt(opt); + } + else + { + work->fDFE = -1; + } + + opt = GCL_GetOption('j'); + if (opt != NULL) + { + work->fE58 |= 0x20; + work->fE04 = NewAsiato2(&work->control, &work->body, GCL_StrToInt(opt), (int *)work->fE00); + } + + work->fE00[0] = 0; + work->fE00[1] = 1; + work->fE44 = 0; + work->fE40 = 0; + work->fDFC = 0; + work->fC10 = 0; + work->fC14 = 0; + work->fC18 = 0; + work->fB94 = 0; + + work->fBA0 = work->fA94[0]; + hzd = HZD_GetAddress(work->control.map->hzd, &work->fBA0, -1); + work->fBB4 = hzd; + work->fBB8 = GM_CurrentMap; + + work->control.mov = work->fA94[0]; + + work->fB98 = work->fA94[0]; + work->fBAC = GM_CurrentMap; + hzd = HZD_GetAddress(work->control.map->hzd, &work->control.mov, -1); + work->fBA8 = hzd; + work->fBC4 = hzd; + work->fBB0 = hzd; + work->fBC8 = work->fB98; + + ry = work->fC40[(work->fB98.pad & 0x300) >> 8]; + work->control.rot.vy = ry; + work->control.turn.vy = ry; + + s01a_doll_800DC9FC(work); + + return 0; +} + +void *NewDemoDoll(int name, int where, int argc, char **argv) +{ + DollWork *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(DollWork)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, DollAct_800DBE9C, DollDie_800DC8F0, "doll.c"); + + if (DollGetResources_800DCAA4(work, name, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/animal/doll/doll.h b/components/mgs/mgs/source/animal/doll/doll.h new file mode 100644 index 00000000..310a7b97 --- /dev/null +++ b/components/mgs/mgs/source/animal/doll/doll.h @@ -0,0 +1,96 @@ +#ifndef __MGS_ANIMAL_DOLL_H__ +#define __MGS_ANIMAL_DOLL_H__ + +#include "libgv/libgv.h" +#include "game/game.h" + +struct _DollWork; +typedef void (*DollFunc)(struct _DollWork *, int); + +typedef struct _DollMotion +{ + int index; + SVECTOR entries[4]; +} DollMotion; + +typedef struct _DollWork +{ + GV_ACT actor; + CONTROL control; + OBJECT body; + MOTION_CONTROL m_ctrl; + MOTION_SEGMENT oars[42]; + SVECTOR rots[20]; + SVECTOR adjust[20]; + MATRIX light[2]; + char pad[0x4]; + OBJECT weapon; + GV_ACT *glight; + char pad13[0x54]; + int fA78; + char pad25[0xA]; + short fA86; + char pad16[0x4]; + char fA8C[4]; + int fA90; + SVECTOR fA94[32]; + int fB94; + SVECTOR fB98; + SVECTOR fBA0; + int fBA8; + int fBAC; + int fBB0; + int fBB4; + int fBB8; + char pad9[0x8]; + int fBC4; + SVECTOR fBC8; + DollFunc fBD0; + char pad20[0x4]; + int fBD8; + char pad7[0x4]; + int fBE0; + int fBE4; + int fBE8[5]; + int fBFC; + char pad14[0x4]; + int fC04; + short fC08; + short fC0A; + short fC0C; + short fC0E; + int fC10; + int fC14; + int fC18; + int fC1C; + int fC20; + char pad8[0xC]; + short fC30[8]; + short fC40[4]; + DollMotion fC48[12]; + int fDF8; + short fDFC; + short fDFE; + short fE00[2]; + GV_ACT *fE04; + int fE08; + int fE0C; + char pad21[0x4]; + GV_ACT *shadow; + int fE18[8]; + int fE38; + short fE3C; + short fE3E; + int fE40; + int fE44; + char fE48[16]; + int fE58; + int fE5C; + int fE60[8]; + short fE80; + short fE82; +} DollWork; + +void *NewDemoDoll(int name, int where, int argc, char **argv); + +#endif // __MGS_ANIMAL_DOLL_H__ diff --git a/components/mgs/mgs/source/animal/liquid/bombled.c b/components/mgs/mgs/source/animal/liquid/bombled.c new file mode 100644 index 00000000..60183045 --- /dev/null +++ b/components/mgs/mgs/source/animal/liquid/bombled.c @@ -0,0 +1,139 @@ +#include "bombled.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" + +extern void s08c_800C42B0(SVECTOR *pos); + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_USER + +typedef struct _Work +{ + GV_ACT actor; + int unused1; + SVECTOR led_pos[5]; + int unused2; + int unused3; + int timer; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void UpdateBombLed(Work *work) +{ + int index; + + index = 0; + + // We love jump tables! + switch ((work->timer % 60) / 3) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + index = 0; + break; + case 7: + case 19: + index = 1; + break; + case 8: + case 18: + index = 2; + break; + case 9: + case 17: + index = 3; + break; + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + case 16: + index = 4; + break; + } + + s08c_800C42B0(&work->led_pos[index]); +} + +static void Act(Work *work) +{ + UpdateBombLed(work); + work->timer++; +} + +static void Die(Work *work) +{ +} + +const SVECTOR d18a_dword_800DA370 = {65436, 50, 65466}; +const SVECTOR d18a_dword_800DA378 = {100, 50, 65466}; + +static int GetResources(Work *work, SVECTOR *pos) +{ + MATRIX rot; + SVECTOR svec1; + SVECTOR svec2; + SVECTOR svec3; + + svec1 = d18a_dword_800DA370; + svec2 = d18a_dword_800DA378; + DG_SetPos2(pos, &DG_ZeroVector); + DG_MovePos(&svec1); + + ReadRotMatrix(&rot); + work->led_pos[0].vx = rot.t[0]; + work->led_pos[0].vy = rot.t[1]; + work->led_pos[0].vz = rot.t[2]; + DG_SetPos2(pos, &DG_ZeroVector); + DG_MovePos(&svec2); + + ReadRotMatrix(&rot); + work->led_pos[4].vx = rot.t[0]; + work->led_pos[4].vy = rot.t[1]; + work->led_pos[4].vz = rot.t[2]; + GV_SubVec3(&work->led_pos[4], &work->led_pos[0], &svec3); + + svec3.vx /= 4; + svec3.vy /= 4; + svec3.vz /= 4; + GV_AddVec3(&work->led_pos[0], &svec3, &work->led_pos[1]); + GV_AddVec3(&work->led_pos[1], &svec3, &work->led_pos[2]); + GV_AddVec3(&work->led_pos[2], &svec3, &work->led_pos[3]); + + work->timer = 0; + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewBombLed(SVECTOR *pos) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "bombled.c"); + if (GetResources(work, pos) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/animal/liquid/bombled.h b/components/mgs/mgs/source/animal/liquid/bombled.h new file mode 100644 index 00000000..ef771003 --- /dev/null +++ b/components/mgs/mgs/source/animal/liquid/bombled.h @@ -0,0 +1,10 @@ +#ifndef __MGS_ANIMAL_BOMBLED_H__ +#define __MGS_ANIMAL_BOMBLED_H__ + +#include +#include +#include + +void *NewBombLed(SVECTOR *pos); + +#endif // __MGS_ANIMAL_BOMBLED_H__ diff --git a/components/mgs/mgs/source/animal/meryl72/action.c b/components/mgs/mgs/source/animal/meryl72/action.c new file mode 100644 index 00000000..8b179854 --- /dev/null +++ b/components/mgs/mgs/source/animal/meryl72/action.c @@ -0,0 +1,1488 @@ +#include +#include "meryl72.h" + +/****Inlines**********************************************************************************************/ +static inline void SetAction( Meryl72Work *work, int n_action, int interp ) +{ + extern short ActTable_800C3310[]; + work->f8BC.field_18 = n_action; + GM_ConfigObjectAction( &( work->body ), ActTable_800C3310[n_action], 0, interp ); +} + +static inline void UnsetMode( Meryl72Work *work ) +{ + extern short ActTable_800C3310[]; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3310[STANDSTILL], 0, ACTINTERP, 0 ); + + work->action2 = 0; + work->time2 = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; +} +/*********************************************************************************************************/ + +extern void s07c_meryl72_unk1_800C86EC( Meryl72Work* work, int time ) ; + +short ActTable_800C3310[58] = +{ + 0x10, 0x11, 0x11, 0x18, 0x10, 0x10, 0x10, 0x10, + 0x18, 0x12, 0x13, 0x1B, 0x1C, 0x1D, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x01, 0x10, 0x10, 0x10, + 0x10, 0x10, 0x10, 0x10, 0x14, 0x1A, 0x19, 0x15, + 0x0A, 0x0B, 0x0C, 0x0D, 0x08, 0x16, 0x17, 0x10, + 0x04, 0x04, 0x04, 0x02, 0x17, 0x05, 0x09, 0x05, + 0x0E, 0x0F, 0x0E, 0x0F, 0x06, 0x07, 0x05, 0x09, + 0x05, 0x18 +}; + +unsigned char s07c_dword_800C3384[4] = { 1, 4, 0, 0 }; +unsigned char s07c_dword_800C3388[4] = { 255, 4, 0, 0 }; + +SVECTOR s07c_dword_800C338C = { -150, 0, 300 }; + +void ActStandStill_800C8580( Meryl72Work* work, int time ) +{ + work->act_status = 1; + work->vision.length = 4000; + + if ( time == 0 ) + { + SetAction( work, 0, ACTINTERP ) ; + GM_ConfigMotionAdjust( &( work->body ), &( work->rots[ 16 ] ) ); + } + + if ( CheckDamage_800C7F6C( work ) || CheckPad_800C8308( work ) ) + { + UnsetMode( work ) ; + return ; + } + + if ( work->pad.dir >= 0 ) + { + if ( GV_DiffDirAbs( work->control.rot.vy, work->pad.dir ) < 0x100 ) + { + if ( work->pad.mode & 0x1 ) + { + SetAction( work, ACTION2, ACTINTERP ); + } + else + { + SetAction( work, ACTION1, ACTINTERP ); + } + SetMode( work, s07c_meryl72_unk1_800C86EC ); + + } + else + { + work->control.turn.vy = work->pad.dir; + } + } + + work->vision.facedir = work->control.rot.vy; + work->target->class |= 0x9E; +} + +void s07c_meryl72_unk1_800C86EC( Meryl72Work* work, int time ) +{ + int s0; + int tmp; + int dist; + CONTROL * ctrl; + int dir; + int cur_act; + SVECTOR* svec; + + work->act_status = 1; + work->vision.length = 4000; + + if ( CheckDamage_800C7F6C( work ) ) + { + UnsetMode( work ) ; + return ; + } + + if ( CheckPad_800C8308( work ) ) + { + UnsetMode( work ) ; + return ; + } + + dist = -1; + dir = work->pad.dir; + ctrl = &(work->control ); + cur_act = work->f8BC.field_18; + svec = work->control.vecs; + + if ( (work->pad.mode & 0x1) && ( cur_act != ACTION2 ) ) + { + SetAction( work, ACTION2, ACTINTERP ) ; + } + + if ( dir >= 0 ) + { + s0 = ctrl->n_touches; + if ( s0 > 0 ) + { + dist = GV_VecDir2( svec ); + + if ( s0 >= 2 ) + { + tmp = GV_VecDir2( &ctrl->vecs[1] ); + if ( GV_DiffDirAbs( dir, tmp ) < GV_DiffDirAbs( dir, dist ) ) + { + dist = tmp; + } + } + } + } + + if ( dir < 0 ) + { + SetMode( work, ActStandStill_800C8580 ); + UnsetMode( work ) ; + return; + } + + + switch ( s07c_meryl72_unk1_800C7FCC( dir, dist ) ) + { + case 1: + dir = ( dist - 1024 ) & 0xFFF; + break; + case 2: + dir = ( dist + 1024 ) & 0xFFF; + break; + } + + if ( !work->body.objs->bound_mode ) + { + ctrl->rot.vy = dir; + } + + ctrl->turn.vy = dir; + work->vision.facedir = GV_NearExp4P( work->vision.facedir, work->control.rot.vy ); + SetTargetClass( work->target, TARGET_FLAG ); +} + +extern void s07c_meryl72_unk1_800CA0EC( Meryl72Work* work, int time ) ; + +void s07c_meryl72_unk1_800C88EC( Meryl72Work* work, int time ) +{ + if ( time == 0 ) + { + work->f8BC.field_14 = 4; + SetAction( work, ACTION41, 4 ); + } + + if ( !( CheckDamage_800C7F6C( work ) ) && ( work->body.is_end ) ) + { + work->pad.tmp = 0; + SetMode( work, s07c_meryl72_unk1_800CA0EC ); + } +} + +void s07c_meryl72_unk1_800C8970( Meryl72Work* work, int time ) +{ + work->act_status = 1 ; + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = 4000 ; + + if ( time == 0 ) + { + SetAction( work, ACTION21, ACTINTERP ) ; + } + + if ( CheckDamage_800C7F6C( work ) ) + { + return ; + } + + if ( work->body.is_end || !( work->pad.press & 0x20 ) ) + { + SetMode( work, ActStandStill_800C8580 ) ; + return ; + } + + work->control.turn.vy = work->sn_dir ; +} + +void s07c_meryl72_unk1_800C8A30( Meryl72Work* work, int time ) +{ + int dir; + + work->vision.length = 4000 ; + work->act_status |= 9 ; + + if ( time == 0 ) + { + GM_GameStatus |= STATE_PADRELEASE; + SetAction( work, ACTION28, ACTINTERP ); + } + + if ( time == 1 ) + { + dir = work->sn_dir; + GM_PlayerControl->turn.vy = ( dir + 0x800 ) & 0xFFF ; + } + + if ( time == 98 ) + { + GM_SeSet( &( work->control.mov ), SE_PUNCH_SWING ); + if ( !work->stage ) + { + GM_SeSet( &( work->control.mov ), 189 ); + } + else + { + GM_SeSet( &( work->control.mov ), 199 ); + } + } + + if ( time == 101 && AttackForce_800C80DC( work, 0 ) ) + { + if ( !work->stage ) + { + GM_SeSet( &( work->control.mov ), 176 ); + } + else + { + GM_SeSet( &( work->control.mov ), 200 ); + } + if ( s07c_meryl72_unk1_800C829C( work ) ) + { + ExecProc_800C7C58( work, 2 ); + } + } + + if ( work->body.is_end ) + { + work->fC08 = 3; + GM_GameStatus &= ~STATE_PADRELEASE; + SetMode( work, ActStandStill_800C8580 ); + } + else + { + work->control.turn.vy = work->sn_dir; + } +} + +void s07c_meryl72_unk1_800C8BC4( Meryl72Work* work, int time ) +{ + work->vision.length = 4000 ; + work->act_status |= 9 ; + + if ( time == 0 ) + { + SetAction( work, ACTION13, ACTINTERP ); + } + + if ( time == 30 ) + { + GM_SeSetMode( &( work->control.mov ), 193, 1 ); + } + + if ( work->body.is_end ) + { + work->fC08 = 3; + SetMode( work, ActStandStill_800C8580 ); + } + else + { + work->control.turn.vy = work->sn_dir; + } +} + +void s07c_meryl72_unk1_800C8C7C( Meryl72Work* work, int time ) +{ + int local_time; + + work->vision.length = 4000 ; + work->act_status |= 9 ; + + if ( time == 0 ) + { + GM_GameStatus |= STATE_PADRELEASE; + SetAction( work, ACTION31, ACTINTERP ); + } + + if ( time == 90 ) + { + GM_CurrentItemId = -1; + GM_SeSet( &( work->control.mov ), SE_ITEM_EQUIP ); + } + + if ( time == 95 ) + { + int dir = work->sn_dir; + GM_PlayerControl->turn.vy = ( dir + 0x800 ) & 0xFFF ; + } + + local_time = 95; + + if ( time == 95 ) + { + SetAction( work, ACTION28, ACTINTERP ); + } + + if ( time == 193 ) + { + GM_SeSet( &( work->control.mov ), SE_PUNCH_SWING ); + if ( !work->stage ) + { + GM_SeSet( &( work->control.mov ), 189 ); + } + else + { + GM_SeSet( &( work->control.mov ), 199 ); + } + } + + if ( time == ( local_time + 101 ) && AttackForce_800C80DC( work, 0 ) ) + { + if ( !work->stage ) + { + GM_SeSet( &( work->control.mov ), 176 ); + } + else + { + GM_SeSet( &( work->control.mov ), 200 ); + } + if ( s07c_meryl72_unk1_800C829C( work ) ) + { + ExecProc_800C7C58( work, 2 ); + } + } + + if ( local_time < time && work->body.is_end ) + { + work->fC08 = 3; + GM_GameStatus &= ~STATE_PADRELEASE; + SetMode( work, ActStandStill_800C8580 ); + } + else + { + work->control.turn.vy = work->sn_dir; + } +} + +void s07c_meryl72_unk1_800C8E74( Meryl72Work* work, int time ) +{ + work->vision.length = 4000 ; + work->act_status |= 9 ; + + if ( time == 0 ) + { + GM_GameStatus |= STATE_PADRELEASE; + SetAction( work, ACTION28, ACTINTERP ); + } + + if ( time == 50 ) + { + SetAction( work, ACTION29, ACTINTERP ); + } + + if ( time == 63 ) + { + GM_SeSet( &( work->control.mov ), SE_PUNCH_SWING ); + if ( !work->stage ) + { + GM_SeSet( &( work->control.mov ), 189 ); + } + else + { + GM_SeSet( &( work->control.mov ), 199 ); + } + } + + if ( time == 67 && AttackForce_800C80DC( work, 1 ) ) + { + GM_SeSet( &( work->control.mov ), SE_KICK_HIT ); + if ( s07c_meryl72_unk1_800C829C( work ) ) + { + ExecProc_800C7C58( work, 2 ); + } + } + + if ( time > 0x32 && work->body.is_end ) + { + work->fC08 = 3; + GM_GameStatus &= ~STATE_PADRELEASE; + SetMode( work, ActStandStill_800C8580 ); + } + else + { + work->control.turn.vy = work->sn_dir; + } +} + +void s07c_meryl72_unk1_800C9000( Meryl72Work* work, int time ) +{ + + int press = work->pad.press; + work->act_status = 1 ; + work->vision.length = 4000 ; + + + if ( time == 0 ) + { + SetAction( work, ACTION9, ACTINTERP ) ; + } + + if ( !CheckDamage_800C7F6C( work ) ) + { + if ( !(press & 0x02000000) ) + { + if ( !CheckPad_800C8308( work ) ) + { + SetMode( work, ActStandStill_800C8580 ) ; + } + } + else + { + work->control.turn.vy = work->sn_dir ; + work->vision.facedir = work->control.rot.vy ; + SetTargetClass( work->target, TARGET_FLAG ); + } + } +} + +void s07c_meryl72_unk1_800C90C8( Meryl72Work* work, int time ) +{ + + int press = work->pad.press; + work->act_status = 1 ; + work->vision.length = 4000 ; + + + if ( time == 0 ) + { + SetAction( work, ACTION10, ACTINTERP ) ; + } + + if ( !CheckDamage_800C7F6C( work ) ) + { + if ( !(press & 0x04000000) ) + { + if ( !CheckPad_800C8308( work ) ) + { + SetMode( work, ActStandStill_800C8580 ) ; + } + } + else + { + work->control.turn.vy = work->sn_dir; + work->vision.facedir = work->control.rot.vy; + SetTargetClass( work->target, TARGET_FLAG ); + } + } +} + +void s07c_meryl72_unk1_800C9190( Meryl72Work* work, int time ) +{ + + int press = work->pad.press; + work->act_status = 1 ; + work->vision.length = 4000 ; + + + if ( time == 0 ) + { + SetAction( work, ACTION11, ACTINTERP ) ; + } + + if ( !CheckDamage_800C7F6C( work ) ) + { + if ( !(press & 0x20000000) ) + { + if ( !CheckPad_800C8308( work ) ) + { + SetMode( work, ActStandStill_800C8580 ) ; + } + } + else + { + work->control.turn.vy = work->sn_dir; + work->vision.facedir = work->control.rot.vy; + SetTargetClass( work->target, TARGET_FLAG ); + } + } +} + +void s07c_meryl72_unk1_800C9258( Meryl72Work* work, int time ) +{ + work->act_status = 1 ; + work->vision.length = 4000 ; + + if ( time == 0 ) + { + SetAction( work, ACTION31, ACTINTERP ) ; + } + + if ( CheckDamage_800C7F6C( work ) ) return ; + if ( CheckPad_800C8308( work ) ) return ; + + if ( work->body.is_end ) + { + SetMode( work, ActStandStill_800C8580 ) ; + } + else + { + work->control.turn.vy = work->sn_dir; + work->vision.facedir = work->control.rot.vy; + SetTargetClass( work->target, TARGET_FLAG ); + } +} + +void s07c_meryl72_unk1_800C9318( Meryl72Work* work, int time ) +{ + work->act_status = 1 ; + work->vision.length = 4000 ; + + if ( time == 0 ) + { + SetAction( work, ACTION12, ACTINTERP ) ; + } + + if ( CheckDamage_800C7F6C( work ) ) return ; + if ( CheckPad_800C8308( work ) ) return ; + + if ( work->body.is_end ) + { + SetMode( work, ActStandStill_800C8580 ) ; + } + else + { + work->control.turn.vy = work->sn_dir; + work->vision.facedir = work->control.rot.vy; + SetTargetClass( work->target, TARGET_FLAG ); + } +} + +int s07c_meryl72_unk1_800C93D8( Meryl72Work* work ) +{ + int diff = GV_DiffDirS( work->control.rot.vy, work->sn_dir ); + + if ( diff < 768 && diff > -768 ) + { + return work->control.turn.vy ; + } + + return work->sn_dir ; +} + +void ActReadyGun_800C9428( Meryl72Work* work, int time ) +{ + int press; + work->act_status = 1 ; + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = 4000 ; + press = work->pad.press; + + if ( time == 0 ) + { + if ( press & 0x10000 ) + { + SetAction( work, ACTION3, ACTINTERP ) ; + } + else + { + SetAction( work, ACTION5, ACTINTERP ); + } + GM_ConfigMotionAdjust( &( work->body ), &work->rots[16] ) ; + } + + ReviseReadyGun_800C8020( work ); + if ( CheckDamage_800C7F6C( work ) ) + { + work->rots[21].vx = 0; + work->rots[21].vy = 0; + return ; + } + + if ( !(press & 0x30000) ) + { + work->rots[21].vx = 0; + work->rots[21].vy = 0; + SetMode( work, ActStandStill_800C8580 ) ; + return ; + } + + if ( press & 0x40000 ) + { + SetMode( work, s07c_meryl72_unk1_800C9594 ) ; + return ; + } + + if ( press & 0x80000 ) + { + SetMode( work, s07c_meryl72_unk1_800C964C ) ; + return ; + } + + if ( press & 0x100000 ) + { + SetMode( work, ActGrenade_800C9790 ) ; + return ; + } + + if ( press & 0x200000 ) + { + SetMode( work, ActGrenade_800C9790 ) ; + return ; + } + + work->control.turn.vy = s07c_meryl72_unk1_800C93D8( work ) ; + work->vision.facedir = work->control.rot.vy + work->rots[21].vy ; +} + +void s07c_meryl72_unk1_800C9594( Meryl72Work* work, int time ) +{ + work->act_status = 1 ; + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = 4000 ; + + if ( time == 0 ) + { + SetAction( work, ACTION4, 0 ) ; + ML72_SetPutChar_800CB584( work, 3 ) ; //Meryl_SetPutChar + GM_ConfigMotionAdjust( &( work->body ), &work->rots[16] ) ; + } + + work->control.turn.vy = work->sn_dir; + + if ( CheckDamage_800C7F6C( work ) ) + { + return ; + } + + SetMode( work, ActReadyGun_800C9428 ); +} + +void s07c_meryl72_unk1_800C964C( Meryl72Work *work, int time ) +{ + SVECTOR *rot; + + work->act_status = 1 ; + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = 4000 ; + + if ( time == 0 ) + { + GM_ConfigMotionAdjust( &( work->body ), &work->rots[16] ); + } + + rot = &work->control.rot; + if ( time == 1 || time == 7 ) + { + rot->vy = ( rot->vy - 0x100 ) & 0xFFF; + } + + if ( time == 3 || time == 5 ) + { + rot->vy = ( rot->vy + 0x100 ) & 0xFFF; + } + + if ( time == 2 || time == 4 || time == 6 ) + { + SetAction( work, ACTION6, 0 ); + ML72_SetPutChar_800CB584( work, GUNSHOOT ) ; + } + + if ( CheckDamage_800C7F6C( work ) ) + { + return ; + } + + if ( work->body.is_end ) + { + SetMode( work, ActReadyGun_800C9428 ); + } + + work->control.rot.vy = rot->vy; +} + +void ActGrenade_800C9790( Meryl72Work* work, int time ) +{ + work->act_status = 1 ; + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = 4000 ; + + if ( time == 0 ) + { + extern void *NewGrenadeEnemy( CONTROL *, OBJECT *, int, u_long *, SVECTOR *, int ) ; + + SetAction( work, GRENADE, ACTINTERP ) ; + work->subweapon = NewGrenadeEnemy( &(work->control), &(work->body), 9, + &(work->trigger), &GM_PlayerPosition, ENEMY_SIDE ) ; + } + + if ( time > ACTINTERP ) + { + work->trigger |= WEAPON_TAKE ; + } + if ( time == 17 ) + { + GM_SeSet( &( work->control.mov ), SE_PINNUKI ) ; + } + if ( time == 45 ) + { + work->trigger |= WEAPON_TRIG2 ; + } + + if ( CheckDamage_800C7F6C( work ) ) + { + GV_DestroyActor( work->subweapon ) ; + return ; + } + + + if ( work->body.is_end ) + { + GV_DestroyActor( work->subweapon ) ; + SetMode( work, ActReadyGun_800C9428 ) ; + } +} + +void s07c_meryl72_unk1_800C98E0( Meryl72Work* work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + + if ( time == 0 ) + { + SetAction( work, ACTION24, ACTINTERP ) ; + } + + if ( !CheckDamage_800C7F6C( work ) && work->body.is_end ) + { + SetMode( work, ActStandStill_800C8580 ) ; + } +} + +void s07c_meryl72_unk1_800C9970( Meryl72Work* work, int time ) +{ + TARGET *trgt = work->target ; + CONTROL *ctrl = &work->control ; + + work->f8BC.field_1C = 0 ; + + if ( time == 0 ) + { + work->fC08 = 1; + work->f8BC.field_14 = 7 ; + SetAction( work, ACTION36, ACTINTERP ) ; + } + + if ( ( time > 7 ) && ( time < 20 ) ) + { + GM_MoveTarget( &work->target2, &ctrl->mov ); + GM_PowerTarget( &work->target2 ); + } + + if ( time == 24 ) + { + if ( !work->stage ) + { + GM_SeSet( &ctrl->mov, 195 ); + } + else + { + GM_SeSet( &ctrl->mov, 194 ); + } + GM_SeSet( &ctrl->mov, SE_HIT_FLOOR ); + } + + if ( work->body.is_end ) + { + SetMode( work, s07c_meryl72_unk1_800CA0EC ) ; + } + + if ( time < 24 ) + { + ctrl->turn = *trgt->field_1C ; + } +} + +void s07c_meryl72_unk1_800C9AB0( Meryl72Work* work ) +{ + SVECTOR svec; + + work->control.turn = GM_PlayerControl->rot; + DG_SetPos2( &GM_PlayerPosition, &GM_PlayerControl->rot ) ; + DG_PutVector( &s07c_dword_800C338C, &svec, 1 ); + GV_SubVec3( &svec, &work->control.mov, &work->control.step ); +} + +void s07c_meryl72_unk1_800C9B30( Meryl72Work* work, int time ) +{ + int s2; + TARGET *target; + + target = work->target; + s2 = work->f8BC.field_18; + + work->f8BC.field_1C = 0; + + if ( time == 0 ) + { + work->fC08 = 1; + work->fAEA = target->faint; + work->fAEC = 0; + if ( !work->stage ) + { + GM_SeSet( &work->control.mov, 190 ); + } + else + { + GM_SeSet( &work->control.mov, 196 ); + } + } + + if ( work->fAEA != target->faint ) + { + if ( !work->stage ) + { + GM_SeSet( &work->control.mov, 190 ); + } + else + { + GM_SeSet( &work->control.mov, 196 ); + } + work->fAEA = target->faint; + } + + if ( target->damaged & TARGET_POWER ) + { + ML72_PutBlood_800CB2EC( work, 5, 0 ) ; + if ( !work->stage ) + { + GM_SeSet( &work->control.mov, 195 ); + } + else + { + GM_SeSet( &work->control.mov, 194 ); + } + target->force = DG_ZeroVector; + target->damage = 0; + target->damaged = TARGET_STALE; + + if ( target->vital <= 0 ) + { + work->f8BC.field_14 = 6; + target->side = ENEMY_SIDE; + if ( !GM_GameOverTimer && GM_SnakeCurrentHealth > 0 ) + { + ExecProc_800C7C58( work, 1 ) ; + GM_GameOver(); + GM_GameOverTimer = -2; + } + SetMode( work, s07c_meryl72_unk1_800CA538 ); + target->captured = 0; + } + } + + switch ( GM_PlayerAction ) + { + case 0x7: + case 0xD: + if( s2 != 0x20 && s2 != 0x1E ) + { + target->side = PLAYER_SIDE; + SetAction( work, ACTION32, ACTINTERP ); + work->fAEC = 0; + } + + if ( work->fAEC == 0x32 && target->faint > 0 ) + { + SetAction( work, ACTION30, ACTINTERP ); + } + + if ( s2 == 0x1E && !( work->fAEC & 7 ) ) + { + NewPadVibration(s07c_dword_800C3384, 1); + NewPadVibration(s07c_dword_800C3388, 2); + } + work->control.turn = GM_PlayerControl->rot; + s07c_meryl72_unk1_800C9AB0( work ); + work->fAEC++; + break; + case 0x26: + if ( s2 != 0x21 ) + { + target->side = PLAYER_SIDE; + SetAction( work, ACTION33, ACTINTERP ); + } + work->control.turn = GM_PlayerControl->rot; + s07c_meryl72_unk1_800C9AB0( work ); + break; + case 0x27: + work->f8BC.field_14 = 5; + target->side = ENEMY_SIDE; + target->vital = 0; + if ( !GM_GameOverTimer && GM_SnakeCurrentHealth > 0 ) + { + ExecProc_800C7C58( work, 1 ) ; + GM_GameOver(); + GM_GameOverTimer = -2; + } + SetMode( work, s07c_meryl72_unk1_800CA538 ); + return ; + default: + work->f8BC.field_14 = 6 ; + target->side = ENEMY_SIDE ; + SetMode( work, s07c_meryl72_unk1_800CA538 ) ; + return ; + } + + if ( work->sn_dis > 800 ) + { + target->captured = 0 ; + } + + work->target->class |= ( TARGET_SEEK | TARGET_POWER ) ; + work->vision.facedir = work->control.rot.vy ; +} + +void s07c_meryl72_unk1_800C9F30( Meryl72Work *work, int time ) +{ + if ( time == 0 ) + { + SetAction( work, ACTION44, ACTINTERP ); + } + + if (work->body.is_end ) + { + SetMode( work, s07c_meryl72_unk1_800CAA48 ); + } +} + +void s07c_meryl72_unk1_800C9F98( Meryl72Work *work, int time ) +{ + CONTROL *ctrl; + int time_offset; + + ctrl = &work->control; + work->f8BC.field_1C = 0; + work->vision.length = 0; + + if ( time == 0 ) + { + work->fC08 = 1 ; + SetAction( work, ACTION37, ACTINTERP ) ; + work->target->faint = 0; + if ( GM_StreamStatus() != -1 ) + { + GM_StreamPlayStop(); + } + } + + if ( CheckDamage_800C7F6C( work ) ) + { + return ; + } + + time_offset = 10; + + if ( time == time_offset ) + { + work->f8BC.field_14 = 2; + SetAction( work, ACTION38, ACTINTERP ) ; + } + else + { + SetTargetClass( work->target, TARGET_FLAG ); + } + + if ( time == time_offset + 48 ) + { + GM_SeSet( &ctrl->mov, SE_ENEMY_COLLAPSE ); + } + + if ( time == time_offset + 74 ) + { + GM_SeSet( &ctrl->mov, SE_HIT_FLOOR ); + } + + if ( time >= time_offset + 50 && work->body.is_end ) + { + SetMode( work, s07c_meryl72_unk1_800CA0EC ) ; + } +} + +extern void AN_Piyopiyo( SVECTOR *pos ) ; + +void s07c_meryl72_unk1_800CA0EC( Meryl72Work *work, int time ) +{ + SVECTOR svec; + + if ( time == 0 ) + { + if ( work->f8BC.field_14 < 4 ) + { + if ( work->f8BC.field_14 == 1 ) + { + SetAction( work, ACTION47, ACTINTERP ); + } + else + { + SetAction( work, ACTION45, ACTINTERP ); + } + } + else + { + SetAction( work, ACTION46, ACTINTERP ); + } + + if ( work->target->faint <= 0 ) + { + work->target->faint = 7 ; + svec.vx = work->body.objs->objs[6].world.t[0]; + svec.vy = work->body.objs->objs[6].world.t[1]; + svec.vz = work->body.objs->objs[6].world.t[2]; + AN_Piyopiyo( &svec ) ; + work->fAEA = 0x96; + } + else + { + work->fAEA = 0xA; + } + } + + if (s07c_meryl72_unk1_800C7D00( work ) ) + { + SetMode( work, s07c_meryl72_unk1_800CA278 ); + return; + } + + if ( work->fAEA < time ) + { + if ( work->target->faint <= 0 ) + { + work->target->faint = work->param.faint; + } + SetMode( work, s07c_meryl72_unk1_800CA314 ); + return; + } + + work->target->class |= 0x22; +} + +void s07c_meryl72_unk1_800CA278( Meryl72Work *work, int time ) +{ + if ( time == 0 ) + { + if ( work->f8BC.field_14 < 4 ) + { + SetAction( work, ACTION52, ACTINTERP ); + } + else + { + SetAction( work, ACTION53, ACTINTERP ); + } + GM_SeSet( &work->control.mov, SE_ITEM_CURSOR ); + } + + if ( work->body.is_end ) + { + SetMode( work, s07c_meryl72_unk1_800CAA48 ); + } +} + +void s07c_meryl72_unk1_800CA314( Meryl72Work* work, int time ) +{ + work->f8BC.field_1C = 0; + + if ( time == 0 ) + { + if ( work->f8BC.field_14 < 4 ) + { + if ( work->fAEA == 0x96 ) + { + SetAction( work, ACTION50, ACTINTERP ); + } + else + { + SetAction( work, ACTION48, ACTINTERP ); + } + } + else + { + if ( work->fAEA == 0x96 ) + { + SetAction( work, ACTION51, ACTINTERP ); + } + else + { + SetAction( work, ACTION49, ACTINTERP ); + } + } + } + + if ( work->body.is_end ) + { + work->fC34 = 0x1E; + SetMode( work, ActStandStill_800C8580 ); + work->fC08 = 2; + } +} + +void s07c_meryl72_unk1_800CA408( Meryl72Work* work, int time ) +{ + TARGET* target; + work->f8BC.field_1C = 0; + work->vision.length = 4000; + + if ( CheckDamage_800C7F6C( work ) ) + { + return ; + } + + target = work->target; + if (time == 0 ) + { + work->fC08 = 1; + SetAction( work, ACTION20, ACTINTERP ) ; + if ( !work->stage ) + { + GM_SeSet( &work->control.mov, 195 ); + } + else + { + GM_SeSet( &work->control.mov, 194 ); + } + + if ( target->a_mode == 3 ) + { + GM_SeSet( &work->control.mov, SE_PUNCH_HIT ); + ML72_PutBreath_800CB35C( work ); // putfog + } + else + { + ML72_PutBlood_800CB2EC( work, 5, 0 ); // putblood + } + } + + if ( work->body.is_end) + { + work->fC34 = 0x1E; + work->fC08 = 2 ; + printf( " nokezori end!!\n" ) ; + SetMode( work, ActStandStill_800C8580 ) ; + } + SetTargetClass( work->target, TARGET_FLAG ); +} + +void s07c_meryl72_unk1_800CA538( Meryl72Work *work, int time ) +{ + CONTROL* ctrl; + UNK *unk; + int s2; + + s2 = 0xF; + unk = (UNK*)&work->f8BC; + work->f8BC.field_1C = 0; + work->control.step = work->target->force; + ctrl = &work->control; + + + + if ( time == 0 ) + { + work->fC08 = 1; + switch( unk->field_14 ) + { + case 0: + GM_SeSet( &ctrl->mov, SE_PUNCH_HIT ); + SetAction( work, ACTION40, ACTINTERP ); + if ( !work->stage ) + { + GM_SeSet( &ctrl->mov, 195 ); + } + else + { + GM_SeSet( &ctrl->mov, 194 ); + } + ML72_PutBlood_800CB2EC( work, 5, 0 ); + work->fAEA = 17; + s2 = 0x10; + break; + case 1: + SetAction( work, ACTION43, ACTINTERP ); + if ( work->target->vital <= 0 ) + { + if ( !work->stage ) + { + GM_SeSet( &ctrl->mov, 196 ); + } + else + { + GM_SeSet( &ctrl->mov, 195 ); + } + if ( GM_CurrentWeaponId == WP_Rifle ) + { + ML72_PutBlood_800CB2EC( work, 6, 2 ); + } + else + { + ML72_PutBlood_800CB2EC( work, 6, 1 ); + } + if ( work->target->a_mode == 3 ) + { + GM_SeSet( &work->control.mov, SE_PUNCH_HIT ); + } + } + else + { + + if ( !work->stage ) + { + GM_SeSet( &ctrl->mov, 195 ); + } + else + { + GM_SeSet( &ctrl->mov, 194 ); + } + ML72_PutBlood_800CB2EC( work, 5, 0 ); + } + work->fAEA = 46; + s2 = 0xF; + break; + case 4: + if ( !work->stage ) + { + GM_SeSet( &ctrl->mov, 195 ); + } + else + { + GM_SeSet( &ctrl->mov, 194 ); + } + s2 = 0xF; + SetAction( work, ACTION41, ACTINTERP ); + ML72_PutBlood_800CB2EC( work, 5, 0 ); + work->fAEA = 14; + break; + case 3: + if ( !work->stage ) + { + GM_SeSet( &ctrl->mov, 195 ); + } + else + { + GM_SeSet( &ctrl->mov, 194 ); + } + s2 = 0x11; + SetAction( work, ACTION42, ACTINTERP ); + ML72_PutBlood_800CB2EC( work, 5, 0 ); + work->fAEA = 22; + break; + case 5: + if ( !work->stage ) + { + GM_SeSet( &ctrl->mov, 191 ); + } + else + { + GM_SeSet( &ctrl->mov, 197 ); + } + s2 = 0xF; + GM_SeSet( &ctrl->mov, 144 ); + SetAction( work, ACTION34, ACTINTERP ); + work->fAEA = 67; + break; + case 6: + SetAction( work, ACTION35, ACTINTERP ); + s2 = 0x10; + work->fAEA = 15; + break; + } + + if ( GM_StreamStatus() != -1 ) + { + GM_StreamPlayStop(); + } + } + + if ( time == 2 && work->target->vital <= 0 ) + { + GM_GameStatus |= STATE_PADRELEASE; + if ( GM_StreamStatus() == -1 ) + { + GM_VoxStream( work->voices[s2] , 0 ); + } + } + + + switch( unk->field_14 ) + { + case 1: + if ( time == 24 ) + { + GM_SeSet( &work->control.mov, 81 ); + } + break; + case 0: + case 3: + case 4: + if ( time > 6 && time < 30 ) + { + GM_MoveTarget( &work->target2, &work->control.mov ); + GM_PowerTarget( &work->target2 ); + } + break; + case 2: + case 5: + break; + } + + if ( time > 16 && work->control.grounded ) + { + work->control.step = DG_ZeroVector; + } + + if ( time == work->fAEA ) + { + GM_SeSet( &work->control.mov, SE_HIT_FLOOR ) ; + } + + if ( work->body.is_end ) + { + work->f8BC.field_1C = 1; + work->target->force = DG_ZeroVector; + if ( work->target->vital <= 0 ) + { + SetMode( work, s07c_meryl72_unk1_800CAA48 ); + } + else + { + SetMode( work, s07c_meryl72_unk1_800CA0EC ); + } + } +} + +void s07c_meryl72_unk1_800CAA48( Meryl72Work *work, int time ) +{ + if ( time == 0 ) + { + if ( work->f8BC.field_14 < 4 ) + { + if ( work->f8BC.field_14 == 1 ) + { + SetAction( work, ACTION56, ACTINTERP ); + } + else + { + SetAction( work, ACTION54, ACTINTERP ); + } + } + else + { + SetAction( work, ACTION55, ACTINTERP ); + } + GM_ConfigControlAttribute( &work->control, 0 ); + work->target->class = TARGET_AVAIL; + } + + if ( time > 32 && GM_StreamStatus() == -1 && GM_GameOverTimer == -2 ) + { + GM_GameOverTimer = 1; + } + + if ( time == 600 ) + { + GM_GameOverTimer = 0; + GM_GameOver(); + } +} + +void s07a_meryl_unk_800D9508( Meryl72Work *work ) +{ + work->light[0].t[0] += ( work->fC0E * 5 ) ; +} + +void s07c_meryl72_unk1_800CAB68( Meryl72Work *work ) +{ + if ( work->fC34 > 0) + { + work->target->class = TARGET_AVAIL ; + work->fC34 -= 1 ; + return; + } + work->fC34 = 0; +} + +void s07c_meryl72_unk1_800CABA0( Meryl72Work *work ) +{ + OBJECT *body; + short v0; + int time_prev; + ACTION action; + UNK *unk; + CONTROL *ctrl; + + work->trigger = 0; + work->vision.length = 0; + work->target->class = TARGET_AVAIL; + + unk = &work->f8BC; + ctrl = &work->control; + + work->actend = 0; + work->fC08 = 0; + work->act_status = 0; + work->f8BC.field_1C = 1 ; + work->fB9C = 0; + + body = &work->body; + action = work->action; + + time_prev = work->time; + work->time++; + + if ( !action ) + { + action = ActStandStill_800C8580; + work->action = ActStandStill_800C8580; + } + + action( work, time_prev ); + action = work->action2; + + if ( action ) + { + time_prev = work->time2; + work->time2++; + action( work, time_prev ); + } + + ctrl->height = body->height ; + + if ( !unk->field_1C ) + { + ctrl->r_sphere = GV_NearExp2( ctrl->r_sphere, unk->field_1A ); + } + else + { + ctrl->r_sphere = -1; + } + + s07c_meryl72_unk1_800CAB68( work ); + s07c_meryl72_unk1_800C80B4( work ); + s07a_meryl_unk_800D9508( work ); + + if ( GM_AlertMode == ALERT_ACTIVE && work->target->class & TARGET_POWER ) + { + work->hom->flag = TRUE; + } + else + { + work->hom->flag = FALSE; + } + + if ( unk->field_04 < 0 && ctrl->grounded ) + { + unk->field_04 = 0; + } + + v0 = unk->field_04 - 16; + unk->field_04 = v0; + ctrl->step.vy = v0; +} diff --git a/components/mgs/mgs/source/animal/meryl72/check.c b/components/mgs/mgs/source/animal/meryl72/check.c new file mode 100644 index 00000000..c984daee --- /dev/null +++ b/components/mgs/mgs/source/animal/meryl72/check.c @@ -0,0 +1,380 @@ +#include "meryl72.h" + +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" + +extern void s07c_meryl72_unk1_800CA408( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800CA538( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800C9F98( Meryl72Work *work, int time ) ; +extern int s07c_meryl72_unk1_800C7D1C( Meryl72Work* work ) ; +extern int s07c_meryl72_unk1_800C7C9C( Meryl72Work* work ) ; +extern int s07c_meryl72_unk1_800C7F14( Meryl72Work* work ) ; +extern void sna_act_helper2_helper2_80033054(int id, SVECTOR *vec); + +void ExecProc_800C7C58( Meryl72Work *work, int mode ) +{ + GCL_ARGS args ; + long data[4] ; + if ( work->proc_id < 0 ) return ; + args.argc = 1 ; + args.argv = data ; + data[0] = mode ; + GCL_ExecProc( work->proc_id, &args ) ; +} + +extern void s07c_meryl72_unk1_800C9970( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800C9B30( Meryl72Work *work, int time ) ; + +int s07c_meryl72_unk1_800C7C9C( Meryl72Work* work ) +{ + TARGET *target; + + target= work->target; + if ( target->damaged & 2 ) + { + if ( target->a_mode == 0x24 ) + { + SetMode( work, s07c_meryl72_unk1_800C9970 ) ; + } + else + { + SetMode( work, s07c_meryl72_unk1_800C9B30 ) ; + } + target->damaged = 0; + return 1; + } + return 0; +} + +int s07c_meryl72_unk1_800C7D00( Meryl72Work* work ) +{ + int check; + check = work->target->damaged & 0x20; + return check > 0; +} + +int s07c_meryl72_unk1_800C7D1C( Meryl72Work *work ) +{ + TARGET *target; + + target = work->target; + if ( ( target->damaged & TARGET_POWER ) ) + { + switch ( target->a_mode ) + { + case 3: + target->force = DG_ZeroVector; + target->vital -= GM_SnakeMaxHealth / 48; + + if ( ( target->faint <= 0 ) || ( ( target->vital << 16 ) <= 0 ) ) + { + work->f8BC.field_14 = 1; + SetMode( work, s07c_meryl72_unk1_800CA538 ) ; + + } + else + { + SetMode( work, s07c_meryl72_unk1_800CA408 ) ; + } + break; + case 4: + work->f8BC.field_14 = 0; + target->vital -= GM_SnakeMaxHealth / 32; + SetMode( work, s07c_meryl72_unk1_800CA538 ) ; + break; + case 1: + target->force = DG_ZeroVector; + if ( target->vital <= 0 ) + { + work->f8BC.field_14 = 1; + SetMode( work, s07c_meryl72_unk1_800CA538 ) ; + } + else + { + SetMode( work, s07c_meryl72_unk1_800CA408 ) ; + } + break; + case 2: + work->f8BC.field_14 = 3; + SetMode( work, s07c_meryl72_unk1_800CA538 ) ; + } + + target->damage = 0; + target->damaged = 0; + + if ( target->vital <= 0 && !GM_GameOverTimer && GM_SnakeCurrentHealth > 0 ) + { + ExecProc_800C7C58( work, 1 ) ; + GM_GameOver(); + GM_GameOverTimer = -2; + } + + return 1; + } + return 0; +} + +int s07c_meryl72_unk1_800C7F14(Meryl72Work* work) +{ + if ( GM_GameStatus & 2 && work->control.map->index & GM_PlayerMap) + { + SetMode(work, s07c_meryl72_unk1_800C9F98); + return 1; + } + return 0; +} + +int CheckDamage_800C7F6C(Meryl72Work* work ) +{ + if ( s07c_meryl72_unk1_800C7D1C( work ) || s07c_meryl72_unk1_800C7C9C( work ) || s07c_meryl72_unk1_800C7F14( work ) ) + { + work->target->class = TARGET_AVAIL ; + return 1; + } + return 0; +} + +int s07c_meryl72_unk1_800C7FCC( int dir, int dist ) +{ + int diff ; + if ( dist < 0 || dir < 0 ) return 0 ; + + diff = GV_DiffDirS( dir, dist ) ; + + if ( diff + 0x380 > 0x700u ) return 0 ; + if ( diff >= 0 ) return 1 ; + + return 2 ; +} + +void ReviseReadyGun_800C8020( Meryl72Work* work ) +{ + int trans; + int near; + + trans = ( ( GM_PlayerBody->objs->objs[6].world.t[1] - work->body.objs->objs[6].world.t[1] ) ); + trans = ( ratan2( work->sn_dis, trans ) & 0xFFF ) - 0x400; + + if (trans > 0x100) + { + trans = 0x100; + } + else if (trans < -0x100) + { + trans = -0x100; + } + + work->rots[21].vx = trans; + near = GV_DiffDirS( work->control.rot.vy, work->sn_dir ); + if ( ( near + 0x1FF ) < 0x3FFu) + { + work->rots[21].vy = near; + } +} + +void s07c_meryl72_unk1_800C80B4( Meryl72Work* work ) +{ + sna_act_helper2_helper2_80033054( work->control.name, &work->rots[22] ); +} + +const SVECTOR rp_shift_800D4830 = { -200, 100, 900 }; +const SVECTOR rp_shift2_800D4838 = { -200, -500, 600 }; +const SVECTOR force_800D4840 = { 5, 0, 100 }; +const SVECTOR size_800D4848 = { 400, 200, 400 }; + +int AttackForce_800C80DC( Meryl72Work * work, int check ) +{ + TARGET *target; + + SVECTOR svec; + SVECTOR rp_shift; + SVECTOR rp_shift2; + SVECTOR force; + SVECTOR force2; + SVECTOR size; + + rp_shift = rp_shift_800D4830; /* 右パンチ */ + rp_shift2 = rp_shift2_800D4838; + force = force_800D4840; + memset(&force2, 0, 8); + size = size_800D4848; + + target = &work->punch; + GM_SetTarget( target, TARGET_POWER, ENEMY_SIDE, &size ); + DG_SetPos2( &work->control.mov, &work->control.rot ); + + if ( !check ) + { + DG_RotVector( &force, &svec, 1 ); + GM_SetPowerTarget( target, POWER_ONCE, 2, 16, 1, &svec ); + DG_PutVector( &rp_shift, &work->punch.center, 1 ); + } + else + { + DG_RotVector( &force2, &svec, 1 ); + GM_SetPowerTarget( target, POWER_ONCE, 2, 16, 1, &svec ); + DG_PutVector( &rp_shift2, &work->punch.center, 1 ); + } + + return GM_PowerTarget( target ); +} + +int s07c_meryl72_unk1_800C829C( Meryl72Work * work ) +{ + if ( GM_CurrentItemId != 8 ) + { + if ( ( GM_SnakeCurrentHealth < 17 ) ) + { + if ( GM_CurrentItemId == 13 ) + { + if ( 1 == GM_FrozenItemsState ) + { + return 1; + } + } + else + { + return 1; + } + } + } + else if ( GM_SnakeCurrentHealth < 9 ) + { + return 1; + } + return 0; +} + +int CheckPad_800C8308( Meryl72Work *work ) +{ + int press = work->pad.press; + + if ( press & 0x01 ) + { + SetMode2( work, s07c_meryl72_unk1_800CAD30 ); + return 0; + } + + if ( press & 0x02 ) + { + SetMode2( work, s07c_meryl72_unk1_800CADEC ); + return 0; + } + + if ( press & 0x04 ) + { + SetMode2( work, ActOverScoutD_800CAEA8 ); + return 0; + } + + if ( press & 0x2000 ) + { + SetMode2( work, s07c_meryl72_unk1_800CAF30 ); + return 0; + } + + if ( press & 0x40 ) + { + SetMode2( work, s07c_meryl72_unk1_800CAFB8 ); + return 0; + } + + if ( press & 0x80 ) + { + SetMode2( work, s07c_meryl72_unk1_800CB038 ); + return 0; + } + + if ( press & 0x400 ) + { + SetMode2( work, s07c_meryl72_unk1_800CB0B8 ); + return 0; + } + + if ( press & 0x800 ) + { + SetMode2( work, s07c_meryl72_unk1_800CB134 ); + return 0; + } + + if ( press & 0x200 ) + { + SetMode2( work, s07c_meryl72_unk1_800CB1B4 ); + return 0; + } + + if ( press & 0x100 ) + { + SetMode( work, s07c_meryl72_unk1_800C88EC ); + return 1; + } + + if ( press & 0x20 ) + { + SetMode( work, s07c_meryl72_unk1_800C8970 ); + return 1; + } + + if ( press & SP_DANBOWLKERI ) + { + SetMode( work, s07c_meryl72_unk1_800C8A30 ); + return 1; + } + + if ( press & 0x4000 ) + { + SetMode( work, s07c_meryl72_unk1_800C8BC4 ); + return 1; + } + + if ( press & 0x800000 ) + { + SetMode( work, s07c_meryl72_unk1_800C8E74 ); + return 1; + } + + if ( press & 0x1000000 ) + { + SetMode( work, s07c_meryl72_unk1_800C8C7C ); + return 1; + } + + if ( press & 0x2000000 ) + { + SetMode( work, s07c_meryl72_unk1_800C9000 ); + return 1; + } + + if ( press & 0x4000000 ) + { + SetMode( work, s07c_meryl72_unk1_800C90C8 ); + return 1; + } + + if ( press & 0x20000000 ) + { + SetMode( work, s07c_meryl72_unk1_800C9190 ); + return 1; + } + + if ( press & 0x10000000 ) + { + SetMode( work, s07c_meryl72_unk1_800C9258 ); + return 1; + } + + if ( press & 0x40000000 ) + { + SetMode( work, s07c_meryl72_unk1_800C9318 ); + return 1; + } + + if ( press & 0x30000 ) + { + SetMode( work, ActReadyGun_800C9428 ); + return 1; + } + + return 0; +} \ No newline at end of file diff --git a/components/mgs/mgs/source/animal/meryl72/meryl72.c b/components/mgs/mgs/source/animal/meryl72/meryl72.c new file mode 100644 index 00000000..0ef79f02 --- /dev/null +++ b/components/mgs/mgs/source/animal/meryl72/meryl72.c @@ -0,0 +1,677 @@ +#include +#include "common.h" +#include "mts/mts.h" // for fprintf +#include "chara/snake/shadow.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "libhzd/libhzd.h" +#include "game/game.h" + +#include "linkvar.h" +#include "strcode.h" + +#include "meryl72.h" + +extern GM_CameraSystemWork GM_Camera; +extern UnkCameraStruct2 gUnkCameraStruct2_800B7868; + +int SECTION(".bss") meryl72_800D5274; +GM_CameraSystemWork SECTION(".bss") meryl72_camera_800D5278; +int SECTION(".bss") meryl72_800D52F4; +SVECTOR SECTION(".bss") meryl72_800D52F8; + +void Meryl72ActionMain_800CBC44( Meryl72Work * ); +void s07c_meryl72_unk1_800CBCD8( Meryl72Work * ); + +void s07c_meryl72_unk2_800D0220( void ); +void s07c_meryl72_unk2_800D025C( void ); + +#define EXEC_LEVEL GV_ACTOR_PREV + +void s07c_meryl72_800C6AF8( Meryl72Work *work ) +{ + int lo, hi; + + lo = GM_PlayerAddress & 0xFF; + hi = ( GM_PlayerAddress >> 8 ) & 0xFF; + + if ( lo == hi && lo != 0xFF ) + { + work->player_addr = GM_PlayerAddress; + work->player_pos = GM_PlayerPosition; + work->player_map = GM_PlayerMap; + } +} + +int RootFlagCheck_800C6B5C( Meryl72Work *work ) +{ + SVECTOR sp10; + CONTROL *control; + GV_MSG *msg; + int i; + + control = &work->control; + control->n_msg = GV_ReceiveMessage( control->name, &control->msg ); + + msg = control->msg; + for ( i = control->n_msg; i > 0; i-- ) + { + if ( msg->message[0] == GV_StrCode( "ルート変更" ) ) + { + work->fB0B = msg->message[1]; + + sp10.vx = msg->message[2]; + sp10.vy = msg->message[3]; + sp10.vz = msg->message[4]; + + if ( sp10.vy < 30000 ) + { + work->param.defends[0] = HZD_GetAddress( work->control.map->hzd, &sp10, -1 ); + } + else + { + work->param.defends[0] = 255; + } + + return 1; + } + + msg++; + } + + return 0; +} + +MENU_BAR_CONF s07c_dword_800C32E4 = {"MERYL", {31, 191, 95}, {31, 191, 95}}; + +void s07c_meryl72_800C6C48( Meryl72Work *work ) +{ + PARAM *param; + TARGET *target; + + param = &work->param; + target = work->target; + + if ( param->life != target->vital ) + { + param->life = target->vital; + work->fC36 = 150; + } + + if ( param->life < 0 ) + { + param->life = 0; + } + + if ( param->life < param->max_life ) + { + param->max_life -= 4; + } + + MENU_DrawBar2( 28, param->max_life, param->life, GM_SnakeMaxHealth, &s07c_dword_800C32E4 ); + + if ( work->fC36 > 0 ) + { + work->fC36--; + } + else if ( param->life > ( GM_SnakeMaxHealth / 2 ) ) + { + GM_GameStatus &= ~STATE_SHOW_LIFEBAR; + } +} + +void Meryl72Act_800C6D54( Meryl72Work *work ) +{ + CONTROL *control; + TARGET *target; + + control = &work->control; + + if ( GM_CheckMessage( &work->actor, control->name, HASH_KILL ) ) + { + GV_DestroyActor( &work->actor ); + return; + } + + s07c_meryl72_unk1_800CBCD8( work ); + + GM_ActControl( control ); + GM_ActObject2( &work->body ); + GM_ActObject2( &work->weapon ); + + DG_GetLightMatrix2( &control->mov, work->light ); + + s07c_meryl72_800C6AF8( work ); + RootFlagCheck_800C6B5C( work ); + Meryl72ActionMain_800CBC44( work ); + + target = work->target; + GM_MoveTarget( target, &control->mov ); + GM_PushTarget( target ); + + s07c_meryl72_800C6C48( work ); + work->fC04++; + + meryl72_800D52F8 = work->control.mov; +} + +SVECTOR s07c_dword_800C32F0 = { 300, 1500, 300, 0 }; +SVECTOR s07c_dword_800C32F8 = { 0, 0, 0, 0 }; +SVECTOR s07c_dword_800C3300 = { 100, 300, 100, 0 }; +SVECTOR s07c_dword_800C3308 = { 100, 0, 0, 0 }; + +void s07c_meryl72_800C6E48( Meryl72Work *work ) +{ + TARGET *target; + int life; + int faint; + + target = work->target; + life = work->param.life; + faint = work->param.faint; + + GM_SetTarget( target, TARGET_FLAG, ENEMY_SIDE, &s07c_dword_800C32F0 ); + GM_SetPowerTarget( target, POWER_DECREASE, -1, life, faint, &s07c_dword_800C32F8 ); + GM_SetCaptureTarget( target, -1, faint, NULL, NULL ); + GM_TargetBody( target, &work->body.objs->objs[1].world ); + + GM_SetTarget( &work->target2, TARGET_POWER | TARGET_TOUCH, PLAYER_SIDE, &s07c_dword_800C3300 ); + GM_SetPowerTarget( &work->target2, POWER_CONST | POWER_EXPLODE, 5, 0, 0, &s07c_dword_800C3308 ); +} + +void s07c_meryl72_800C6F30( Meryl72Work *work ) +{ + UNK *unk; + + unk = &work->f8BC; + GV_ZeroMemory( unk, sizeof(UNK) ); + unk->field_00 = 0; + unk->field_1A = 450; + unk->field_1C = 1; + + work->action = 0; + work->action2 = 0; + work->time = 0; + work->time2 = 0; +} + +int s07c_meryl72_800C6F8C( Meryl72Work *work, int name, int map ) +{ + SVECTOR indices; + CONTROL *control; + char *pos; + char *dir; + OBJECT *body; + OBJECT *weapon; + int motion; + + control = &work->control; + + if ( GM_InitControl( control, name, map ) < 0 ) + { + return -1; + } + + pos = GCL_GetOption( 'p' ); + dir = GCL_GetOption( 'd' ); + GM_ConfigControlString( control, pos, dir ); + GM_ConfigControlAttribute( control, RADAR_ALL_MAP | RADAR_SIGHT | RADAR_VISIBLE ); + GM_ConfigControlInterp( control, 4 ); + control->seg_flag = 2; + GM_ConfigControlTrapCheck( control ); + + body = &work->body; + weapon = &work->weapon; + + if ( work->stage == 0 ) + { + motion = MOTION_DATA; + } + else + { + motion = MOTION_DATA2; + } + + GM_InitObject( body, BODY_DATA, BODY_FLAG, motion ); + GM_ConfigObjectJoint( body ); + GM_ConfigMotionControl( body, &work->m_ctrl, motion, work->m_segs1, work->m_segs2, control, work->rots ); + GM_ConfigObjectLight( body, work->light ); + + work->hom = GM_AllocHomingTarget( &work->body.objs->objs[6].world, control ); + + GM_InitObject( weapon, WEAPON_DATA, WEAPON_FLAG, 0 ); + GM_ConfigObjectLight( weapon, work->light ); + GM_ConfigObjectRoot( weapon, body, 4 ); + + indices.vx = 0; + indices.vy = 6; + indices.vz = 12; + indices.pad = 15; + work->shadow = (GV_ACT *)NewShadow2( control, body, indices, &work->shadow_enable ); + + ML72_SetPutChar_800CB584( work, 2 ); + + if ( work->stage != 0 ) + { + ML72_SetPutChar_800CB584( work, 4 ); + } + + s07c_meryl72_800C6F30( work ); + return 0; +} + +void s07c_meryl72_800C7194( Meryl72Work *work ) +{ + GCL_ARGS args; + long data[4]; + + GM_FreeHomingTarget( work->hom ); + GM_FreeControl( &work->control ); + GM_FreeObject( &work->body ); + GM_FreeObject( &work->weapon ); + GM_FreeTarget( work->target ); + GV_DestroyActor( work->shadow ); + + if ( work->fC3C >= 0 ) + { + args.argc = 1; + args.argv = data; + + data[0] = work->target->vital; + GCL_ForceExecProc( work->fC3C, &args ); + } +} + +void s07c_meryl72_800C722C( void ) +{ + printf( " type=0x%x\n", gUnkCameraStruct2_800B7868.type ); + printf( " rotate x=%d y=%d z=%d\n", + gUnkCameraStruct2_800B7868.rotate.vx, + gUnkCameraStruct2_800B7868.rotate.vy, + gUnkCameraStruct2_800B7868.rotate.vz ); + printf( " track=%d\n", gUnkCameraStruct2_800B7868.track ); + + memcpy( &meryl72_camera_800D5278, &GM_Camera, sizeof(GM_Camera) ); + + GM_Camera.rotate.vx = 480; + GM_Camera.type = 2; + GM_Camera.interp_mode = 2; + GM_Camera.interp = 0; + GM_Camera.flag |= 0x2; +} + +void s07c_meryl72_800C730C(void) +{ + memcpy( &GM_Camera, &meryl72_camera_800D5278, sizeof(GM_Camera) ); +} + +void s07c_meryl72_800C7368( void ) +{ + GM_Camera.track = GV_NearExp8( GM_Camera.track, 4000 ); + printf( " track=%d\n", GM_Camera.track ); +} + +void Meryl72Die_800C73AC( Meryl72Work *work ) +{ + s07c_meryl72_800C7194( work ); +} + +int s07c_meryl72_800C73CC( Meryl72Work *work ) +{ + PARAM *param; + int c_root; + HZD_PAT *pat; + HZD_PTP *points; + int i; + + param = &work->param; + c_root = param->c_root; + + pat = work->control.map->hzd->def->routes; + pat += param->roots[ c_root ]; + + fprintf( 1, "c_root= %d pat %d n_points = %d \n", c_root, (int)pat, pat->n_points ); + + work->n_patrols = pat->n_points; + if ( work->n_patrols <= 0 ) + { + return -1; + } + + points = pat->points; + for ( i = 0; i < work->n_patrols; i++ ) + { + work->nodes[ i ].vx = points->x; + work->nodes[ i ].vy = points->y; + work->nodes[ i ].vz = points->z; + work->nodes[ i ].pad = points->command; + points++; + + fprintf( 1, "action = 0x%x \n", work->nodes[ i ].pad ); + } + + return 0; +} + +int s07c_meryl72_800C74E0( char *opt, char *roots ) +{ + int count; + char *param; + + count = 0; + while ( ( param = GCL_NextStr() ) ) + { + *roots++ = GCL_StrToInt( param ); + count++; + } + + return count; +} + + +int s07c_meryl72_800C7538( char *opt, int *voices ) +{ + int count; + char *param; + + count = 0; + while ( ( param = GCL_NextStr() ) ) + { + *voices++ = GCL_StrToInt( param ); + count++; + } + + return count; +} + +int s07c_meryl72_800C7590( Meryl72Work *work ) +{ + char *opt; + + opt = GCL_GetOption( 'v' ); + if ( opt && s07c_meryl72_800C7538( opt, work->voices ) > 25 ) + { + fprintf( 0, "Err Err Err Sound Buff Over !!\n" ); + return -1; + } + + return 0; +} + +int s07c_meryl72_800C75F0( Meryl72Work *work, char *opt, char *defends ) +{ + SVECTOR zone; + int count; + char *param; + + count = 0; + while ( ( param = GCL_NextStr() ) ) + { + *defends = GCL_StrToInt( param ); + GCL_StrToSV( param, (short *)&zone ); + + if ( zone.vy < 30000 ) + { + *defends = HZD_GetAddress( work->control.map->hzd, &zone, -1 ); + } + else + { + *defends = 255; + } + + printf( " defence zone =%d ", *defends ); + + count++; + defends++; + } + + return count; +} + +// unused for now +const char s07c_aErrnotenoughwork_800D4788[] = "Err not enough work !!\n"; + +int s07c_meryl72_800C76B0( Meryl72Work *work ) +{ + int i; + char *opt; + + for ( i = 0; i < 6; i++ ) + { + work->fC1C[ i ] = -1; + } + + i = 0; + + opt = GCL_GetOption( 'a' ); + while ( opt ) + { + if ( i >= 6 ) + { + return -1; + } + + work->fC1C[ i ] = GCL_StrToInt( opt ); + opt = GCL_NextStr(); + i++; + } + + return i; +} + +int Meryl72GetResources_800C7738( Meryl72Work *work, int arg1, int arg2 ) +{ + char *opt; + int i; + CONTROL *control; + int addr; + + work->stage = 0; + + opt = GCL_GetOption( 's' ); + if ( opt ) + { + work->stage = GCL_StrToInt( opt ); + } + + work->fB96 = 0; + + opt = GCL_GetOption( 'c' ); + if ( opt ) + { + work->fB96 = GCL_StrToInt( opt ); + } + + if ( s07c_meryl72_800C6F8C( work, arg1, arg2 ) ) + { + return -1; + } + + if ( s07c_meryl72_800C7590( work ) < 0 ) + { + return -1; + } + + if ( s07c_meryl72_800C76B0( work ) < 0 ) + { + return -1; + } + + work->param.fAF8 = 0; + work->param.roots[0] = 0; + work->param.c_root = 0; + + opt = GCL_GetOption( 'r' ); + if ( opt && s07c_meryl72_800C74E0( opt, work->param.roots ) >= 4 ) + { + fprintf( 0, "Err Err Err Change Root Num Over !!\n" ); + } + + work->fB58 = 2000; + work->fB0B = work->param.roots[0]; + work->param.life = GM_SnakeMaxHealth; + + opt = GCL_GetOption( 'l' ); + if ( opt ) + { + work->param.life = GCL_StrToInt( opt ); + } + + if ( work->param.life < 32 ) + { + work->param.life = 512; + } + + work->param.max_life = work->param.life; + printf( "Meryl life = %d \n", work->param.max_life ); + + work->param.faint = 20; + + opt = GCL_GetOption( 'f' ); + if ( opt ) + { + work->param.faint = GCL_StrToInt( opt ); + } + + work->param.fAF9 = 65; + + opt = GCL_GetOption( 'b' ); + if ( opt ) + { + work->param.fAF9 = GCL_StrToInt( opt ); + } + + work->param.defends[ work->param.c_root ] = 255; + + opt = GCL_GetOption( 'n' ); + if ( opt && s07c_meryl72_800C75F0( work, opt, work->param.defends ) >= 4 ) + { + fprintf( 0, "Err Err Err Change Defend Num Over !!\n" ); + } + + work->param.fAFA = 65; + + opt = GCL_GetOption( 'a' ); + if ( opt ) + { + work->param.fAFA = GCL_StrToInt( opt ); + } + + work->fB0A = 1; + + if ( s07c_meryl72_800C73CC( work ) < 0 ) + { + fprintf( 1, "watchar.c : action point Err\n" ); + } + + if ( GCL_GetOption( 'e' ) ) + { + work->fC3C = GCL_StrToInt( GCL_NextStr() ); + } + else + { + work->fC3C = -1; + } + + if ( GCL_GetOption( 'z' ) ) + { + work->proc_id = GCL_StrToInt( GCL_NextStr() ); + } + else + { + work->proc_id = -1; + } + + work->target = GM_AllocTarget(); + if ( work->target ) + { + s07c_meryl72_800C6E48( work ); + } + + GM_SetCameraCallbackFunc( 1, s07c_meryl72_800C7368 ); + + work->think1 = 0; + work->think2 = 0; + work->think3 = 0; + work->think4 = 0; + work->count3 = 0; + work->next_node = 0; + + work->target_pos = work->nodes[ 0 ]; + work->target_addr = HZD_GetAddress( work->control.map->hzd, &work->target_pos, -1 ); + work->target_map = GM_CurrentMap; + + work->vision.angle = 2048; //vision length + work->vision.length = 4000; //vision angle + work->fB18 = 0; + work->vision.facedir = 0; + work->pad.sound = 0; + work->pad.time = 0; + work->fB28 = DG_ZeroVector; + work->subweapon = 0; + work->fB98 = 0; + work->fC0A = 0; + work->fC0C = 0; + work->fC0E = 0; + + if ( work->fB96 != 0 ) + { + work->control.mov = work->nodes[ 3 ]; + } + else + { + work->control.mov = work->nodes[ 0 ]; + } + + work->fC10[0] = work->fC10[1] = work->fC10[2] = 0; + + work->fC34 = 0; + work->fC36 = 0; + + for ( i = 0; i < 8; i++ ) + { + work->modetime[ i ] = 0; + } + + work->modetime[ 7 ] = 23; + work->modetime[ 4 ] = -1; + + control = &work->control; + GM_ConfigControlRadarparam( control, 0, 4000, 2048, 0 ); + + work->start_pos = work->nodes[ 0 ]; + work->start_map = GM_CurrentMap; + + addr = HZD_GetAddress( work->control.map->hzd, &control->mov, -1 ); + work->start_addr = addr; + work->fB60 = addr; + work->fB4C = addr; + + work->fB6C = work->start_pos; + work->fC04 = 0; + work->fAF4 = 0; + + s07c_meryl72_unk2_800D025C(); + s07c_meryl72_unk2_800D0220(); + + return 0; +} + +void *NewMeryl72_800C7BC4( int arg0, int arg1 ) +{ + Meryl72Work *work; + + work = GV_NewActor( EXEC_LEVEL, sizeof(Meryl72Work) ); + if (work) + { + GV_SetNamedActor( &work->actor, Meryl72Act_800C6D54, Meryl72Die_800C73AC, "meryl72.c" ); + + if ( Meryl72GetResources_800C7738( work, arg0, arg1 ) < 0 ) + { + GV_DestroyActor( &work->actor ); + return NULL; + } + } + + return (void *)work; +} \ No newline at end of file diff --git a/components/mgs/mgs/source/animal/meryl72/meryl72.h b/components/mgs/mgs/source/animal/meryl72/meryl72.h new file mode 100644 index 00000000..2aefa9a2 --- /dev/null +++ b/components/mgs/mgs/source/animal/meryl72/meryl72.h @@ -0,0 +1,314 @@ +#ifndef __MGS_ANIMAL_MERYL72_H__ +#define __MGS_ANIMAL_MERYL72_H__ + +#include "common.h" +#include "game/game.h" +#include "linkvar.h" +#include "game/vibrate.h" +#include "anime/animconv/anime.h" +#include "weapon/weapon.h" + +typedef struct _PARAM +{ + signed char fAF8; + char fAF9; + char fAFA; + signed char c_root; + char defends[4]; + signed char roots[4]; + short life; + short max_life; // for the red damage bar + short faint; +} PARAM; + +typedef struct UNK +{ + int field_00; //0x00 //0x8BC + short field_04; //0x04 //0x8C0 + short field_06; //0x06 //0x8C2 + int field_08; //0x08 //0x8C4 + int field_0C; //0x0C //0x8C8 + int field_10; //0x10 //0x8CC + int field_14; //0x14 //0x8D0 + short field_18; //0x18 //0x8D4 + short field_1A; //0x1A //0x8D6 + short field_1C; //0x1C //0x8D8 + short field_1E; //0x1E //0x8DA +} UNK; + +typedef struct _Meryl72Pad +{ + int press; //0x04 //0xACC + int mode; //0x08 //0xAD0 + int tmp; //0x0A //0xAD4 + short time; //0x0C //0xAD8 + short dir; //0x10 //0xADA + short sound; //0x12 //0xADC + short field_14; //0x14 //0xADE +} Meryl72Pad; + + +typedef struct _VISION +{ + short facedir; //0xB10 + short angle; //0xB12 + short length; //0xB14 + short field_06; //0xB16 +} VISION; + +typedef struct Meryl72Work +{ + GV_ACT actor; + CONTROL control; //0x20 + OBJECT body; //0x9C + MOTION_CONTROL m_ctrl; + MOTION_SEGMENT m_segs1[17]; + MOTION_SEGMENT m_segs2[17]; + SVECTOR rots[32]; + OBJECT weapon; //0x798 + MATRIX light[2]; //0x87C + UNK f8BC; //0x8BC + void* action; //0x8DC + void* action2; //0x8E0 + int time; //0x8E4 + int time2; //0x8E8 + int actend; //0x8EC + TARGET *target; //0x8F0 + TARGET target2; + TARGET punch; + HOMING *hom; + int n_patrols; + SVECTOR nodes[32]; //0x98C + GV_ACT *shadow; + int *shadow_enable; + GV_ACT *glight; + int *glight_enable; + void *fA9C[8]; + short think1; + short think2; + short think3; + short think4; + int count3; + int next_node; + Meryl72Pad pad; //0xACC + u_long trigger; //0xAE0 + GV_ACT *subweapon; //0xAE4 + short fAE8; + short fAEA; + short fAEC; + short fAEE; + int fAF0; + int fAF4; + PARAM param; //0xAF8 + char fB0A; + char fB0B; + int fB0C; + VISION vision; //0xB10 + int fB18; + signed char modetime[8]; //0xB1C + int act_status; //0xB24 + SVECTOR fB28; + int fB2C; + SVECTOR start_pos; //0xB34 + SVECTOR target_pos; //0xB3C + int start_addr; //0xB44 + int start_map; //0xB48 + int fB4C; + int target_addr; //0xB50 + int target_map; //0xB54 + int fB58; + int fB5C; + int fB60; + int fB64; + int fB68; + SVECTOR fB6C; + int field_B74; + int field_B78; + int sn_dis; + int sn_dir; + int player_addr; + SVECTOR player_pos; + int player_map; + short stage; + short fB96; + int fB98; + int fB9C; + int voices[25]; + int fC04; + short fC08; + short fC0A; + short fC0C; + short fC0E; + int fC10[3]; + int fC1C[6]; + short fC34; + short fC36; + int fC38; + int fC3C; + int proc_id; +} Meryl72Work; + +//not sure if this goes in c or h but some pa +#define MOTION_DATA GV_StrCode( "mel_07a" ) /* ステージ7モーションデータ */ +#define MOTION_DATA2 GV_StrCode( "mel_09a" ) /* ステージ9モーションデータ */ +#define BODY_DATA GV_StrCode( "meryl" ) /* モデルデータ */ +#define WEAPON_DATA GV_StrCode( "desert" ) /* 初期手持ち武器モデルデータ */ + +#define TARGET_FLAG ( TARGET_POWER | TARGET_CAPTURE | TARGET_PUSH | TARGET_TOUCH | TARGET_SEEK ) +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_GBOUND | DG_FLAG_SHADE | DG_FLAG_AMBIENT | DG_FLAG_IRTEXTURE ) +#define WEAPON_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_GBOUND | DG_FLAG_SHADE | DG_FLAG_ONEPIECE ) +//#define SHADOW_PRIM_TYPE ( DG_PRIM_P... + +typedef void ( *ACTION )( Meryl72Work *, int ) ; +typedef void ( *PUTFUNC )( Meryl72Work * ) ; +/* +static inline void SetModeFields( Meryl72Work *work, ACTION action ) +{ + work->action = action; + work->time = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; +} +*/ +static inline void SetMode( Meryl72Work *work, ACTION action ) +{ + work->action = action; + work->time = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; + //GM_ConfigMotionAdjust( &( work->body ), 0 ); +} + +static inline void SetMode2( Meryl72Work *work, void *func ) +{ + if ( work->action2 == NULL ) + { + work->action2 = func; + work->time2 = 0; + } + + work->control.turn.vz = 0; + work->control.turn.vx = 0; + GM_ConfigMotionAdjust( &( work->body ), 0 ); +} + +//action.c +#define COM_ST_DANBOWL 0x2000 +#define SP_DANBOWLKERI 0x400000 + +#define GUNSHOOT 3 + +#define ACTINTERP 4 + +#define STANDSTILL 0 +#define ACTION1 1 +#define ACTION2 2 +#define ACTION3 3 +#define ACTION4 4 +#define ACTION5 5 +#define ACTION6 6 +#define GRENADE 7 +#define ACTION8 8 +#define ACTION9 9 +#define ACTION10 10 +#define ACTION11 11 +#define ACTION12 12 +#define ACTION13 13 +#define ACTION14 14 +#define ACTION15 15 +#define ACTION16 16 +#define DANBOWLKERI 17 +#define DANBOWLPOSE 18 +#define ACTION19 19 +#define ACTION20 20 +#define ACTION21 21 +#define ACTION22 22 +#define ACTION23 23 +#define ACTION24 24 +#define ACTION25 25 +#define ACTION26 26 +#define ACTION27 27 +#define ACTION28 28 +#define ACTION29 29 +#define ACTION30 30 +#define ACTION31 31 +#define ACTION32 32 +#define ACTION33 33 +#define ACTION34 34 +#define ACTION35 35 +#define ACTION36 36 +#define ACTION37 37 +#define ACTION38 38 +#define ACTION39 39 +#define ACTION40 40 +#define ACTION41 41 +#define ACTION42 42 +#define ACTION43 43 +#define ACTION44 44 +#define ACTION45 45 +#define ACTION46 46 +#define ACTION47 47 +#define ACTION48 48 +#define ACTION49 49 +#define ACTION50 50 +#define ACTION51 51 +#define ACTION52 52 +#define ACTION53 53 +#define ACTION54 54 +#define ACTION55 55 +#define ACTION56 56 +#define ACTION57 57 +#define ACTION58 58 + +//check funcs +int CheckPad_800C8308( Meryl72Work *work ) ; +int CheckDamage_800C7F6C(Meryl72Work* work ) ; +void ReviseReadyGun_800C8020( Meryl72Work* work ) ; +int s07c_meryl72_unk1_800C829C( Meryl72Work* work ) ; +int s07c_meryl72_unk1_800C7D00( Meryl72Work* work ) ; +int s07c_meryl72_unk1_800C7FCC( int dir, int dist ) ; +void s07c_meryl72_unk1_800C80B4( Meryl72Work* work ) ; +void ExecProc_800C7C58( Meryl72Work *work, int mode ) ; +int AttackForce_800C80DC( Meryl72Work * work, int check ) ; + +//action funcs +extern void s07c_meryl72_unk1_800C88EC( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800C8970( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800C8A30( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800C8BC4( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800C8E74( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800C8C7C( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800C9000( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800C90C8( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800C9318( Meryl72Work *work, int time ) ; +extern void ActReadyGun_800C9428( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800C9258( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800C9190( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800C9594( Meryl72Work* work, int time ) ; +extern void s07c_meryl72_unk1_800C964C( Meryl72Work* work, int time ) ; +extern void ActGrenade_800C9790( Meryl72Work* work, int time ) ; +extern void s07c_meryl72_unk1_800CA0EC( Meryl72Work* work, int time ) ; +extern void s07c_meryl72_unk1_800CA538( Meryl72Work* work, int time ) ; +extern void s07c_meryl72_unk1_800CAA48( Meryl72Work* work, int time ) ; +extern void s07c_meryl72_unk1_800CA314( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800CA278( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800C98E0( Meryl72Work* work, int time ) ; + +//override funcs +extern void s07c_meryl72_unk1_800CAD30( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800CADEC( Meryl72Work *work, int time ) ; +extern void ActOverScoutD_800CAEA8( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800CAF30( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800CAFB8( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800CB038( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800CB0B8( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800CB134( Meryl72Work *work, int time ) ; +extern void s07c_meryl72_unk1_800CB1B4( Meryl72Work *work, int time ) ; + +//put funcs +extern void ML72_PutBreath_800CB35C( Meryl72Work* ) ; +extern void ML72_PutBlood_800CB2EC( Meryl72Work*, int, int ) ; +extern int ML72_SetPutChar_800CB584( Meryl72Work *work, int idx ); +extern int ML72_ClearPutChar_800CB5CC( Meryl72Work *work, void* func ) ; + +#endif // __MGS_ANIMAL_MERYL72_H__ diff --git a/components/mgs/mgs/source/animal/meryl72/ml72act.c b/components/mgs/mgs/source/animal/meryl72/ml72act.c new file mode 100644 index 00000000..7572aa44 --- /dev/null +++ b/components/mgs/mgs/source/animal/meryl72/ml72act.c @@ -0,0 +1,330 @@ +#include "meryl72.h" +#include "libhzd/libhzd.h" +#include "mts/mts.h" +#include "game/game.h" + +#define SEGMENT_ATR ( HZD_SEG_NO_PLAYER ) + +void ML72_ExecPutChar_800CB600(Meryl72Work *); +void s07c_meryl72_unk1_800CABA0(Meryl72Work *); + +void Meryl72Think_800CEA04(Meryl72Work *); +void Meryl9Think_800D0154(Meryl72Work *); + +void s07c_meryl72_unk1_800CB660(Meryl72Work *work) +{ + RADAR_SIGHT_PARAM *r_param; + + r_param = &work->control.radar_param; + r_param->dir = work->vision.facedir; + r_param->dis = work->vision.length; + r_param->range = 1024; + r_param->r = 0; +} + +void s07c_meryl72_unk1_800CB688(Meryl72Work *work) +{ + if (work->modetime[7] & 0x4) + { + if (work->target->damaged & (TARGET_POWER | TARGET_CAPTURE)) + { + work->modetime[6] |= 0x4; + } + + if (work->target2.damaged & TARGET_TOUCH) + { + work->vision.field_06 = 2; + } + } +} + +int s07c_meryl72_unk1_800CB6E8(HZD_HDL *hzd, SVECTOR *from, SVECTOR *to) +{ + int to_addr, from_addr; + + from_addr = HZD_GetAddress(hzd, from, -1); + to_addr = HZD_GetAddress(hzd, to, -1); + + from_addr &= 0xFF; + to_addr &= 0xFF; + + return HZD_ZoneDistance(hzd, from_addr, to_addr); +} + +void s07c_meryl72_unk1_800CB748(Meryl72Work* work) +{ + CONTROL *control; + int dist; + + control = &work->control; + + if (!(work->modetime[7] & 1)) + { + return; + } + + if (!(control->map->index & GM_PlayerMap)) + { + return; + } + + if (GM_NoisePower == 0) + { + return; + } + + switch (GM_NoisePower) + { + case 5: + if (GV_DiffVec3(&GM_NoisePosition, &control->mov) < 500) + { + work->modetime[6] |= 0x1; + } + break; + + case 200: + if (GV_DiffVec3(&GM_NoisePosition, &control->mov) < 4000) + { + work->modetime[6] |= 0x1; + } + break; + + case 100: + dist = GV_DiffVec3(&GM_NoisePosition, &control->mov); + fprintf(1, "noise dis =%d,\n", dist); + + if (dist < 4000) + { + dist = s07c_meryl72_unk1_800CB6E8(control->map->hzd, &control->mov, &GM_NoisePosition); + + if (dist < 300) + { + work->modetime[6] |= 0x1; + GM_NoiseLength = 0; + GM_NoisePower = 0; + } + } + break; + + case 255: + work->modetime[6] |= 0x1; + break; + } +} + +void s07c_meryl72_unk1_800CB8D4(Meryl72Work *work) +{ +} + +void s07c_meryl72_unk1_800CB8DC(Meryl72Work *work) +{ + work->modetime[6] = 0; + + s07c_meryl72_unk1_800CB748(work); + + if (GM_AlertMode != ALERT_ACTIVE) + { + s07c_meryl72_unk1_800CB688(work); + s07c_meryl72_unk1_800CB8D4(work); + } +} + +void s07c_meryl72_unk1_800CB928(Meryl72Work *work) +{ + switch (work->vision.field_06) + { + case 0: + if (work->fB18 != 255 || !(GM_PlayerStatus & PLAYER_INTRUDE)) + { + work->fB18 -= 4; + } + break; + + case 1: + work->fB18--; + break; + + case 2: + work->fB18++; + break; + } + + if (work->fB18 < 0) + { + work->fB18 = 0; + } + else if (work->fB18 > 255) + { + work->fB18 = 255; + } +} + +void s07c_meryl72_unk1_800CB9DC(Meryl72Work *work) +{ + switch (work->vision.field_06) + { + case 0: + case 1: + work->fC0A -= 4; + break; + + case 2: + if (GM_PlayerStatus & PLAYER_WATCH) + { + if (GV_DiffDirAbs(work->sn_dir, GM_PlayerControl->rot.vy) > 1800) + { + work->fC0A++; + } + else + { + work->fC0A -= 2; + } + } + else + { + work->fC0A -= 4; + } + break; + } + + if (work->fC0A < 0) + { + work->fC0A = 0; + } + else if (work->fC0A > 1000) + { + work->fC0A = 1000; + } +} + +void s07c_meryl72_unk1_800CBA9C(Meryl72Work* work) +{ + SVECTOR sn_diff; + SVECTOR *mov; + short *pad; + int sn_dir, sn_dis; + int height; + MAP *map; + + pad = &work->vision.field_06; + + mov = &work->control.mov; + GV_SubVec3(&GM_PlayerPosition, mov, &sn_diff); + sn_diff.vy = 0; + + sn_dir = GV_VecDir2(&sn_diff); + work->sn_dir = sn_dir; + + sn_dis = GV_VecLen3(&sn_diff); + work->sn_dis = sn_dis; + + height = ABS(GM_PlayerPosition.vy - work->control.mov.vy); + + if (!(work->control.map->index & GM_PlayerMap) || (GM_PlayerStatus & PLAYER_INTRUDE)) + { + work->vision.field_06 = 0; + return; + } + + if ((GV_Time % 3) != work->param.fAF8) + { + return; + } + + if ((sn_dis > 6000) || (height > 2000) || (GV_DiffDirAbs(work->vision.facedir, sn_dir) > work->vision.angle)) + { + *pad = 0; + return; + } + + map = work->control.map; + if (HZD_OnlineHazardCheck(map->hzd, &GM_PlayerPosition, mov, HZD_CHK_ALL, SEGMENT_ATR) || + GM_OnlineTargetCheckAny(mov, &GM_PlayerPosition, map->index, &sn_diff)) + { + work->vision.field_06 = 0; + } + else if (work->vision.length < sn_dis) + { + work->vision.field_06 = 1; + } + else + { + work->vision.field_06 = 2; + } +} + +void Meryl72ActionMain_800CBC44(Meryl72Work *work) +{ + if (work->f8BC.field_18 < 54) + { + s07c_meryl72_unk1_800CB660(work); + s07c_meryl72_unk1_800CBA9C(work); + s07c_meryl72_unk1_800CB8DC(work); + s07c_meryl72_unk1_800CB928(work); + s07c_meryl72_unk1_800CB9DC(work); + + if (work->stage == 0) + { + Meryl72Think_800CEA04(work); + } + else + { + Meryl9Think_800D0154(work); + } + + ML72_ExecPutChar_800CB600(work); + } + + s07c_meryl72_unk1_800CABA0(work); +} + +void s07c_meryl72_unk1_800CBCD8(Meryl72Work *work) +{ + int ang; + TARGET *target; + int f18; + CONTROL *control; + + ang = 0; + + target = work->target; + if (!(target->damaged & TARGET_PUSH)) + { + return; + } + + GV_AddVec3(&target->offset, &work->control.step, &work->control.step); + target->damaged &= ~TARGET_PUSH; + + f18 = work->f8BC.field_18; + if (f18 != 1 && f18 != 2) + { + return; + } + + if (target->offset.pad != 0) + { + if (GV_Time & 256) + { + ang = target->offset.pad * 1024; + if (!(work->param.fAF8 & 0x1)) + { + ang = (target->offset.pad + 2) * 1024; + } + } + else if (work->param.fAF8 & 1) + { + ang = (target->offset.pad + 2) * 1024; + } + else + { + ang = target->offset.pad * 1024; + } + + ang &= 0xFFF; + } + + control = &work->control; + control->turn.vy = ang; + control->r_sphere = GV_NearExp2(control->r_sphere, work->f8BC.field_1A); +} diff --git a/components/mgs/mgs/source/animal/meryl72/override.c b/components/mgs/mgs/source/animal/meryl72/override.c new file mode 100644 index 00000000..c2f002e3 --- /dev/null +++ b/components/mgs/mgs/source/animal/meryl72/override.c @@ -0,0 +1,190 @@ +#include "meryl72.h" + +/****Inlines**********************************************************************************************/ +static inline void UnsetAction( Meryl72Work *work, int n_action ) +{ + extern short ActTable_800C3310[]; + + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3310[n_action], 0, ACTINTERP, 0x3FE ); +} + +static inline void UnsetMode( Meryl72Work *work ) +{ + extern short ActTable_800C3310[]; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3310[STANDSTILL], 0, ACTINTERP, 0 ); + + work->action2 = 0; + work->time2 = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; +} +/*********************************************************************************************************/ + +void s07c_meryl72_unk1_800CAD30( Meryl72Work* work, int time ) +{ + if ( time == 0 ) + { + UnsetAction( work, ACTION14 ); + } + + if ( !( work->pad.press & 1 ) ) + { + UnsetMode( work ); + } + else + { + if ( time < 4 ) + { + work->vision.facedir = ( work->control.rot.vy - ( time * 256 ) ) & 0xFFF; + } + else + { + work->vision.facedir = ( work->control.rot.vy - 1024 ) & 0xFFF; + } + } +} + +void s07c_meryl72_unk1_800CADEC( Meryl72Work* work, int time) +{ + if ( time == 0 ) + { + UnsetAction( work, ACTION15 ); + } + + if ( !( work->pad.press & 2 ) ) + { + UnsetMode( work ); + } + else + { + if ( time < 4 ) + { + work->vision.facedir = ( work->control.rot.vy + ( time * 256 ) ) & 0xFFF; + } + else + { + work->vision.facedir = ( work->control.rot.vy + 1024 ) & 0xFFF; + } + } +} + +void ActOverScoutD_800CAEA8( Meryl72Work* work, int time) +{ + work->vision.length = 2000; + + if ( time == 0 ) + { + UnsetAction( work, ACTION16 ); + } + + if ( !( work->pad.press & 0x4 ) ) + { + UnsetMode( work ); + } +} + +void s07c_meryl72_unk1_800CAF30( Meryl72Work* work, int time) +{ + work->vision.length = 2000; + + if ( time == 0 ) + { + UnsetAction( work, ACTION19 ); + } + + if ( !( work->pad.press & 0x2000 ) ) + { + UnsetMode( work ); + } +} + +void s07c_meryl72_unk1_800CAFB8( Meryl72Work* work, int time) +{ + work->vision.length = 0; + if ( time == 0 ) + { + UnsetAction( work, ACTION27 ); + } + + if ( work->body.time2 ) + { + UnsetMode( work ); + } +} + +void s07c_meryl72_unk1_800CB038( Meryl72Work* work, int time) +{ + work->vision.length = 0; + if ( time == 0 ) + { + UnsetAction( work, ACTION25 ); + } + + if ( work->body.time2 ) + { + UnsetMode( work ); + } +} + +void s07c_meryl72_unk1_800CB0B8( Meryl72Work* work, int time) +{ + if ( time == 0 ) + { + UnsetAction( work, ACTION26 ); + } + + if ( work->body.time2 ) + { + UnsetMode( work ); + } +} + +void s07c_meryl72_unk1_800CB134( Meryl72Work* work, int time) +{ + work->vision.length = 0; + if ( time == 0 ) + { + UnsetAction( work, ACTION22 ); + } + + if ( work->body.time2 ) + { + UnsetMode( work ); + } +} + +extern void AN_Sleep( SVECTOR *pos ) ; + +void s07c_meryl72_unk1_800CB1B4( Meryl72Work *work, int time ) +{ + SVECTOR mov; + work->vision.length = 0; + + if ( time == 0 ) + { + UnsetAction( work, ACTION23 ); + if ( work->control.map->index & GM_PlayerMap && GV_RandU( 12 ) > 4 ) + { + work->pad.sound = 240; + } + } + + if ( time == 20 ) + { + mov = work->control.mov; + mov.vy += 500; + AN_Sleep( &mov ); + } + + if ( work->pad.press & 0x1000 ) + { + SetMode( work, s07c_meryl72_unk1_800C98E0 ); + UnsetMode( work ); + return; + } + + if ( time == 130 ) + { + UnsetMode( work ); + } +} \ No newline at end of file diff --git a/components/mgs/mgs/source/animal/meryl72/put.c b/components/mgs/mgs/source/animal/meryl72/put.c new file mode 100644 index 00000000..de60b3e2 --- /dev/null +++ b/components/mgs/mgs/source/animal/meryl72/put.c @@ -0,0 +1,181 @@ +#include "meryl72.h" + +extern void AN_Breath( MATRIX * ) ; +extern void AN_Fog( SVECTOR *pos ) ; +extern void NewBlood( MATRIX *, int ) ; +extern void *AN_Unknown_800CA1EC( MATRIX* mat, int mark ); + +extern void *NewBullet(MATRIX *pMtx, int whichSide, int a3, int noiseLen); + +SVECTOR s07c_dword_800C3394 = { 0, 0, 100, 0 }; +SVECTOR s07c_dword_800C339C = { -1024, 0, 0 }; + +void ML72_PutBlood_800CB2EC( Meryl72Work* work, int obj_idx, int count ) +{ + MATRIX mat; + DG_SetPos( &work->body.objs->objs[ obj_idx ].world ); + DG_MovePos( &s07c_dword_800C3394 ); + DG_RotatePos( &s07c_dword_800C339C ); + ReadRotMatrix( &mat ); + NewBlood( &mat, count ); +} + +SVECTOR s07c_dword_800C33A4 = { 0, 0, 100, 0 }; +SVECTOR s07c_dword_800C33AC = { -1024, 0, 0 }; + +void ML72_PutBreath_800CB35C( Meryl72Work *work ) +{ + MATRIX mat; + SVECTOR svec; + + DG_SetPos( &work->body.objs->objs[1].world ); + DG_MovePos( &s07c_dword_800C33A4 ); + DG_RotatePos( &s07c_dword_800C33AC ); + ReadRotMatrix( &mat ); + + svec.vx = mat.t[ 0 ]; + svec.vy = mat.t[ 1 ]; + svec.vz = mat.t[ 2 ]; + + AN_Fog( &svec ); +} + +void ML72_PutMark_800CB3C0( Meryl72Work* work, int mark ) +{ + if ( work->control.map->index & GM_PlayerMap ) + { + AN_Unknown_800CA1EC( &work->body.objs->objs[6].world , mark ); + } +} + +void ML72_Put_800CB404( Meryl72Work *work ) {} + +void ML72_Put_800CB40C( Meryl72Work *work ) {} + +void ML72_PutBreath_800CB414( Meryl72Work* work ) +{ + if ( ( GV_Time % 64 ) == ( work->param.fAF8 * 16 ) ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } +} + +void ML72_PutSound_800CB468( Meryl72Work *work ) +{ + int t0; + int a2; + int sd1; + int sd2; + + t0 = work->f8BC.field_18; + a2 = work->m_ctrl.info1.frame; + sd1 = 192; + + if( work->stage == 0 ) + { + sd1 = SE_STEP_RIGHT; + sd2 = SE_STEP_LEFT; + } + else + { + sd2 = 191; + } + + if ( t0 == 1 ) + { + if ( a2 == 10 ) + { + GM_SeSet( &work->control.mov, sd1 ); + } + else if ( a2 == 1 ) + { + sd1 = sd2; + GM_SeSet( &work->control.mov, sd1 ); + } + } + else if ( t0 == 2 ) + { + if ( a2 == 10 ) + { + GM_SeSet( &work->control.mov, sd1 ); + } + else if ( a2 == 1 ) + { + sd1 = sd2; + GM_SeSet( &work->control.mov, sd1 ); + } + } +} + +void ML72_PutBullet_800CB4F0( Meryl72Work *work ); + +PUTFUNC ML72_PutFuncs[5] = { + ML72_Put_800CB404, + ML72_Put_800CB40C, + ML72_PutSound_800CB468, + ML72_PutBullet_800CB4F0, + ML72_PutBreath_800CB414 +}; + +SVECTOR s07c_dword_800C33C8 = { 5, -500, 80, 0 }; + +void ML72_PutBullet_800CB4F0( Meryl72Work *work ) +{ + MATRIX* mat; + MATRIX local_mat; + + mat = &work->body.objs->objs[4].world; + DG_SetPos( mat ); + DG_MovePos( &s07c_dword_800C33C8 ); + ReadRotMatrix( &local_mat ); + NewBullet( &local_mat, 2, 1, 0 ); + GM_SeSet( &work->control.mov, SE_ENEMY_SHOT ); + AN_BulletSmoke( mat, 0 ); + AN_FamasFlash( &local_mat ); + + ML72_ClearPutChar_800CB5CC( work, ML72_PutBullet_800CB4F0 ) ; +} + +int ML72_SetPutChar_800CB584( Meryl72Work *work, int idx ) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( work->fA9C[ i ] == NULL ) + { + work->fA9C[ i ] = ML72_PutFuncs[ idx ]; + return 1; + } + } + return 0; +} + +int ML72_ClearPutChar_800CB5CC( Meryl72Work *work, void *func ) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( work->fA9C[ i ] == func ) + { + work->fA9C[ i ] = 0; + return 1; + } + } + return 0; +} + +void ML72_ExecPutChar_800CB600( Meryl72Work* work ) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( work->fA9C[ i ] ) + { + PUTFUNC func = work->fA9C[ i ]; + func( work ); + } + } +} \ No newline at end of file diff --git a/components/mgs/mgs/source/animal/meryl72/think.c b/components/mgs/mgs/source/animal/meryl72/think.c new file mode 100644 index 00000000..9e89d061 --- /dev/null +++ b/components/mgs/mgs/source/animal/meryl72/think.c @@ -0,0 +1,1309 @@ +#include + +#include "meryl72.h" + +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "libhzd/libhzd.h" + +#include "takabe/cineutil.h" // for OpenCinemaScreen, etc. + +#define TH1_PHASE0 0 +#define TH1_PHASE1 1 +#define TH1_PHASE2 2 +#define TH1_PHASE3 3 + +void s07c_meryl72_unk2_800CC9A8(Meryl72Work *work) +{ + work->target_addr = work->player_addr; + work->target_pos = work->player_pos; + work->target_map = work->player_map; +} + +void s07c_meryl72_unk2_800CC9E0(Meryl72Work *work) +{ + int x; + + x = work->next_node + 1; + if ( x >= work->n_patrols ) + { + x = 0; + } + work->next_node = x; + work->target_pos = work->nodes[ x ]; + work->target_addr = HZD_GetAddress( work->control.map->hzd, &work->target_pos, -1 ); + work->target_map = work->start_map; +} + +void s07c_meryl72_unk2_800CCA68(Meryl72Work *work) +{ + work->target_pos = work->nodes[ work->next_node ]; + work->target_addr = HZD_GetAddress( work->control.map->hzd, &work->target_pos, -1 ); + work->target_map = work->start_map; +} + +void s07c_meryl72_unk2_800CCAD8( Meryl72Work* work ) +{ + work->target_pos.vx = -5500; + work->target_pos.vz = -18000; + work->target_pos.vy = 0; + work->target_addr = HZD_GetAddress( work->control.map->hzd, &work->target_pos, -1 ); + work->target_map = work->start_map; +} + +void s07c_meryl72_unk2_800CCB2C( Meryl72Work *work, int index ) +{ + int proc_id; + + proc_id = work->fC1C[ index ]; + if ( proc_id >= 0 ) + { + GCL_ExecProc( proc_id, NULL ); + } +} + +int s07c_meryl72_unk2_800CCB64( Meryl72Work *work ) +{ + SVECTOR diff; + SVECTOR *mov; + HZD_HDL *hzd; + int to_addr, from_addr; + int reach; + int link; + HZD_ZON *zone; + + mov = &work->control.mov; + hzd = work->control.map->hzd; + + to_addr = work->target_addr; + from_addr = HZD_GetAddress( hzd, mov, work->fB5C ); + + work->fB5C = from_addr; + reach = HZD_ReachTo( hzd, from_addr, work->fB60 ); + + if ( to_addr != work->fB4C || reach <= 0 ) + { + work->fB4C = to_addr; + + if ( HZD_ReachTo( hzd, from_addr, to_addr ) < 2 ) + { + work->fB6C = work->target_pos; + work->fB60 = to_addr; + GV_SubVec3(&work->fB6C, mov, &diff); + work->pad.dir = GV_VecDir2(&diff); + return -1; + } + + link = HZD_Navigate( hzd, from_addr, to_addr, mov ); + zone = &hzd->def->zones[ link ]; + + work->fB6C.vx = zone->x; + work->fB6C.vy = zone->y; + work->fB6C.vz = zone->z; + + work->fB60 = ( link & 0xff ) | ( ( link & 0xff ) << 8 ); + } + + GV_SubVec3( &work->fB6C, &work->control.mov, &diff ); + return GV_VecDir2( &diff ); +} + +int s07c_meryl72_unk2_800CCCC8( Meryl72Work *work ) +{ + if ( ( work->count3 % 32 ) == 0 ) + { + work->fB5C = -1; + work->fB4C = -1; + s07c_meryl72_unk2_800CCB64( work ); + } + + work->pad.dir = s07c_meryl72_unk2_800CCB64( work ); + return work->pad.dir < 0; +} + +int s07c_meryl72_unk2_800CCD1C(Meryl72Work *work, int range) +{ + SVECTOR diff; + int dx, dz; + + diff.vx = dx = work->target_pos.vx - work->control.mov.vx; + diff.vz = dz = work->target_pos.vz - work->control.mov.vz; + + if (dx > -range && dx < range && dz > -range && dz < range) + { + return 1; + } + + work->pad.dir = GV_VecDir2(&diff); + return 0; +} + +void s07c_meryl72_unk2_800CCD98( Meryl72Work *work ) +{ + if ( !work->fB96 ) + { + if ( work->count3 == 0 ) + { + s07c_meryl72_unk2_800CCB2C( work, 1 ) ; + GM_GameStatus |= PLAYER_SECOND_AVAILABLE; + } + + if ( work->count3 == 1 ) + { + work->pad.dir = work->sn_dir; + GM_GameStatus |= ( PLAYER_SECOND_AVAILABLE | PLAYER_WEAPON_DISABLE | PLAYER_TIMERBOMB_THROWN | PLAYER_CHECK_WALL | PLAYER_GAME_OVER ) ; + } + + if ( work->count3 == 60 ) + { + GM_VoxStream( work->voices[ 0 ], 0 ) ; + } + + if ( work->count3 > 60 && GM_StreamStatus() == -1 ) + { + s07c_meryl72_unk2_800CCB2C( work, 0 ); + s07c_meryl72_unk2_800CCB2C( work, 4 ); + work->think3 = 1; + work->count3 = 0; + return; + } + } + else + { + work->next_node = 2; + s07c_meryl72_unk2_800CC9E0( work ); + work->think1 = 1; + work->think2 = 2; + work->think3 = 4; + work->count3 = 0; + return; + } + work->count3++; +} + +void s07c_meryl72_unk2_800CCEC4(Meryl72Work *work) +{ + if (work->count3 > 30 && GM_StreamStatus() == -1) + { + if (GM_GameOverTimer == 0 && GM_SnakeCurrentHealth > 0) + { + GM_VoxStream(work->voices[6], 0); + } + + work->think3 = 4; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CCF5C(Meryl72Work *work) +{ + if (work->count3 > 60 && GM_StreamStatus() == -1) + { + work->think2 = 1; + work->think3 = 2; + work->count3 = 0; + GM_GameStatus &= ~STATE_PADRELEASE; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CCFE0(Meryl72Work *work) +{ + if (work->fC08 == 2) + { + work->think3 = 2; + work->count3 = 0; + } + else if (work->count3 > 6000 && work->act_status & 0x1) + { + work->think3 = 2; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CD038(Meryl72Work *work) +{ + if (work->fC08 == 1) + { + work->think3 = 6; + work->count3 = 0; + return; + } + + if (work->count3 == 0) + { + if (GM_PlayerStatus & (PLAYER_GROUND | PLAYER_SQUAT)) + { + work->pad.press |= 0x800000; + } + else if (GM_PlayerStatus & PLAYER_CB_BOX) + { + work->pad.press |= 0x1000000; + + if (GM_StreamStatus() == -1) + { + GM_VoxStream(work->voices[12], 0); + } + } + else + { + work->pad.press |= 0x400000; + } + + work->fC0E -= 2; + if (work->fC0E < 0) + { + work->fC0E = 0; + } + + printf(" binta dis=%d\n", work->sn_dis); + } + + if (work->fC08 == 3) + { + work->think3 = 8; + work->count3 = 0; + } + else if (work->count3 > 300) + { + work->think3 = 8; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CD14C(Meryl72Work *work) +{ + int tick; + + work->pad.press |= 0x10000; + work->pad.dir = work->sn_dir; + + if (work->fC08 == 1) + { + work->think3 = 6; + work->count3 = 0; + + if (GM_StreamStatus() != -1) + { + GM_StreamPlayStop(); + } + } + else + { + if (work->count3 == 0 && GM_StreamStatus() == -1) + { + tick = GV_Time % 2; + GM_VoxStream(work->voices[13 + tick], 0); + } + + if (work->count3 > 30) + { + if (GM_StreamStatus() == -1) + { + s07c_meryl72_unk2_800CCA68(work); + work->think2 = 2; + work->think3 = 4; + work->count3 = 0; + } + } + else + { + work->count3++; + } + } +} + +void s07c_meryl72_unk2_800CD260(Meryl72Work *work) +{ + if (work->count3 == 0) + { + s07c_meryl72_unk2_800CC9E0(work); + } + + if (s07c_meryl72_unk2_800CCD1C(work, 350)) + { + if (work->next_node == 2) + { + work->think3 = 4; + work->count3 = 0; + return; + } + + s07c_meryl72_unk2_800CC9E0(work); + } + + work->count3++; +} + +void s07c_meryl72_unk2_800CD2E0(Meryl72Work *work) +{ + if (work->count3 == 0) + { + s07c_meryl72_unk2_800CC9E0(work); + } + + if (work->count3 == 30) + { + GM_GameStatus &= ~STATE_PADRELEASE; + GM_GameStatus &= ~(STATE_PADRELEASE | STATE_PAUSE_ONLY); + CloseCinemaScreen(); + } + + if (s07c_meryl72_unk2_800CCD1C(work, 350)) + { + work->think3 = 4; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +int s07c_meryl72_unk2_800CD380(SVECTOR *pos) +{ + if (pos->vx < 5500 || pos->vx > 7000 || pos->vz < -10500 || pos->vz > -8000) + { + return 0; + } + + return 1; +} + +int s07c_meryl72_unk2_800CD3C4(SVECTOR *pos) +{ + if (pos->vx < 5500 || pos->vx > 7000 || pos->vz < -8500 || pos->vz > -7000) + { + return 0; + } + + return 1; +} + +void s07c_meryl72_unk2_800CD408(void) +{ + GV_MSG msg; + + msg.address = GV_StrCode("スネーク"); + msg.message[0] = GV_StrCode("run_move"); + msg.message[1] = 6500; + msg.message[2] = 500; + msg.message[3] = 58036; + msg.message[4] = 500; + msg.message[5] = 0; + msg.message[6] = 0; + msg.message_len = 7; + + GV_SendMessage(&msg); +} + +void s07c_meryl72_unk2_800CD474(Meryl72Work *work) +{ + switch (work->fAF4) + { + case 0: + if (work->count3 == 0) + { + work->pad.dir = 3076; + GM_GameStatus &= ~STATE_PADRELEASE; + GM_GameStatus &= ~(STATE_PADRELEASE | STATE_PAUSE_ONLY); + } + + if (s07c_meryl72_unk2_800CD380(&GM_PlayerPosition) && GM_SnakeCurrentHealth != 0) + { + s07c_meryl72_unk2_800CD408(); + OpenCinemaScreen(0, 60000); + work->fAF4 = 100; + work->count3 = 0; + GM_GameStatus |= STATE_PADRELEASE; + GM_GameStatus |= STATE_PADRELEASE | STATE_PAUSE_ONLY; + } + break; + + case 100: + if (work->count3 > 30 && (s07c_meryl72_unk2_800CD3C4(&GM_PlayerPosition) || work->count3 > 120) && work->act_status & 0x1) + { + work->pad.dir = work->sn_dir; + + if (GM_StreamStatus() == -1) + { + GM_VoxStream(work->voices[1], 0); + } + + s07c_meryl72_unk2_800CCB2C(work, 2); + work->fAF4 = 1; + } + break; + + case 101: + break; + + case 1: + if (work->count3 < 140) + { + work->pad.press |= 0x2000000; + } + + if (work->count3 > 60 && GM_StreamStatus() == -1) + { + GM_VoxStream(work->voices[2], 0); + work->count3 = 0; + work->fAF4 = 2; + s07c_meryl72_unk2_800CCB2C(work, 3); + return; + } + + if (GM_GameOverTimer != 0) + { + if (GM_StreamStatus() != -1) + { + GM_StreamPlayStop(); + } + + s07c_meryl72_unk2_800CCB2C(work, 4); + work->fAF4 = 255; + } + break; + + case 2: + if (work->count3 > 60 && GM_StreamStatus() == -1) + { + GM_VoxStream(work->voices[3], 0); + work->count3 = 0; + work->fAF4 = 3; + } + + if (GM_GameOverTimer != 0) + { + if (GM_StreamStatus() != -1) + { + GM_StreamPlayStop(); + } + + s07c_meryl72_unk2_800CCB2C(work, 4); + work->fAF4 = 255; + } + break; + + case 3: + if (work->count3 > 60 && GM_StreamStatus() == -1) + { + s07c_meryl72_unk2_800CCB2C(work, 4); + work->think1 = 1; + work->think2 = 2; + work->think3 = 1; + work->count3 = 0; + work->fC04 = 0; + return; + } + break; + } + + work->count3++; +} + +int s07c_meryl72_unk2_800CD76C(int xmin, int zmin, int xmax, int zmax) +{ + int x, z; + + if (!(GM_PlayerStatus & PLAYER_CAUTION)) + { + return 0; + } + + x = GM_PlayerPosition.vx; + z = GM_PlayerPosition.vz; + + if (x < xmin || x > xmax || z < zmin || z > zmax) + { + return 0; + } + + return 1; +} + +int s07c_meryl72_unk2_800CD7C4(void) +{ + if (GM_PlayerPosition.vz < -14000 && + GM_PlayerPosition.vx > -6000 && + GM_PlayerPosition.vx < -4000) + { + return 1; + } + + return 0; +} + +void s07c_meryl72_unk2_800CD80C(Meryl72Work *work) +{ + CONTROL *control; + + if (work->fC08 == 1) + { + work->think2 = 3; + work->think3 = 6; + work->count3 = 0; + return; + } + + if (GM_StreamStatus() != -1) + { + work->count3 = 0; + } + + if (work->count3 > 60) + { + if (s07c_meryl72_unk2_800CD76C(-1000, -9000, 1500, -8500)) + { + if (work->fC10[0] == 0) + { + if (GM_GameOverTimer == 0 && GM_SnakeCurrentHealth > 0) + { + GM_VoxStream(work->voices[4], 0); + } + + work->fC10[0] = 1800; + } + } + else if (s07c_meryl72_unk2_800CD76C(5500, -9000, 7000, -8500)) + { + if (work->fC10[1] == 0) + { + if (GM_GameOverTimer == 0 && GM_SnakeCurrentHealth > 0) + { + GM_VoxStream(work->voices[5], 0); + } + + work->fC10[1] = 1800; + } + } + else if (s07c_meryl72_unk2_800CD76C(-9000, -9000, -7500, -8500)) + { + if (work->fC10[2] == 0) + { + if (GM_GameOverTimer == 0 && GM_SnakeCurrentHealth > 0) + { + GM_VoxStream(work->voices[5], 0); + } + + work->fC10[2] = 1800; + } + } + } + + if (work->count3 > 60 && work->fC0A > 150) + { + work->think3 = 5; + work->count3 = 0; + work->pad.press |= 0x10000; + } + else if (work->fC04 > 3600 || s07c_meryl72_unk2_800CD7C4()) + { + work->think1 = 2; + work->think2 = 2; + work->think3 = 4; + work->count3 = 0; + work->fC04 = 0; + } + else + { + if ((work->count3 % 32) == 0) + { + control = &work->control; + if (!HZD_InsideZone(control->map->hzd, &control->mov, work->param.defends[0])) + { + work->think3 = 2; + work->count3 = 0; + } + } + + work->pad.press |= 0x10000; + work->pad.dir = work->sn_dir; + work->count3++; + } +} + +void s07c_meryl72_unk2_800CDA88(Meryl72Work *work) +{ + work->fC0A = 0; + + if (work->fC08 == 1) + { + work->think2 = 3; + work->think3 = 6; + work->count3 = 0; + + if (GM_StreamStatus() != -1) + { + GM_StreamPlayStop(); + } + + return; + } + + if (work->count3 == 0) + { + if (GM_StreamStatus() == -1) + { + if (GM_GameOverTimer == 0 && GM_SnakeCurrentHealth > 0) + { + GM_VoxStream(work->voices[work->fC0C + 7], 0); + } + + printf(" mitumete voice = %d \n", work->fC0C + 7); + + if (++work->fC0E > 14) + { + work->fC0E = 14; + } + + if (++work->fC0C > 4) + { + work->fC0C = 0; + } + } + else + { + work->think3 = 4; + work->count3 = 0; + return; + } + } + + if (work->count3 > 60 && GM_StreamStatus() == -1) + { + work->think3 = 4; + work->count3 = 0; + work->pad.press |= 0x10000; + } + else + { + work->pad.press |= 0x10000; + work->pad.dir = work->sn_dir; + work->count3++; + } +} + +void s07c_meryl72_unk2_800CDC34(Meryl72Work *work) +{ + if (GM_StreamStatus() != -1) + { + work->count3 = 0; + } + + if (work->fC08 == 1) + { + work->think2 = 3; + work->think3 = 6; + work->count3 = 0; + return; + } + + if (work->count3 > 15) + { + if (s07c_meryl72_unk2_800CD76C(1000, -9000, 1500, -8500)) + { + if (work->fC10[0] == 0) + { + if (GM_GameOverTimer == 0 && GM_SnakeCurrentHealth > 0) + { + GM_VoxStream(work->voices[4], 0); + } + + work->fC10[0] = 1800; + } + } + else if (s07c_meryl72_unk2_800CD76C(5500, -9000, 7000, -8500)) + { + if (work->fC10[1] == 0) + { + if (GM_GameOverTimer == 0 && GM_SnakeCurrentHealth > 0) + { + GM_VoxStream(work->voices[5], 0); + } + + work->fC10[1] = 1800; + } + } + else if (s07c_meryl72_unk2_800CD76C(-9000, -9000, -7500, -8500)) + { + if (work->fC10[2] == 0) + { + if (GM_GameOverTimer == 0 && GM_SnakeCurrentHealth > 0) + { + GM_VoxStream(work->voices[5], 0); + } + + work->fC10[2] = 1800; + } + } + } + + if (work->count3 > 60 && work->fC0A > 150) + { + work->think3 = 5; + work->count3 = 0; + work->pad.press |= 0x10000; + } + else if (work->fC04 > 3600 && !(work->modetime[6] & 0x4) && !(GM_GameStatus & STATE_STUN)) + { + work->think1 = 3; + work->think2 = 0; + work->think3 = 0; + GM_GameStatus |= STATE_PADRELEASE; + work->count3 = 0; + work->fC04 = 0; + } + else if (work->sn_dis > 3000 && !(GM_PlayerStatus & PLAYER_INTRUDE)) + { + work->think2 = 1; + work->think3 = 2; + work->count3 = 0; + } + else + { + work->pad.press |= 0x4000000; + work->pad.dir = work->sn_dir; + work->count3++; + } +} + +void s07c_meryl72_unk2_800CDEC4(Meryl72Work *work) +{ + CONTROL *control; + + if (work->fC08 == 1) + { + work->think2 = 3; + work->think3 = 6; + work->count3 = 0; + return; + } + + if (work->count3 == 0) + { + s07c_meryl72_unk2_800CCAD8(work); + } + + control = &work->control; + if ((work->count3 % 32) == 0 && !HZD_InsideZone(control->map->hzd, &control->mov, work->param.defends[1])) + { + work->think2 = 1; + work->think3 = 2; + work->count3 = 0; + } + + if (work->count3 > 60 && work->fC0A > 150) + { + work->think3 = 5; + work->count3 = 0; + work->pad.press |= 0x10000; + } + else + { + work->pad.press |= 0x4000000; + work->pad.dir = work->sn_dir; + work->count3++; + } +} + +void s07c_meryl72_unk2_800CDFC8(Meryl72Work *work) +{ + int think2_next; + + if (work->fC08 == 1) + { + work->think2 = 3; + work->think3 = 6; + work->count3 = 0; + return; + } + + if (work->sn_dis > 3000) + { + work->pad.mode = 1; + } + else + { + work->pad.mode = 0; + } + + s07c_meryl72_unk2_800CC9A8(work); + + if (s07c_meryl72_unk2_800CCCC8(work)) + { + work->think3 = 3; + work->pad.mode = 0; + work->count3 = 0; + } + else if (work->sn_dis < 2000 && work->vision.field_06 == 2) + { + think2_next = work->vision.field_06; + work->think2 = think2_next; + work->think3 = 4; + work->pad.mode = 0; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CE08C(Meryl72Work *work) +{ + if (work->fC08 == 1) + { + work->think2 = 3; + work->think3 = 6; + work->count3 = 0; + } + else if (s07c_meryl72_unk2_800CCCC8(work)) + { + work->think3 = 3; + work->count3 = 0; + } + else if (work->fC04 > 3600) + { + work->think1 = 2; + work->think2 = 2; + work->think3 = 4; + work->count3 = 0; + work->fC04 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CE124(Meryl72Work *work) +{ + s07c_meryl72_unk2_800CC9A8(work); + + if (s07c_meryl72_unk2_800CCCC8(work)) + { + work->think3 = 3; + work->count3 = 0; + } + else if (work->sn_dis < 1000 && work->vision.field_06 == 2) + { + work->think3 = 7; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CE198(Meryl72Work *work) +{ + if (work->count3 == 0) + { + s07c_meryl72_unk2_800CCAD8(work); + } + + if (work->fC08 == 1) + { + work->think2 = 3; + work->think3 = 6; + work->count3 = 0; + return; + } + + if (s07c_meryl72_unk2_800CCCC8(work)) + { + work->think3 = 3; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CE218(Meryl72Work *work) +{ + if (work->fC08 == 1) + { + work->think2 = 3; + work->think3 = 6; + work->count3 = 0; + return; + } + + if (work->sn_dis > 3000) + { + work->pad.mode = 1; + } + else + { + work->pad.mode = 0; + } + + s07c_meryl72_unk2_800CC9A8(work); + + if (work->count3 & 32) + { + work->fB5C = HZD_GetAddress(work->control.map->hzd, &work->control.mov, -1); + + if (HZD_ReachTo(work->control.map->hzd, work->fB5C, work->target_addr) >= 2) + { + work->think3 = 2; + work->count3 = 0; + return; + } + } + + if (s07c_meryl72_unk2_800CCD1C(work, 2000) > 0) + { + work->think2 = 2; + work->think3 = 4; + work->pad.mode = 0; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CE314(Meryl72Work *work) +{ + if (work->fC08 == 1) + { + work->think2 = 3; + work->think3 = 6; + work->count3 = 0; + } + else if (s07c_meryl72_unk2_800CCD1C(work, 300) > 0) + { + work->think3 = 4; + work->pad.mode = 0; + work->count3 = 0; + } + else if (work->fC04 > 3600) + { + work->think1 = 2; + work->think2 = 2; + work->think3 = 4; + work->count3 = 0; + work->fC04 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CE3BC(Meryl72Work *work) +{ + int in_range; + + s07c_meryl72_unk2_800CC9A8(work); + + in_range = s07c_meryl72_unk2_800CCD1C(work, 1000); + if (in_range < 0 || work->vision.field_06 != 2) + { + work->think3 = 2; + work->count3 = 0; + } + else if (in_range > 0) + { + printf(" stop dis=%d\n", work->sn_dis); + work->think3 = 7; + work->pad.mode = 0; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CE450(Meryl72Work *work) +{ + if (work->fC08 == 1) + { + work->think2 = 3; + work->think3 = 6; + work->count3 = 0; + return; + } + + if (work->count3 & 32) + { + work->fB5C = HZD_GetAddress(work->control.map->hzd, &work->control.mov, -1); + + if (HZD_ReachTo(work->control.map->hzd, work->fB5C, work->target_addr) >= 2) + { + work->think3 = 2; + work->count3 = 0; + return; + } + } + + if (s07c_meryl72_unk2_800CCD1C(work, 300) > 0) + { + work->think2 = 2; + work->think3 = 4; + work->pad.mode = 0; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CE524(Meryl72Work *work) +{ + switch (work->think3) + { + case 0: + s07c_meryl72_unk2_800CCD98(work); + break; + + case 1: + s07c_meryl72_unk2_800CD260(work); + break; + + case 4: + s07c_meryl72_unk2_800CD474(work); + break; + } + + work->fC34 = 30; +} + +void s07c_meryl72_unk2_800CE5AC(Meryl72Work *work) +{ + switch (work->think3) + { + case 0: + s07c_meryl72_unk2_800CCEC4(work); + break; + + case 4: + s07c_meryl72_unk2_800CCF5C(work); + break; + } +} + +void s07c_meryl72_unk2_800CE5FC(Meryl72Work *work) +{ + switch (work->think3) + { + case 2: + s07c_meryl72_unk2_800CDFC8(work); + break; + + case 3: + s07c_meryl72_unk2_800CE218(work); + break; + } +} + +void s07c_meryl72_unk2_800CE64C(Meryl72Work *work) +{ + switch (work->think3) + { + case 2: + s07c_meryl72_unk2_800CE198(work); + break; + + case 3: + s07c_meryl72_unk2_800CE450(work); + break; + } +} + +void s07c_meryl72_unk2_800CE69C(Meryl72Work *work) +{ + switch (work->think3) + { + case 1: + s07c_meryl72_unk2_800CD2E0(work); + break; + + case 2: + s07c_meryl72_unk2_800CE08C(work); + break; + + case 3: + s07c_meryl72_unk2_800CE314(work); + break; + + case 4: + s07c_meryl72_unk2_800CD80C(work); + break; + + case 5: + s07c_meryl72_unk2_800CDA88(work); + break; + } +} + +void s07c_meryl72_unk2_800CE738(Meryl72Work *work) +{ + switch (work->think3) + { + case 6: + s07c_meryl72_unk2_800CCFE0(work); + break; + + case 2: + s07c_meryl72_unk2_800CE124(work); + break; + + case 3: + s07c_meryl72_unk2_800CE3BC(work); + break; + + case 7: + s07c_meryl72_unk2_800CD038(work); + break; + + case 8: + s07c_meryl72_unk2_800CD14C(work); + break; + } +} + +void s07c_meryl72_unk2_800CE7D4(Meryl72Work *work) +{ + switch (work->think3) + { + case 4: + s07c_meryl72_unk2_800CDC34(work); + break; + + case 5: + s07c_meryl72_unk2_800CDA88(work); + break; + } +} + +void s07c_meryl72_unk2_800CE824(Meryl72Work *work) +{ + switch (work->think3) + { + case 4: + s07c_meryl72_unk2_800CDEC4(work); + break; + + case 5: + s07c_meryl72_unk2_800CDA88(work); + break; + } +} + +void Think1_Phase0_800CE874( Meryl72Work* work ) +{ + if (work->think2 == 0) + { + s07c_meryl72_unk2_800CE524( work ); + } +} + +void Think1_Phase1_800CE8A4( Meryl72Work* work ) +{ + switch (work->think2) + { + case 2: + s07c_meryl72_unk2_800CE69C( work ); + return; + case 3: + s07c_meryl72_unk2_800CE738( work ); + return; + } +} + +void Think1_Phase2_800CE8F4( Meryl72Work* work ) +{ + switch ( work->think2 ) + { + case 1: + s07c_meryl72_unk2_800CE5FC( work ); + return; + case 2: + s07c_meryl72_unk2_800CE7D4( work ); + return; + case 3: + s07c_meryl72_unk2_800CE738( work ); + return; + } +} + +void Think1_Phase3_800CE970( Meryl72Work* work ) +{ + switch ( work->think2 ) + { + case 0: + s07c_meryl72_unk2_800CE5AC( work ); + return; + case 1: + s07c_meryl72_unk2_800CE64C( work ); + return; + case 2: + s07c_meryl72_unk2_800CE824( work ); + return; + case 3: + s07c_meryl72_unk2_800CE738( work ); + return; + } +} + +void Meryl72Think_800CEA04( Meryl72Work* work ) +{ + int i ; + + work->pad.dir = -1 ; + work->pad.press = 0 ; +/* + if ( ToilletArea( &ENEMYPOS ) ) { + work->control.radar_atr = RADAR_SIGHT | RADAR_ALL_MAP ; + } else { + work->control.radar_atr = RADAR_VISIBLE | RADAR_SIGHT | RADAR_ALL_MAP ; + } +*/ + + switch ( work->think1 ) { + case TH1_PHASE0 : /* トイレから出る */ + Think1_Phase0_800CE874( work ) ; + break ; + case TH1_PHASE1 : /* スネークが準備するまで待つ */ + Think1_Phase1_800CE8A4( work ) ; + break ; + case TH1_PHASE2 : /* スネークについていく */ + Think1_Phase2_800CE8F4( work ) ; + break ; + case TH1_PHASE3 : /* 所.. */ + Think1_Phase3_800CE970( work ) ; + break ; + } + + for ( i = 0 ; i < 3 ; i++ ) + { + if ( --work->fC10[i] < 0 ) + { + work->fC10[i] = 0 ; + } + } +} diff --git a/components/mgs/mgs/source/animal/meryl72/think9.c b/components/mgs/mgs/source/animal/meryl72/think9.c new file mode 100644 index 00000000..f0f30b3d --- /dev/null +++ b/components/mgs/mgs/source/animal/meryl72/think9.c @@ -0,0 +1,903 @@ +#include + +#include "meryl72.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "libhzd/libhzd.h" + +extern unsigned short GM_WeaponTypes[]; +extern unsigned short GM_ItemTypes[]; + +int SECTION(".bss") meryl72_800D5300; +int SECTION(".bss") meryl72_800D5304; + +extern SVECTOR meryl72_800D52F8; + +#define TH1_PHASE0 0 +#define TH1_PHASE1 1 +#define TH1_PHASE2 2 + +void s07c_meryl72_unk2_800CEBBC(Meryl72Work *work) +{ + work->target_addr = work->player_addr; + work->target_pos = work->player_pos; + work->target_map = work->player_map; +} + +void s07c_meryl72_unk2_800CEBF4(Meryl72Work *work) +{ + work->target_pos = work->nodes[work->next_node]; + work->target_addr = HZD_GetAddress(work->control.map->hzd, &work->target_pos, -1); + work->target_map = work->start_map; +} + +int s07c_meryl72_unk2_800CEC64(SVECTOR *vec) +{ + if (vec->vz > 1000 || vec->vx < 6000) + { + return 0; + } + + if (vec->vz > -1000 && vec->vx < 8500) + { + return 0; + } + + return 1; +} + +int s07c_meryl72_unk2_800CECB4(SVECTOR *vec) +{ + if (vec->vz > -5000 || vec->vx < 6000) + { + return 0; + } + + return 1; +} + +void s07c_meryl72_unk2_800CECEC(Meryl72Work *work, int index) +{ + int proc_id; + + proc_id = work->fC1C[index]; + if (proc_id >= 0) + { + GCL_ExecProc(proc_id, NULL); + } +} + +int s07c_meryl72_unk2_800CED24(Meryl72Work *work) +{ + SVECTOR diff; + SVECTOR *mov; + HZD_HDL *hzd; + int to_addr, from_addr; + int reach; + int link; + HZD_ZON *zone; + + mov = &work->control.mov; + hzd = work->control.map->hzd; + + to_addr = work->target_addr; + from_addr = HZD_GetAddress(hzd, mov, work->fB5C); + + work->fB5C = from_addr; + reach = HZD_ReachTo(hzd, from_addr, work->fB60); + + if (to_addr != work->fB4C || reach <= 0) + { + work->fB4C = to_addr; + + if (HZD_ReachTo(hzd, from_addr, to_addr) < 2) + { + work->fB6C = work->target_pos; + work->fB60 = to_addr; + GV_SubVec3(&work->fB6C, mov, &diff); + work->pad.dir = GV_VecDir2(&diff); + return -1; + } + + link = HZD_Navigate(hzd, from_addr, to_addr, mov); + zone = &hzd->def->zones[link]; + + work->fB6C.vx = zone->x; + work->fB6C.vy = zone->y; + work->fB6C.vz = zone->z; + + work->fB60 = (link & 0xff) | ((link & 0xff) << 8); + } + + GV_SubVec3(&work->fB6C, &work->control.mov, &diff); + return GV_VecDir2(&diff); +} + +int s07c_meryl72_unk2_800CEE88(Meryl72Work *work) +{ + if ((work->count3 % 32) == 0) + { + work->fB5C = -1; + work->fB4C = -1; + s07c_meryl72_unk2_800CED24(work); + } + + work->pad.dir = s07c_meryl72_unk2_800CED24(work); + return work->pad.dir < 0; +} + +int s07c_meryl72_unk2_800CEEDC(Meryl72Work *work, int range) +{ + SVECTOR diff; + int dx, dz; + + diff.vx = dx = work->target_pos.vx - work->control.mov.vx; + diff.vz = dz = work->target_pos.vz - work->control.mov.vz; + + if (dx > -range && dx < range && dz > -range && dz < range) + { + return 1; + } + + work->pad.dir = GV_VecDir2(&diff); + return 0; +} + +void s07c_meryl72_unk2_800CEF58(Meryl72Work *work) +{ + if (work->stage == 1) + { + work->think3 = 4; + work->fC04 = 0; + } + else + { + work->think1 = 2; + work->think2 = 2; + work->think3 = 4; + work->fC04 = 5400; + } + + work->count3 = 0; +} + +void s07c_meryl72_unk2_800CEF98(Meryl72Work *work) +{ + CONTROL *control; + int dir; + + control = &work->control; + + if (work->fC38 > 900) + { + work->think3 = 4; + work->fC38 = 0; + work->count3 = 0; + } + else if (!HZD_InsideZone(control->map->hzd, &control->mov, work->param.defends[0])) + { + s07c_meryl72_unk2_800CEBF4(work); + work->think3 = 2; + work->count3 = 0; + } + else if (GM_StreamStatus() == -1 && work->act_status & 0x1) + { + dir = work->sn_dir + 2048; + GM_PlayerControl->turn.vy = dir & 0xFFF; + + work->think3 = 4; + work->fC38 = 0; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CF060(Meryl72Work* work) +{ + if (work->count3 == 0 && GM_StreamStatus() != -1) + { + GM_StreamPlayStop(); + } + + if (work->fC08 == 2) + { + work->think3 = 2; + work->count3 = 0; + GM_GameStatus |= STATE_PADRELEASE; + work->pad.dir = work->sn_dir; + } + else if (work->count3 > 6000 && work->act_status & 0x1) + { + s07c_meryl72_unk2_800CEBF4(work); + work->think2 = 2; + work->think3 = 4; + work->count3 = 0; + work->pad.dir = work->sn_dir; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CF144(Meryl72Work *work) +{ + if (work->fC08 == 1) + { + work->think3 = 6; + work->count3 = 0; + GM_GameStatus &= ~STATE_PADRELEASE; + return; + } + + if (work->count3 == 0) + { + if (GM_PlayerStatus & (PLAYER_GROUND | PLAYER_SQUAT)) + { + work->pad.press |= 0x800000; + } + else if (GM_PlayerStatus & PLAYER_CB_BOX) + { + work->pad.press |= 0x1000000; + + if (GM_StreamStatus() == -1) + { + GM_VoxStream(work->voices[12], 0); + } + } + else + { + work->pad.press |= 0x400000; + } + + work->fC0E -= 2; + if (work->fC0E < 0) + { + work->fC0E = 0; + } + + printf(" binta dis=%d\n", work->sn_dis); + } + + if (work->count3 > 30 && !(work->act_status & 0x8)) + { + work->think3 = 9; + work->count3 = 0; + GM_GameStatus &= ~STATE_PADRELEASE; + } + else if (work->fC08 == 3) + { + work->think3 = 9; + work->count3 = 0; + GM_GameStatus &= ~STATE_PADRELEASE; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CF2A4(Meryl72Work *work) +{ + if (work->fC08 == 1) + { + work->think3 = 6; + work->count3 = 0; + return; + } + + if (work->count3 == 0) + { + meryl72_800D5300 = 1; + } + + if (work->count3 == 2 && meryl72_800D5300 & 0x2) + { + meryl72_800D5300 &= ~0x2; + work->think3 = 7; + work->count3 = 0; + return; + } + + if (work->count3 == 4) + { + work->pad.press |= 0x4000; + work->fC0E -= 2; + if (work->fC0E < 0) + { + work->fC0E = 0; + } + } + + if (work->count3 >= 5 && (work->count3 > 1500 || !(meryl72_800D5300 & 0x1))) + { + work->think3 = 9; + work->count3 = 0; + GM_GameStatus &= ~STATE_PADRELEASE; + return; + } + + work->count3++; +} + +void s07c_meryl72_unk2_800CF3AC(Meryl72Work *work) +{ + int tick; + + work->pad.press |= 0x10000; + work->pad.dir = work->sn_dir; + + if (work->fC08 == 1) + { + work->think3 = 6; + work->count3 = 0; + + if (GM_StreamStatus() != -1) + { + GM_StreamPlayStop(); + } + } + else + { + if (work->count3 == 0 && GM_StreamStatus() == -1) + { + tick = GV_Time % 2; + GM_VoxStream(work->voices[13 + tick], 0); + } + + if (work->count3 > 30) + { + if (GM_StreamStatus() == -1) + { + s07c_meryl72_unk2_800CEBF4(work); + work->think2 = 2; + work->think3 = 4; + work->count3 = 0; + } + } + else + { + work->count3++; + } + } +} + +void s07c_meryl72_unk2_800CF4C0(Meryl72Work *work) +{ + CONTROL *control; + + if (s07c_meryl72_unk2_800CEC64(&GM_PlayerPosition)) + { + work->pad.press |= 0x10000; + } + else + { + work->pad.press |= 0x20000000; + } + + work->pad.dir = work->sn_dir; + + if ((work->count3 % 32) == 0) + { + control = &work->control; + + if (!HZD_InsideZone(control->map->hzd, &control->mov, work->param.defends[0])) + { + s07c_meryl72_unk2_800CEBF4(work); + work->think3 = 2; + work->count3 = 0; + } + } + + work->count3++; +} + +void s07c_meryl72_unk2_800CF568(Meryl72Work* work) +{ + if (work->count3 == 0) + { + work->pad.dir = work->sn_dir; + GM_VoxStream(work->voices[0], 0x40000000); + } + + if (work->count3 < 150) + { + work->pad.press |= 0x2000000; + } + + if (work->count3 == 160) + { + GM_StreamPlayStart(); + work->fC10[0] = 1800; + work->pad.press |= 0x40000000; + } + + if (work->count3 > 180 && GM_StreamStatus() == -1) + { + work->think1 = 2; + work->think2 = 2; + work->think3 = 4; + work->count3 = 0; + work->fC04 = 0; + + GM_GameStatus &= ~(STATE_PADRELEASE | STATE_PAUSE_ONLY | GAME_FLAG_BIT_07); + s07c_meryl72_unk2_800CECEC(work, 1); + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CF67C(Meryl72Work *work) +{ + work->fC0A = 0; + + if (s07c_meryl72_unk2_800CEC64(&GM_PlayerPosition) && work->fC08 == 1) + { + work->think2 = 3; + work->think3 = 6; + work->count3 = 0; + + if (GM_StreamStatus() != -1) + { + GM_StreamPlayStop(); + } + + return; + } + + if (work->count3 == 0) + { + if (GM_StreamStatus() == -1) + { + if (GM_GameOverTimer == 0 && GM_SnakeCurrentHealth > 0) + { + GM_VoxStream(work->voices[work->fC0C + 7], 0); + } + + if (++work->fC0E > 14) + { + work->fC0E = 14; + } + + if (++work->fC0C > 4) + { + work->fC0C = 0; + } + } + else + { + work->think3 = 4; + work->count3 = 0; + return; + } + } + + if (work->count3 > 60 && GM_StreamStatus() == -1) + { + work->think3 = 4; + work->count3 = 0; + work->pad.press |= 0x10000; + } + else + { + work->pad.press |= 0x10000; + work->pad.dir = work->sn_dir; + work->count3++; + } +} + +void s07c_meryl72_unk2_800CF824(Meryl72Work *work) +{ + CONTROL *control; + + if (s07c_meryl72_unk2_800CEC64(&GM_PlayerPosition)) + { + work->pad.press |= 0x10000; + } + else + { + work->pad.press |= 0x20000000; + work->fC04++; + } + + work->pad.dir = work->sn_dir; + + if (GM_StreamStatus() != -1) + { + work->count3 = 0; + } + + if (s07c_meryl72_unk2_800CEC64(&GM_PlayerPosition) && work->fC08 == 1) + { + work->think2 = 3; + work->think3 = 6; + work->count3 = 0; + return; + } + + if (work->fC04 > 5400) + { + if (s07c_meryl72_unk2_800CECB4(&GM_PlayerPosition)) + { + if (GM_StreamStatus() == -1 && GM_GameOverTimer == 0 && GM_SnakeCurrentHealth > 0) + { + GM_VoxStream(work->voices[18], 0); + } + + work->fC04 = 0; + } + } + else if (work->count3 > 60 && work->fC0A > 150) + { + work->think3 = 5; + work->count3 = 0; + work->pad.press |= 0x10000; + return; + } + + control = &work->control; + + if ((work->count3 & 0x1F) == 0 && !HZD_InsideZone(control->map->hzd, &control->mov, work->param.defends[0])) + { + s07c_meryl72_unk2_800CEBF4(work); + work->think3 = 2; + work->count3 = 0; + } + + work->count3++; +} + +void s07c_meryl72_unk2_800CF9E0(Meryl72Work *work) +{ + if (s07c_meryl72_unk2_800CEE88(work)) + { + work->think3 = 3; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CFA28(Meryl72Work *work) +{ + if (work->fC38 > 900) + { + work->think3 = 4; + work->fC38 = 0; + work->count3 = 0; + } + else if (s07c_meryl72_unk2_800CEE88(work)) + { + work->think3 = 3; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CFA98(Meryl72Work *work) +{ + if (s07c_meryl72_unk2_800CEC64(&GM_PlayerPosition) && work->fC08 == 1) + { + work->think2 = 3; + work->think3 = 6; + work->count3 = 0; + } + else if (s07c_meryl72_unk2_800CEE88(work)) + { + work->think3 = 3; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CFB14(Meryl72Work *work) +{ + s07c_meryl72_unk2_800CEBBC(work); + + if (s07c_meryl72_unk2_800CEE88(work)) + { + work->think3 = 3; + work->count3 = 0; + } + else if (!s07c_meryl72_unk2_800CEC64(&GM_PlayerPosition)) + { + work->think3 = 9; + work->count3 = 0; + GM_GameStatus &= ~STATE_PADRELEASE; + } + else if (work->sn_dis < 1000) + { + work->think3 = 8; + work->count3 = 0; + } + else if (work->count3 > 300) + { + work->think3 = 8; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CFBBC(Meryl72Work *work) +{ + if (s07c_meryl72_unk2_800CEC64(&GM_PlayerPosition) && work->fC08 == 1) + { + work->think2 = 3; + work->think3 = 6; + work->count3 = 0; + } + else if (s07c_meryl72_unk2_800CEEDC(work, 300) > 0) + { + work->think2 = 2; + work->think3 = 4; + work->pad.mode = 0; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CFC54(Meryl72Work *work) +{ + if (s07c_meryl72_unk2_800CEEDC(work, 300) > 0) + { + work->think3 = 4; + work->pad.mode = 0; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CFCA4(Meryl72Work *work) +{ + if (work->fC38 > 900) + { + work->think3 = 4; + work->fC38 = 0; + work->count3 = 0; + } + else if (s07c_meryl72_unk2_800CEEDC(work, 300) > 0) + { + work->think3 = 4; + work->fC38 = 0; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CFD0C(Meryl72Work *work) +{ + s07c_meryl72_unk2_800CEBBC(work); + + if (s07c_meryl72_unk2_800CEEDC(work, 1000) > 0 || work->count3 > 300) + { + printf(" stop dis=%d\n", work->sn_dis); + + work->think3 = 8; + work->pad.mode = 0; + work->count3 = 0; + } + else + { + work->count3++; + } +} + +void s07c_meryl72_unk2_800CFD80(Meryl72Work *work) +{ + switch (work->think3) + { + case 0: + s07c_meryl72_unk2_800CEF58(work); + break; + + case 4: + s07c_meryl72_unk2_800CF4C0(work); + break; + + case 2: + s07c_meryl72_unk2_800CF9E0(work); + break; + + case 3: + s07c_meryl72_unk2_800CFC54(work); + break; + } + + if (s07c_meryl72_unk2_800CECB4(&GM_PlayerPosition)) + { + work->think1 = 1; + work->think2 = 0; + work->think3 = 0; + + // Unequip item if it changes camera + if (GM_ItemTypes[GM_CurrentItemId + 1] & 0x2) + { + GM_CurrentItemId = IT_None; + } + + // Unequip weapon if it changes camera + if (GM_WeaponTypes[GM_CurrentWeaponId + 1] & 0x200) + { + GM_CurrentWeaponId = WP_None; + } + + work->fC38 = 0; + work->count3 = 0; + work->fC04 = 0; + + GM_GameStatus |= STATE_PADRELEASE | STATE_PAUSE_ONLY | GAME_FLAG_BIT_07; + s07c_meryl72_unk2_800CECEC(work, 0); + } +} + +void s07c_meryl72_unk2_800CFED0(Meryl72Work *work) +{ + switch (work->think3) + { + case 0: + s07c_meryl72_unk2_800CEF98(work); + break; + + case 2: + s07c_meryl72_unk2_800CFA28(work); + break; + + case 3: + s07c_meryl72_unk2_800CFCA4(work); + break; + + case 4: + s07c_meryl72_unk2_800CF568(work); + break; + } +} + +void s07c_meryl72_unk2_800CFF64(Meryl72Work *work) +{ + switch (work->think3) + { + case 6: + s07c_meryl72_unk2_800CF060(work); + break; + + case 2: + s07c_meryl72_unk2_800CFB14(work); + break; + + case 3: + s07c_meryl72_unk2_800CFD0C(work); + break; + + case 7: + s07c_meryl72_unk2_800CF144(work); + break; + + case 8: + s07c_meryl72_unk2_800CF2A4(work); + break; + + case 9: + s07c_meryl72_unk2_800CF3AC(work); + break; + } +} + +void s07c_meryl72_unk2_800D0010(Meryl72Work *work) +{ + switch (work->think3) + { + case 2: + s07c_meryl72_unk2_800CFA98(work); + break; + + case 3: + s07c_meryl72_unk2_800CFBBC(work); + break; + + case 4: + s07c_meryl72_unk2_800CF824(work); + break; + + case 5: + s07c_meryl72_unk2_800CF67C(work); + break; + } +} + +void Think1_Phase0_800D00A4(Meryl72Work *work) +{ + if (work->think2 == 0) + { + s07c_meryl72_unk2_800CFD80(work); + } +} + +void Think1_Phase1_800D00D4(Meryl72Work *work) +{ + if (work->think2 == 0) + { + s07c_meryl72_unk2_800CFED0(work); + } +} + +void Think1_Phase2_800D0104(Meryl72Work *work) +{ + switch (work->think2) + { + case 2: + s07c_meryl72_unk2_800D0010(work); + break; + + case 3: + s07c_meryl72_unk2_800CFF64(work); + break; + } +} + +void Meryl9Think_800D0154(Meryl72Work *work) +{ + int i; + + work->pad.dir = -1; + work->pad.press = 0; + + switch (work->think1) + { + case TH1_PHASE0: + Think1_Phase0_800D00A4(work); + break; + + case TH1_PHASE1: + Think1_Phase1_800D00D4(work); + break; + + case TH1_PHASE2: + Think1_Phase2_800D0104(work); + break; + } + + for (i = 0; i < 3; i++) + { + if (--work->fC10[i] < 0) + { + work->fC10[i] = 0; + } + } + + work->fC38++; +} + +void s07c_meryl72_unk2_800D0220(void) +{ + meryl72_800D52F8 = DG_ZeroVector; + meryl72_800D5300 = 0; +} + +void s07c_meryl72_unk2_800D025C(void) +{ +} diff --git a/components/mgs/mgs/source/animal/ninja/bunsin2.c b/components/mgs/mgs/source/animal/ninja/bunsin2.c new file mode 100644 index 00000000..c9fa7f68 --- /dev/null +++ b/components/mgs/mgs/source/animal/ninja/bunsin2.c @@ -0,0 +1,183 @@ +#include "game/game.h" + +#define BODY_DATA GV_StrCode("njatrans") +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE | DG_FLAG_GBOUND | DG_FLAG_AMBIENT | DG_FLAG_IRTEXTURE ) + +typedef struct _Work +{ + GV_ACT actor; + OBJECT body; + OBJECT *root_obj; + SVECTOR *field_108; + SVECTOR rots[32][16]; + MATRIX world[32]; + int field_150C; + int field_1510; + int *field_1514; + char field_1518[32]; + int field_1538; + int field_153C; + MATRIX light[2]; +} Work; + +static void s08b_bunsin2_800D54CC(DG_OBJS *objs) +{ + int i; + + for (i = 0; i < 16; i++) + { + objs->objs[i].model->flags |= DG_FLAG_PAINT; + } +} + +static void s08b_bunsin2_800D54FC(DG_OBJS *objs) +{ + int i; + + for (i = 0; i < 16; i++) + { + objs->objs[i].model->flags &= ~DG_FLAG_PAINT; + } +} + +static void s08b_bunsin2_800D5530(Work *work) +{ + int index, i; + SVECTOR *src, *dst; + + if (++work->field_150C >= 32) + { + work->field_150C = 0; + } + + index = work->field_150C; + work->world[index] = work->root_obj->objs->world; + + src = work->root_obj->objs->rots; + dst = work->rots[work->field_150C]; + for (i = 0; i < 16; i++) + { + *dst = *src; + dst++; + src++; + } +} + +void s08b_bunsin2_800D5600(Work *work) +{ + SVECTOR dir; + int state; + + state = *work->field_1514; + if (state == 2) + { + s08b_bunsin2_800D54CC(work->root_obj->objs); + work->field_153C = 2; + } + + if (work->field_153C > 0) + { + GV_DirVec2(GV_RandU(4096), 500, &dir); + work->world[work->field_150C].t[0] += dir.vx; + work->world[work->field_150C].t[2] += dir.vz; + + work->field_1518[work->field_150C] = 2; + + if (--work->field_153C == 0) + { + s08b_bunsin2_800D54FC(work->root_obj->objs); + } + } + else if (state == 0) + { + work->field_1518[work->field_150C] = 0; + } + else + { + work->field_1518[work->field_150C] = 1; + } +} + +static void s08b_bunsin2_800D5734(Work *work) +{ + int index; + DG_OBJS *objs; + + index = work->field_150C - work->field_1510; + if (index < 0) index += 32; + + objs = work->body.objs; + objs->world = work->world[index]; + objs->rots = work->rots[index]; + + if (work->field_1518[index] != 0) + { + DG_VisibleObjs(objs); + } + else + { + DG_InvisibleObjs(objs); + } + + work->field_1538++; +} + +static void Act(Work *work) +{ + s08b_bunsin2_800D5530(work); + s08b_bunsin2_800D5600(work); + s08b_bunsin2_800D5734(work); + + DG_GetLightMatrix2(work->field_108, work->light); +} + +static void Die(Work *work) +{ + GM_FreeObject(&work->body); +} + +static int GetResources(Work *work, OBJECT *root_obj, int arg2, int *arg3) +{ + OBJECT *body; + int i, bright; + + body = &work->body; + GM_InitObject(body, BODY_DATA, BODY_FLAG, 0); + GM_ConfigObjectJoint(body); + GM_ConfigObjectLight(body, work->light); + + for (i = 0; i < 16; i++) + { + body->objs->objs[i].model->flags |= DG_MODEL_TRANS; + } + + work->root_obj = root_obj; + work->field_1510 = arg2; + work->field_150C = 0; + work->field_1538 = 0; + work->field_153C = 0; + work->field_1514 = arg3; + + bright = (8 - arg2) * 10 + 40; + work->light[0].t[0] = bright; + work->light[0].t[1] = bright; + work->light[0].t[2] = bright; + return 0; +} + +void *NewBunsin2(OBJECT *root_obj, int arg1, int *arg2) +{ + Work *work; + + work = GV_NewActor(GV_ACTOR_USER, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(work, Act, Die, "bunsin2.c"); + if (GetResources(work, root_obj, arg1, arg2) < 0) + { + GV_DestroyActor(work); + return NULL; + } + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/animal/ninja/tenmetu.c b/components/mgs/mgs/source/animal/ninja/tenmetu.c new file mode 100644 index 00000000..7a4ad358 --- /dev/null +++ b/components/mgs/mgs/source/animal/ninja/tenmetu.c @@ -0,0 +1,221 @@ +#include "game/game.h" + +typedef struct _Work +{ + GV_ACT actor; + DG_PRIM *prim; + MATRIX *world; + RECT rect; + SVECTOR pos; + SVECTOR last_pos; + short state; + int *force; + int bright; + int time; + DG_TEX *tex; +} Work; + +void s08c_800C4220( MATRIX *pos ); // anime/effect/rcm2.c + +static void Update0( Work *work ) +{ + if ( *work->force > 0 ) + { + work->state = 2; + return; + } + + if ( *work->force < 0 ) + { + work->state = 3; + return; + } + + work->bright += 8; + if ( work->bright > 150 ) + { + work->state = 1; + } +} + +static void Update1( Work *work ) +{ + if ( *work->force < 0 ) + { + work->state = 3; + return; + } + + if ( work->bright < 160 && *work->force > 0 ) + { + work->state = 2; + return; + } + + work->bright -= 4; + if ( work->bright < 50 ) + { + work->state = 0; + } +} + +static void Update2( Work *work ) +{ + if ( *work->force < 0 ) + { + work->state = 3; + return; + } + + work->bright += 16; + if ( work->bright > 180 ) + { + work->bright = 180; + work->state = 1; + } +} + +static void Update3( Work *work ) +{ + work->bright -= 16; + if ( work->bright < 0 ) + { + work->bright = 0; + if ( *work->force >= 0 ) + { + work->state = 2; + } + } +} + +static void Update( Work *work ) +{ + switch ( work->state ) + { + case 0: + Update0( work ); + break; + case 1: + Update1( work ); + break; + case 2: + Update2( work ); + break; + case 3: + Update3( work ); + break; + } +} + +static void Shade( Work *work ) +{ + POLY_FT4 *pack; + + pack = work->prim->packs[ GV_Clock ]; + setRGB0( pack, work->bright, work->bright, work->bright ); +} + +static void Move( Work *work ) +{ + SVECTOR *last_pos = &work->last_pos; + SVECTOR pos; + int diff; + + pos.vx = work->world->t[ 0 ]; + pos.vy = work->world->t[ 1 ]; + pos.vz = work->world->t[ 2 ]; + + diff = ABS( pos.vx - last_pos->vx ); + if ( diff > 20 ) + { + work->last_pos = pos; + s08c_800C4220( work->world ); + return; + } + + diff = ABS( pos.vy - last_pos->vy ); + if ( diff > 40 ) + { + work->last_pos = pos; + s08c_800C4220( work->world ); + return; + } + + diff = ABS( pos.vz - last_pos->vz ); + if ( diff > 50 ) + { + work->last_pos = pos; + s08c_800C4220( work->world ); + } +} + +static void Act( Work *work ) +{ + Update( work ); + Shade( work ); + Move( work ); + work->time++; +} + +static void Die( Work *work ) +{ + GM_FreePrim( work->prim ); +} + +static void InitPack( POLY_FT4 *pack, DG_TEX *tex ) +{ + setPolyFT4( pack ); + setRGB0( pack, 128, 128, 128 ); + setSemiTrans( pack, 1 ); + DG_SetPacketTexture4( pack, tex ); + pack->tpage = ( pack->tpage & ~( 3 << 5 ) ) | ( 1 << 5 ); +} + +static int GetResources( Work *work, MATRIX *world, int *force ) +{ + RECT rect = {20, 20, 40, 40}; + SVECTOR pos = {0, 0, 100}; + DG_PRIM *prim; + DG_TEX *tex; + + work->force = force; + work->world = world; + work->rect = rect; + work->pos = pos; + + prim = work->prim = GM_MakePrim( DG_PRIM_RECTANGLE | DG_PRIM_POLY_FT4, 1, &work->pos, &work->rect ); + if ( prim == NULL ) + { + return -1; + } + + DG_RaisePrim( prim, 250 ); + prim->root = world; + + tex = work->tex = DG_GetTexture( GV_StrCode( "rcm_l" ) ); + if ( tex == NULL ) + { + return -1; + } + + InitPack( prim->packs[0], tex ); + InitPack( prim->packs[1], tex ); + return 0; +} + +void *NewTenmetu( MATRIX *world, int *force ) +{ + Work *work; + + work = GV_NewActor( GV_ACTOR_USER, sizeof(Work) ); + if ( work != NULL ) + { + GV_SetNamedActor( work, Act, Die, "tenmetu.c" ); + if ( GetResources( work, world, force ) < 0 ) + { + GV_DestroyActor( work ); + return NULL; + } + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/animal/zako11e/zako11e.c b/components/mgs/mgs/source/animal/zako11e/zako11e.c new file mode 100644 index 00000000..5fd35cd1 --- /dev/null +++ b/components/mgs/mgs/source/animal/zako11e/zako11e.c @@ -0,0 +1,590 @@ +#include "enemy/enemy.h" + +#include +#include "common.h" +#include "mts/mts.h" // for fprintf +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "libhzd/libhzd.h" +#include "strcode.h" + +#include "chara/snake/shadow.h" // for NewShadow2 +#include "enemy/glight.h" // for NewGunLight + +/* +extern ENEMY_COMMAND EnemyCommand; +extern SVECTOR ENEMY_TARGET_SIZE; +extern SVECTOR ENEMY_TARGET_FORCE; +extern SVECTOR ENEMY_ATTACK_SIZE; +extern SVECTOR ENEMY_ATTACK_FORCE; +extern SVECTOR ENEMY_TOUCH_SIZE; +extern SVECTOR ENEMY_TOUCH_FORCE; +extern SVECTOR COM_NO_POINT; +extern int COM_EYE_LENGTH; + +extern GM_CameraSystemWork GM_Camera; +*/ + +extern SVECTOR ZAKO_TARGET_SIZE_800C38CC; +extern SVECTOR ZAKO_TARGET_FORCE_800C38D4; +extern SVECTOR ZAKO_ATTACK_SIZE_800C38DC; +extern SVECTOR ZAKO_ATTACK_FORCE_800C38E4; +extern SVECTOR ZAKO_TOUCH_SIZE_800C38EC; +extern SVECTOR ZAKO_TOUCH_FORCE_800C38F4; + +extern int ZAKO11E_EYE_LENGTH_800C3904; +extern SVECTOR ZAKO11E_NO_POINT_800C38FC; + +extern int s11e_zk11ecom_800D9A20( ZakoWork *work ); +extern int Zako11EPushMove_800D889C( ZakoWork *work ); +extern int Zako11EActionMain_800D8830( ZakoWork *work ); +extern void ZAKO11E_SetPutChar_800D8004( ZakoWork *work, int put ); + +extern void *NewKogaku2(CONTROL *pCtrl, OBJECT *pObj, int unit); + +void RootFlagCheck_800D34C8( ZakoWork *work ) +{ + /* do nothing */ +} + +void s11e_zako11e_800D34D0( DG_OBJS* objs, DG_DEF* def ) +{ + int i; + DG_OBJ *obj; + DG_FreeObjsPacket( objs, 0 ); + DG_FreeObjsPacket( objs, 1 ); + + objs->def = def; + obj = objs->objs; + for ( i = 0 ; i < 16 ; i++ ) + { + obj->model = &def->model[ i ]; + obj->n_packs = def->model[ i ].n_faces; + obj++; + } +} + +extern GM_CameraSystemWork GM_Camera; + +void s11e_zako11e_800D354C( ZakoWork *work ) +{ + if ( work->visible ) + { + if ( work->param_low_poly == 1 ) + { + if ( GM_GameStatus & (GAME_FLAG_BIT_07 | STATE_BEHIND_CAMERA) || GM_Camera.first_person ) + { + if ( work->has_kmd != work->param_low_poly ) + { + work->has_kmd = work->param_low_poly; + s11e_zako11e_800D34D0( work->body.objs, work->def ); + } + } + else if ( work->has_kmd ) + { + work->has_kmd = 0; + s11e_zako11e_800D34D0( work->body.objs, work->kmd ); + } + } + DG_VisibleObjs( work->body.objs ); + DG_VisibleObjs( work->weapon.objs ); + work->shadow_enable[0] = 1; + work->glight_enable[0] = 1; + } + else + { + DG_InvisibleObjs( work->body.objs ); + DG_InvisibleObjs( work->weapon.objs ); + work->shadow_enable[0] = 0; + work->glight_enable[0] = 0; + } +} + +extern int s11e_dword_800DF3B4; +extern ZAKO_COMMAND ZakoCommand_800DF280; + +void ZakoAct_800D3684( ZakoWork *work ) +{ + VECTOR vec; + TARGET *trgt; + CONTROL *ctrl; + TARGET *trgt2; + + ctrl = &( work->control ) ; + if (GM_CheckMessage( &( work->actor ) , ctrl->name, HASH_KILL ) ) + { + GV_DestroyActor( &( work->actor ) ); + return; + } + + RootFlagCheck_800D34C8( work ); + if ( !work->faseout ) + { + Zako11EPushMove_800D889C( work ); + GM_ActControl( ctrl ); + GM_ActObject2( &( work->body ) ); + GM_ActObject2( &( work->weapon ) ); + + DG_GetLightMatrix2( &( ctrl->mov ), work->light ); + + Zako11EActionMain_800D8830( work ); + trgt = work->target; + GM_MoveTarget( trgt, &( ctrl->mov ) ); + + GM_PushTarget( trgt ); + + if ( trgt->class & TARGET_TOUCH ) + { + trgt2 = &work->field_94C; + if ( trgt2->class & TARGET_TOUCH ) + { + if ( trgt2->damaged & TARGET_TOUCH ) + { + trgt2->damaged &= ~TARGET_TOUCH; + } + GM_MoveTarget( &( work->field_94C ), &( ctrl->mov ) ); + GM_TouchTarget( &( work->field_94C ) ); + } + } + + vec.vx = vec.vy = vec.vz = work->scale; + ScaleMatrix( &( work->body.objs->world ), &vec ); + } + + s11e_zako11e_800D354C( work ); + *work->glight_enable = 0; + *work->shadow_enable = 0; + + if ( s11e_dword_800DF3B4 == 0xF && ZakoCommand_800DF280.field_0x8C[work->field_B74].field_04 == 1 ) + { + GV_DestroyActor( &( work->actor ) ); + } +} + +void InitTarget_800D3800( ZakoWork *work ) +{ + TARGET *target; + TARGET *target2; + int life; + int faint; + + target = work->target; + life = work->param_life; + faint = work->param_faint; + + GM_SetTarget( target, TARGET_FLAG, ENEMY_SIDE, &ZAKO_TARGET_SIZE_800C38CC ); + GM_SetPowerTarget( target, POWER_DECREASE, -1, life, faint, &ZAKO_TARGET_FORCE_800C38D4 ); + GM_SetCaptureTarget( target, -1, faint, NULL, NULL); + + GM_TargetBody( target, &( work->body.objs->objs[1].world ) ); + + work->local_data = work->param_life; + target2 = &work->field_904; + GM_SetTarget( target2, TARGET_POWER, PLAYER_SIDE, &ZAKO_ATTACK_SIZE_800C38DC ); + GM_SetPowerTarget( target2, POWER_CONST | POWER_EXPLODE, 5, 0, 3, &ZAKO_ATTACK_FORCE_800C38E4 ); + + target2 = &work->field_94C; + GM_SetTarget( target2, ( TARGET_TOUCH ), ENEMY_SIDE, &ZAKO_TOUCH_SIZE_800C38EC ); + GM_SetPowerTarget( target2, POWER_CONST | POWER_EXPLODE, 5, 0, 0, &ZAKO_TOUCH_FORCE_800C38F4 ); +} + +void s11e_zako11e_800D3934( ZakoWork* work ) +{ + WatcherUnk *s; + s = &work->unknown; + + GV_ZeroMemory(s, 0x24); + s->field_00 = 0; + s->field_1C = 0x1C2; + s->field_1E = 1; + + work->action = 0; + work->action2 = 0; + work->time = 0; + work->time2 = 0; +} + +int s11e_zako11e_800D3990( ZakoWork* work, int name, int where ) +{ + int i; + int has_kmd; + char *opt, *opt2; + CONTROL *ctrl; + OBJECT *body; + OBJECT *arm; //? + SVECTOR shadow; + + ctrl = &work->control; + if ( GM_InitControl( ctrl, name, where ) < 0 ) return -1; + + opt = GCL_GetOption( 'p' ); + + GM_ConfigControlString( ctrl, (char*)opt, (char*)GCL_GetOption( 'd' ) ) ; + GM_ConfigControlAttribute( ctrl, 13 ); + GM_ConfigControlInterp( ctrl, 4 ); + + ctrl->seg_flag = 2; + + GM_ConfigControlTrapCheck( ctrl ); + + body = &work->body; + arm = &work->weapon; + + GM_InitObject( body, KMD_IPPANHEI, 0x32D, OAR_ENEMY ) ; + GM_ConfigObjectJoint( body ) ; + GM_ConfigMotionControl( body, &work->m_ctrl, OAR_ENEMY, work->m_segs1, work->m_segs2, ctrl, work->rots ); + GM_ConfigObjectLight( body, work->light ); + + work->param_low_poly = 0; + + opt2 = GCL_GetOption( 'y' ); + if ( opt2 ) { + work->param_low_poly = GCL_StrToInt( (char*)opt2 ); + } + + has_kmd = work->param_low_poly; + if ( has_kmd == 1 ) + { + work->def = body->objs->def; + work->kmd = GV_GetCache( GV_CacheID( HASH_LOPRYHEI, 'k' ) ); + work->has_kmd = has_kmd; + } + + + work->kogaku_body = NewKogaku2( ctrl, body, 0 ); + work->hom = GM_AllocHomingTarget( &body->objs->objs[6].world, ctrl ); + GM_InitObject( arm, KMD_FAMAS, 0x6D, 0 ); + GM_ConfigObjectLight( arm, work->light ) ; + GM_ConfigObjectRoot( arm, body, 4 ); + work->kogaku_weapon = NewKogaku2( ctrl, arm, 0 ); + + //did they just not remove this? + for ( i = 0 ; i < 0 ; i++ ) + { + work->field_B00[i] = 0; + } + shadow.vy = 6; + shadow.vz = 12; + shadow.pad = 15; + shadow.vx = 0; + + work->shadow = (void*)NewShadow2( ctrl, body, shadow, &work->shadow_enable ) ; + work->glight = NewGunLight( &( body->objs->objs[4].world ), &work->glight_enable ) ; + + ZAKO11E_SetPutChar_800D8004( work, 0 ); + s11e_zako11e_800D3934( work ); + + return 0; +} + +extern void s11e_zk11ecom_800D9A64( int ); + +void s11e_zako11e_800D3BD8( ZakoWork* work ) +{ + s11e_zk11ecom_800D9A64( work->field_B74 ); + GM_FreeControl( &( work->control ) ); + GM_FreeObject( &( work->body ) ); + GM_FreeObject( &( work->weapon ) ); + GM_FreeTarget( work->target ); + GV_DestroyActor( work->glight ); + GV_DestroyActor( work->shadow ); + + GM_FreeHomingTarget( work->hom ); + if ( work->kogaku_body ) + { + GV_DestroyActorQuick( work->kogaku_body ); + work->kogaku_body = NULL; + } + if ( work->kogaku_weapon ) + { + GV_DestroyActorQuick( work->kogaku_weapon ); + work->kogaku_weapon = NULL; + } +} + +void ZakoDie_800D3C84( ZakoWork* work ) +{ + s11e_zako11e_800D3BD8( work ); +} + +int ReadNodes_800D3CA4( ZakoWork* work ) +{ + int i; + HZD_PAT *patrol; + HZD_PTP *points; + + patrol = work->control.map->hzd->def->routes; + patrol = &patrol[ work->param_root ]; + + work->n_nodes = patrol->n_points; + + if ( work->n_nodes <= 0 ) return -1; + + points = patrol->points; + for ( i = 0 ; i < work->n_nodes ; i++ ) + { + work->nodes[i].vx = points->x; + work->nodes[i].vy = points->y; + work->nodes[i].vz = points->z; + work->nodes[i].pad = points->command; + points++; + } + return 0; +} + +int s11e_zako11e_800D3D40( char *opt, short* s ) +{ + int i; + unsigned char *res; + + for ( i = 0 ; ( res = GCL_NextStr() ) ; i++ ) + { + *s = GCL_StrToInt( res ); + s++; + } + + return i; +} + +int s11e_zako11e_800D3D98( char *opt, int* l ) +{ + int i; + unsigned char *res; + + for ( i = 0 ; ( res = GCL_NextStr() ) ; i++ ) + { + *l = GCL_StrToInt( res ); + l++; + } + + return i; +} + +extern unsigned short s11e_dword_800C35DC[8]; + +int s11e_zako11e_800D3DF0( ZakoWork *work ) +{ + int i; + char *opt; + int ret; + + for ( i = 0 ; i < 8 ; i++ ) + { + work->field_BB0[i] = s11e_dword_800C35DC[i]; + } + + opt = GCL_GetOption( 't' ); + if ( opt ) + { + ret = s11e_zako11e_800D3D98( opt, &work->field_BB0[1] ); + if ( ret > 4 ) + { + printf( "Err Err Err Set time Over\n" ) ; + return -1; + } + } + + for ( i = 0 ; i < 4 ; i++ ) + { + work->field_BD0[i] = i * 1024; + } + + opt = GCL_GetOption( 'i' ); + + if ( opt ) + { + ret = s11e_zako11e_800D3D40( opt, work->field_BD0 ); + if ( ret > 4 ) + { + printf( "Err Err Err Set Dir Over\n" ) ; + return -1; + } + } + + return 0; +} + +extern int s11e_dword_800C35BC[8]; + +void ZakoGetResources_800D3EC8( ZakoWork *work, int name, int where ) +{ + int addr; + char *opt; + int i; + + s11e_zako11e_800D3990( work, name, where ) ; + work->field_B74 = s11e_zk11ecom_800D9A20( work ) ; + + if ( work->field_B74 << 24 < 0 ) + { + printf( "Err not enough work !!\n" ) ; + } + + s11e_zako11e_800D3DF0( work ) ; + + //root + work->param_root = 0; + opt = GCL_GetOption( 'r' ); + if ( opt ) + { + work->param_root = GCL_StrToInt( ( char* )opt ); + } + + //life + work->param_life = 576; + opt = GCL_GetOption( 'l' ); + if ( opt ) + { + work->param_life = GCL_StrToInt( ( char* )opt ); + } + + //faint + work->param_faint = 7; + opt = GCL_GetOption( 'f' ); + if ( opt ) + { + work->param_faint = GCL_StrToInt( ( char* )opt ); + } + + work->field_B84 = 64; + opt = GCL_GetOption( 'z' ); + if ( opt ) + { + work->field_B84 = GCL_StrToInt( ( char* )opt ); + } + + work->param_blood = 65; + opt = GCL_GetOption( 'b' ); + if ( opt ) + { + work->param_blood = GCL_StrToInt( ( char* )opt ); + } + + printf( " low[pory=%d\n", work->param_low_poly ) ; + work->field_B7D = 0xFF; + + opt = GCL_GetOption('g'); + if ( opt ) + { + work->field_B7D = GCL_StrToInt( ( char* )opt ); + } + if ( work->param_blood == 'Z' ) + { + work->field_B7D = 0; + } + + work->field_BFC = s11e_dword_800C35BC[ work->field_B74 ]; + work->field_C00 = work->field_B74; + work->field_B78 = 0xFF; + + opt = GCL_GetOption( 'n' ); + if ( opt ) + { + work->field_B78 = GCL_StrToInt( ( char* )opt ); + } + + work->param_area = 'A'; + opt = GCL_GetOption( 'a' ); + if (opt != 0) + { + work->param_area = GCL_StrToInt( ( char* )opt ); + } + + if ( work->param_area == 'S' ) ZAKO11E_SetPutChar_800D8004( work, PUTBREATH ) ; /* 白い息はく */ + work->scale = 4096 ; /* スケール */ + + if ( ( opt = GCL_GetOption( 's' ) ) != NULL ) work->scale += GCL_StrToInt( ( char* )opt ); + work->param_item = 1; + + //fprintf(0,"Life=%d Faint=%d Blood=%c Area=%c \n", + // work->param.life, work->param.faint, work->param.blood,work->param.area); + + opt = GCL_GetOption( 'o' ); + if ( opt ) + { + work->field_C48 = GCL_StrToInt( GCL_NextStr() ); + } + else + { + work->field_C48 = -1; + } + + + if( ReadNodes_800D3CA4( work ) < 0 ) fprintf( 1, "watchar.c : action point Err\n" ); + + /* + 当たりデータを初期化する + */ + if( ( work->target = GM_AllocTarget() ) != NULL ) InitTarget_800D3800( work ) ; + + /* + 思考ルーチン用データを初期化する + */ + work->think1 = 0 ; + work->think2 = 0 ; + work->think3 = 0 ; + work->think4 = 0 ; + work->count3 = 0 ; + work->l_count = 0 ; + work->t_count = 0 ; + work->mark_time = 0 ; + work->next_node = 0 ; + work->search_flag = 0 ; + work->act_status = 0 ; + work->target_pos = work->nodes[ 0 ] ; + work->target_addr = HZD_GetAddress( work->control.map->hzd, &( work->target_pos ), -1 ) ; + work->target_map = GM_CurrentMap; + work->alert_level = 0; + work->visible = 1; + work->vision.angle = 0x200; + work->vision.facedir = 0; + work->field_B94 = 0; + work->pad.sound = 0; + work->pad.time = 0; + work->vision.length = ZAKO11E_EYE_LENGTH_800C3904; + work->field_BA4 = ZAKO11E_NO_POINT_800C38FC; + work->subweapon = 0; + + + work->control.mov = work->nodes[ 0 ] ; + work->field_B7B = work->field_B78; + work->field_C4C = 0; + //work->faseout = 0; + work->param_c_root = 0; + + for ( i = 0 ; i <= 7 ; i++ ) + { + work->modetime[i] = 0; + } + + work->field_BA3 = 7; + work->field_BA0 = -1; + + GM_ConfigControlRadarparam( &work->control , 0, 0x200, ZAKO11E_EYE_LENGTH_800C3904, 0 ); + work->start_pos = work->nodes[ 0 ] ; + work->start_map = GM_CurrentMap; + addr = HZD_GetAddress( work->control.map->hzd, &( work->control.mov ), -1 ); + + work->start_addr = addr; + work->field_C10 = addr; + work->field_BF0 = addr; + + work->field_C1C = work->start_pos; +} + +extern void ZakoAct_800D3684(); +extern void ZakoDie_800D3C84(); + +#define EXEC_LEVEL GV_ACTOR_PREV + +void *NewZako11E( int name, int where, int argc, char **argv ) +{ + ZakoWork *work ; + + work = GV_NewActor( EXEC_LEVEL, sizeof( ZakoWork ) ) ; + if ( work != NULL ) { + GV_SetNamedActor( &( work->actor ), ZakoAct_800D3684, ZakoDie_800D3C84, "zako11e.c" ); + ZakoGetResources_800D3EC8( work, name, where ); + } + return (void *)work ; +} diff --git a/components/mgs/mgs/source/animal/zako11e/zk11eaction.c b/components/mgs/mgs/source/animal/zako11e/zk11eaction.c new file mode 100644 index 00000000..9d402812 --- /dev/null +++ b/components/mgs/mgs/source/animal/zako11e/zk11eaction.c @@ -0,0 +1,1867 @@ +#include "enemy/enemy.h" + +#include "common.h" +#include "linkvar.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/item.h" +#include "game/vibrate.h" +#include "okajima/bullet.h" +#include "sound/g_sound.h" + +/*** Inlines *****************************************************************/ + +static inline void UnsetMode( ZakoWork *work ) +{ + extern short ActTable_800C35EC[]; + work->unknown.last_unset = 0; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C35EC[ STANDSTILL ], 0, ACTINTERP, 0 ); + + work->action2 = 0; + work->time2 = 0; + work->unknown.last_unset = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; +} + +static inline void SetAction( ZakoWork *work, int n_action, int interp ) +{ + extern short ActTable_800C35EC[]; + work->unknown.last_set = n_action ; + GM_ConfigObjectAction( &( work->body ), ActTable_800C35EC[ n_action ], 0, interp ); +} + +static inline void UnsetAction( ZakoWork *work, int n_action ) +{ + extern short ActTable_800C35EC[]; + + work->unknown.last_unset = n_action; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C35EC[ n_action ], 0, ACTINTERP, 0x3FE ); +} + +static inline void UnsetActionManual( ZakoWork *work, int n_action, int a4 ) +{ + extern short ActTable_800C35EC[]; + + work->unknown.last_unset = n_action; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C35EC[ n_action ], 0, ACTINTERP, a4 ); +} + +static inline void UnsetAction2( ZakoWork *work ) +{ + extern short ActTable_800C35EC[]; + + work->unknown.last_unset = 0; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C35EC[ STANDSTILL ], 0, ACTINTERP, 0 ); + GV_DestroyOtherActor( work->subweapon ); +} + +/*****************************************************************************/ + +extern SVECTOR ZAKO11E_NO_POINT_800C38FC; +extern int ZAKO11E_EYE_LENGTH_800C3904; +extern ZAKO_COMMAND ZakoCommand_800DF280; + +extern int CheckPad_800D4A28( ZakoWork *work ); +extern int CheckDamage_800D46A0( ZakoWork * work ); + +extern void s11e_zk11ecom_800D4DD4( ZakoWork *work, int time ); + +void ActStandStill_800D4C2C(ZakoWork* work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + work->act_status |= 1; + + if ( ZakoCommand_800DF280.mode == TOP_COMM_TRAVEL ) + { + work->target->class |= TARGET_C4 ; + } + + work->vision.length = ZAKO11E_EYE_LENGTH_800C3904 ; + + if ( time == 0 ) + { + SetAction( work, STANDSTILL, ACTINTERP ); + } + + if ( CheckDamage_800D46A0( work ) || CheckPad_800D4A28( work ) ) + { + UnsetMode( work ) ; + return ; + } + + if ( work->pad.dir >= 0 ) + { + if ( GV_DiffDirAbs( work->control.rot.vy, work->pad.dir ) < 0x100 ) + { + if ( work->pad.mode & 0x1 ) + { + SetAction( work, ACTION2, ACTINTERP ); + } + else + { + SetAction( work, ACTION1, ACTINTERP ); + } + SetZakoMode( work, s11e_zk11ecom_800D4DD4 ); + + } + else + { + work->control.turn.vy = work->pad.dir; + } + } + work->vision.facedir =work->control.rot.vy; +} + +extern int s11e_zk11ecom_800D47D4( int, int ) ; + +void s11e_zk11ecom_800D4DD4( ZakoWork* work, int time ) +{ + int x, z; + int s0; + int interval; + int tmp; + int dist; + CONTROL * ctrl; + int field_8E0; + int dir; + SVECTOR* svec; + + SetTargetClass( work->target, TARGET_FLAG ); + + if ( ZakoCommand_800DF280.mode == TOP_COMM_TRAVEL ) + { + work->target->class |= TARGET_C4 ; + } + + work->vision.length = ZAKO11E_EYE_LENGTH_800C3904 ; + + if ( CheckDamage_800D46A0( work ) || CheckPad_800D4A28( work ) ) + { + UnsetMode( work ) ; + return ; + } + + dist = -1; + ctrl = &(work->control ); + dir = work->pad.dir; + field_8E0 = work->unknown.last_set; + svec = work->control.vecs; + + if ( (work->pad.mode & 0x1) && ( work->unknown.last_set != ACTION2 ) ) + { + SetAction( work, ACTION2, ACTINTERP ); + } + + if ( dir >= 0 ) + { + s0 = ctrl->n_touches; + if ( s0 > 0 ) + { + dist = GV_VecDir2( svec ); + + if ( s0 >= 2 ) + { + tmp = GV_VecDir2( &ctrl->vecs[1] ); + if ( GV_DiffDirAbs( dir, tmp ) < GV_DiffDirAbs( dir, dist ) ) + { + dist = tmp; + } + } + } + } + + if ( dir < 0 ) + { + SetZakoMode( work, ActStandStill_800D4C2C); + UnsetMode( work ) ; + return; + } + + + switch ( s11e_zk11ecom_800D47D4( dir, dist ) ) + { + case 1: + dir = (dist - 1024) & 0xFFF; + break; + case 2: + dir = (dist + 1024) & 0xFFF; + break; + } + + + if ( !work->body.objs->bound_mode ) + { + do { ctrl->rot.vy = dir; } while (0); //probably a macro + } + ctrl->turn.vy = dir; + interval = 0; + + if ( field_8E0 == 1 ) + { + interval = 50; + } + else if ( field_8E0 == 2 ) + { + interval = 100; + } + + x = interval * rsin( dir ); + + if ( x < 0 ) + { + x += 0xFFF; + } + + ctrl->step.vx = x >> 12; + + z = interval * rcos( dir ); + + if ( z < 0 ) + { + z += 0xFFF; + } + + ctrl->step.vz = z >> 12; + work->vision.facedir = work->control.rot.vy; +} + +extern void s11e_zk11ecom_800D603C( ZakoWork* work, int time ) ; + +void s11e_zk11ecom_800D506C( ZakoWork* work, int time ) +{ + if ( time == 0 ) + { + work->unknown.field_14 = 3; + SetAction( work, ACTION35, 4 ); + } + + if ( !( CheckDamage_800D46A0( work ) ) && ( work->body.is_end ) ) + { + work->pad.tmp = 0; + SetZakoMode( work, s11e_zk11ecom_800D603C ); + } +} + +void s11e_zk11ecom_800D50FC( ZakoWork *work, int time ) +{ + CONTROL *ctrl; + + ctrl = &( work->control ); + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = ZAKO11E_EYE_LENGTH_800C3904 ; + + if ( time == 0 ) + { + SetAction( work, ACTION16, ACTINTERP ); + } + + if ( CheckDamage_800D46A0( work ) ) + { + return ; + } + + if ( work->body.is_end || !( work->pad.press & 0x20 ) ) + { + SetZakoMode( work, ActStandStill_800D4C2C ); + return; + } + + ctrl->turn.vy = work->sn_dir; + ctrl->step.vx = 0; + ctrl->step.vz = 0; + work->vision.facedir = work->control.rot.vy; +} + +void s11e_zk11ecom_800D51E4(ZakoWork* work, int time ) +{ + +} + +extern void ReviseReadyGun_800D4828( ZakoWork* work ); +extern void s11e_zk11ecom_800D5360( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D5410( ZakoWork *work, int time ); +extern void ActGrenade_800D54C8( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D5620( ZakoWork *work, int time ); + + +void ActReadyGun_800D51EC( ZakoWork* work, int time ) +{ + int press; + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = ZAKO11E_EYE_LENGTH_800C3904 ; + press = work->pad.press; + + if ( time == 0 ) + { + if ( press & 0x10000 ) + { + SetAction( work, ACTION3, ACTINTERP ) ; + } + else + { + SetAction( work, ACTION5, ACTINTERP ); + } + GM_ConfigMotionAdjust( &( work->body ), work->adjust ) ; + } + + ReviseReadyGun_800D4828( work ); + if ( CheckDamage_800D46A0( work ) ) + { + return ; + } + + if ( !(press & 0x30000) ) + { + SetZakoMode( work, ActStandStill_800D4C2C ) ; + return ; + } + + if ( press & 0x40000 ) + { + SetZakoModeFields( work, s11e_zk11ecom_800D5360 ) ; + return; + } + + if ( press & 0x80000 ) + { + SetZakoMode( work, s11e_zk11ecom_800D5410 ) ; + return; + } + + if ( press & 0x100000 ) + { + SetZakoModeFields( work, ActGrenade_800D54C8 ) ; + return; + } + + if ( press & 0x200000 ) + { + SetZakoModeFields( work, s11e_zk11ecom_800D5620 ) ; + return; + } + + work->control.turn.vy = work->pad.dir; + work->vision.facedir = work->control.rot.vy; +} + +extern int ZAKO11E_SetPutChar_800D8004( ZakoWork *work, int put ); //ZAKO11E_SetPutChar + +void s11e_zk11ecom_800D5360( ZakoWork* work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = ZAKO11E_EYE_LENGTH_800C3904 ; + + if ( time == 0 ) + { + SetAction( work, ACTION4, 0 ) ; + ZAKO11E_SetPutChar_800D8004( work, 3 ) ; + GM_ConfigMotionAdjust( &( work->body ), work->adjust ) ; + } + + ReviseReadyGun_800D4828( work ); + + if ( CheckDamage_800D46A0( work ) ) + { + return ; + } + + SetZakoModeFields( work, ActReadyGun_800D51EC ); +} + +void s11e_zk11ecom_800D5410( ZakoWork* work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = ZAKO11E_EYE_LENGTH_800C3904 ; + + if ( work->pad.press & 0x80000 ) + { + SetAction( work, ACTION4, 0 ); + ZAKO11E_SetPutChar_800D8004( work, GUNSHOOT ); + } + + work->control.turn.vy = work->pad.dir; + if ( CheckDamage_800D46A0( work ) || (work->pad.press & 0x20000) ) + { + return ; + } + + SetZakoModeFields( work, ActReadyGun_800D51EC ); +} + +void ActGrenade_800D54C8( ZakoWork* work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ) ; + work->vision.length = ZAKO11E_EYE_LENGTH_800C3904 ; /* �???? */ + + if ( time == 0 ) + { + extern void *NewGrenadeEnemy( CONTROL *, OBJECT *, int, u_long *, SVECTOR *, int ) ; + + SetAction( work, GRENADE, ACTINTERP ) ; + work->subweapon = NewGrenadeEnemy( &(work->control), &(work->body), 9, + &(work->trigger), &GM_PlayerPosition, ENEMY_SIDE ) ; + } + + if ( time > ACTINTERP ) + { + work->trigger |= WEAPON_TAKE ; + } + if ( time == 17 ) + { + GM_SeSet( &( work->control.mov ), SE_PINNUKI ) ; + } + if ( time == 45 ) + { + work->trigger |= WEAPON_TRIG2 ; + } + + if ( CheckDamage_800D46A0( work ) ) + { + GV_DestroyActor( work->subweapon ) ; + return ; + } + + if ( work->body.is_end ) + { + GV_DestroyActor( work->subweapon ) ; + SetZakoMode( work, ActReadyGun_800D51EC ); + } +} + +extern int AttackForce_800D48B0( ZakoWork * work ); + +void s11e_zk11ecom_800D5620( ZakoWork* work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = ZAKO11E_EYE_LENGTH_800C3904 ; + + if ( time == 0 ) + { + SetAction( work, ACTION8, 0 ); + } + + if ( time == 3 ) + { + if ( AttackForce_800D48B0( work ) ) + { + GM_SeSet( &( work->control.mov ), SE_HIT ); + } + } + + work->control.turn.vy = work->sn_dir; + + if ( CheckDamage_800D46A0( work ) ) + { + return ; + } + + if ( work->body.is_end ) + { + SetZakoMode( work, ActReadyGun_800D51EC ); + } +} + +void s11e_zk11ecom_800D56F8( ZakoWork* work, int time ) +{ + work->vision.length = 0; + + if ( time == 0 ) + { + SetAction( work, ACTION21, ACTINTERP ) ; + } + + if ( CheckDamage_800D46A0( work ) ) + { + return ; + } + + SetTargetClass( work->target, TARGET_FLAG ); + + if ( work->body.is_end ) + { + work->actend = 1; + SetZakoMode( work, ActStandStill_800D4C2C ); + } +} + +extern void s11e_zk11ecom_800D603C( ZakoWork* work, int time ); +extern void s11e_zk11ecom_800D6BD8( ZakoWork* work, int time ); + +extern void s11e_zk11ecom_800D49C0( ZakoWork *work ); +extern void ZAKO11E_PutBlood_800D7A14( ZakoWork *, int, int ); //ZK11E_PutBlood_800D7A14 + +void s11e_zk11ecom_800D57A0( ZakoWork* work, int time ) +{ + CONTROL *ctrl; + TARGET *target; + + target = work->target; + ctrl = &work->control; + work->unknown.field_1E = 0; + work->act_status |= 0x08; + + if ( time == 0 ) + { + work->unknown.field_14 = 6; + SetAction( work, ACTION31, ACTINTERP ); + } + + if ( time - 8 < 12u ) + { + s11e_zk11ecom_800D49C0( work ); + } + + if ( work->unknown.last_set == 31 ) + { + if ( time == 22 ) + { + + if ( ctrl->mov.vy - ctrl->levels[0] < 2000 ) + { + GM_SeSet( &ctrl->mov, VO_ENEMY_PUNCHED ) ; + GM_SeSet( &ctrl->mov, SE_HIT_FLOOR ) ; + ZAKO11E_PutBlood_800D7A14( work, 6, 0 ) ; + GM_SetNoise( 0x64, 4, &work->control.mov ) ; + } + else if ( ctrl->mov.vy - ctrl->levels[0] > 3000 ) + { + work->target->vital = 0; + GM_SeSet( &ctrl->mov, VO_ENEMY_THROWN ) ; + } + } + if ( work->body.is_end ) + { + if ( !ctrl->grounded ) + { + SetAction( work, ACTION40, ACTINTERP ); + } + else + { + SetZakoMode( work, s11e_zk11ecom_800D603C ); + } + } + } + else + { + if ( ctrl->grounded ) + { + GM_SeSet( &ctrl->mov, VO_ENEMY_PUNCHED ) ; + GM_SeSet( &ctrl->mov, SE_HIT_FLOOR ) ; + GM_SetNoise( 0x64, 4, &work->control.mov ) ; + ZAKO11E_PutBlood_800D7A14( work, 6, 1 ) ; + + if ( work->target->vital <= 0 ) + { + SetZakoMode( work, s11e_zk11ecom_800D6BD8 ); + } + else + { + SetZakoMode( work, s11e_zk11ecom_800D603C ); + } + } + } + if ( time < 24 ) + { + ctrl->turn = *target->field_1C; + } + + if ( time < 22 ) + { + work->control.hzd_height = -32767; + } +} + +extern SVECTOR s11e_dword_800C3660; + +void s11e_zk11ecom_800D5A84( ZakoWork* work ) +{ + SVECTOR svec; + + work->control.turn = GM_PlayerControl->rot; + DG_SetPos2(&GM_PlayerPosition, &GM_PlayerControl->rot); + DG_PutVector(&s11e_dword_800C3660, &svec, 1); + GV_SubVec3(&svec, &work->control.mov, &work->control.step); +} + +extern unsigned char s11e_dword_800C3658; +extern unsigned char s11e_dword_800C365C; + +extern void s11e_zk11ecom_800D649C( ZakoWork *work, int time ); + +void s11e_zk11ecom_800D5B04( ZakoWork *work, int time ) +{ + int v1; + int s2; + TARGET *target; + + target = work->target; + s2 = work->unknown.last_set; + + work->unknown.field_1E = 0; + work->act_status |= 0x0C; + + if ( time == 0 ) + { + work->field_B5A = target->faint; + GM_SeSet( &work->control.mov, VO_ENEMY_GRABBED ); + } + + if ( work->field_B5A != target->faint ) + { + GM_SeSet( &work->control.mov, VO_ENEMY_GRABBED ); + work->field_B5A = target->faint; + } + + if ( target->damaged & TARGET_POWER ) + { + ZAKO11E_PutBlood_800D7A14( work, 5, 0 ); + GM_SeSet( &work->control.mov, VO_ENEMY_GRABBED ); + target->force = DG_ZeroVector; + target->damage = 0; + target->damaged = TARGET_STALE; + + if ( target->vital <= 0 ) + { + v1 = target->a_mode; + if ( v1 == 1 ) + { + work->field_C4C = v1; + } + work->unknown.field_14 = 5; + target->side = ENEMY_SIDE; + SetZakoMode( work, s11e_zk11ecom_800D649C ); + target->captured = 0; + } + } + switch ( GM_PlayerAction ) + { + case 0x7: + case 0xD: + if( s2 != 0x1B && s2 != 0x30 ) + { + target->side = PLAYER_SIDE; + SetAction( work, ACTION27, ACTINTERP ); + work->field_B5C = 0; + } + + if ( work->field_B5C == 0x32 && target->faint > 0 ) + { + SetAction( work, ACTION48, ACTINTERP ); + } + + if ( s2 == 0x30 && !( work->field_B5C & 7 ) ) + { + NewPadVibration( &s11e_dword_800C3658, 1); + NewPadVibration( &s11e_dword_800C365C, 2); + } + work->control.turn = GM_PlayerControl->rot; + s11e_zk11ecom_800D5A84( work ); + work->field_B5C++; + break; + case 0x26: + if ( s2 != 0x1C ) + { + target->side = PLAYER_SIDE; + SetAction( work, ACTION28, ACTINTERP ); + } + work->control.turn = GM_PlayerControl->rot; + s11e_zk11ecom_800D5A84( work ); + break; + case 0x27: + work->unknown.field_14 = 4; + target->side = ENEMY_SIDE; + target->vital = 0; + SetZakoMode( work, s11e_zk11ecom_800D649C ); + return; + default: + work->unknown.field_14 = 5; + target->side = ENEMY_SIDE; + SetZakoMode( work, s11e_zk11ecom_800D649C ); + return; + } + + if ( work->sn_dis > 800 ) + { + target->captured = 0; + } + + work->target->class |= ( TARGET_SEEK | TARGET_POWER) ; + work->vision.facedir = work->control.rot.vy; +} + +void s11e_zk11ecom_800D5E78( ZakoWork *work, int time ) +{ + if ( time == 0 ) + { + SetAction( work, ACTION38, ACTINTERP ); + } + + if (work->body.is_end ) + { + SetZakoMode( work, s11e_zk11ecom_800D6BD8 ); + } +} + +void s11e_zk11ecom_800D5EEC( ZakoWork *work, int time ) +{ + CONTROL *ctrl; + int time_offset; + + ctrl = &work->control; + work->unknown.field_1E = 0; + work->vision.length = 0; + work->act_status |= 0x8; + + if ( time == 0 ) + { + SetAction( work, ACTION32, ACTINTERP ) ; + work->target->faint = 0; + } + + if ( CheckDamage_800D46A0( work ) ) + { + return ; + } + + time_offset = 10; + + if ( time == time_offset ) + { + work->unknown.field_14 = 1; + SetAction( work, ACTION37, ACTINTERP ) ; + } + else + { + SetTargetClass( work->target, TARGET_FLAG ); + } + + if ( time == time_offset + 24 ) + { + GM_SeSet( &ctrl->mov, SE_ENEMY_COLLAPSE ); + } + + if ( time == time_offset + 46 ) + { + GM_SeSet( &ctrl->mov, SE_HIT_FLOOR ); + } + + if ( time >= time_offset + 50 && work->body.is_end ) + { + SetZakoMode( work, s11e_zk11ecom_800D603C ) ; + } +} + +extern void s11e_zk11ecom_800D61B4( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D627C( ZakoWork *work, int time ); + +extern int s11e_zk11ecom_800D43E8( ZakoWork* work ); + +void s11e_zk11ecom_800D603C( ZakoWork *work, int time ) +{ + int x; + + work->act_status |= 0x20; + if ( time == 0 ) + { + + if ( work->unknown.field_14 < 3 ) + { + if ( work->unknown.field_14 == 1 ) + { + SetAction( work, ACTION41, ACTINTERP ); + } + else + { + SetAction( work, ACTION39, ACTINTERP ); + } + } + else + { + SetAction( work, ACTION40, ACTINTERP ); + } + + x = work->control.mov.vx % 30; + if ( x < 0 ) + { + x = -x; + } + work->field_B5A = x + 10; + } + + + if (s11e_zk11ecom_800D43E8( work ) ) + { + SetZakoMode( work, s11e_zk11ecom_800D61B4 ); + } + else + { + if ( work->field_B5A < time ) + { + if ( work->target->faint <= 0 ) + { + work->target->faint = work->param_faint; + } + SetZakoMode( work, s11e_zk11ecom_800D627C ); + } + } +} + +extern void ZAKO11E_PutItem_800D7AE8( ZakoWork *work ); + +void s11e_zk11ecom_800D61B4( ZakoWork *work, int time ) +{ + if ( time == 0 ) + { + if ( work->unknown.field_14 < 3 ) + { + SetAction( work, 0x2E, ACTINTERP ); + } + else + { + SetAction( work, 0x2F, ACTINTERP ); + } + GM_SeSet( &work->control.mov, SE_ITEM_CURSOR ); + } + + if ( time == 4 ) + { + ZAKO11E_PutItem_800D7AE8( work ); + } + + if ( work->body.is_end ) + { + SetZakoMode( work, s11e_zk11ecom_800D6BD8 ); + } +} + +void s11e_zk11ecom_800D627C( ZakoWork* work, int time ) +{ + work->unknown.field_1E = 0; + work->act_status |= 0x08; + + if ( time == 0 ) + { + if ( work->unknown.field_14 < 3 ) + { + if ( work->field_B5A >= 0x96 ) + { + SetAction( work, ACTION44, ACTINTERP ); + } + else + { + SetAction( work, ACTION42, ACTINTERP ); + } + } + else + { + if ( work->field_B5A >= 0x96 ) + { + SetAction( work, ACTION45, ACTINTERP ); + } + else + { + SetAction( work, ACTION43, ACTINTERP ); + } + } + } + + if ( work->body.is_end ) + { + work->local_data = work->target->vital; + SetZakoMode( work, ActStandStill_800D4C2C ); + } +} + +extern void ZAKO11E_PutFog_800D7A84( ZakoWork * ); + +void s11e_zk11ecom_800D638C( ZakoWork* work, int time ) +{ + TARGET* target; + + work->unknown.field_1E = 0; + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = ZAKO11E_EYE_LENGTH_800C3904 ; + work->act_status |= 0x08; + + if ( CheckDamage_800D46A0( work ) ) + { + return ; + } + + target = work->target; + if (time == 0 ) + { + SetAction( work, ACTION15, ACTINTERP ) ; + + GM_SeSet( &work->control.mov, VO_ENEMY_PUNCHED ); + if ( target->a_mode == 3 ) + { + GM_SeSet( &work->control.mov, SE_PUNCH_HIT ); + ZAKO11E_PutFog_800D7A84( work ); + } + else + { + ZAKO11E_PutBlood_800D7A14( work, 5, 0 ); + + } + } + + if ( work->body.is_end) + { + SetZakoMode( work, ActStandStill_800D4C2C ) ; + } +} + +extern void s11e_zk11ecom_800D69F8( ZakoWork *work, int time ); + +void s11e_zk11ecom_800D649C( ZakoWork *work, int time ) +{ + GCL_ARGS args; + long data[3]; + CONTROL* ctrl; + WatcherUnk *unk; + + unk = &work->unknown; + work->unknown.field_1E = 0; + work->act_status |= 0x8; + work->control.step = work->target->force; + + + ctrl = &work->control; + if ( time == 0 ) + { + switch( unk->field_14 ) + { + case 0: + GM_SeSet( &ctrl->mov, SE_PUNCH_HIT ); + SetAction( work, ACTION34, ACTINTERP ); + GM_SeSet( &ctrl->mov, VO_ENEMY_PUNCHED ); + ZAKO11E_PutBlood_800D7A14( work, 5, 0 ); + work->field_B5A = 17; + break; + case 1: + SetAction( work, ACTION37, ACTINTERP ); + if ( work->target->vital <= 0 ) + { + if ( GM_CurrentWeaponId == WP_Rifle ) + { + ZAKO11E_PutBlood_800D7A14( work, 6, 2 ); + } + else + { + ZAKO11E_PutBlood_800D7A14( work, 6, 1 ); + } + } + else + { + ZAKO11E_PutBlood_800D7A14( work, 5, 0 ); + + } + GM_SeSet( &ctrl->mov, VO_ENEMY_PUNCHED ); + work->field_B5A = 46; + break; + case 3: + GM_SeSet( &ctrl->mov, SE_ENEMY_DAMAGE ); + SetAction( work, ACTION35, ACTINTERP ); + ZAKO11E_PutBlood_800D7A14( work, 5, 0 ); + work->field_B5A = 17; + break; + case 2: + GM_SeSet( &ctrl->mov, VO_ENEMY_THROWN ); + SetAction( work, ACTION36, ACTINTERP ); + ZAKO11E_PutBlood_800D7A14( work, 5, 0 ); + work->field_B5A = 22; + break; + case 4: + GM_SeSet( &ctrl->mov, VO_ENEMY_SNAPPED ); + SetAction( work, ACTION29, ACTINTERP ); + work->field_B5A = 67; + break; + case 5: + SetAction( work, ACTION30, ACTINTERP ); + work->field_B5A = 15; + break; + } + + if ( work->target->vital <= 0 && work->field_C48 >= 0 ) + { + args.argc = 3; + args.argv = data; + data[0] = ctrl->mov.vx; + data[1] = ctrl->mov.vy; + data[2] = ctrl->mov.vz; + + //seems it doesn't even get used + GCL_ExecProc( work->field_C48, 0 ); + } + } + + + switch( unk->field_14 ) + { + case 0: + if ( time < 12 ) + { + ctrl->turn.vy += 170; + } + + if ( time - 7 < 23u ) + { + s11e_zk11ecom_800D49C0( work ); + } + + if ( time < 20 ) + { + work->control.hzd_height = -32767; + } + break; + case 1: + if ( time == 24 ) + { + GM_SeSet( &ctrl->mov, SE_ENEMY_COLLAPSE ); + } + break; + case 3: + if ( time - 7 < 23u ) + { + s11e_zk11ecom_800D49C0( work ); + } + if ( time < 15 ) + { + ctrl->hzd_height = -32767; + } + break; + case 2: + if ( time - 7 < 23u ) + { + s11e_zk11ecom_800D49C0( work ); + } + if ( time < 20 ) + { + ctrl->hzd_height = -32767; + } + break; + case 4: + case 5: + work->act_status |= 0x04; + break; + } + + if ( time > 16 && ctrl->grounded ) + { + ctrl->step = DG_ZeroVector; + } + + if ( time == work->field_B5A ) + { + if (ctrl->mov.vy - ctrl->levels[0] < 2000) + { + GM_SeSet( &ctrl->mov, SE_HIT_FLOOR ) ; + GM_SetNoise( 0x64, 4, &ctrl->mov ) ; + ZAKO11E_PutBlood_800D7A14( work, 6, 0 ) ; + } + else + { + if (ctrl->mov.vy - ctrl->levels[0] > 3000) + { + work->target->vital = 0; + SetZakoMode( work, s11e_zk11ecom_800D69F8 ); + return; + } + } + } + + if ( work->body.is_end ) + { + work->unknown.field_1E = 1; + work->target->force = DG_ZeroVector; + if ( work->target->vital <= 0 ) + { + SetZakoMode( work, s11e_zk11ecom_800D6BD8 ); + } + else + { + SetZakoMode( work, s11e_zk11ecom_800D603C ); + } + } +} + +void s11e_zk11ecom_800D69F8( ZakoWork* work, int time ) +{ + CONTROL *ctrl; + + ctrl = &work->control; + work->unknown.field_1E = 0; + work->act_status |= 0x8; + + ctrl->step = work->target->force; + + if ( time == 0 && work->unknown.field_14 != 2 ) + { + GM_SeSet( &ctrl->mov, VO_ENEMY_THROWN ); + } + + if ( time > 16 && ctrl->grounded ) + { + ctrl->step = DG_ZeroVector; + } + + if ( work->unknown.last_set < 39 ) + { + if ( work->body.is_end ) + { + if ( work->unknown.field_14 < 3 ) + { + if ( work->unknown.field_14 == 1 ) + { + SetAction( work, ACTION41, ACTINTERP ); + } + else + { + SetAction( work, ACTION39, ACTINTERP ); + } + } + else + { + SetAction( work, ACTION40, ACTINTERP ); + } + } + } + else if ( ctrl->grounded ) + { + work->unknown.field_1E = 1; + work->target->force = DG_ZeroVector; + GM_SeSet( &ctrl->mov, SE_HIT_FLOOR ); + ZAKO11E_PutBlood_800D7A14( work, 6, 1 ); + SetZakoMode( work, s11e_zk11ecom_800D6BD8 ); + } +} + +extern void s11e_zk11ecom_800D6CE8( ZakoWork *work, int time ); + +void s11e_zk11ecom_800D6BD8( ZakoWork *work, int time ) +{ + work->act_status |= 0x40; + if ( time == 0 ) + { + if ( work->unknown.field_14 < 3 ) + { + if ( work->unknown.field_14 == 1 ) + { + SetAction( work, ACTION51, ACTINTERP ); + } + else + { + SetAction( work, ACTION49, ACTINTERP ); + } + } + else + { + SetAction( work, ACTION50, ACTINTERP ); + } + GM_ConfigControlAttribute( &work->control, 0 ); + work->alert_level = 0; + } + + if ( time == 4 ) + { + ZAKO11E_PutItem_800D7AE8( work ); + } + + if ( time & 2 ) + { + work->visible = 0; + } + else + { + work->visible = 1; + } + + if ( time > 8 ) + { + SetZakoMode( work, s11e_zk11ecom_800D6CE8 ); + } +} + +extern void s11e_zk11ecom_800D4700( ZakoWork* work ); + +void s11e_zk11ecom_800D6CE8( ZakoWork *work, int time ) +{ + work->act_status |= 0x40; + + if ( time == 0 ) + { + work->visible = 0; + work->control.mov = ZAKO11E_NO_POINT_800C38FC; + ZakoCommand_800DF280.field_0x8C[ work->field_B74 ].field_04 = 1; + + if ( !work->field_C4C ) + { + GM_TotalEnemiesKilled++; + } + } + + if ( ZakoCommand_800DF280.field_0x8C[ work->field_B74 ].field_04 == 2 ) + { + s11e_zk11ecom_800D4700( work ); + SetZakoMode( work, ActStandStill_800D4C2C) ; + } +} + +void s11e_zk11ecom_800D6DDC( ZakoWork *work ) +{ + short v0; + int time_prev; + ZAKOACTION action; + CONTROL *ctrl; + WatcherUnk *unk; + + work->trigger = 0; + work->vision.length = 0; + work->target->class = TARGET_AVAIL; + + unk = &work->unknown; + + work->actend = 0; + work->act_status = 0; + + action = work->action; + ctrl = &work->control; + + work->field_C38 = 0; + + ctrl->height = ( work->body.height * work->scale ) / 4096 ; + ctrl->hzd_height = ctrl->levels[0] + 750; + + time_prev = work->time; + work->time++; + + if ( !action ) + { + action = ActStandStill_800D4C2C; + work->action = ActStandStill_800D4C2C; + } + + action( work, time_prev ); + action = work->action2; + + if ( action ) + { + time_prev = work->time2; + work->time2++; + action( work, time_prev ); + } + + if ( !unk->field_1E ) + { + ctrl->r_sphere = GV_NearExp2( ctrl->r_sphere, unk->field_1C ); + } + else + { + ctrl->r_sphere = -1; + } + + if ( work->target->class & TARGET_POWER ) + { + work->hom->flag = TRUE; + } + else + { + work->hom->flag = FALSE; + } + + if ( unk->field_04 < 0 && ctrl->grounded ) + { + unk->field_04 = 0; + } + + v0 = unk->field_04 - 16; + unk->field_04 = v0; + ctrl->step.vy = v0; + + if ( work->mark_time > 0 ) + { + work->mark_time--; + } +} + +//override.c? +void s11e_zk11ecom_800D6F68( ZakoWork* work, int time ) +{ + if ( time == 0 ) + { + UnsetAction( work, ACTION9 ); + } + + if ( !( work->pad.press & 1 ) ) + { + UnsetMode( work ); + } + else + { + if ( time < 4 ) + { + work->vision.facedir = ( work->control.rot.vy - ( time * 256 ) ) & 0xFFF; + } + else + { + work->vision.facedir = ( work->control.rot.vy - 1024 ) & 0xFFF; + } + } +} + +void s11e_zk11ecom_800D7034( ZakoWork* work, int time) +{ + if ( time == 0 ) + { + UnsetAction( work, ACTION10 ); + } + + if ( !( work->pad.press & 2 ) ) + { + UnsetMode( work ); + } + else + { + if ( time < 4 ) + { + work->vision.facedir = ( work->control.rot.vy + ( time * 256 ) ) & 0xFFF; + } + else + { + work->vision.facedir = ( work->control.rot.vy + 1024 ) & 0xFFF; + } + } +} + +void ActOverScoutD_800D7100( ZakoWork* work, int time ) +{ + work->vision.length = 3000; + + if ( time == 0 ) + { + UnsetAction( work, ACTION11 ); + } + + if ( !( work->pad.press & 4 ) ) + { + UnsetMode( work ); + } +} + +void s11e_zk11ecom_800D7198( ZakoWork* work, int time ) +{ + work->vision.length = 3000; + + if ( time == 0 ) + { + UnsetAction( work, ACTION14 ); + } + + if ( !( work->pad.press & 0x2000 ) ) + { + UnsetMode( work ); + } +} + +void s11e_zk11ecom_800D7230( ZakoWork* work, int time ) +{ + work->vision.length = 0; + + if ( time == 0 ) + { + UnsetAction( work, ACTION24 ); + GM_SeSet( &work->control.mov, VO_ENEMY_SNEEZE ); + } + + if ( work->body.time2 || !( work->pad.press & 0x40 ) ) + { + work->pad.time = 0; + UnsetMode( work ); + } +} + +void s11e_zk11ecom_800D72E4( ZakoWork* work, int time ) +{ + work->vision.length = 0; + + if ( time == 0 ) + { + UnsetAction( work, ACTION22 ); + } + + if ( time == 60 ) + { + GM_SeSet( &work->control.mov, VO_ENEMY_YAWN ); + } + + if ( work->body.time2 || !( work->pad.press & 0x80 ) ) + { + work->pad.time = 0; + UnsetMode( work ); + } +} + +void s11e_zk11ecom_800D73AC( ZakoWork* work, int time ) +{ + if ( time == 0 ) + { + UnsetAction( work, ACTION23 ); + } + + if ( work->body.time2 || !( work->pad.press & 0x400 ) ) + { + work->pad.time = 0; + UnsetMode( work ); + } +} + +void s11e_zk11ecom_800D7450( ZakoWork* work, int time ) +{ + work->vision.length = 0; + + if ( time == 0 ) + { + UnsetAction( work, ACTION19 ); + } + + if ( time == 90 ) + { + GM_SeSet( &work->control.mov, VO_ENEMY_YAWN ); + } + + if ( work->body.time2 || !( work->pad.press & 0x800 ) ) + { + work->pad.time = 0; + UnsetMode( work ); + } +} + +void s11e_zk11ecom_800D7518( ZakoWork *work, int time ) +{ + SVECTOR mov; + work->vision.length = 0; + + if ( time == 0 ) + { + UnsetAction( work, ACTION19 ); + } + + if ( time == 90 ) + { + GM_SeSet( &work->control.mov, VO_ENEMY_YAWN ); + } + + if ( time == 110 ) + { + UnsetAction( work, ACTION20 ); + } + + if ( time > 110 ) + { + work->act_status |= 0x10; + if ( work->m_ctrl.info2.frame == 30 ) + { + mov = work->control.mov; + mov.vy += 500; + AN_Sleep( &mov ); + GM_SeSet( &work->control.mov, VO_ENEMY_SNORE ); + + if ( work->sn_dis < 1000 && ( GM_PlayerStatus & PLAYER_WATCH ) ) + { + int res = GV_RandU( 12 ); + if ( res > 10 ) + { + work->pad.sound = 240; + } + } + } + if ( work->pad.press & 0x1000 ) + { + SetZakoMode( work, s11e_zk11ecom_800D56F8 ); + UnsetMode( work ); + return; + } + } + + if ( !( work->pad.press & 0x200 ) ) + { + UnsetMode( work ); + } +} + +extern void AN_Unknown_800C3B7C( MATRIX *matrix ); +extern SVECTOR s11e_dword_800C3668; + +void s11e_zk11ecom_800D76F0( ZakoWork* work ) +{ + MATRIX mat; + + DG_SetPos( &work->body.objs->objs[6].world ); + DG_MovePos( &s11e_dword_800C3668 ); + ReadRotMatrix( &mat ); + AN_Unknown_800C3B7C( &mat ); +} + +void s11e_zk11ecom_800D7730( ZakoWork *work, int time ) +{ + int diff; + work->vision.length = 3000; + work->act_status |= 0x80; + + if ( time == 0 ) + { + UnsetAction( work, ACTION25 ); + } + + diff = time - 1000; + if ( time >= 1000 && time < 1210 ) + { + if ( diff == 0 || diff == 4 || diff == 8 || diff == 12 || + diff == 20 || diff == 28 || diff == 36 || diff == 48 || + diff == 60 ) + { + GM_SeSet( &work->control.mov, SE_WET_STEP ); + s11e_zk11ecom_800D76F0( work ); + } + } + else if ( time > 30 && time & 1 ) + { + GM_SeSet( &work->control.mov, SE_WET_STEP ); + if ( time > 45 ) + { + s11e_zk11ecom_800D76F0( work ); + } + } + + if ( !( work->pad.press & 0x800000 ) ) + { + UnsetMode( work ); + } +} + +void s11e_zk11ecom_800D7878( ZakoWork *work, int time ) +{ + work->vision.length = 3000; + + if ( time == 0 ) + { + UnsetAction( work, ACTION25 ); + } + + if ( time == 186 ) + { + UnsetActionManual( work, ACTION26, 0xFFFF ); + } + + //? + if ( time == 0 || time == 4 || time == 8 || time == 12 || + time == 20 || time == 28 || time == 36 || time == 48 || + time == 60 || time == 120 || time == 122 || time == 124 || + time == 126 || time == 180 || time == 182 || time == 184 || + time == 186 ) + { + GM_SeSet( &work->control.mov, SE_WET_STEP ); + s11e_zk11ecom_800D76F0( work ); + } + + if ( ( time > 186 && work->body.time2 ) || !( work->pad.press & 0x1000000 ) ) + { + work->pad.time = 0; + UnsetMode( work ); + } +} + +//put funcs +extern SVECTOR s11e_dword_800C3670; +extern SVECTOR s11e_dword_800C3678; +extern SVECTOR s11e_dword_800C3680; +extern SVECTOR s11e_dword_800C3688; + + +extern void NewBlood( MATRIX *, int ); + +void ZAKO11E_PutBlood_800D7A14( ZakoWork* work, int obj_idx, int count ) +{ + MATRIX mat; + DG_SetPos( &work->body.objs->objs[ obj_idx ].world ); + DG_MovePos( &s11e_dword_800C3670 ); + DG_RotatePos( &s11e_dword_800C3678 ); + ReadRotMatrix( &mat ); + NewBlood( &mat, count ); +} + +void ZAKO11E_PutFog_800D7A84( ZakoWork *work ) +{ + MATRIX mat; + SVECTOR svec; + + DG_SetPos( &work->body.objs->objs[1].world ); + DG_MovePos( &s11e_dword_800C3680 ); + DG_RotatePos( &s11e_dword_800C3688 ); + ReadRotMatrix( &mat ); + + svec.vx = mat.t[ 0 ]; + svec.vy = mat.t[ 1 ]; + svec.vz = mat.t[ 2 ]; + + AN_Fog( &svec ); +} + +extern const char *s11e_dword_800C3690[3]; + +void ZAKO11E_PutItem_800D7AE8( ZakoWork* work ) +{ + int rand; + SVECTOR svec; + CONTROL *ctrl; + Item_Info item; + + svec = work->unknown.field_0C; + rand = 10; + ctrl = &work->control; + svec.vx += GV_RandU( rand ); + svec.vy += 100; + rand = GV_RandU( rand ); + svec.vz += rand; + rand = GV_RandU( 4 ); + + switch ( work->field_B74 ) + { + case 0: + item.type = 4; + item.id = IT_Ration; + item.num = 1; + item.message = s11e_dword_800C3690[0]; + break; + case 1: + case 2: + case 3: + if ( GM_FamasFlag < 0 ) + { + item.type = 2; + item.message = s11e_dword_800C3690[1]; + item.id = WP_Socom; + item.num = 12; + } + else + { + item.type = 2; + item.id = WP_Famas; + item.message = s11e_dword_800C3690[2]; + item.num = 25; + } + break; + } + item.time = 900; + NewItemPut( &ctrl->mov, &svec, &item ); +} + +void ZAKO11E_PutMark_800D7C10( ZakoWork *work, int mark ) +{ + MATRIX *mat; + if ( !( work->control.map->index & GM_PlayerMap ) ) + { + return; + } + + mat = &work->body.objs->objs[6].world; + if( mark == 0 ) + { + GM_SeSet( &work->control.mov, SE_EXCLAMATION ); + } + + if ( work->mark_time ) + { + GV_DestroyActor( work->mark ); + } + + work->mark = AN_Unknown_800CA1EC( mat, mark ) ; + work->mark_time = 30; +} + +void ZAKO11E_PutSound_800D7CAC( ZakoWork* work ) +{ + int a1, a3; + int a2; + int v1; + + a3 = work->unknown.last_set; + a2 = work->m_ctrl.info1.frame; + + v1 = ( ( work->field_B74 % 4 ) * 2 ) + 0xA0; + a1 = ( ( work->field_B74 % 4 ) * 2 ) + 0xA1; + + if( a3 == 1 ) + { + if ( a2 == 22 ) + { + GM_SeSet( &work->control.mov, a1 ); + } + else if ( a2 == 11 ) + { + GM_SeSet( &work->control.mov, v1 ); + } + } + else if ( a3 == 2 ) + { + if ( a2 == 16 ) + { + GM_SeSet( &work->control.mov, a1 ); + } + else if ( a2 == 8 ) + { + GM_SeSet( &work->control.mov, v1 ); + } + } +} + +extern void NewLSight( SVECTOR *from, SVECTOR *to, int color ) ; +extern void AN_Breath( MATRIX * ); + +void ZAKO11E_PutBreath_800D7D44( ZakoWork *work, int arg1 ) +{ + int frame; + if ( ZakoCommand_800DF280.mode == TOP_COMM_ALERT ) + { + return; + } + + if ( !( work->control.map->index & GM_PlayerMap ) ) + { + return; + } + + if ( work->unknown.last_unset == 20 ) + { + frame = work->m_ctrl.info2.frame; + if ( frame == 31 ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } + else if ( work->unknown.last_unset == 22 ) + { + frame = work->m_ctrl.info2.frame; + if ( ( frame == 15 ) || ( frame == 35 ) || ( frame == 50 ) || ( frame == 60 ) || + ( frame == 70 ) || ( frame == 74 ) || ( frame == 78 ) ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } + else if ( work->unknown.last_unset == 19 ) + { + frame = work->m_ctrl.info2.frame; + if ( ( frame == 30 ) || ( frame == 40 ) || ( frame == 50 ) || ( frame == 60 ) || + ( frame == 70 ) || ( frame == 80 ) || ( frame == 90 ) || ( frame == 95 ) || + ( frame == 100 ) || ( frame == 105 ) ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } + else + { + if ( ( GV_Time % 64 ) == ( work->field_B74 * 16 ) ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } +} + +void s11e_zk11ecom_800D7E8C( ZakoWork* work ) +{ + if ( work->vision.field_B92 == 2 ) + { + NewLSight( &GM_PlayerPosition, &work->control.mov, 0x00008F ); + } +} + +extern SVECTOR s11e_dword_800C36AC; + +extern void AN_BulletSmoke( MATRIX *, int ); +extern void AN_FamasFlash( MATRIX * ); + +extern int ZAKO11E_ClearPutChar_800D804C( ZakoWork *work, void *func ); + +void ZAKO11E_PutBulletEx_800D7EC8( ZakoWork *work ) +{ + MATRIX* mat; + SVECTOR svec; + MATRIX local_mat; + + svec = DG_ZeroVector; + svec.vz = GV_RandU( 128 ); + mat = &work->body.objs->objs[4].world; + + DG_SetPos( mat ); + DG_MovePos( &s11e_dword_800C36AC ); + DG_RotatePos( &svec ); + + svec.vx = GV_RandS( 16 ) + 1024; + svec.vz = 0; + + DG_RotatePos( &svec ); + ReadRotMatrix( &local_mat ); + + if ( GV_Time & 3 ) + { + NewBulletEx( BULLET_RECOILSPARK, &local_mat, 2, 1, 0, 0xA, work->field_B84, 0x2710, 0x2EE); + } + else + { + NewBulletEx( BULLET_RECOILSPARK | BULLET_NO_HZD, &local_mat, 2, 1, 0, 0xA, work->field_B84, 0x2710, 0x2EE); + } + + GM_SeSetMode( &work->control.mov, SE_ENEMY_SHOT, GM_SEMODE_BOMB ); + AN_BulletSmoke( mat, 0 ); + AN_FamasFlash( &local_mat ); + ZAKO11E_ClearPutChar_800D804C( work, ZAKO11E_PutBulletEx_800D7EC8 ); +} + +extern void *s11e_dword_800C369C[]; + +int ZAKO11E_SetPutChar_800D8004( ZakoWork *work, int idx ) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( work->field_B00[ i ] == NULL ) + { + work->field_B00[ i ] = s11e_dword_800C369C[ idx ]; + return 1; + } + } + return 0; +} + +int ZAKO11E_ClearPutChar_800D804C( ZakoWork *work, void *func ) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( work->field_B00[ i ] == func ) + { + work->field_B00[ i ] = 0; + return 1; + } + } + return 0; +} + +void ZAKO11E_ExecPutChar_800D8080( ZakoWork* work ) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( work->field_B00[i] ) + { + ZAKOPUTFUNC func = work->field_B00[i]; + func( work ); + } + } +} \ No newline at end of file diff --git a/components/mgs/mgs/source/animal/zako11e/zk11echeck.c b/components/mgs/mgs/source/animal/zako11e/zk11echeck.c new file mode 100644 index 00000000..60143a3a --- /dev/null +++ b/components/mgs/mgs/source/animal/zako11e/zk11echeck.c @@ -0,0 +1,375 @@ +#include "enemy/enemy.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" + +extern void s11e_zk11ecom_800D57A0( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D5B04( ZakoWork *work, int time ); + +extern void s11e_zk11ecom_800D649C( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D638C( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D5E78( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D5EEC( ZakoWork *work, int time ); + +extern SVECTOR ZAKO_TARGET_SIZE_800C38CC; +extern SVECTOR ZAKO_TARGET_FORCE_800C38D4; +extern SVECTOR ZAKO_ATTACK_SIZE_800C38DC; +extern SVECTOR ZAKO_ATTACK_FORCE_800C38E4; +extern SVECTOR ZAKO_TOUCH_SIZE_800C38EC; +extern SVECTOR ZAKO_TOUCH_FORCE_800C38F4; + +extern int ZAKO11E_EYE_LENGTH_800C3904; +extern SVECTOR ZAKO11E_NO_POINT_800C38FC; + +int s11e_zk11ecom_800D435C( ZakoWork *work ) +{ + TARGET *target; + + target= work->target; + if ( target->damaged & 2 ) + { + if ( target->a_mode == 0x24 ) + { + SetZakoMode( work, s11e_zk11ecom_800D57A0 ) ; + } + else + { + SetZakoMode( work, s11e_zk11ecom_800D5B04 ) ; + } + target->damaged = 0; + return 1; + } + return 0; +} + +int s11e_zk11ecom_800D43E8( ZakoWork* work ) +{ + int check; + check = work->target->damaged & 0x20; + return check > 0; +} + +int s11e_zk11ecom_800D4404( SVECTOR* svec ) +{ + return abs( svec->vx ) + abs( svec->vy ) + abs( svec->vz ) ; +} + +int s11e_zk11ecom_800D4440( ZakoWork *work ) { + short val; + TARGET *target; + + target = work->target; + if ( !( target->damaged & TARGET_POWER ) ) return 0; + + val = target->a_mode - 1; + switch ( val ) + { + case 2: + target->force = DG_ZeroVector; + if ( target->faint <= 0 ) + { + work->unknown.field_14 = 1; + SetZakoMode( work, s11e_zk11ecom_800D649C ) ; + } + else + { + SetZakoMode( work, s11e_zk11ecom_800D638C ) ; + } + break; + case 3: + work->unknown.field_14 = 0; + SetZakoMode( work, s11e_zk11ecom_800D649C ) ; + break; + case 0: + target->force = DG_ZeroVector; + if ( target->vital <= 0 || ( work->local_data - target->vital ) >= 192 ) + { + work->unknown.field_14 = 1; + SetZakoMode( work, s11e_zk11ecom_800D649C ) ; + } + else + { + + SetZakoMode( work, s11e_zk11ecom_800D638C ) ; + } + break; + case 1: + if ( s11e_zk11ecom_800D4404( &target->force ) < 100 ) + { + work->unknown.field_14 = 3; + } + else + { + work->unknown.field_14 = 2; + } + SetZakoMode( work, s11e_zk11ecom_800D649C ) ; + break; + case 4: + work->unknown.field_14 = 3; + SetZakoMode( work, s11e_zk11ecom_800D649C ) ; + break; + case 7: + SetZakoMode( work, s11e_zk11ecom_800D5E78 ) ; + break; + case 8: + SetZakoMode( work, s11e_zk11ecom_800D5EEC ) ; + break; + } + + target->damage = 0; + target->damaged = 0; + return 1; +} + +int s11e_zk11ecom_800D4648( ZakoWork * work ) +{ + if ( GM_GameStatus & STATE_STUN ) + { + SetZakoMode( work, s11e_zk11ecom_800D5EEC ) ; + return 1; + } + + return 0; +} + +int CheckDamage_800D46A0( ZakoWork * work ) +{ + if ( s11e_zk11ecom_800D4440( work ) || s11e_zk11ecom_800D435C( work ) || s11e_zk11ecom_800D4648( work ) ) + { + work->target->class = TARGET_AVAIL ; + return 1; + } + + return 0; +} + +void s11e_zk11ecom_800D4700( ZakoWork* work ) +{ + int addr; + CONTROL *ctrl; + + ctrl = &work->control; + ctrl->mov.vx = 30000; + ctrl->mov.vy = 30000; + ctrl->mov.vz = 30000; + + work->param_item = 1; + work->think3 = 1; + work->visible = 0; + work->think1 = 0; + work->think2 = 0; + work->think4 = 0; + work->count3 = 0; + + work->next_node = work->n_nodes - 1; + work->target_addr = work->start_addr; + work->target_pos = work->start_pos; + + work->vision.angle = 512; + work->vision.length = ZAKO11E_EYE_LENGTH_800C3904; + work->alert_level = 0; + work->vision.facedir = 0; + work->pad.sound = 0; + work->pad.mode = 1; + work->field_C4C = 0; + + work->target_map = work->start_map; + work->field_C1C = work->start_pos; + + addr = work->start_addr; + work->field_C10 = addr; + work->field_BF0 = addr; +} + +int s11e_zk11ecom_800D47D4( int dir, int dist ) +{ + int diff ; + if ( dist < 0 || dir < 0 ) return 0 ; + + diff = GV_DiffDirS( dir, dist ) ; + + if ( diff + 0x380 > 0x700u ) return 0 ; + if ( diff >= 0 ) return 1 ; + + return 2 ; +} + +void ReviseReadyGun_800D4828( ZakoWork* work ) +{ + int trans; + int rotx; + + trans = ( ( GM_PlayerBody->objs->objs[6].world.t[1] - work->body.objs->objs[6].world.t[1] ) ); + trans = ratan2( work->sn_dis, trans ) & 0xFFF; + + rotx = GV_NearExp8( work->adjust[2].vx, trans - 0x400 ); + work->adjust[2].vx = rotx; /* 右腕 */ + work->adjust[6].vx = rotx; /* 頭 */ + if ( rotx < 0 ) /* 左腕 */ + { + work->adjust[7].vx = rotx * 3 ; + } + else + { + work->adjust[7].vx = ( rotx * 3 ) / 2 ; + } +} + +const SVECTOR rp_shift_800DEB98 = {-200, 1000, 600}; +const SVECTOR force_800DEBA0 = {5, 0, 100}; +const SVECTOR size_800DEBA8 = {400, 1000, 400}; +const char s11e_dword_800DEBB0[] = {0x0, 0x0, 0x0, 0x0}; + +/* + 打撃攻撃 + ヒットすれば1を返す + */ +int AttackForce_800D48B0( ZakoWork * work ) +{ + TARGET *target; + SVECTOR svec; + SVECTOR rp_shift = rp_shift_800DEB98; /* 右パンチ */ + SVECTOR force = force_800DEBA0; + SVECTOR size = size_800DEBA8; + + target = &work->punch; + GM_SetTarget( target, TARGET_POWER, ENEMY_SIDE, &size ); + DG_SetPos2( &work->control.mov, &work->control.rot ); + DG_RotVector( &force, &svec, 1 ); + GM_SetPowerTarget( target, POWER_ONCE, 2, 32, 1, &svec ); + DG_PutVector( &rp_shift, &work->punch.center, 1 ); + return GM_PowerTarget( target ); +} + +void s11e_zk11ecom_800D49C0( ZakoWork *work ) +{ + TARGET* target; + + target = &work->field_904; + GM_SetPowerTarget( target, POWER_CONST | POWER_EXPLODE, 5, 0, 3, &ZAKO_ATTACK_FORCE_800C38E4 ); + GM_MoveTarget( target, &work->control.mov ); + GM_PowerTarget( target ); +} + +extern void s11e_zk11ecom_800D6F68( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D7034( ZakoWork *work, int time ); +extern void ActOverScoutD_800D7100( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D7198( ZakoWork *work, int time ); + +extern void s11e_zk11ecom_800D6F68( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D7034( ZakoWork *work, int time ); +extern void ActOverScoutD_800D7100( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D7198( ZakoWork *work, int time ); + +extern void s11e_zk11ecom_800D7230( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D72E4( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D73AC( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D7450( ZakoWork *work, int time ); + +extern void s11e_zk11ecom_800D7518( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D7730( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D7878( ZakoWork *work, int time ); + +extern void s11e_zk11ecom_800D506C( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D50FC( ZakoWork *work, int time ); +extern void s11e_zk11ecom_800D51E4( ZakoWork *work, int time ); +extern void ActReadyGun_800D51EC( ZakoWork *work, int time ); + +int CheckPad_800D4A28( ZakoWork *work ) +{ + int press = work->pad.press; + + if ( press & 0x01 ) + { + SetZakoMode2( work, s11e_zk11ecom_800D6F68 ); + return 0; + } + + if ( press & 0x02 ) + { + SetZakoMode2( work, s11e_zk11ecom_800D7034 ); + return 0; + } + + if ( press & 0x04 ) + { + SetZakoMode2( work, ActOverScoutD_800D7100 ); // //ActOverScoutD_800D7100 + return 0; + } + + if ( press & 0x2000 ) + { + SetZakoMode2( work, s11e_zk11ecom_800D7198 ); + return 0; + } + + if ( press & 0x40 ) + { + SetZakoMode2( work, s11e_zk11ecom_800D7230 ); + return 0; + } + + if ( press & 0x80 ) + { + SetZakoMode2( work, s11e_zk11ecom_800D72E4 ); + return 0; + } + + if ( press & 0x400 ) + { + SetZakoMode2( work, s11e_zk11ecom_800D73AC ); + return 0; + } + + if ( press & 0x800 ) + { + SetZakoMode2( work, s11e_zk11ecom_800D7450 ); + return 0; + } + + if ( press & 0x200 ) + { + SetZakoMode2( work, s11e_zk11ecom_800D7518 ); + return 0; + } + + if ( press & 0x800000 ) + { + SetZakoMode2( work, s11e_zk11ecom_800D7730 ); + return 0; + } + + if ( press & 0x1000000 ) + { + SetZakoMode2( work, s11e_zk11ecom_800D7878 ); + return 0; + } + + if ( press & 0x100 ) + { + SetZakoMode( work, s11e_zk11ecom_800D506C ); + return 1; + } + + if ( press & 0x20 ) + { + SetZakoMode( work, s11e_zk11ecom_800D50FC ); + return 1; + } + + if ( press & SP_DANBOWLKERI ) + { + SetZakoMode( work, s11e_zk11ecom_800D51E4 ); + return 1; + } + + if ( press & 0x30000 ) + { + SetZakoMode( work, ActReadyGun_800D51EC ); + work->adjust[2].vx = 0; + return 1; + } + + return 0; +} diff --git a/components/mgs/mgs/source/animal/zako11e/zk11ecom.c b/components/mgs/mgs/source/animal/zako11e/zk11ecom.c new file mode 100644 index 00000000..14f00d9d --- /dev/null +++ b/components/mgs/mgs/source/animal/zako11e/zk11ecom.c @@ -0,0 +1,973 @@ +#include "enemy/enemy.h" + +#include +#include "common.h" +#include "mts/mts.h" // for fprintf +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "libhzd/libhzd.h" +#include "game/game.h" +#include "linkvar.h" + +extern ZAKO_COMMAND ZakoCommand_800DF280; +extern TOPCOMMAND_STRUCT TOPCOMMAND_800DF3A8; + +extern int s11e_dword_800DF3B0; +extern int s11e_dword_800DF3B4; + +extern int ZAKOCOM_PlayerAddress_800DF3B8; +extern SVECTOR ZAKOCOM_PlayerPosition_800DF278; +extern int ZAKOCOM_PlayerMap_800DF3BC; + +extern CONTROL *GM_WhereList[94]; + +int s11e_zk11ecom_800D9A20( ZakoWork *work ) +{ + int i; + for ( i = 0 ; i < 8 ; i++ ) + { + if ( ZakoCommand_800DF280.field_0x8C[ i ].watcher == 0 ) + { + ZakoCommand_800DF280.field_0x8C[ i ].watcher = ( WatcherWork * )work; + ZakoCommand_800DF280.field_0x8C[ i ].field_04 = 2; + return i; + } + } + return -1; +} + +void s11e_zk11ecom_800D9A64( int idx ) +{ + ZakoCommand_800DF280.field_0x8C[ idx ].watcher = 0; + ZakoCommand_800DF280.field_0x8C[ idx ].field_04 = 1; +} + +void ZAKO11E_SetTopCommAL_800D9A84( int alert ) +{ + TOPCOMMAND_800DF3A8.alert = alert; +} + +void ZAKO11E_SetTopCommMD_800D9A90( int mode ) +{ + TOPCOMMAND_800DF3A8.mode = mode; +} + +void ZAKO11E_SetGoPointLast_800D9A9C( void ) +{ + ZakoCommand_800DF280.com_addr = ZAKOCOM_PlayerAddress_800DF3B8; + ZakoCommand_800DF280.com_pos = ZAKOCOM_PlayerPosition_800DF278; + ZakoCommand_800DF280.com_map = ZAKOCOM_PlayerMap_800DF3BC; +} + +void s11e_zk11ecom_800D9AE8(void) +{ + ZakoCommand_800DF280.com_addr = HZD_GetAddress( GM_WhereList[ 0 ]->map->hzd, &GM_NoisePosition, -1 ); + ZakoCommand_800DF280.com_pos = GM_NoisePosition; + ZakoCommand_800DF280.com_map = GM_PlayerMap; +} + +int s11e_zk11ecom_800D9B60( int map_id, int val ) +{ + int i; + A4_STRUCT *unk = &ZakoCommand_800DF280.field_0x68; + + if ( unk->map_id == map_id ) + { + for ( i = 0 ; i < unk->n_entry ; i++ ) + { + if ( unk->field_04[ i ].field_00 == val ) + { + return unk->field_04[ i ].field_02; + } + } + goto end; + } + return 0; +end: + return -1; +} + +int s11e_zk11ecom_800D9BD4( char *ops, A4_STRUCT *unk ) +{ + int i; + i = 0; + + do { + unk->field_04[ i ].field_00 = GCL_StrToInt( GCL_NextStr() ); + unk->field_04[ i ].field_02 = GCL_StrToInt( GCL_NextStr() ); + i++; + } while ( GCL_NextStr() != NULL ); + + + return i; +} + +int s11e_zk11ecom_800D9C34( char *ops, short* s ) +{ + int i; + unsigned char *res; + + for ( i = 0 ; ( res = GCL_NextStr() ) ; i++ ) + { + *s = GCL_StrToInt( res ); + s++; + } + return i; +} + +void s11e_zk11ecom_800D9C8C( void ) +{ + int i; + int dist2; + int dist1; + int reset_pos; + HZD_ZON *zone; + SVECTOR svec; + + i = dist1 = reset_pos = 0; + + for ( ; i < ZakoCommand_800DF280.field_0x34 ; i++ ) + { + zone = &ZakoCommand_800DF280.field_0x64->hzd->def->zones[ ZakoCommand_800DF280.field_0x38[ i ] ]; + + svec.vx = zone->x; + svec.vy = GM_PlayerPosition.vy; + svec.vz = zone->z; + + + dist2 = GV_DiffVec3( &svec, &GM_PlayerPosition ); + if ( dist1 < dist2 ) + { + dist1 = dist2; + reset_pos = i; + } + } + + ZakoCommand_800DF280.field_0x36 = reset_pos; +} + +int s11e_zk11ecom_800D9D7C( int val ) +{ + return ZakoCommand_800DF280.field_0x0C % val; +} + +void s11e_zk11ecom_800D9DBC( int* val ) +{ + switch ( s11e_zk11ecom_800D9D7C( 2 ) ) + { + case 0: + fprintf( 1, "dareda!!\n" ); + *val = VO_ENEMY_WHOSTHAT; + break; + case 1: + fprintf( 1, "dareda!!\n" ); + *val = VO_ENEMY_WHOSTHAT; + break; + } +} + +void s11e_zk11ecom_800D9E10( int* val ) +{ + switch ( s11e_zk11ecom_800D9D7C( 2 ) ) + { + case 0: + fprintf( 1, "nanno otoda!!\n" ); + *val = VO_ENEMY_THATNOISE; + break; + case 1: + fprintf( 1, "nanno otoda!!\n" ); + *val = VO_ENEMY_THATNOISE; + break; + } +} + +void s11e_zk11ecom_800D9E64( int* val ) +{ + switch ( s11e_zk11ecom_800D9D7C( 2 ) ) + { + case 0: + fprintf( 1, "tadano hakoka\n" ); + *val = VO_ENEMY_JUSTABOX; + break; + case 1: + fprintf( 1, "tadano hakoka\n" ); + *val = VO_ENEMY_JUSTABOX; + break; + } +} + +void s11e_zk11ecom_800D9EB8( int* val ) +{ + switch ( s11e_zk11ecom_800D9D7C( 2 ) ) + { + case 0: + fprintf( 1, "kono asiatoha??\n" ); + *val = VO_ENEMY_FOOTPRINTS; + break; + case 1: + fprintf( 1, "kono asiatoha??\n" ); + *val = VO_ENEMY_FOOTPRINTS; + break; + } +} + +void s11e_zk11ecom_800D9F0C( int* val ) +{ + switch ( s11e_zk11ecom_800D9D7C( 2 ) ) + { + case 0: + fprintf( 1, "kinoseika !!\n" ); + *val = VO_ENEMY_NOTHINGHERE; + break; + case 1: + fprintf( 1, "kinoseika !!\n" ); + *val = VO_ENEMY_NOTHINGHERE; + break; + } +} + +void s11e_zk11ecom_800D9F60( int* val ) +{ + switch ( s11e_zk11ecom_800D9D7C( 3 ) ) + { + case 0: + fprintf( 1, "itazo \n" ); + *val = VO_ENEMY_LETSGO; + return; + case 1: + fprintf( 1, "kottida!!\n" ); + *val = VO_ENEMY_THISWAY; + break; + case 2: + fprintf( 1, "kottida!!\n" ); + *val = VO_ENEMY_THISWAY; + break; + } +} + +void s11e_zk11ecom_800D9FF0( int* val ) +{ + switch ( s11e_zk11ecom_800D9D7C( 2 ) ) + { + case 0: + fprintf( 1, "haitini modoruzo!!\n" ); + *val = VO_ENEMY_RETURN; + break; + case 1: + fprintf( 1, "haitini modoruzo!!\n" ); + *val = VO_ENEMY_RETURN; + break; + } +} + +void s11e_zk11ecom_800DA044( int* val ) +{ + switch ( s11e_zk11ecom_800D9D7C( 2 ) ) + { + case 0: + fprintf( 1, "kottida !!\n" ); + *val = VO_ENEMY_THISWAY; + return; + case 1: + *val = 0; + return; + } +} + +int s11e_zk11ecom_800DA0A8( int val ) +{ + switch ( val ) + { + case 240: + s11e_zk11ecom_800D9DBC( &val ); + break; + case 241: + s11e_zk11ecom_800D9E10( &val ); + break; + case 242: + s11e_zk11ecom_800D9E64( &val ); + break; + case 243: + s11e_zk11ecom_800D9EB8( &val ); + break; + case 244: + s11e_zk11ecom_800D9F0C( &val ); + break; + case 245: + s11e_zk11ecom_800D9FF0( &val ); + break; + case 246: + s11e_zk11ecom_800D9F60( &val ); + break; + case 247: + s11e_zk11ecom_800DA044( &val ); + break; + } + + return val; +} + +extern int s11e_dword_800C3908[]; + +void s11e_zk11ecom_800DA16C( ZAKO_COMMAND* command ) +{ + int i; + int t2; + int t3; + int t4; + int dists[8]; + int temp1, temp2; + int temp3, temp4; + ZakoWork *work; + + for ( i = 0 ; i < command->field_0x08 ; i ++ ) + { + work = ( ZakoWork * )command->field_0x8C[ ZakoCommand_800DF280.field_0x40[ i ] ].watcher; + dists[i] = work->sn_dis; + } + + for ( t3 = command->field_0x08 - 1 ; t3 > -1 ; t3 = t4 ) + { + t4 = -1; + for ( i = 1 ; t3 >= i ; i ++ ) + { + //TODO, fix without using dowhile + do + { + temp2 = dists[ i - 1 ]; + t2 = i - 1; + } while (0); + + temp1 = dists[ i ]; + + if ( temp1 < temp2 ) + { + dists[ i - 1 ] = temp1; + dists[ i ] = temp2; + + temp4 = ZakoCommand_800DF280.field_0x40[ i - 1 ]; + temp3 = ZakoCommand_800DF280.field_0x40[ i ]; + t4 = t2; + + ZakoCommand_800DF280.field_0x40[ i - 1 ] = temp3; + ZakoCommand_800DF280.field_0x40[ i ] = temp4; + } + } + } + + for ( i = 0 ; i < command->field_0x08 ; i++ ) + { + work = ( ZakoWork * )command->field_0x8C[ ZakoCommand_800DF280.field_0x40[ i ] ].watcher; + work->field_BFC = s11e_dword_800C3908[ ZakoCommand_800DF280.field_0x40[ i ] ]; + } +} + +extern int s11e_dword_800C3928; + +void s11e_zk11ecom_800DA2BC( ZAKO_COMMAND* command ) +{ + int i; + int dis; + int sound; + ZakoWork *work; + + s11e_dword_800C3928--; + + if ( s11e_dword_800C3928 < 0 ) + { + s11e_dword_800C3928 = 0; + } + + if ( s11e_dword_800C3928 > 0 ) + { + return; + } + + sound = 0; + dis = 100000; + for ( i = 0 ; i < command->field_0x08 ; i++ ) + { + if ( command->field_0x8C[ i ].field_04 == 2 ) + { + work = (ZakoWork * )command->field_0x8C[ i ].watcher; + if ( work->pad.sound && work->sn_dis < dis ) + { + sound = work->pad.sound; + dis = work->sn_dis; + } + work->pad.sound = SE_DUMMY; + } + } + + if ( sound ) + { + s11e_dword_800C3928 = 0x1E; + GM_SeSet2(NULL, 0x30, s11e_zk11ecom_800DA0A8( sound ) ); + } +} + +void s11e_zk11ecom_800DA3A0( WATCHER *struct_c8 , int i ) +{ + int x = struct_c8->field_08; + switch ( x ) + { + case 0: + if ( struct_c8->field_04 == 1 ) + { + struct_c8->field_08 = 1; + struct_c8->field_00 = 1; + ZakoCommand_800DF280.field_0x60++; + } + return; + case 1: + struct_c8->field_00--; + if ( struct_c8->field_00 <= 0 ) + { + struct_c8->field_08 = 2; + struct_c8->field_00 = 0; + } + return; + case 2: + switch ( ZakoCommand_800DF280.mode ) + { + case TOP_COMM_TRAVEL: + if ( ZakoCommand_800DF280.field_0x10C & 1 ) + { + s11e_zk11ecom_800D9C8C(); + struct_c8->field_04 = x; + struct_c8->field_08 = 0; + } + return; + case TOP_COMM_ALERT: + if ( ZakoCommand_800DF280.field_0x60 < 0 ) + { + s11e_zk11ecom_800D9C8C(); + struct_c8->field_04 = x; + struct_c8->field_08 = 0; + } + return; + } + return; + } +} + +int s11e_zk11ecom_800DA4B8( int a0, int a1 ) +{ + if ( a0 < a1 ) + { + a0 = a1; + } + + return a0; +} + +void s11e_zk11ecom_800DA4D0( int val, ZAKO_COMMAND* command ) +{ + if ( command->alert - val > 4 ) + { + command->alert -= 4; + } + else + { + command->alert = val; + } + + if ( command->alert < TOPCOMMAND_800DF3A8.alert ) + { + command->alert = TOPCOMMAND_800DF3A8.alert; + } + + ZAKO11E_SetTopCommAL_800D9A84( 0 ); +} + +void s11e_zk11ecom_800DA534( ZAKO_COMMAND *command ) +{ + int alert; + + switch ( command->mode ) + { + case 0: + if ( command->alert >= 255 ) + { + command->alert = 255; + GM_AlertModeSet(3); + command->mode = 1; + + if ( ZakoCommand_800DF280.field_0x11C >= 0 ) + { + GCL_ExecProc( ZakoCommand_800DF280.field_0x11C, NULL ); + } + } + break; + case 1: + if ( command->alert <= 0 ) + { + GM_AlertModeSet(2); + command->mode = 2; + command->field_0x14 = 0; + } + alert = command->alert; + if ( alert >= 0x101 ) + { + alert = 0x100; + } + GM_SetAlertMax( alert ); + break; + case 2: + command->field_0x14--; + if ( command->field_0x14 <= 0 ) + { + GM_AlertModeSet(0); + command->mode = 0; + command->field_0x14 = 0; + } + if ( command->alert >= 255 ) + { + command->alert = 0xFF; + GM_AlertModeSet(3); + command->mode = 1; + } + alert = command->field_0x14; + if ( alert >= 0x101 ) + { + alert = 0x100; + } + GM_SetAlertMax( alert ); + break; + } + + TOPCOMMAND_800DF3A8.mode = command->mode; +} + +void s11e_zk11ecom_800DA690( ZakoCommanderWork* work ) +{ + int i; + int alert; + ZakoWork *watcher; + + alert = 0; + + for ( i = 0; i < ZakoCommand_800DF280.field_0x08; i++ ) + { + if ( ZakoCommand_800DF280.field_0x8C[ i ].field_04 == 2 ) + { + watcher = ( ZakoWork * )ZakoCommand_800DF280.field_0x8C[ i ].watcher; + alert = s11e_zk11ecom_800DA4B8( alert, watcher->alert_level ); + } + else if ( ZakoCommand_800DF280.field_0x8C[ i ].field_04 == 1 ) + { + s11e_zk11ecom_800DA3A0( &ZakoCommand_800DF280.field_0x8C[ i ], i ); + } + } + + s11e_zk11ecom_800DA2BC( &ZakoCommand_800DF280 ); + s11e_zk11ecom_800DA4D0( alert, &ZakoCommand_800DF280 ); + s11e_zk11ecom_800DA534( &ZakoCommand_800DF280 ); + s11e_zk11ecom_800DA16C( &ZakoCommand_800DF280 ); + + ZakoCommand_800DF280.field_0x10C = 0; +} + +void s11e_zk11ecom_800DA784( void ) +{ + int addr; + int addr2; + + addr = GM_PlayerAddress & 0xFF; + addr2 = ( GM_PlayerAddress >> 8 ) & 0xFF; + + if ( addr == addr2 && addr != 0xFF ) + { + ZAKOCOM_PlayerAddress_800DF3B8 = GM_PlayerAddress; + ZAKOCOM_PlayerPosition_800DF278 = GM_PlayerPosition; + ZAKOCOM_PlayerMap_800DF3BC = GM_PlayerMap; + } +} + +int s11e_zk11ecom_800DA7F8( char *arg0 ) +{ + int i; + unsigned char* res; + int proc_id; + + res = arg0; + i = 0; + if ( res ) + { + do + { + proc_id = GCL_StrToInt( res ); + res = GCL_NextStr(); + GCL_ExecProc( proc_id, NULL ); + i++; + } while ( res ); + } + return i; +} + +void s11e_zk11ecom_800DA85C(void) +{ + int i; + int a0 = ZakoCommand_800DF280.field_0x60; + + if ( GM_GameOverTimer || GM_SnakeCurrentHealth <= 0 ) + { + return; + } + + switch( s11e_dword_800DF3B4 ) + { + case 0: + GM_GameStatus |= STATE_PADRELEASE; + + if ( GM_StreamStatus() == 1 ) + { + GM_StreamPlayStart(); + } + + if ( ZakoCommand_800DF280.field_0x10 > 90 ) + { + + s11e_dword_800DF3B4 = 1; + ZakoCommand_800DF280.field_0x10 = 0; + GM_GameStatus &= ~STATE_PADRELEASE; + } + break; + case 1: + if ( ZakoCommand_800DF280.field_0x10 > 60 ) + { + s11e_dword_800DF3B4 = 2; + ZakoCommand_800DF280.field_0x10 = 0; + } + + if ( a0 > 3 ) + { + s11e_dword_800DF3B4 = 15; + } + break; + case 2: + if ( ZakoCommand_800DF280.field_0x10 > 90 ) + { + s11e_dword_800DF3B4 = 3; + ZakoCommand_800DF280.field_0x10 = 0; + } + + if ( a0 > 3 ) + { + s11e_dword_800DF3B4 = 15; + } + break; + case 3: + if ( ZakoCommand_800DF280.field_0x10 > 60 ) + { + s11e_dword_800DF3B4 = 4; + ZakoCommand_800DF280.field_0x10 = 0; + } + + if ( a0 > 3 ) + { + s11e_dword_800DF3B4 = 15; + } + break; + case 4: + if ( ZakoCommand_800DF280.field_0x10 > 90 ) + { + s11e_dword_800DF3B4 = 5; + ZakoCommand_800DF280.field_0x10 = 0; + } + + if ( a0 > 3 ) + { + s11e_dword_800DF3B4 = 15; + } + break; + case 5: + if ( ZakoCommand_800DF280.field_0x10 > 60 ) + { + s11e_dword_800DF3B4 = 6; + ZakoCommand_800DF280.field_0x10 = 0; + } + + if ( a0 > 3 ) + { + s11e_dword_800DF3B4 = 15; + } + break; + case 6: + if ( ZakoCommand_800DF280.field_0x10 > 90 ) + { + s11e_dword_800DF3B4 = 7; + ZakoCommand_800DF280.field_0x10 = 0; + } + + if ( a0 > 3 ) + { + s11e_dword_800DF3B4 = 15; + } + break; + case 7: + if ( ZakoCommand_800DF280.field_0x10 > 60 ) + { + s11e_dword_800DF3B4 = 8; + ZakoCommand_800DF280.field_0x10 = 0; + } + + if ( a0 > 3 ) + { + s11e_dword_800DF3B4 = 15; + } + break; + case 8: + if ( ZakoCommand_800DF280.field_0x10 > 90 ) + { + s11e_dword_800DF3B4 = 9; + ZakoCommand_800DF280.field_0x10 = 0; + } + + if ( a0 > 3 ) + { + s11e_dword_800DF3B4 = 15; + } + break; + case 9: + if ( ZakoCommand_800DF280.field_0x10 > 120 ) + { + s11e_dword_800DF3B4 = 10; + ZakoCommand_800DF280.field_0x10 = 0; + } + + if ( a0 > 3 ) + { + s11e_dword_800DF3B4 = 15; + } + break; + case 10: + if ( ZakoCommand_800DF280.field_0x10 > 90 ) + { + s11e_dword_800DF3B4 = 1; + ZakoCommand_800DF280.field_0x10 = 0; + } + + if ( a0 > 3 ) + { + s11e_dword_800DF3B4 = 15; + } + break; + case 11: + if ( ZakoCommand_800DF280.field_0x10 > 90 ) + { + s11e_dword_800DF3B4 = 12; + ZakoCommand_800DF280.field_0x10 = 0; + } + + if ( a0 > 3 ) + { + s11e_dword_800DF3B4 = 15; + } + break; + case 12: + if ( ZakoCommand_800DF280.field_0x10 > 120 ) + { + s11e_dword_800DF3B4 = 1; + ZakoCommand_800DF280.field_0x10 = 0; + } + + if ( a0 > 3 ) + { + s11e_dword_800DF3B4 = 15; + } + break; + case 13: + if ( ZakoCommand_800DF280.field_0x10 > 120 ) + { + s11e_dword_800DF3B4 = 0; + ZakoCommand_800DF280.field_0x10 = 0; + } + + if ( a0 > 3 ) + { + s11e_dword_800DF3B4 = 15; + } + break; + case 15: + if ( TOPCOMMAND_800DF3A8.mode == 0 ) + { + s11e_dword_800DF3B4 = 16; + for ( i = 0 ; i < 8 ; i++ ) + { + if ( ZakoCommand_800DF280.field_0x8C[i].watcher ) + { + s11e_dword_800DF3B4 = 15; + } + } + } + if ( ZakoCommand_800DF280.field_0x120 >= 0 ) + { + GCL_ExecProc( ZakoCommand_800DF280.field_0x120, 0 ); + ZakoCommand_800DF280.field_0x120 = -1; + } + break; + case 16: + break; + } + + ZakoCommand_800DF280.field_0x10++; +} + +/*---------------------------------------------------------------------------*/ + +static void Act( ZakoCommanderWork* work ) +{ + s11e_zk11ecom_800DA85C(); + s11e_zk11ecom_800DA784(); + s11e_zk11ecom_800DA690( work ); + ZakoCommand_800DF280.field_0x0C++; + + if ( s11e_dword_800DF3B4 == 0x10 ) + { + GV_DestroyActor( &work->actor ); + } +} + +static void Die( ZakoCommanderWork* work ) +{ + if ( ZakoCommand_800DF280.field_0x118 >= 0 ) + { + GCL_ExecProc( ZakoCommand_800DF280.field_0x118, NULL ); + ZakoCommand_800DF280.field_0x118 = -1; + } +} + +static void GetResources( ZakoCommanderWork *work, int name, int where ) +{ + int i; + char *ops; + + s11e_dword_800DF3B4 = 0; + + ZakoCommand_800DF280.field_0x20 = 0; + ZakoCommand_800DF280.alert = 0; + ZakoCommand_800DF280.field_0x10 = 0; + ZakoCommand_800DF280.field_0x60 = 0; + + for ( i = 0 ; i < 8 ; i++ ) + { + ZakoCommand_800DF280.field_0x8C[i].field_00 = 0; + ZakoCommand_800DF280.field_0x8C[i].field_04 = 0; + ZakoCommand_800DF280.field_0x8C[i].field_08 = 0; + ZakoCommand_800DF280.field_0x8C[i].watcher = 0; + } + + for ( i = 0 ; i < 8 ; i++ ) + { + ZakoCommand_800DF280.field_0x40[i] = i; + } + + ZakoCommand_800DF280.field_0x0C = 0; + ZakoCommand_800DF280.mode = 0; + + TOPCOMMAND_800DF3A8.mode = 0; + TOPCOMMAND_800DF3A8.alert = 0; + + ZakoCommand_800DF280.field_0x64 = GM_GetMap( where ); + + ops = GCL_GetOption( 'v' ); + if ( ops ) + { + ZakoCommand_800DF280.field_0x34 = s11e_zk11ecom_800D9C34( ops, ZakoCommand_800DF280.field_0x38 ); + } + else + { + ZakoCommand_800DF280.field_0x34 = 1; + ZakoCommand_800DF280.field_0x38[0] = 0; + } + + for ( i = 0 ; i < ZakoCommand_800DF280.field_0x34 ; i++ ) + { + fprintf( 1, "zone=%d ", ZakoCommand_800DF280.field_0x38[i] ); + } + + ZakoCommand_800DF280.field_0x36 = 0; + s11e_dword_800DF3B0 = 0; + + ops = GCL_GetOption( 'j' ); + if ( ops ) + { + ZakoCommand_800DF280.field_0x68.n_entry = s11e_zk11ecom_800D9BD4( ops, &ZakoCommand_800DF280.field_0x68 ); + } + else + { + ZakoCommand_800DF280.field_0x68.n_entry = 1; + ZakoCommand_800DF280.field_0x68.field_04->field_00 = 0; + ZakoCommand_800DF280.field_0x68.field_04->field_02 = 0; + } + + ZakoCommand_800DF280.field_0x68.map_id = where; + ZakoCommand_800DF280.field_0x114 = 0; + + ops = GCL_GetOption( 'b' ); + if ( ops ) + { + int res = GCL_StrToInt( (unsigned char*)ops ); + ZakoCommand_800DF280.field_0x114 = res; + printf( "ENEMY VOICE [0x%x]\n", res ); + } + + ops = GCL_GetOption( 'e' ); + if ( ops ) + { + ZakoCommand_800DF280.field_0x118 = GCL_StrToInt( GCL_NextStr() ); + } + else + { + ZakoCommand_800DF280.field_0x118 = -1; + } + + ops = GCL_GetOption( 'm' ); + if ( ops ) + { + ZakoCommand_800DF280.field_0x120 = GCL_StrToInt( GCL_NextStr() ); + } + else + { + ZakoCommand_800DF280.field_0x120 = -1; + } + + ops = GCL_GetOption( 'k' ); + if ( ops ) + { + ZakoCommand_800DF280.field_0x11C = GCL_StrToInt( GCL_NextStr() ); + } + else + { + ZakoCommand_800DF280.field_0x11C = -1; + } + + ZakoCommand_800DF280.field_0x08 = 0; + ops = GCL_GetOption( 'n' ); + if ( ops ) + { + ZakoCommand_800DF280.field_0x08 = s11e_zk11ecom_800DA7F8( ops ); + } + + ZakoCommand_800DF280.field_0x00 = 0; + ZakoCommand_800DF280.field_0x04 = 0; + ZakoCommand_800DF280.field_0x110 = 0; + GM_VoxStream( ZakoCommand_800DF280.field_0x114, 0x40000000 ); +} + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_PREV + +void *NewZako11ECommander( int name, int where, int argc, char **argv ) +{ + ZakoCommanderWork *work ; + + work = GV_NewActor( EXEC_LEVEL, sizeof( ZakoCommanderWork ) ) ; + if ( work != NULL ) { + GV_SetNamedActor( &( work->actor ), Act, Die, "zk11ecom.c" ); + GetResources( work, name, where ); + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/animal/zako11e/zk11eenemy.c b/components/mgs/mgs/source/animal/zako11e/zk11eenemy.c new file mode 100644 index 00000000..3bf3d65e --- /dev/null +++ b/components/mgs/mgs/source/animal/zako11e/zk11eenemy.c @@ -0,0 +1,388 @@ +#include "enemy/enemy.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libhzd/libhzd.h" +#include "game/game.h" + +#define SEGMENT_ATR ( HZD_SEG_NO_PLAYER ) + +extern ZAKO_COMMAND ZakoCommand_800DF280; +extern int ZAKO11E_EYE_LENGTH_800C3904; + +extern CONTROL *GM_WhereList[94]; + +// in enemy/asiato.c +extern int AsiatoCheck( HZD_HDL *, SVECTOR * ); +extern int SearchNearAsiato( HZD_HDL *, SVECTOR *, short, short, short ); + +void s11e_zk11ecom_800D80E0( ZakoWork* work ) +{ + VISION *vision = &work->vision; + RADAR_SIGHT_PARAM *r_param = &work->control.radar_param; + + r_param->dir = vision->facedir; + r_param->dis = vision->length; + r_param->range = vision->angle * 2; + r_param->r = 0; +} + +void s11e_zk11ecom_800D810C( ZakoWork *work ) +{ + unsigned short flags; + if ( !( work->field_BA3 & 4 ) ) + { + return; + } + + flags = work->target->damaged; + if ( flags & 6 ) + { + work->field_BA2 |= 4; + return; + } + + flags = work->field_94C.damaged; + if ( flags & 0x80 ) + { + if ( ( GM_PlayerStatus & ( PLAYER_MOVE | PLAYER_CB_BOX ) ) == PLAYER_CB_BOX ) + { + return; + } + work->field_BA2 |= 12; + } +} + +int s11e_zk11ecom_800D8190( HZD_HDL *hzd, SVECTOR *pos, SVECTOR *pos2 ) +{ + int from; + int to; + + from = HZD_GetAddress( hzd, pos, -1 ); + + //TODO: fix + do + { + to = HZD_GetAddress( hzd, pos2, -1 ); + do { + return HZD_ZoneDistance( hzd, from & 0xFF, to & 0xFF ); + } while (0); + } while (0); +} + +void s11e_zk11ecom_800D81F0( ZakoWork* work ) +{ + CONTROL *ctrl; + if ( !( work->field_BA3 & 1 ) ) + { + return; + } + + if ( work->act_status & 0x68 ) + { + return; + } + + ctrl = &work->control; + + + if ( !( ctrl->map->index & GM_PlayerMap ) ) + { + return; + } + + if ( !GM_NoisePower ) + { + return; + } + + switch ( GM_NoisePower ) + { + case 5: + if ( GV_DiffVec3( &GM_NoisePosition, &ctrl->mov ) < 500 ) + { + break; + } + return; + case 200: + if ( GV_DiffVec3( &GM_NoisePosition, &ctrl->mov ) < 8000 ) + { + break; + } + return; + case 255: + break; + case 100: + if ( GV_DiffVec3( &GM_NoisePosition, &ctrl->mov ) < 8000 && s11e_zk11ecom_800D8190( ctrl->map->hzd, &ctrl->mov, &GM_NoisePosition ) < 300 ) + { + work->field_BA2 |= 1; + GM_NoiseLength = 0; + GM_NoisePower = 0; + return; + } + return; + default: + return; + } + work->field_BA2 |= 1; +} + +void s11e_zk11ecom_800D8370( ZakoWork *work ) +{ + + if ( work->field_BA3 & 2 && work->vision.field_B92 == 2 && ( ( GM_PlayerStatus & PLAYER_CB_BOX ) == PLAYER_CB_BOX ) ) + { + if ( ( GV_DiffVec3( &work->field_BA4, &GM_PlayerPosition ) > 50 ) || ( work->field_BAC != GM_WhereList[0]->rot.vy ) ) + { + if ( ZakoCommand_800DF280.mode != TOP_COMM_ALERT ) + { + work->field_BA4 = GM_PlayerPosition; + work->field_BAC = GM_WhereList[0]->rot.vy; + work->field_BA2 |= 0x2; + } + else + { + return; + } + } + else + { + if ( GV_DiffVec3( &work->control.mov, &GM_PlayerPosition ) < 1500 ) + { + work->field_BA2 |= 0x40; + } + } + work->vision.field_B92 = 0; + work->field_BA2 |= 0x80; + } +} + +void s11e_zk11ecom_800D8498( ZakoWork* work ) +{ + HZD_HDL *hzd; + CONTROL *ctrl; + VISION *vision; + + if ( !( work->field_BA3 & 16 ) ) + { + return; + } + + ctrl = &work->control; + if ( work->think2 == 5 ) + { + return; + } + + hzd = work->control.map->hzd; + vision = &work->vision; + if ( !( AsiatoCheck( hzd, &ctrl->mov ) ) ) + { + return; + } + + if ( SearchNearAsiato( hzd, &ctrl->mov, vision->facedir, vision->angle, vision->length ) < 0 ) + { + return; + } + + work->field_BA2 |= 16; +} + +void s11e_zk11ecom_800D8544( ZakoWork* work ) +{ + work->field_BA2 = 0; + s11e_zk11ecom_800D81F0( work ); + + if ( ZakoCommand_800DF280.mode != TOP_COMM_ALERT ) + { + s11e_zk11ecom_800D810C( work ); + s11e_zk11ecom_800D8370( work ); + s11e_zk11ecom_800D8498( work ); + } +} + +void s11e_zk11ecom_800D8598( ZakoWork* work ) +{ + int val; + val = work->vision.field_B92; + switch ( val ) + { + case 0: + if ( work->field_B94 != 2 || !( GM_PlayerStatus & PLAYER_INTRUDE ) || work->vision.length == 0 ) + { + work->field_B94 = 0; + work->alert_level -= 4; + } + break; + case 1: + work->field_B94 = val; + work->alert_level -= 1; + break; + case 2: + work->field_B94 = val; + work->alert_level += 1; + break; + } + + if ( work->alert_level < 0 ) + { + work->alert_level = 0; + } + else if ( work->alert_level > 255 ) + { + work->alert_level = 255; + } +} + +void s11e_zk11ecom_800D8668( ZakoWork* work ) +{ + int dir; + int dis; + int diff; + SVECTOR *pos; + SVECTOR svec; + CONTROL *ctrl; + MAP *map; + + ctrl = &work->control; + pos = &GM_PlayerPosition; + GV_SubVec3( pos, &ctrl->mov, &svec ); + + dir = GV_VecDir2( &svec ); + work->sn_dir = dir; + dis = GV_VecLen3( &svec ); + + work->sn_dis = dis; + + diff = pos->vy - work->control.mov.vy; + if ( diff < 0 ) + { + diff = work->control.mov.vy - pos->vy; + } + + if ( !( work->control.map->index & GM_PlayerMap ) || GM_PlayerStatus & PLAYER_INTRUDE ) + { + work->vision.field_B92 = 0; + return; + } + + if ( ZakoCommand_800DF280.field_0x0C % ZakoCommand_800DF280.field_0x08 == work->field_B74 ) + { + //ridiculous, but its the only way it matches + if ( ZAKO11E_EYE_LENGTH_800C3904 + 2000 >= dis ) + { + if ( 2000 >= diff ) + { + if ( dis < 500 || GV_DiffDirAbs( work->vision.facedir, dir ) < work->vision.angle ) + { + map = work->control.map; + if ( !( HZD_OnlineHazardCheck( map->hzd, pos, &ctrl->mov, HZD_CHK_ALL, SEGMENT_ATR ) ) ) + { + if ( !( GM_OnlineTargetCheckAny( &ctrl->mov, pos, map->index, &svec ) ) ) + { + if ( work->vision.length < dis ) + { + work->vision.field_B92 = 1; + } + else + { + work->vision.field_B92 = 2; + } + } + else + { + work->vision.field_B92 = 0; + } + } + else + { + work->vision.field_B92 = 0; + } + } + else + { + work->vision.field_B92 = 0; + } + } + else + { + work->vision.field_B92 = 0; + } + } + else + { + work->vision.field_B92 = 0; + } + } +} + +extern void Zako11EThink_800D99B8( ZakoWork * ); +extern void s11e_zk11ecom_800D6DDC( ZakoWork * ); +extern void ZAKO11E_ExecPutChar_800D8080( ZakoWork* work ); + +void Zako11EActionMain_800D8830( ZakoWork *work ) +{ + if ( work->unknown.last_set < 0x31 ) + { + s11e_zk11ecom_800D80E0( work ); + s11e_zk11ecom_800D8668( work ); + s11e_zk11ecom_800D8544( work ); + s11e_zk11ecom_800D8598( work ); + Zako11EThink_800D99B8( work ); + ZAKO11E_ExecPutChar_800D8080( work ); + } + + s11e_zk11ecom_800D6DDC( work ); +} + +void Zako11EPushMove_800D889C( ZakoWork *work ) +{ + int s1; + CONTROL *ctrl; + TARGET *target; + + target = work->target; + s1 = 0; + if ( !( target->damaged & 0x8 ) ) + { + return; + } + + GV_AddVec3( &target->offset, &work->control.step, &work->control.step ); + target->damaged &= ~( 0x8 ); + + if ( work->unknown.last_set - 1 >= 2u ) + { + return; + } + + if ( target->offset.pad ) + { + if ( GV_Time & 256 ) + { + s1 = target->offset.pad * 1024; + if ( !( work->field_B74 & 1 ) ) + { + s1 = ( target->offset.pad + 2 ) * 1024; + } + } + else + { + if ( work->field_B74 & 1 ) + { + s1 = ( target->offset.pad + 2 ) * 1024; + } + else + { + s1 = target->offset.pad * 1024; + } + } + s1 &= 0xFFF; + } + + ctrl = &work->control; + ctrl->turn.vy = s1; + ctrl->r_sphere = GV_NearExp2( ctrl->r_sphere, work->unknown.field_1C ); +} diff --git a/components/mgs/mgs/source/animal/zako11e/zk11ethink.c b/components/mgs/mgs/source/animal/zako11e/zk11ethink.c new file mode 100644 index 00000000..f6a55726 --- /dev/null +++ b/components/mgs/mgs/source/animal/zako11e/zk11ethink.c @@ -0,0 +1,736 @@ +#include "enemy/enemy.h" +#include "enemy/eyeflash.h" // for NewEyeflash + +#include +#include +#include + +#include "libgv/libgv.h" +#include "libhzd/libhzd.h" +#include "linkvar.h" +#include "sound/g_sound.h" + +extern int ZAKOCOM_PlayerAddress_800DF3B8; +extern SVECTOR ZAKOCOM_PlayerPosition_800DF278; +extern int ZAKOCOM_PlayerMap_800DF3BC; +extern ZAKO_COMMAND ZakoCommand_800DF280; + +extern CONTROL *GM_WhereList[94]; + + +void s11e_zk11ecom_800D89A0( ZakoWork* work ) +{ + work->target_addr = ZAKOCOM_PlayerAddress_800DF3B8; + work->target_pos = ZAKOCOM_PlayerPosition_800DF278; + work->target_map = ZAKOCOM_PlayerMap_800DF3BC; +} + +void s11e_zk11ecom_800D89E8( ZakoWork* work ) +{ + int v0, v1, a2; + HZD_HDL *hzd; + void *a1; + MAP *map; + HZD_DEF *hdr; + + v0 = work->field_B78; + do {} while (0); + map = work->control.map; + v1 = v0 << 1; + v1 = v1 + v0; + hzd = map->hzd; + a2 = v0 << 8; + hdr = hzd->def; + v0 = v0 | a2; + a1 = hdr->zones; + v1 = v1 << 3; + work->target_addr = v0; + a1 = a1 + v1; + + work->target_pos.vx = ((HZD_ZON*)a1)->x; + work->target_pos.vy = ((HZD_ZON*)a1)->y; + work->target_pos.vz = ((HZD_ZON*)a1)->z; + work->target_map = work->start_map; +} + +void s11e_zk11ecom_800D8A44( ZakoWork* work ) +{ + if ( work->field_B78 == 0xFF ) + { + work->think2 = 1; + work->think1 = 0; + work->think3 = 4; + } + else + { + work->think2 = 3; + work->think1 = 0; + work->think3 = 0x10; + s11e_zk11ecom_800D89E8( work ); + } + work->count3 = 0; + work->pad.mode = TOP_COMM_ALERT; +} + + +void s11e_zk11ecom_800D8AB0( ZakoWork* work ) +{ + + work->think1 = 0; + work->think2 = 0; + work->think3 = 0; + work->count3 = 0; + work->pad.mode = TOP_COMM_ALERT; +} + +int s11e_zk11ecom_800D8ACC( SVECTOR* arg0, SVECTOR* arg1 ) +{ + SVECTOR svec; + + GV_SubVec3(arg1, arg0, &svec); + svec.vy = 0; + return GV_VecDir2(&svec); +} + +extern int s11e_zk11ecom_800D9B60( int map_id, int val ); + +int s11e_zk11ecom_800D8B04( ZakoWork *work ) +{ + int temp; + int temp_addr; + SVECTOR svec; + CONTROL *ctrl; + HZD_HDL *hzd; + HZD_ZON *zone; + int addr, addr2, addr3; + int reach; + SVECTOR *target_pos; + + hzd = work->control.map->hzd; + + if ( work->target_map == work->control.map->index ) + { + target_pos = &work->target_pos; + addr = work->target_addr; + } + else + { + target_pos = &work->target_pos; + temp_addr = s11e_zk11ecom_800D9B60( work->control.map->index , work->target_map ); + zone = &hzd->def->zones[ temp_addr ]; + target_pos->vx = zone->x; + target_pos->vy = zone->y; + target_pos->vz = zone->z; + addr = (temp_addr << 8) | temp_addr; + } + + ctrl = &work->control; + work->field_C0C = HZD_GetAddress( hzd, &ctrl->mov, work->field_C0C ); + addr2 = work->field_C0C; + reach = HZD_ReachTo( hzd, addr2, work->field_C10 ); + + if ( addr != work->field_BF0 || reach <= 0 ) + { + work->field_BF0 = addr; + + if ( HZD_ReachTo( hzd, addr2, addr ) < 2 ) + { + if ( work->target_map == work->control.map->index ) + { + work->field_C1C = *target_pos; + work->field_C10 = addr; + + GV_SubVec3( &work->field_C1C, &ctrl->mov, &svec ); + + work->pad.dir = GV_VecDir2( &svec ); + return -1; + } + + addr2 = s11e_zk11ecom_800D9B60( work->target_map, ctrl->map->index ); + ctrl->map = GM_GetMap( work->target_map ); + zone = &ctrl->map->hzd->def->zones[ addr2 ]; + work->control.mov.vx = zone->x; + ctrl->mov.vy = zone->y; + ctrl->mov.vz = zone->z; + work->field_C0C = -1; + work->field_BF0 = -1; + return ctrl->rot.vy; + } + + addr3 = HZD_Navigate( hzd, addr2, addr, &ctrl->mov ); + zone = &hzd->def->zones[ addr3 ]; + if ( work->field_BFC == 0xFA0 ) + { + work->field_C1C.vx = zone->x + 0xFA; + work->field_C1C.vy = zone->y; + work->field_C1C.vz = zone->z + 0xFA; + } else + { + work->field_C1C.vx = zone->x; + work->field_C1C.vy = zone->y; + work->field_C1C.vz = zone->z; + } + + work->field_C1C.vx = zone->x; + work->field_C1C.vy = zone->y; + work->field_C1C.vz = zone->z; + + temp = addr3 & 0xFF; + work->field_C10 = temp | temp << 8; + } + + GV_SubVec3( &work->field_C1C, &work->control.mov, &svec ); + target_pos = &svec; + return GV_VecDir2( target_pos ); +} + +extern void ZAKO11E_PutMark_800D7C10( ZakoWork *work, int mark ); + +int s11e_zk11ecom_800D8DC4( ZakoWork* work ) +{ + if ( work->count3 == 0 ) + { + ZAKO11E_PutMark_800D7C10( work, 0 ); + GM_SeSet( &work->control.mov, SE_EXCLAMATION ); + NewEyeflash( &work->body.objs->objs[6].world, &work->control.mov, "kirari01", 0 ); + } + + if ( work->count3 < 20 ) + { + work->pad.press |= 0x20; + work->count3++; + return 0; + } + + return 1; +} + +int s11e_zk11ecom_800D8E64( ZakoWork* work ) +{ + if ( work->count3 == 0 ) + { + GM_SeSet( &work->control.mov, SE_EXCLAMATION ); + ZAKO11E_PutMark_800D7C10( work, 0 ); + } + + if ( work->count3 > 20 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s11e_zk11ecom_800D8ECC( ZakoWork *work) +{ + + short temp_v0; + + if (!(work->count3 & 0x1F)) { + + work->field_C0C = -1; + work->field_BF0 = -1; + s11e_zk11ecom_800D8B04(work); + } + + temp_v0 = s11e_zk11ecom_800D8B04(work); + work->pad.dir = temp_v0; + + if (work->pad.dir < 0) { + + return 1; + } + + work->count3 += 1; + return 0; +} + +int DirectTrace_800D8F3C( ZakoWork* work, int a1 ) +{ + int x, z; + SVECTOR svec; + + + svec.vx = x = work->target_pos.vx - work->control.mov.vx; + svec.vz = z = work->target_pos.vz - work->control.mov.vz; + + if ( -a1 >= x || x >= a1 || -a1 >= z || z >= a1 ) + { + work->pad.dir = GV_VecDir2( &svec ); + work->count3++; + return 0; + } + + return 1; +} + +int s11e_zk11ecom_800D8FC4( ZakoWork *work ) +{ + MAP *map; + s11e_zk11ecom_800D89A0( work ); + + map = work->control.map; + + if ( !( map->index == work->target_map ) ) + { + return -1; + } + + if ( work->count3 & 16 ) + { + work->field_C0C = HZD_GetAddress( map->hzd, &work->control.mov, -1 ); + if ( HZD_ReachTo( work->control.map->hzd, work->field_C0C, work->target_addr ) > 1 ) + { + return -1; + } + } + + return DirectTrace_800D8F3C( work, 1500 ); +} + +int s11e_zk11ecom_800D9058( ZakoWork *work ) +{ + int count; + work->pad.press |= 0x10000; + work->pad.dir = work->sn_dir; + count = work->count3; + + if ( count == 0 ) + { + work->count3 = GV_RandU( 8 ); + } + + if ( count < 9 ) + { + if ( !( count & 1 ) ) + { + work->pad.press |= 0x40000; + } + } + + if ( count > 11 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s11e_zk11ecom_800D90F4( ZakoWork *work ) +{ + int count; + work->pad.press |= 0x20000; + work->pad.dir = 0x800; + count = work->count3; + + if ( count >= 80 ) + { + return 1; + } + + if ( !( count & 2 ) ) + { + work->pad.press |= 0x80000; + } + + work->count3++; + return 0; +} + +extern SVECTOR s11e_dword_800C36B4[]; + +int s11e_zk11ecom_800D9150( ZakoWork *work ) +{ + work->pad.press |= 0x10000; + + if ( work->count3 == 30 ) + { + work->pad.press |= 0x100000; + work->field_C04 = s11e_dword_800C36B4[ work->field_B74 ]; + } + else if ( work->body.is_end ) + { + return 1; + } + + work->pad.dir = s11e_zk11ecom_800D8ACC( &work->control.mov, &s11e_dword_800C36B4[ work->field_B74 ] ); + work->count3++; + return 0; +} + +int s11e_zk11ecom_800D9214( ZakoWork *work ) +{ + work->pad.press |= 0x10000; + work->pad.dir = work->sn_dir; + + if ( work->count3 == 0 ) + { + work->pad.press |= 0x200000; + } + else + { + if ( work->actend || work->count3 > 30 ) + { + return 1; + } + } + + work->count3++; + return 0; +} + +int s11e_zk11ecom_800D9280( ZakoWork *work ) +{ + int count = work->count3; + work->pad.press |= 0x10000; + work->pad.dir = work->sn_dir; + + if ( count < 10 ) + { + if ( !( count & 1 ) ) + { + work->pad.press |= 0x40000; + } + } + else if ( count >= 20 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s11e_zk11ecom_800D92EC( ZakoWork* work ) +{ + int count = work->count3; + if ( count > 200 ) + { + return 1; + } + + work->pad.press |= 0x10000; + work->pad.dir = work->sn_dir; + work->count3++; + return 0; +} + +int s11e_zk11ecom_800D9334( ZakoWork *work ) +{ + if ( work->sn_dis < 800 ) + { + work->pad.press |= 0x10000; + return 1; + } + + work->pad.dir = work->sn_dir; + work->count3++; + return 0; +} + +typedef struct ACTIONPATTERN +{ + int set; +} ACTIONPATTERN; + +//extern int s11e_dword_800C36CC[ 2 ][ 16 ]; +extern ACTIONPATTERN s11e_dword_800C36CC[ 16 ][ 16 ]; +extern int s11e_dword_800DF3B4; + + +#define ATTACKNEAR_DIS 800 + +int s11e_zk11ecom_800D937C( ZakoWork *work ) +{ + work->pad.press |= 0x10000; //SP_WEAPON? + work->pad.dir = work->sn_dir; + + + if ( work->count3 == 16 ) + { + work->count3 += GV_RandU( 14 ); + } + + if ( work->count3 > 24 ) + { + //int nextset = ActionPattern[ ZAKO11A_GameFlag ][ ID ].set ; + int nextset = s11e_dword_800C36CC[ work->field_B74 ][ s11e_dword_800DF3B4 ].set; + switch ( nextset ) + { + case 0: + if ( work->sn_dis < ATTACKNEAR_DIS && !( GM_PlayerStatus & ( PLAYER_SQUAT | PLAYER_GROUND | PLAYER_CB_BOX ) ) ) + { + return 10; + } + return 6; + case 2: + return 7; + case 3: + return 12; + case 4: + return 13; + case 1: + if ( work->sn_dis < ATTACKNEAR_DIS && GM_PlayerStance == 0 ) + { + return 10; + } + return 14; + } + } + + work->count3++; + return 0; +} + + +extern ACTIONPATTERN s11e_dword_800C37CC[ 16 ][ 16 ]; + +int s11e_zk11ecom_800D94C0( ZakoWork *work ) +{ + work->pad.press |= 0x10000; + work->pad.dir = work->sn_dir; + work->field_B78 = s11e_dword_800C37CC[ work->field_B74 ][ s11e_dword_800DF3B4 ].set; + return 15; +} + +void s11e_zk11ecom_800D9510( ZakoWork* work ) +{ + s11e_zk11ecom_800D8A44( work ); +} + +void s11e_zk11ecom_800D9530( ZakoWork* work ) +{ + if ( !work->think3 ) + { + s11e_zk11ecom_800D9510( work ); + } +} + +void s11e_zk11ecom_800D9560( ZakoWork *work ) +{ + switch( work->think3 ) + { + case 1: + if ( s11e_zk11ecom_800D8DC4( work ) ) + { + s11e_zk11ecom_800D8A44( work ); + if ( ZakoCommand_800DF280.mode == TOP_COMM_TRAVEL ) + { + work->pad.sound = 240; + } + else + { + work->pad.sound = 246; + } + work->alert_level = 255; + } + break; + case 2: + if ( s11e_zk11ecom_800D8E64( work ) ) + { + work->think2 = 3; + work->think3 = 0x10; + work->pad.mode = TOP_COMM_ALERT; + work->pad.sound = 0xF0; + work->think1 = 0; + work->count3 = 0; + work->alert_level = 255; + } + break; + case 3: + if ( s11e_zk11ecom_800D8E64( work ) ) + { + s11e_zk11ecom_800D8A44( work ); + work->pad.sound = 240; + work->alert_level = 255; + } + } +} + +extern int ZAKO11E_SetGoPointLast_800D9A9C(); + +void s11e_zk11ecom_800D9654( ZakoWork *work ) { + int res; + short x; + x = work->think3 - 6; + switch ( x ) + { + case 0: + if ( s11e_zk11ecom_800D9058( work ) ) + { + work->think3 = 14; + work->count3 = 0; + } + break; + case 1: + if ( s11e_zk11ecom_800D90F4( work ) ) + { + work->think3 = 14; + work->count3 = 0; + } + break; + case 3: + if ( s11e_zk11ecom_800D9150( work ) ) + { + s11e_zk11ecom_800D8AB0( work ); + work->count3 = 0; + } + break; + case 4: + if ( s11e_zk11ecom_800D9214( work ) ) + { + work->think3 = 14; + work->count3 = 0; + } + break; + case 5: + if ( s11e_zk11ecom_800D9280( work ) ) + { + work->think3 = 14; + work->count3 = 0; + } + break; + case 6: + if ( s11e_zk11ecom_800D92EC( work ) ) + { + work->think3 = 14; + work->count3 = 0; + } + break; + case 7: + if ( s11e_zk11ecom_800D9334( work ) ) + { + work->think3 = 10; + work->count3 = 0; + } + break; + case 9: + res = s11e_zk11ecom_800D937C( work ); + if ( res ) + { + work->think3 = res; + work->count3 = 0; + } + break; + case 8: + res = s11e_zk11ecom_800D94C0( work ); + if ( res ) + { + work->think3 = res; + work->count3 = 0; + } + + if (!(HZD_InsideZone( work->control.map->hzd, &work->control.mov, work->field_B78 ))) + { + s11e_zk11ecom_800D8A44( work ); + } + break; + } + + if ( work->vision.field_B92 == 2 ) + { + ZAKO11E_SetGoPointLast_800D9A9C(); + work->alert_level = 255; + } +} + +void s11e_zk11ecom_800D97D8( ZakoWork *work ) +{ + int res; + switch ( work->think3 ) + { + case 4: + s11e_zk11ecom_800D89A0( work ); + if ( s11e_zk11ecom_800D8ECC(work) ) + { + work->think3 = 5; + work->count3 = 0; + } + break; + case 5: + res = s11e_zk11ecom_800D8FC4( work ); //ZoneTrace + if ( res < 0 ) + { + work->think3 = 4; + work->count3 = 0; + } + else if ( res > 0 ) + { + work->think2 = 2; //TH2_ATTACK + work->think3 = 14; //TH3_ATTACK_SETUP + work->count3 = 0; + } + break; + } + + if ( work->sn_dis < ATTACKNEAR_DIS && GM_PlayerStance == 0 ) + { + work->pad.press |= 0x10000 ; //SP_WEAPON + work->think2 = 2; //TH2_ATTACK + work->think3 = 10 ; //TH3_ATTACK_NEAR + work->count3 = 0 ; + work->pad.dir = work->sn_dir; + } + + if ( work->vision.field_B92 == 2 ) + { + work->alert_level = 255; + } + + ZAKO11E_SetGoPointLast_800D9A9C(); +} + +void s11e_zk11ecom_800D98D8( ZakoWork* work ) +{ + if ( (work->think3 == 16 ) && s11e_zk11ecom_800D8ECC( work ) ) + { + work->think2 = 2; + work->think3 = 14; + work->count3 = 0; + } +} + +void s11e_zk11ecom_800D9928( ZakoWork *work ) +{ + switch ( work->think2 ) + { + case 0: + s11e_zk11ecom_800D9530( work ); + break; + case 1: + s11e_zk11ecom_800D97D8( work ); + break; + case 2: + s11e_zk11ecom_800D9654( work ); + break; + case 3: + s11e_zk11ecom_800D98D8( work ); + break; + case 4: + s11e_zk11ecom_800D9560( work ); + break; + } +} + +void Zako11EThink_800D99B8( ZakoWork* work ) +{ + work->pad.dir = -1; + work->pad.press = 0; + work->control.radar_atr = RADAR_VISIBLE | RADAR_SIGHT; + + if ( work->think1 == 0) + { + work->unknown.field_1E = 1; + s11e_zk11ecom_800D9928( work ); + } + + if ( s11e_dword_800DF3B4 > 0 ) + { + work->alert_level = 0xFF; + return; + } + + work->alert_level = 0; +} diff --git a/components/mgs/mgs/source/animal/zako11f/action.c b/components/mgs/mgs/source/animal/zako11f/action.c new file mode 100644 index 00000000..e6bee215 --- /dev/null +++ b/components/mgs/mgs/source/animal/zako11f/action.c @@ -0,0 +1,2002 @@ +#include "enemy/enemy.h" +#include "linkvar.h" +#include "game/vibrate.h" + +short ActTable_800C3330[54] = +{ + 0x13, 0x14, 0x0A, 0x16, 0x09, 0x22, 0x23, 0x24, 0x2D, + 0x0D, 0x0C, 0x1A, 0x0F, 0x0E, 0x10, 0x1B, 0x08, 0x2E, + 0x32, 0x15, 0x1E, 0x1F, 0x20, 0x21, 0x25, 0x34, 0x35, + 0x19, 0x05, 0x02, 0x2A, 0x04, 0x27, 0x28, 0x06, 0x07, + 0x26, 0x03, 0x29, 0x11, 0x12, 0x2B, 0x17, 0x18, 0x1C, + 0x1D, 0x00, 0x01, 0x33, 0x11, 0x12, 0x2B, 0x16, 0x00 +}; + +// vibration data +char s11i_800C339C[] = {1, 4, 0, 0}; +char s11i_800C33A0[] = {255, 4, 0, 0}; + +SVECTOR s11i_800C33A4 = {-150, 0, 300, 0}; +SVECTOR s11i_800C33AC = {0, -700, 900, 0}; +SVECTOR s11i_800C33B4 = {0, 0, 100, 0}; +SVECTOR s11i_800C33BC = {-1024, 0, 0, 0}; +SVECTOR s11i_800C33C4 = {0, 0, 100, 0}; +SVECTOR s11i_800C33CC = {-1024, 0, 0, 0}; + + +const char *s11i_dword_800C33D4[3] = { + (const char *)0x800D5260, + (const char *)0x800D524C, + (const char *)0x800D5238 +}; + +void ZAKO11F_PutSound_800CD2C0( Zako11FWork *work ); +void ZAKO11F_PutBreath_800CD358( Zako11FWork *work ); +void ZAKO11F_PutLSight_800CD4A0( Zako11FWork *work ); +void ZAKO11F_PutBulletEx_800CD4DC( Zako11FWork *work ); +void ZAKO11F_PutBulletEx2_800CD618( Zako11FWork *work ); + +void *PutFuncList_800C33E0[5] = { + ZAKO11F_PutSound_800CD2C0, + ZAKO11F_PutBreath_800CD358, + ZAKO11F_PutLSight_800CD4A0, + ZAKO11F_PutBulletEx_800CD4DC, + ZAKO11F_PutBulletEx2_800CD618 +}; + +SVECTOR s11i_800C33F4 = {5, -500, 80, 0}; +SVECTOR s11i_800C33FC = {5, -500, 80, 0}; + +extern SVECTOR ZAKO11F_TARGET_SIZE_800C365C; +extern SVECTOR ZAKO11F_TARGET_FORCE_800C3664; +extern SVECTOR ZAKO11F_ATTACK_SIZE_800C366C; +extern SVECTOR ZAKO11F_ATTACK_FORCE_800C3674; +extern SVECTOR ZAKO11F_TOUCH_SIZE_800C367C; +extern SVECTOR ZAKO11F_TOUCH_FORCE_800C3684; +extern SVECTOR ZAKO11F_NO_POINT_800C368C; + +extern int ZAKO11F_EYE_LENGTH_800C3694; + +extern ZAKO11F_COMMAND Zako11FCommand_800D5AF8; + +void AN_Unknown_800C3B7C( MATRIX *matrix ); +void AN_Sleep( SVECTOR *pos ); + +// action +void s11i_asiato_800CA25C( Zako11FWork *work, int time ); +void s11i_asiato_800CA4F4( Zako11FWork *work, int time ); +void s11i_asiato_800CA584( Zako11FWork *work, int time ); +void ActBoxKick_800CA66C( Zako11FWork *work, int time ); +void ActReadyGun_800CA674( Zako11FWork *work, int time ); +void s11i_asiato_800CA84C( Zako11FWork *work, int time ); +void s11i_asiato_800CA904( Zako11FWork *work, int time ); +void ActGrenade_800CAA54( Zako11FWork *work, int time ); +void s11i_asiato_800CABAC( Zako11FWork *work, int time ); +void s11i_asiato_800CAC84( Zako11FWork *work, int time ); +void s11i_asiato_800CADDC( Zako11FWork *work, int time ); +void s11i_asiato_800CB140( Zako11FWork *work, int time ); +void s11i_asiato_800CB4B4( Zako11FWork *work, int time ); +void s11i_asiato_800CB528( Zako11FWork *work, int time ); +void s11i_asiato_800CB678( Zako11FWork *work, int time ); +void s11i_asiato_800CB7F0( Zako11FWork *work, int time ); +void s11i_asiato_800CB8B8( Zako11FWork *work, int time ); +void s11i_asiato_800CB9BC( Zako11FWork *work, int time ); +void s11i_asiato_800CBACC( Zako11FWork *work, int time ); +void s11i_asiato_800CC038( Zako11FWork *work, int time ); +void s11i_asiato_800CC218( Zako11FWork *work, int time ); +void s11i_asiato_800CC314( Zako11FWork *work, int time ); +void s11i_asiato_800CC528( Zako11FWork *work, int time ); +void s11i_asiato_800CC5F4( Zako11FWork *work, int time ); +void ActOverScoutD_800CC6C0( Zako11FWork *work, int time ); +void s11i_asiato_800CC758( Zako11FWork *work, int time ); +void s11i_asiato_800CC7F0( Zako11FWork *work, int time ); +void s11i_asiato_800CC8A4( Zako11FWork *work, int time ); +void s11i_asiato_800CC96C( Zako11FWork *work, int time ); +void s11i_asiato_800CCA10( Zako11FWork *work, int time ); +void s11i_asiato_800CCAD8( Zako11FWork *work, int time ); +void s11i_asiato_800CCCF0( Zako11FWork *work, int time ); +void s11i_asiato_800CCE38( Zako11FWork *work, int time ); + +void ZAKO11F_PutBlood_800CCFD4( Zako11FWork *work, int unit, int count ); +void ZAKO11F_PutFog_800CD044( Zako11FWork *work ); +void ZAKO11F_PutItem_800CD0A8( Zako11FWork *work ); +void ZAKO11F_SetPutChar_800CD700( Zako11FWork *work, int); + +// command +void s11i_zk11fcom_800D0DB8( void ); +void s11i_zk11fcom_800D0DD8( void ); + +/***Inlines***********************************************************************************************/ +static inline void SetZako11FMode( Zako11FWork *work, ZAKO11FACTION action ) // rename +{ + work->action = action; + work->time = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; + GM_ConfigMotionAdjust( &( work->body ), NULL ); +} + +static inline void SetZako11FMode2( Zako11FWork *work, ZAKO11FACTION action ) // rename +{ + if ( !work->action2 ) + { + work->action2 = action; + work->time2 = 0; + } + + work->control.turn.vz = 0; + work->control.turn.vx = 0; + GM_ConfigMotionAdjust( &( work->body ), 0 ); +} + +static inline void SetZako11FModeFields( Zako11FWork *work, ZAKO11FACTION action ) // rename +{ + work->action = action; + work->time = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; +} + +static inline void UnsetMode( Zako11FWork *work ) +{ + extern short ActTable_800C3330[]; + work->unknown.last_unset = 0; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3330[STANDSTILL], 0, ACTINTERP, 0 ); + + work->action2 = 0; + work->time2 = 0; + work->unknown.last_unset = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; +} + +static inline void SetAction( Zako11FWork *work, int n_action, int interp ) +{ + extern short ActTable_800C3330[]; + work->unknown.last_set = n_action ; + GM_ConfigObjectAction( &( work->body ), ActTable_800C3330[n_action], 0, interp ); +} + +static inline void UnsetAction( Zako11FWork *work, int n_action ) +{ + extern short ActTable_800C3330[]; + + work->unknown.last_unset = n_action; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3330[n_action], 0, ACTINTERP, 0x3FE ); +} + +static inline void UnsetActionManual( Zako11FWork *work, int n_action, int a4 ) +{ + extern short ActTable_800C3330[]; + + work->unknown.last_unset = n_action; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3330[n_action], 0, ACTINTERP, a4 ); +} +/**********************************************************************************************************/ + +int s11i_asiato_800C9840(Zako11FWork *work) +{ + TARGET *target; + + target = work->target; + if (target->damaged & TARGET_CAPTURE) + { + if (target->a_mode == 36) + { + SetZako11FMode(work, s11i_asiato_800CADDC); + } + else + { + SetZako11FMode(work, s11i_asiato_800CB140); + } + + target->damaged = 0; + return 1; + } + + return 0; +} + +int s11i_asiato_800C98CC(Zako11FWork *work) +{ + if (work->target->damaged & TARGET_DOWN) + { + return 1; + } + + return 0; +} + +int s11i_asiato_800C98E8(SVECTOR *vec) +{ + return ABS(vec->vx) + ABS(vec->vy) + ABS(vec->vz); +} + +int s11i_asiato_800C9924( Zako11FWork *work ) +{ + short a_mode; + TARGET *target; + + target = work->target; + if ( !( target->damaged & TARGET_POWER ) ) return 0; + + a_mode = target->a_mode - 1; + switch ( a_mode ) + { + case 2: + target->force = DG_ZeroVector; + if ( target->faint <= 0 ) + { + work->unknown.field_14 = 1; + SetZako11FMode( work, s11i_asiato_800CBACC ) ; + } + else + { + SetZako11FMode( work, s11i_asiato_800CB9BC ) ; + } + break; + case 3: + work->unknown.field_14 = 0; + SetZako11FMode( work, s11i_asiato_800CBACC ) ; + break; + case 0: + target->force = DG_ZeroVector; + if ( target->vital <= 0 ) + { + work->unknown.field_14 = 1; + SetZako11FMode( work, s11i_asiato_800CBACC ) ; + } + else + { + SetZako11FMode( work, s11i_asiato_800CB9BC ) ; + } + break; + case 1: + if ( s11i_asiato_800C98E8( &target->force ) < 100 ) + { + work->unknown.field_14 = 3; + } + else + { + work->unknown.field_14 = 2; + } + SetZako11FMode( work, s11i_asiato_800CBACC ) ; + break; + case 4: + work->unknown.field_14 = 3; + SetZako11FMode( work, s11i_asiato_800CBACC ) ; + break; + case 7: + SetZako11FMode( work, s11i_asiato_800CB4B4 ) ; + break; + case 8: + SetZako11FMode( work, s11i_asiato_800CB528 ) ; + break; + } + + target->damage = 0; + target->damaged = 0; + return 1; +} + +int s11i_asiato_800C9B14( Zako11FWork *work ) +{ + if ( GM_GameStatus & STATE_STUN ) + { + SetZako11FMode( work, s11i_asiato_800CB528 ); + return 1; + } + + return 0; +} + +int CheckDamage_800C9B6C( Zako11FWork *work ) +{ + if ( s11i_asiato_800C9840( work ) | s11i_asiato_800C9924( work ) | s11i_asiato_800C9B14( work ) ) + { + work->target->class = TARGET_AVAIL; + s11i_zk11fcom_800D0DD8(); + return 1; + } + + return 0; +} + +int s11i_asiato_800C9BE0( int from, int to ) +{ + int diff; + + if ( to < 0 || from < 0 ) return 0 ; + + diff = GV_DiffDirS( from, to ) ; + + if ( diff < -896 || diff > 896 ) return 0 ; + if ( diff >= 0 ) return 1 ; + + return 2 ; +} + +void ReviseReadyGun_800C9C34( Zako11FWork *work ) +{ + int trans; + int near; + + trans = ( ( GM_PlayerBody->objs->objs[6].world.t[1] - work->body.objs->objs[6].world.t[1] ) ); + trans = ratan2( work->sn_dis, trans ) & 0xFFF; + + near = GV_NearExp8( work->adjust[2].vx, trans - 1024 ); + work->adjust[2].vx = near; + work->adjust[6].vx = near; + + if ( near < 0 ) + { + work->adjust[7].vx = near * 3 ; + } + else + { + work->adjust[7].vx = ( near * 3 ) / 2 ; + } +} + +void ReviseReadyGun2_800C9CBC( Zako11FWork *work ) +{ + int trans; + int near; + + trans = ( ( GM_PlayerBody->objs->objs[6].world.t[1] - work->body.objs->objs[6].world.t[1] ) ); + trans = ratan2( work->sn_dis, trans ) & 0xFFF; + + near = trans - 1024; + work->adjust[2].vx = near; + work->adjust[6].vx = near; + + if ( near < 0 ) + { + work->adjust[7].vx = near * 3 ; + } + else + { + work->adjust[7].vx = ( near * 3 ) / 2 ; + } +} + +int AttackForce_800C9D38( Zako11FWork *work ) +{ + TARGET *target; + SVECTOR svec; + SVECTOR rp_shift = {-200, 1000, 600}; /* ?�³ã????³ã?? */ + SVECTOR force = {5, 0, 100}; + SVECTOR size = {400, 1000, 400}; + + target = &work->punch; + GM_SetTarget( target, TARGET_POWER, ENEMY_SIDE, &size ); + DG_SetPos2( &work->control.mov, &work->control.rot ); + DG_RotVector( &force, &svec, 1 ); + GM_SetPowerTarget( target, POWER_ONCE, 2, 32, 1, &svec ); + DG_PutVector( &rp_shift, &work->punch.center, 1 ); + return GM_PowerTarget( target ); +} + +// TODO: remove +const int s11i_dword_800D5204 = 0; + +void s11i_asiato_800C9E48( Zako11FWork *work ) +{ + TARGET *target; + + target = &work->field_904; + GM_SetPowerTarget( target, POWER_CONST | POWER_EXPLODE, 5, 0, 3, &ZAKO11F_ATTACK_FORCE_800C3674 ); + GM_MoveTarget( target, &work->control.mov ); + GM_PowerTarget( target ); +} + +int CheckPad_800C9EB0( Zako11FWork *work ) +{ + int press; + + press = work->pad.press; + + if ( press & 0x1 ) + { + SetZako11FMode2( work, s11i_asiato_800CC528 ); + return 0; + } + + if ( press & 0x2 ) + { + SetZako11FMode2( work, s11i_asiato_800CC5F4 ); + return 0; + } + + if ( press & 0x4 ) + { + SetZako11FMode2( work, ActOverScoutD_800CC6C0 ); + return 0; + } + + if ( press & 0x2000 ) + { + SetZako11FMode2( work, s11i_asiato_800CC758 ); + return 0; + } + + if ( press & 0x40 ) + { + SetZako11FMode2( work, s11i_asiato_800CC7F0 ); + return 0; + } + + if ( press & 0x80 ) + { + SetZako11FMode2( work, s11i_asiato_800CC8A4 ); + return 0; + } + + if ( press & 0x400 ) + { + SetZako11FMode2( work, s11i_asiato_800CC96C ); + return 0; + } + + if ( press & 0x800 ) + { + SetZako11FMode2( work, s11i_asiato_800CCA10 ); + return 0; + } + + if ( press & 0x200 ) + { + SetZako11FMode2( work, s11i_asiato_800CCAD8 ); + return 0; + } + + if ( press & 0x800000 ) + { + SetZako11FMode2( work, s11i_asiato_800CCCF0 ); + return 0; + } + + if ( press & 0x1000000 ) + { + SetZako11FMode2( work, s11i_asiato_800CCE38 ); + return 0; + } + + if ( press & 0x100 ) + { + SetZako11FMode( work, s11i_asiato_800CA4F4 ); + return 1; + } + + if ( press & 0x20 ) + { + SetZako11FMode( work, s11i_asiato_800CA584 ); + return 1; + } + + if ( press & SP_DANBOWLKERI ) + { + SetZako11FMode( work, ActBoxKick_800CA66C ); + return 1; + } + + if ( press & (SP_WEAPON | SP_WEAPON2) ) + { + SetZako11FMode( work, ActReadyGun_800CA674 ); + work->adjust[2].vx = 0; + return 1; + } + + return 0; +} + +void ActStandStill_800CA0B4( Zako11FWork *work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + work->act_status |= 0x1; + + if ( Zako11FCommand_800D5AF8.mode == TOP_COMM_TRAVEL ) + { + work->target->class |= TARGET_C4 ; + } + + work->vision.length = ZAKO11F_EYE_LENGTH_800C3694; + + if ( time == 0 ) + { + SetAction( work, STANDSTILL, ACTINTERP ); + } + + if ( CheckDamage_800C9B6C( work ) || CheckPad_800C9EB0( work ) ) + { + UnsetMode( work ) ; + return ; + } + + if ( work->pad.dir >= 0 ) + { + if ( GV_DiffDirAbs( work->control.rot.vy, work->pad.dir ) < 256 ) + { + if ( work->pad.mode & 0x1 ) + { + SetAction( work, ACTION2, ACTINTERP ); + } + else + { + SetAction( work, ACTION1, ACTINTERP ); + } + + SetZako11FMode( work, s11i_asiato_800CA25C ); + } + else + { + work->control.turn.vy = work->pad.dir; + } + } + + work->vision.facedir = work->control.rot.vy; +} + +void s11i_asiato_800CA25C( Zako11FWork *work, int time ) +{ + CONTROL *control; + int action; + int mindir, mindir2; + int dir; + SVECTOR *svec; + int near; + int speed; + + SetTargetClass( work->target, TARGET_FLAG ); + + if ( Zako11FCommand_800D5AF8.mode == TOP_COMM_TRAVEL ) + { + work->target->class |= TARGET_C4 ; + } + + work->vision.length = ZAKO11F_EYE_LENGTH_800C3694 ; + + if ( CheckDamage_800C9B6C( work ) || CheckPad_800C9EB0( work ) ) + { + UnsetMode( work ) ; + return ; + } + + control = &work->control; + action = work->unknown.last_set; + mindir = -1; + dir = work->pad.dir; + svec = work->control.vecs; + + if ( (work->pad.mode & 0x1) && ( action != ACTION2 ) ) + { + SetAction( work, ACTION2, ACTINTERP ); + } + + if ( dir >= 0 ) + { + near = control->n_touches; + + if ( near > 0 ) + { + mindir = GV_VecDir2( &svec[0] ); + + if ( near >= 2 ) + { + mindir2 = GV_VecDir2( &control->vecs[1] ); + + if ( GV_DiffDirAbs( dir, mindir2 ) < GV_DiffDirAbs( dir, mindir ) ) + { + mindir = mindir2; + } + } + } + } + + if ( dir < 0 ) + { + SetZako11FMode( work, ActStandStill_800CA0B4); + UnsetMode( work ) ; + return; + } + + switch ( s11i_asiato_800C9BE0( dir, mindir ) ) + { + case 1: + dir = (mindir - 1024) & 0xFFF; + break; + case 2: + dir = (mindir + 1024) & 0xFFF; + break; + } + + if ( work->body.objs->bound_mode == 0 ) + { + control->rot.vy = dir; + control->turn.vy = dir; + } + else + { + control->turn.vy = dir; + } + + speed = 0; + if ( action == ACTION1 ) + { + speed = 50; + } + else if ( action == ACTION2 ) + { + speed = 100; + } + + control->step.vx = ( speed * rsin( dir ) ) / 4096; + control->step.vz = ( speed * rcos( dir ) ) / 4096; + work->vision.facedir = work->control.rot.vy; +} + +void s11i_asiato_800CA4F4( Zako11FWork *work, int time ) +{ + if ( time == 0 ) + { + work->unknown.field_14 = 3; + SetAction( work, ACTION35, ACTINTERP ); + } + + if ( CheckDamage_800C9B6C( work ) ) + { + return; + } + + if ( work->body.is_end ) + { + work->pad.tmp = 0; + SetZako11FMode( work, s11i_asiato_800CB678 ); + } +} + +void s11i_asiato_800CA584( Zako11FWork *work, int time ) +{ + CONTROL *control; + + control = &work->control; + + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = ZAKO11F_EYE_LENGTH_800C3694 ; + + if ( time == 0 ) + { + SetAction( work, ACTION16, ACTINTERP ); + } + + if ( CheckDamage_800C9B6C( work ) ) + { + return; + } + + if ( work->body.is_end || !( work->pad.press & 0x20 ) ) + { + SetZako11FMode( work, ActStandStill_800CA0B4 ); + return; + } + + control->turn.vy = work->sn_dir; + control->step.vx = 0; + control->step.vz = 0; + work->vision.facedir = work->control.rot.vy; +} + +void ActBoxKick_800CA66C( Zako11FWork *work, int time ) {} + +void ActReadyGun_800CA674( Zako11FWork *work, int time ) +{ + int press; + + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = ZAKO11F_EYE_LENGTH_800C3694; + + press = work->pad.press; + + if ( time == 0 ) + { + if ( press & 0x10000 ) + { + SetAction( work, ACTION3, ACTINTERP ) ; + } + else + { + SetAction( work, ACTION5, ACTINTERP ); + } + + GM_ConfigMotionAdjust( &( work->body ), work->adjust ) ; + } + + ReviseReadyGun_800C9C34( work ); + + if ( CheckDamage_800C9B6C( work ) ) + { + return ; + } + + if ( !(press & 0x30000) ) + { + SetZako11FMode( work, ActStandStill_800CA0B4 ) ; + return ; + } + + if ( press & 0x40000 ) + { + SetZako11FModeFields( work, s11i_asiato_800CA84C ) ; + return; + } + + if ( press & 0x80000 ) + { + SetZako11FModeFields( work, s11i_asiato_800CA904 ) ; + return; + } + + if ( press & 0x100000 ) + { + SetZako11FModeFields( work, ActGrenade_800CAA54 ) ; + return; + } + + if ( press & 0x200000 ) + { + SetZako11FModeFields( work, s11i_asiato_800CABAC ) ; + return; + } + + if ( press & 0x2000000 ) + { + SetZako11FModeFields( work, s11i_asiato_800CAC84 ) ; + UnsetMode(work); + return; + } + + work->control.turn.vy = work->pad.dir; + work->vision.facedir = work->control.rot.vy; +} + +void s11i_asiato_800CA84C( Zako11FWork *work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = ZAKO11F_EYE_LENGTH_800C3694 ; + + if ( time == 0 ) + { + SetAction(work, ACTION4, 0); + ZAKO11F_SetPutChar_800CD700(work, GUNSHOOT); + GM_ConfigMotionAdjust(&work->body, work->adjust); + } + + ReviseReadyGun_800C9C34(work); + work->control.turn.vy = work->sn_dir; + + if (CheckDamage_800C9B6C(work)) + { + return; + } + + SetZako11FModeFields(work, ActReadyGun_800CA674); +} + +void s11i_asiato_800CA904( Zako11FWork *work, int time ) +{ + SVECTOR *rot; + + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = ZAKO11F_EYE_LENGTH_800C3694 ; + + if ( time == 0 ) + { + GM_ConfigMotionAdjust( &( work->body ), work->adjust ); + } + + rot = &work->control.rot; + if ( time == 1 || time == 7 ) + { + rot->vy = ( rot->vy - 256 ) & 0xFFF; + } + + if ( time == 3 || time == 5 ) + { + rot->vy = ( rot->vy + 256 ) & 0xFFF; + } + + if ( time == 2 || time == 4 || time == 6 ) + { + SetAction( work, ACTION6, 0 ); + ZAKO11F_SetPutChar_800CD700( work, GUNSHOOT ); + } + + ReviseReadyGun_800C9C34( work ); + + if ( CheckDamage_800C9B6C( work ) ) + { + return ; + } + + if ( work->body.is_end ) + { + SetZako11FMode( work, ActReadyGun_800CA674 ); + } + + work->control.rot.vy = rot->vy; +} + +void ActGrenade_800CAA54( Zako11FWork *work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = ZAKO11F_EYE_LENGTH_800C3694; + + if ( time == 0 ) + { + extern void *NewGrenadeEnemy( CONTROL *, OBJECT *, int, u_long *, SVECTOR *, int ) ; + + SetAction(work, GRENADE, ACTINTERP); + work->subweapon = NewGrenadeEnemy(&work->control, &(work->body), 9, + &(work->trigger), &GM_PlayerPosition, ENEMY_SIDE); + } + + if ( time > ACTINTERP ) + { + work->trigger |= WEAPON_TAKE; + } + + if ( time == 17 ) + { + GM_SeSet( &( work->control.mov ), SE_PINNUKI ) ; + } + + if ( time == 45 ) + { + work->trigger |= WEAPON_TRIG2 ; + } + + if ( CheckDamage_800C9B6C( work ) ) + { + GV_DestroyActor( work->subweapon ); + return; + } + + if ( work->body.is_end ) + { + GV_DestroyActor( work->subweapon ); + SetZako11FMode( work, ActReadyGun_800CA674 ); + } +} + +void s11i_asiato_800CABAC( Zako11FWork *work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + + work->vision.length = ZAKO11F_EYE_LENGTH_800C3694; + + if ( time == 0 ) + { + SetAction( work, ACTION8, 0 ); + } + + if ( time == 3 ) + { + if ( AttackForce_800C9D38( work ) ) + { + GM_SeSet( &( work->control.mov ), SE_HIT ); + } + } + + work->control.turn.vy = work->sn_dir; + + if ( CheckDamage_800C9B6C( work ) ) + { + return ; + } + + if ( work->body.is_end ) + { + SetZako11FMode( work, ActReadyGun_800CA674 ); + } +} + +void s11i_asiato_800CAC84( Zako11FWork *work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + + work->vision.length = ZAKO11F_EYE_LENGTH_800C3694; + + if ( time == 0 ) + { + SetAction( work, ACTION4, 0 ); + ZAKO11F_SetPutChar_800CD700(work, 4); + GM_ConfigMotionAdjust(&( work->body ), work->adjust); + } + + ReviseReadyGun2_800C9CBC(work); + + if ( CheckDamage_800C9B6C( work ) ) + { + return; + } + + SetZako11FModeFields(work, ActReadyGun_800CA674); +} + +void s11i_asiato_800CAD34( Zako11FWork *work, int time ) +{ + work->vision.length = 0; + + if ( time == 0 ) + { + SetAction( work, ACTION21, ACTINTERP ) ; + } + + if ( CheckDamage_800C9B6C( work ) ) + { + return ; + } + + SetTargetClass( work->target, TARGET_FLAG ); + + if ( work->body.is_end ) + { + work->actend = 1; + SetZako11FMode( work, ActStandStill_800CA0B4 ); + } +} + +void s11i_asiato_800CADDC( Zako11FWork *work, int time ) +{ + TARGET *target; + CONTROL *control; + + target = work->target; + control = &work->control; + + work->unknown.field_1E = 0; + work->act_status |= 0x8; + + if ( time == 0 ) + { + work->unknown.field_14 = 6; + SetAction( work, ACTION31, ACTINTERP ); + } + + if ( time - 8 < 12u ) + { + s11i_asiato_800C9E48( work ); + } + + if ( work->unknown.last_set == ACTION31 ) + { + if ( time == 22 ) + { + if ( control->mov.vy - control->levels[0] < 2000 ) + { + GM_SeSet( &control->mov, VO_ENEMY_PUNCHED ) ; + GM_SeSet( &control->mov, SE_HIT_FLOOR ) ; + ZAKO11F_PutBlood_800CCFD4( work, 6, 0 ) ; + GM_SetNoise( 100, 4, &work->control.mov ) ; + } + // if they fall from a height? + else if ( control->mov.vy - control->levels[0] > 3000 ) + { + work->target->vital = 0; + GM_SeSet( &control->mov, VO_ENEMY_THROWN ) ; + } + } + + if ( work->body.is_end ) + { + if ( control->grounded == 0 ) + { + SetAction( work, ACTION40, ACTINTERP ); + } + else + { + SetZako11FMode( work, s11i_asiato_800CB678 ); + } + } + } + else if ( control->grounded ) + { + GM_SeSet( &control->mov, VO_ENEMY_PUNCHED ); + GM_SeSet( &control->mov, SE_HIT_FLOOR ) ; + GM_SetNoise( 100, 4, &work->control.mov ) ; + + ZAKO11F_PutBlood_800CCFD4( work, 6, 1 ) ; + + if ( work->target->vital <= 0 ) + { + SetZako11FMode( work, s11i_asiato_800CC218 ); + } + else + { + SetZako11FMode( work, s11i_asiato_800CB678 ); + } + } + + if ( time < 24 ) + { + control->turn = *target->field_1C; + } + + if ( time < 22 ) + { + work->control.hzd_height = -32767; + } +} + +void s11i_asiato_800CB0C0( Zako11FWork *work ) +{ + SVECTOR to; + + work->control.turn = GM_PlayerControl->rot; + + DG_SetPos2(&GM_PlayerPosition, &GM_PlayerControl->rot); + DG_PutVector(&s11i_800C33A4, &to, 1); + GV_SubVec3(&to, &work->control.mov, &work->control.step); +} + +void s11i_asiato_800CB140( Zako11FWork *work, int time ) +{ + TARGET *target; + int action; + int a_mode; + + target = work->target; + action = work->unknown.last_set; + + work->unknown.field_1E = 0; + work->act_status |= 0xC; + + if ( time == 0 ) + { + work->field_B5A = target->faint; + GM_SeSet( &work->control.mov, VO_ENEMY_GRABBED ); + } + + if ( work->field_B5A != target->faint ) + { + GM_SeSet( &work->control.mov, VO_ENEMY_GRABBED ); + work->field_B5A = target->faint; + } + + if ( target->damaged & TARGET_POWER ) + { + ZAKO11F_PutBlood_800CCFD4( work, 5, 0 ); + GM_SeSet( &work->control.mov, VO_ENEMY_GRABBED ); + target->force = DG_ZeroVector; + target->damage = 0; + target->damaged = TARGET_STALE; + + if ( target->vital <= 0 ) + { + a_mode = target->a_mode; + if (target->a_mode == 1) + { + work->field_C40 = a_mode; + } + + work->unknown.field_14 = 5; + target->side = ENEMY_SIDE; + SetZako11FMode( work, s11i_asiato_800CBACC ); + target->captured = 0; + } + } + + switch ( GM_PlayerAction ) + { + case 7: + case 13: + if( action != ACTION27 && action != ACTION48 ) + { + target->side = PLAYER_SIDE; + SetAction( work, ACTION27, ACTINTERP ); + work->field_B5C = 0; + } + + if ( work->field_B5C == 50 && target->faint > 0 ) + { + SetAction( work, ACTION48, ACTINTERP ); + } + + if ( action == ACTION48 && !( work->field_B5C & 0x7 ) ) + { + NewPadVibration(s11i_800C339C, 1); + NewPadVibration(s11i_800C33A0, 2); + } + + work->control.turn = GM_PlayerControl->rot; + s11i_asiato_800CB0C0( work ); + + work->field_B5C++; + break; + + case 38: + if ( action != ACTION28 ) + { + target->side = PLAYER_SIDE; + SetAction( work, ACTION28, ACTINTERP ); + } + + work->control.turn = GM_PlayerControl->rot; + s11i_asiato_800CB0C0( work ); + break; + + case 39: + work->unknown.field_14 = 4; + target->side = ENEMY_SIDE; + target->vital = 0; + SetZako11FMode( work, s11i_asiato_800CBACC ); + return; + + default: + work->unknown.field_14 = 5; + target->side = ENEMY_SIDE; + SetZako11FMode( work, s11i_asiato_800CBACC ); + return; + } + + if ( work->sn_dis > 800 ) + { + target->captured = 0; + } + + work->target->class |= (TARGET_SEEK | TARGET_POWER); + work->vision.facedir = work->control.rot.vy; +} + +void s11i_asiato_800CB4B4(Zako11FWork *work, int time) +{ + if (time == 0) + { + SetAction(work, ACTION38, ACTINTERP); + } + + if (work->body.is_end) + { + SetZako11FMode(work, s11i_asiato_800CC218); + } +} + +void s11i_asiato_800CB528(Zako11FWork *work, int time) +{ + CONTROL *control; + int time_offset; + + control = &work->control; + + work->unknown.field_1E = 0; + work->vision.length = 0; + work->act_status |= 0x8; + + if (time == 0) + { + SetAction(work, ACTION32, ACTINTERP); + work->target->faint = 0; + } + + if (CheckDamage_800C9B6C(work)) + { + return; + } + + time_offset = 10; + + if (time == time_offset) + { + work->unknown.field_14 = 1; + SetAction(work, ACTION37, ACTINTERP); + } + else + { + SetTargetClass(work->target, TARGET_FLAG); + } + + if (time == time_offset + 24) + { + GM_SeSet(&control->mov, SE_ENEMY_COLLAPSE); + } + + if (time == time_offset + 46) + { + GM_SeSet(&control->mov, SE_HIT_FLOOR); + } + + if (time >= time_offset + 50 && work->body.is_end) + { + SetZako11FMode(work, s11i_asiato_800CB678); + } +} + +// enemy on the floor +void s11i_asiato_800CB678(Zako11FWork *work, int time) +{ + int x; + + work->act_status |= 0x20; + + if (time == 0) + { + if (work->unknown.field_14 < 3) + { + if (work->unknown.field_14 == 1) + { + SetAction(work, ACTION41, ACTINTERP); + } + else + { + SetAction(work, ACTION39, ACTINTERP); + } + } + else + { + SetAction(work, ACTION40, ACTINTERP); + } + + x = work->control.mov.vx % 30; + x = (x < 0) ? -x : x; + work->field_B5A = x + 10; + } + + if (s11i_asiato_800C98CC(work)) + { + SetZako11FMode(work, s11i_asiato_800CB7F0); + } + else if (work->field_B5A < time) + { + if (work->target->faint <= 0) + { + work->target->faint = work->param.faint; + } + + SetZako11FMode(work, s11i_asiato_800CB8B8); + } +} + +void s11i_asiato_800CB7F0(Zako11FWork *work, int time) +{ + if (time == 0) + { + if (work->unknown.field_14 < 3) + { + SetAction(work, ACTION46, ACTINTERP); + } + else + { + SetAction(work, ACTION47, ACTINTERP); + } + + GM_SeSet(&work->control.mov, SE_ITEM_CURSOR); + } + + if (time == 4) + { + ZAKO11F_PutItem_800CD0A8(work); + } + + if (work->body.is_end) + { + SetZako11FMode(work, s11i_asiato_800CC218); + } +} + +// enemy getting up +void s11i_asiato_800CB8B8(Zako11FWork *work, int time) +{ + work->unknown.field_1E = 0; + work->act_status |= 0x8; + + if (time == 0) + { + if (work->unknown.field_14 < 3) + { + if (work->field_B5A >= 150) + { + SetAction(work, ACTION44, ACTINTERP); + } + else + { + SetAction(work, ACTION42, ACTINTERP); + } + } + else + { + if (work->field_B5A >= 150) + { + SetAction(work, ACTION45, ACTINTERP); + } + else + { + SetAction(work, ACTION43, ACTINTERP); + } + } + } + + if (work->body.is_end) + { + SetZako11FMode(work, ActStandStill_800CA0B4); + } +} + +void s11i_asiato_800CB9BC(Zako11FWork *work, int time) +{ + TARGET *target; + + work->unknown.field_1E = 0; + SetTargetClass(work->target, TARGET_FLAG); + work->vision.length = ZAKO11F_EYE_LENGTH_800C3694; + work->act_status |= 0x8; + + if (CheckDamage_800C9B6C(work)) + { + return; + } + + target = work->target; + if (time == 0) + { + SetAction(work, ACTION15, ACTINTERP); + + GM_SeSet(&work->control.mov, VO_ENEMY_PUNCHED); + if (target->a_mode == 3) + { + GM_SeSet(&work->control.mov, SE_PUNCH_HIT); + ZAKO11F_PutFog_800CD044(work); + } + else + { + ZAKO11F_PutBlood_800CCFD4(work, 5, 0); + } + } + + if (work->body.is_end) + { + SetZako11FMode(work, ActStandStill_800CA0B4); + } +} + +void s11i_asiato_800CBACC(Zako11FWork *work, int time) +{ + WatcherUnk *unk; + CONTROL *control; + + unk = &work->unknown; + work->unknown.field_1E = 0; + work->act_status |= 0x8; + work->control.step = work->target->force; + + control = &work->control; + if (time == 0) + { + if (work->target->vital <= 0) + { + GM_SeSetMode(&work->control.mov, VO_ENEMY_PUNCHED, GM_SEMODE_BOMB); + s11i_zk11fcom_800D0DB8(); + } + + switch(unk->field_14) + { + case 0: + GM_SeSet(&control->mov, SE_PUNCH_HIT); + SetAction(work, ACTION34, ACTINTERP); + GM_SeSetMode(&control->mov, VO_ENEMY_THROWN, GM_SEMODE_BOMB); + ZAKO11F_PutBlood_800CCFD4(work, 5, 0); + work->field_B5A = 17; + break; + + case 1: + SetAction(work, ACTION37, ACTINTERP); + if (work->target->vital <= 0) + { + if (GM_CurrentWeaponId == WP_Rifle) + { + ZAKO11F_PutBlood_800CCFD4(work, 6, 2); + } + else + { + ZAKO11F_PutBlood_800CCFD4(work, 6, 1); + } + + GM_SeSetMode(&control->mov, VO_ENEMY_KILLED, GM_SEMODE_BOMB); + } + else + { + ZAKO11F_PutBlood_800CCFD4(work, 5, 0); + GM_SeSetMode(&control->mov, VO_ENEMY_THROWN, GM_SEMODE_BOMB); + } + + work->field_B5A = 46; + break; + + case 3: + GM_SeSetMode(&control->mov, VO_ENEMY_THROWN, GM_SEMODE_BOMB); + SetAction(work, ACTION35, ACTINTERP); + ZAKO11F_PutBlood_800CCFD4(work, 5, 0); + work->field_B5A = 17; + break; + + case 2: + GM_SeSetMode(&control->mov, VO_ENEMY_THROWN, GM_SEMODE_BOMB); + SetAction(work, ACTION36, ACTINTERP); + ZAKO11F_PutBlood_800CCFD4(work, 5, 0); + work->field_B5A = 22; + break; + + case 4: + GM_SeSetMode(&control->mov, VO_ENEMY_SNAPPED, GM_SEMODE_BOMB); + SetAction(work, ACTION29, ACTINTERP); + work->field_B5A = 67; + break; + + case 5: + SetAction(work, ACTION30, ACTINTERP); + work->field_B5A = 15; + break; + } + } + + switch(unk->field_14) + { + case 0: + if (time < 12) + { + control->turn.vy += 170; + } + + if (time > 6 && time < 30) + { + s11i_asiato_800C9E48(work); + } + + if (time < 20) + { + work->control.hzd_height = -32767; + } + break; + + case 1: + if (time == 24) + { + GM_SeSet(&control->mov, SE_ENEMY_COLLAPSE); + } + break; + + case 3: + if (time > 6 && time < 30) + { + s11i_asiato_800C9E48(work); + } + + if (time < 15) + { + control->hzd_height = -32767; + } + break; + + case 2: + if (time > 6 && time < 30) + { + s11i_asiato_800C9E48(work); + } + + if (time < 20) + { + control->hzd_height = -32767; + } + break; + + case 4: + case 5: + work->act_status |= 0x4; + break; + } + + if (time > 16 && control->grounded) + { + control->step = DG_ZeroVector; + } + + if (time == work->field_B5A) + { + if (control->mov.vy - control->levels[0] < 2000) + { + GM_SeSetMode(&control->mov, SE_HIT_FLOOR, GM_SEMODE_BOMB); + GM_SetNoise(0x64, 4, &control->mov); + ZAKO11F_PutBlood_800CCFD4(work, 6, 0); + } + else if (control->mov.vy - control->levels[0] > 3000) + { + if (work->target->vital > 0) + { + s11i_zk11fcom_800D0DB8(); + } + + work->target->vital = 0; + SetZako11FMode(work, s11i_asiato_800CC038); + return; + } + } + + if (work->body.is_end) + { + work->unknown.field_1E = 1; + work->target->force = DG_ZeroVector; + + if (work->target->vital <= 0) + { + SetZako11FMode(work, s11i_asiato_800CC218); + } + else + { + SetZako11FMode(work, s11i_asiato_800CB678); + } + } +} + +void s11i_asiato_800CC038(Zako11FWork *work, int time) +{ + CONTROL *control; + + control = &work->control; + + work->unknown.field_1E = 0; + work->act_status |= 0x8; + + control->step = work->target->force; + + if (time == 0 && work->unknown.field_14 != 2) + { + GM_SeSet(&control->mov, VO_ENEMY_KILLED); + } + + if (time > 16 && control->grounded) + { + control->step = DG_ZeroVector; + } + + if (work->unknown.last_set < 39) + { + if (work->body.is_end) + { + if (work->unknown.field_14 < 3) + { + if (work->unknown.field_14 == 1) + { + SetAction(work, ACTION41, ACTINTERP); + } + else + { + SetAction(work, ACTION39, ACTINTERP); + } + } + else + { + SetAction(work, ACTION40, ACTINTERP); + } + } + } + else if (control->grounded != 0) + { + work->unknown.field_1E = 1; + work->target->force = DG_ZeroVector; + GM_SeSet(&control->mov, SE_HIT_FLOOR); + ZAKO11F_PutBlood_800CCFD4(work, 6, 1); + SetZako11FMode(work, s11i_asiato_800CC218); + } +} + +void s11i_asiato_800CC218(Zako11FWork *work, int time) +{ + work->act_status |= 0x40; + + if (time == 0) + { + if (work->unknown.field_14 < 3) + { + if (work->unknown.field_14 == 1) + { + SetAction(work, ACTION51, ACTINTERP); + } + else + { + SetAction(work, ACTION49, ACTINTERP); + } + } + else + { + SetAction(work, ACTION50, ACTINTERP); + } + + GM_ConfigControlAttribute(&work->control, RADAR_OFF); + work->alert_level = 0; + } + + if (time & 2) + { + work->visible = 0; + } + else + { + work->visible = 1; + } + + if (time > 8) + { + SetZako11FMode(work, s11i_asiato_800CC314); + } +} + +void s11i_asiato_800CC314(Zako11FWork *work, int time) +{ + work->act_status |= 0x40; + + if (time == 0) + { + work->visible = 0; + work->control.mov = ZAKO11F_NO_POINT_800C368C; + Zako11FCommand_800D5AF8.watchers[work->param.index].field_04 = 1; + + if (work->field_C40 == 0) + { + GM_TotalEnemiesKilled++; + } + } +} + +void s11i_asiato_800CC39C(Zako11FWork *work) +{ + WatcherUnk *unk; + ZAKO11FACTION action; + CONTROL *control; + int time_prev; + + work->trigger = 0; + work->vision.length = 0; + work->target->class = TARGET_AVAIL; + + unk = &work->unknown; + + work->actend = 0; + work->act_status = 0; + + action = work->action; + control = &work->control; + + work->field_C38 = 0; + + control->height = (work->body.height * work->scale) / 4096; + control->hzd_height = control->levels[0] + 750; + + time_prev = work->time; + work->time++; + + if (!action) + { + action = ActStandStill_800CA0B4; + work->action = ActStandStill_800CA0B4; + } + + action(work, time_prev); + action = work->action2; + + if (action) + { + time_prev = work->time2; + work->time2++; + action(work, time_prev); + } + + if (unk->field_1E == 0) + { + control->r_sphere = GV_NearExp2(control->r_sphere, unk->field_1C); + } + else + { + control->r_sphere = -1; + } + + if (work->target->class & TARGET_POWER) + { + work->hom->flag = TRUE; + } + else + { + work->hom->flag = FALSE; + } + + if (unk->field_04 < 0 && control->grounded) + { + unk->field_04 = 0; + } + + unk->field_04 -= 16; + control->step.vy = unk->field_04; + + if (work->mark_time > 0) + { + work->mark_time--; + } +} + +void s11i_asiato_800CC528(Zako11FWork *work, int time) +{ + if (time == 0) + { + UnsetAction(work, ACTION9); + } + + if (!(work->pad.press & 0x1)) + { + UnsetMode(work); + } + else if (time < 4) + { + work->vision.facedir = (work->control.rot.vy - (time * 256)) & 0xFFF; + } + else + { + work->vision.facedir = (work->control.rot.vy - 1024) & 0xFFF; + } +} + +void s11i_asiato_800CC5F4(Zako11FWork *work, int time) +{ + if (time == 0) + { + UnsetAction(work, ACTION10); + } + + if (!(work->pad.press & 0x2)) + { + UnsetMode(work); + } + else if (time < 4) + { + work->vision.facedir = (work->control.rot.vy + (time * 256)) & 0xFFF; + } + else + { + work->vision.facedir = (work->control.rot.vy + 1024) & 0xFFF; + } +} + +void ActOverScoutD_800CC6C0(Zako11FWork *work, int time) +{ + work->vision.length = 3000; + + if (time == 0) + { + UnsetAction(work, ACTION11); + } + + if (!(work->pad.press & 0x4)) + { + UnsetMode(work); + } +} + +void s11i_asiato_800CC758(Zako11FWork *work, int time) +{ + work->vision.length = 3000; + + if (time == 0) + { + UnsetAction(work, ACTION14); + } + + if (!(work->pad.press & 0x2000)) + { + UnsetMode(work); + } +} + +void s11i_asiato_800CC7F0(Zako11FWork *work, int time) +{ + work->vision.length = 0; + + if (time == 0) + { + UnsetAction(work, ACTION24); + GM_SeSet(&work->control.mov, VO_ENEMY_SNEEZE); + } + + if (work->body.time2 != 0 || !(work->pad.press & 0x40)) + { + work->pad.time = 0; + UnsetMode(work); + } +} + +void s11i_asiato_800CC8A4(Zako11FWork *work, int time) +{ + work->vision.length = 0; + + if (time == 0) + { + UnsetAction(work, ACTION22); + } + + if (time == 60) + { + GM_SeSet(&work->control.mov, VO_ENEMY_YAWN); + } + + if ((work->body.time2 != 0) || !(work->pad.press & 0x80)) + { + work->pad.time = 0; + UnsetMode(work); + } +} + +void s11i_asiato_800CC96C(Zako11FWork *work, int time) + { + if (time == 0) + { + UnsetAction(work, ACTION23); + } + + if ((work->body.time2 != 0) || !(work->pad.press & 0x400)) + { + work->pad.time = 0; + UnsetMode(work); + } +} + +void s11i_asiato_800CCA10(Zako11FWork *work, int time) +{ + work->vision.length = 0; + + if (time == 0) + { + UnsetAction(work, ACTION19); + } + + if (time == 90) + { + GM_SeSet(&work->control.mov, VO_ENEMY_YAWN); + } + + if (work->body.time2 != 0 || !(work->pad.press & 0x800)) + { + work->pad.time = 0; + UnsetMode(work); + } +} + +void s11i_asiato_800CCAD8(Zako11FWork *work, int time) +{ + SVECTOR pos; + + work->vision.length = 0; + + if (time == 0) + { + UnsetAction(work, ACTION19); + } + + if (time == 90) + { + GM_SeSet(&work->control.mov, VO_ENEMY_YAWN); + } + + if (time == 110) + { + UnsetAction(work, ACTION20); + } + + if (time > 110) + { + work->act_status |= 0x10; + + if (work->m_ctrl.info2.frame == 30) + { + pos = work->control.mov; + pos.vy += 500; + + AN_Sleep(&pos); + + GM_SeSet(&work->control.mov, VO_ENEMY_SNORE); + + if (work->sn_dis < 1000 && (GM_PlayerStatus & PLAYER_WATCH) && GV_RandU(12) > 10) + { + work->pad.sound = 240; + } + } + + if (work->pad.press & 0x1000) + { + SetZako11FMode(work, s11i_asiato_800CAD34); + UnsetMode(work); + return; + } + } + + if (!(work->pad.press & 0x200)) + { + UnsetMode(work); + } +} + +void s11i_asiato_800CCCB0(Zako11FWork *work) +{ + MATRIX rot; + + DG_SetPos(&work->body.objs->objs[6].world); + DG_MovePos(&s11i_800C33AC); + ReadRotMatrix(&rot); + + AN_Unknown_800C3B7C(&rot); +} + +void s11i_asiato_800CCCF0(Zako11FWork *work, int time) +{ + int diff; + + work->vision.length = 3000; + work->act_status |= 0x80; + + if (time == 0) + { + UnsetAction(work, ACTION25); + } + + if (time >= 1000 && time < 1210) + { + diff = time - 1000; + + if (diff == 0 || diff == 4 || diff == 8 || diff == 12 || + diff == 20 || diff == 28 || diff == 36 || diff == 48 || + diff == 60) + { + GM_SeSet(&work->control.mov, SE_WET_STEP); + s11i_asiato_800CCCB0(work); + } + } + else if (time > 30 && time & 1) + { + GM_SeSet(&work->control.mov, SE_WET_STEP); + if (time > 45) + { + s11i_asiato_800CCCB0(work); + } + } + + if (!(work->pad.press & 0x800000)) + { + UnsetMode(work); + } +} + +void s11i_asiato_800CCE38(Zako11FWork *work, int time) +{ + work->vision.length = 3000; + + if (time == 0) + { + UnsetAction(work, ACTION25); + } + + if (time == 186) + { + UnsetActionManual(work, ACTION26, 0xFFFF); + } + + if (time == 0 || time == 4 || time == 8 || time == 12 || + time == 20 || time == 28 || time == 36 || time == 48 || + time == 60 || time == 120 || time == 122 || time == 124 || + time == 126 || time == 180 || time == 182 || time == 184 || + time == 186) + { + GM_SeSet(&work->control.mov, SE_WET_STEP); + s11i_asiato_800CCCB0(work); + } + + if ((time > 186 && work->body.time2 != 0) || !(work->pad.press & 0x1000000)) + { + work->pad.time = 0; + UnsetMode(work); + } +} diff --git a/components/mgs/mgs/source/animal/zako11f/override.c b/components/mgs/mgs/source/animal/zako11f/override.c new file mode 100644 index 00000000..bc556ef6 --- /dev/null +++ b/components/mgs/mgs/source/animal/zako11f/override.c @@ -0,0 +1,789 @@ +#include "enemy/enemy.h" +#include "linkvar.h" + +extern SVECTOR s11i_dword_800C34C4[3]; +extern int s11i_dword_800C34DC[3][16]; +extern int s11i_dword_800C359C[3][16]; +extern int ZAKO11F_EYE_LENGTH_800C3694; +extern SVECTOR ZAKOCOM_PlayerPosition_800D5AF0; +extern int ZAKO11F_GameFlag_800D5C4C; +extern int ZAKOCOM_PlayerAddress_800D5C50; +extern int ZAKOCOM_PlayerMap_800D5C54; + +void ZAKO11F_PutMark_800CD224( Zako11FWork *work, int mark ); + +void s11i_zk11fcom_800D0C70( void ); +int s11i_zk11fcom_800D0D34( int map_id, int val ); +int s11i_zk11fcom_800D0DA8( void ); + +void s11i_zk11fcom_800CF9E4( Zako11FWork *work ) +{ + work->target_addr = ZAKOCOM_PlayerAddress_800D5C50; + work->target_pos = ZAKOCOM_PlayerPosition_800D5AF0; + work->target_map = ZAKOCOM_PlayerMap_800D5C54; +} + +void s11i_zk11fcom_800CFA2C( Zako11FWork *work ) +{ + int addr; + int index; + HZD_ZON *zone; + + addr = work->param.field_B7C; + + index = addr << 1; + index = index + addr; + zone = (HZD_ZON *)((char *)work->control.map->hzd->def->zones + index * 8); + + work->target_addr = addr | addr << 8; + + work->target_pos.vx = zone->x; + work->target_pos.vy = zone->y; + work->target_pos.vz = zone->z; + work->target_map = work->start_map; +} + +void s11i_zk11fcom_800CFA88( Zako11FWork *work ) +{ + if ( work->param.field_B7C == 0xFF ) + { + work->think2 = 1; + work->think1 = 0; + work->think3 = 4; + } + else + { + work->think2 = 3; + work->think1 = 0; + work->think3 = 16; + s11i_zk11fcom_800CFA2C( work ); + } + + work->count3 = 0; + work->pad.mode = TOP_COMM_ALERT; +} + +void s11i_zk11fcom_800CFAF4( Zako11FWork *work ) +{ + work->think1 = 0; + work->think2 = 4; + work->think3 = 1; + work->count3 = 0; +} + +void s11i_zk11fcom_800CFB10( Zako11FWork *work ) +{ + work->think1 = 0; + work->think2 = 0; + work->think3 = 0; + work->count3 = 0; + work->pad.mode = TOP_COMM_ALERT; +} + +int s11i_zk11fcom_800CFB2C( SVECTOR *from, SVECTOR *to ) +{ + SVECTOR diff; + + GV_SubVec3(to, from, &diff); + diff.vy = 0; + + return GV_VecDir2(&diff); +} + +int s11i_zk11fcom_800CFB64( Zako11FWork *work ) +{ + SVECTOR svec; + HZD_HDL *hzd; + SVECTOR *target_pos; + int addr, addr2, addr3; + int route; + HZD_ZON *zone; + CONTROL *control; + int reach; + + hzd = work->control.map->hzd; + + if ( work->target_map == work->control.map->index ) + { + target_pos = &work->target_pos; + addr = work->target_addr; + } + else + { + target_pos = &work->target_pos; + route = s11i_zk11fcom_800D0D34( work->control.map->index , work->target_map ); + + zone = &hzd->def->zones[ route ]; + target_pos->vx = zone->x; + target_pos->vy = zone->y; + target_pos->vz = zone->z; + + addr = (route << 8) | route; + } + + control = &work->control; + + work->field_C0C = HZD_GetAddress( hzd, &control->mov, work->field_C0C ); + + addr2 = work->field_C0C; + reach = HZD_ReachTo( hzd, addr2, work->field_C10 ); + + if ( addr != work->field_BF0 || reach <= 0 ) + { + work->field_BF0 = addr; + + if ( HZD_ReachTo( hzd, addr2, addr ) < 2 ) + { + if ( work->target_map == work->control.map->index ) + { + work->field_C1C = *target_pos; + work->field_C10 = addr; + + GV_SubVec3( &work->field_C1C, &control->mov, &svec ); + + work->pad.dir = GV_VecDir2( &svec ); + return -1; + } + + addr2 = s11i_zk11fcom_800D0D34( work->target_map, control->map->index ); + control->map = GM_GetMap( work->target_map ); + zone = &control->map->hzd->def->zones[ addr2 ]; + work->control.mov.vx = zone->x; + control->mov.vy = zone->y; + control->mov.vz = zone->z; + work->field_C0C = -1; + work->field_BF0 = -1; + return control->rot.vy; + } + + addr3 = HZD_Navigate( hzd, addr2, addr, &control->mov ); + zone = &hzd->def->zones[ addr3 ]; + if ( work->field_BFC == 0xFA0 ) + { + work->field_C1C.vx = zone->x + 0xFA; + work->field_C1C.vy = zone->y; + work->field_C1C.vz = zone->z + 0xFA; + } else + { + work->field_C1C.vx = zone->x; + work->field_C1C.vy = zone->y; + work->field_C1C.vz = zone->z; + } + + work->field_C1C.vx = zone->x; + work->field_C1C.vy = zone->y; + work->field_C1C.vz = zone->z; + + work->field_C10 = (addr3 & 0xFF) | (addr3 & 0xFF) << 8; + } + + GV_SubVec3( &work->field_C1C, &work->control.mov, &svec ); + target_pos = &svec; + return GV_VecDir2( target_pos ); +} + +int s11i_zk11fcom_800CFE24( Zako11FWork *work ) +{ + if ( work->count3 == 0 && !( work->act_status & 0x8 ) ) + { + ZAKO11F_PutMark_800CD224( work, 0 ); + } + + if ( work->count3 < 20 ) + { + work->pad.press |= 0x20; + work->count3++; + return 0; + } + + return 1; +} + +int s11i_zk11fcom_800CFEA4( Zako11FWork *work ) +{ + if ( work->count3 == 0 ) + { + GM_SeSet( &work->control.mov, SE_EXCLAMATION ); + ZAKO11F_PutMark_800CD224( work, 0 ); + } + + if ( work->count3 > 20 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s11i_zk11fcom_800CFF0C( Zako11FWork *work ) +{ + if ((work->count3 % 32) == 0) + { + work->field_C0C = -1; + work->field_BF0 = -1; + s11i_zk11fcom_800CFB64(work); + } + + work->pad.dir = s11i_zk11fcom_800CFB64(work); + if (work->pad.dir < 0) + { + return 1; + } + + work->count3++; + return 0; +} + +int DirectTrace_800CFF7C( Zako11FWork *work, int range ) +{ + SVECTOR dist; + int x, z; + + dist.vx = x = work->target_pos.vx - work->control.mov.vx; + dist.vz = z = work->target_pos.vz - work->control.mov.vz; + + if ( x <= -range || x >= range || z <= -range || z >= range ) + { + work->pad.dir = GV_VecDir2( &dist ); + work->count3++; + return 0; + } + + return 1; +} + +int s11i_zk11fcom_800D0004( Zako11FWork *work ) +{ + MAP *map; + + s11i_zk11fcom_800CF9E4( work ); + + map = work->control.map; + + if ( map->index != work->target_map ) + { + return -1; + } + + if ( work->count3 & 0x10 ) + { + work->field_C0C = HZD_GetAddress( map->hzd, &work->control.mov, -1 ); + if ( HZD_ReachTo( work->control.map->hzd, work->field_C0C, work->target_addr ) > 1 ) + { + return -1; + } + } + + return DirectTrace_800CFF7C( work, 1500 ); +} + +int s11i_zk11fcom_800D0098( Zako11FWork *work ) +{ + work->pad.dir = work->sn_dir; + work->pad.press |= 0x10000; + + if ( work->count3 == 0 ) + { + work->pad.press |= 0x40000; + } + else if ( work->count3 == 4 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s11i_zk11fcom_800D00EC( Zako11FWork *work ) +{ + work->pad.press |= 0x20000; + + if ( work->count3 == 0 ) + { + work->pad.press |= 0x80000; + } + + if ( work->count3 > 80 ) + { + return 1; + } + + work->pad.dir = 0xA00; + work->count3++; + return 0; +} + +int s11i_zk11fcom_800D0148( Zako11FWork *work ) +{ + work->pad.press |= 0x10000; + + if ( work->count3 == 30 ) + { + work->pad.press |= 0x100000; + work->field_C04 = s11i_dword_800C34C4[ work->param.index ]; + } + else if ( work->body.is_end ) + { + return 1; + } + + work->pad.dir = s11i_zk11fcom_800CFB2C( &work->control.mov, &s11i_dword_800C34C4[ work->param.index ] ); + work->count3++; + return 0; +} + +int s11i_zk11fcom_800D020C( Zako11FWork *work ) +{ + work->pad.press |= 0x10000; + + if ( work->count3 == 0 ) + { + work->pad.press |= 0x200000; + } + else if ( work->actend || work->count3 > 30 ) + { + return 1; + } + + work->pad.dir = work->sn_dir; + work->count3++; + return 0; +} + +int s11i_zk11fcom_800D0274( Zako11FWork *work ) +{ + int count3; + + work->pad.press |= 0x10000; + + count3 = work->count3; + if ( count3 == 0 ) + { + work->pad.press |= 0x40; + } + else if ( count3 >= 60 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s11i_zk11fcom_800D02BC( Zako11FWork *work ) +{ + int count3; + + work->pad.press |= 0x10000; + + count3 = work->count3; + if (count3 < 10) + { + if ((work->count3 % 2) == 0) + { + work->pad.press |= 0x2000000; + } + } + else if (count3 >= 20) + { + return 1; + } + + work->pad.dir = work->sn_dir; + + if (GM_PlayerPosition.vz > 10500 && (GM_PlayerPosition.vx < 5500 || GM_PlayerPosition.vx > 7500)) + { + work->pad.dir &= 0xFFF; + + if (work->pad.dir >= 39 && work->pad.dir <= 1023) + { + work->pad.dir = 38; + } + + if (work->pad.dir < 6 || work->pad.dir > 3072) + { + work->pad.dir = 6; + } + } + + work->pad.dir += 47; + work->count3++; + return 0; +} + +int s11i_zk11fcom_800D039C( Zako11FWork *work ) +{ + if (work->sn_dis < 800) + { + work->pad.press |= 0x10000; + return 1; + } + + work->pad.dir = work->sn_dir; + work->count3++; + return 0; +} + +int s11i_zk11fcom_800D03E4( Zako11FWork *work ) +{ + work->pad.press |= 0x10000; + + if (work->count3 == 8) + { + work->count3 += GV_RandU(8); + } + + work->pad.dir = work->sn_dir & 0xFFF; + + if (GM_PlayerPosition.vz > 10500 && (GM_PlayerPosition.vx < 5500 || GM_PlayerPosition.vx > 7500)) + { + if (work->pad.dir > 38 && work->pad.dir < 1024) + { + work->pad.dir = 38; + } + + if (work->pad.dir < 5 || work->pad.dir > 3072) + { + work->pad.dir = 5; + } + } + + if (work->count3 > 18) + { + switch (s11i_dword_800C34DC[work->param.index][work->field_C3C]) + { + case 0: + if (work->vision.field_B92 != 2) + { + return 14; + } + + if (work->sn_dis < 1000 && !(GM_PlayerStatus & (PLAYER_CB_BOX | PLAYER_GROUND | PLAYER_SQUAT))) + { + return 10; + } + + if (work->sn_dis < work->field_BFC) + { + return 6; + } + + return 14; + + case 1: + return 14; + + case 3: + return 7; + + case 6: + work->pad.dir += 47; + return 12; + + case 5: + return 13; + + case 2: + if (GV_RandU(8) == 0) + { + return 11; + } + + return 14; + } + } + + if (work->field_C3C != ZAKO11F_GameFlag_800D5C4C) + { + return 14; + } + + work->count3++; + return 0; +} + +int s11i_zk11fcom_800D05D4( Zako11FWork *work ) +{ + work->pad.dir = work->sn_dir; + work->pad.press |= 0x10000; + + work->field_C3C = ZAKO11F_GameFlag_800D5C4C; + work->param.field_B7C = s11i_dword_800C359C[work->param.index][ZAKO11F_GameFlag_800D5C4C]; + return 15; +} + + +void s11i_zk11fcom_800D0628( Zako11FWork *work ) +{ + work->field_C3C = ZAKO11F_GameFlag_800D5C4C; + work->param.field_B7C = s11i_dword_800C359C[work->param.index][ZAKO11F_GameFlag_800D5C4C]; + s11i_zk11fcom_800CFA88(work); +} + +void s11i_zk11fcom_800D0674( Zako11FWork *work ) +{ + switch (work->think3) + { + case 0: + s11i_zk11fcom_800D0628(work); + break; + } +} + +void s11i_zk11fcom_800D06A4( Zako11FWork *work ) +{ + switch (work->think3) + { + case 1: + if (s11i_zk11fcom_800CFE24(work)) + { + work->think2 = 2; + work->think3 = 14; + work->count3 = 0; + } + break; + + case 2: + if (s11i_zk11fcom_800CFEA4(work)) + { + work->think2 = 3; + work->think3 = 16; + work->pad.mode = 1; + work->pad.sound = 240; + work->think1 = 0; + work->count3 = 0; + work->alert_level = 0xFF; + } + break; + + case 3: + if (s11i_zk11fcom_800CFEA4(work)) + { + s11i_zk11fcom_800CFA88(work); + work->pad.sound = 240; + work->alert_level = 0xFF; + } + break; + } +} + +void s11i_zk11fcom_800D078C( Zako11FWork *work ) +{ + int think; + + switch(work->think3) + { + case 6: + if (s11i_zk11fcom_800D0098(work)) + { + work->think3 = 14; + work->count3 = 0; + } + break; + + case 7: + if (s11i_zk11fcom_800D00EC(work)) + { + work->think3 = 14; + work->count3 = 0; + } + break; + + case 9: + if (s11i_zk11fcom_800D0148(work)) + { + s11i_zk11fcom_800CFB10(work); + work->count3 = 0; + } + break; + + case 10: + if (s11i_zk11fcom_800D020C(work)) + { + work->think3 = 14; + work->count3 = 0; + } + break; + + case 11: + if (s11i_zk11fcom_800D0274(work)) + { + work->think3 = 14; + work->count3 = 0; + } + + if (s11i_zk11fcom_800D0DA8() && work->sn_dis > ZAKO11F_EYE_LENGTH_800C3694 + 2000) + { + s11i_zk11fcom_800CFAF4(work); + } + break; + + case 12: + if (s11i_zk11fcom_800D02BC(work)) + { + work->think3 = 14; + work->count3 = 0; + } + + if (s11i_zk11fcom_800D0DA8() && work->sn_dis > ZAKO11F_EYE_LENGTH_800C3694 + 2000) + { + s11i_zk11fcom_800CFAF4(work); + } + break; + + case 13: + if (s11i_zk11fcom_800D039C(work)) + { + work->think3 = 10; + work->count3 = 0; + } + break; + + case 15: + think = s11i_zk11fcom_800D03E4(work); + if (think != 0) + { + work->think3 = think; + work->count3 = 0; + } + + if (s11i_zk11fcom_800D0DA8() && work->sn_dis > ZAKO11F_EYE_LENGTH_800C3694 + 2000) + { + s11i_zk11fcom_800CFAF4(work); + } + break; + + case 14: + think = s11i_zk11fcom_800D05D4(work); + if (think != 0) + { + work->think3 = think; + work->count3 = 0; + } + + if (work->param.field_B7C == 0xFF) + { + if (work->field_C0C == ZAKOCOM_PlayerAddress_800D5C50 || work->sn_dis <= work->field_BFC) + { + break; + } + } + else if (HZD_InsideZone(work->control.map->hzd, &work->control.mov, work->param.field_B7C)) + { + break; + } + + s11i_zk11fcom_800CFA88(work); + break; + } + + if (work->vision.field_B92 == 2) + { + s11i_zk11fcom_800D0C70(); + work->alert_level = 0xFF; + } +} + +void s11i_zk11fcom_800D098C( Zako11FWork *work ) +{ + int trace; + + switch (work->think3) + { + case 4: + s11i_zk11fcom_800CF9E4(work); + + if (s11i_zk11fcom_800CFF0C(work)) + { + work->think3 = 5; + work->count3 = 0; + } + break; + + case 5: + trace = s11i_zk11fcom_800D0004(work); + if (trace < 0) + { + work->think3 = 4; + work->count3 = 0; + } + else if (trace > 0) + { + work->think2 = 2; + work->think3 = 0xE; + work->count3 = 0; + } + break; + } + + if (work->sn_dis < work->field_BFC && GM_PlayerStance == 0) + { + work->think2 = 2; + work->think3 = 14; + work->count3 = 0; + work->pad.press |= 0x10000; + } + + if (work->vision.field_B92 == 2) + { + work->alert_level = 0xFF; + } + + s11i_zk11fcom_800D0C70(); +} + +void s11i_zk11fcom_800D0A88( Zako11FWork *work ) +{ + if (work->think3 == 16 && s11i_zk11fcom_800CFF0C(work)) + { + work->think2 = 2; + work->think3 = 14; + work->count3 = 0; + } + + if (work->sn_dis < 800 && GM_PlayerStance == 0) + { + work->think2 = 2; + work->think3 = 10; + work->count3 = 0; + work->pad.press |= 0x10000; + } +} + +void s11i_zk11fcom_800D0B1C( Zako11FWork *work ) +{ + switch ( work->think2 ) + { + case 0: + s11i_zk11fcom_800D0674( work ); + break; + case 1: + s11i_zk11fcom_800D098C( work ); + break; + case 2: + s11i_zk11fcom_800D078C( work ); + break; + case 3: + s11i_zk11fcom_800D0A88( work ); + break; + case 4: + s11i_zk11fcom_800D06A4( work ); + break; + } +} + +void Zako11FThink_800D0BAC( Zako11FWork *work ) +{ + work->pad.dir = -1; + work->pad.press = 0; + work->control.radar_atr = RADAR_VISIBLE | RADAR_SIGHT; + work->alert_level = 0xFF; + + if (work->think1 == 0) + { + work->unknown.field_1E = 1; + s11i_zk11fcom_800D0B1C( work ); + } +} diff --git a/components/mgs/mgs/source/animal/zako11f/put.c b/components/mgs/mgs/source/animal/zako11f/put.c new file mode 100644 index 00000000..f0736bc8 --- /dev/null +++ b/components/mgs/mgs/source/animal/zako11f/put.c @@ -0,0 +1,345 @@ +#include "enemy/enemy.h" +#include "anime/animconv/anime.h" +#include "game/item.h" +#include "linkvar.h" +#include "okajima/blood.h" +#include "okajima/bullet.h" + +extern SVECTOR s11i_800C33B4; +extern SVECTOR s11i_800C33BC; +extern SVECTOR s11i_800C33C4; +extern SVECTOR s11i_800C33CC; +extern SVECTOR s11i_800C33F4; +extern SVECTOR s11i_800C33FC; + +extern const char *s11i_dword_800C33D4[3]; +extern void *PutFuncList_800C33E0[5]; + +extern ZAKO11F_COMMAND Zako11FCommand_800D5AF8; + +void AN_Breath( MATRIX *world ); +void NewLSight( SVECTOR *from, SVECTOR *to, int color ); + +int ZAKO11F_ClearPutChar_800CD748( Zako11FWork *work, void *func ); + +void ZAKO11F_PutBlood_800CCFD4(Zako11FWork *work, int unit, int count) +{ + MATRIX world; + + DG_SetPos(&work->body.objs->objs[unit].world); + DG_MovePos(&s11i_800C33B4); + DG_RotatePos(&s11i_800C33BC); + ReadRotMatrix(&world); + NewBlood(&world, count); +} + +void ZAKO11F_PutFog_800CD044(Zako11FWork *work) +{ + MATRIX world; + SVECTOR pos; + + DG_SetPos( &work->body.objs->objs[1].world ); + DG_MovePos( &s11i_800C33C4 ); + DG_RotatePos( &s11i_800C33CC ); + ReadRotMatrix( &world ); + + pos.vx = world.t[ 0 ]; + pos.vy = world.t[ 1 ]; + pos.vz = world.t[ 2 ]; + + AN_Fog( &pos ); +} + +void ZAKO11F_PutItem_800CD0A8( Zako11FWork *work ) +{ + int rand; + SVECTOR svec; + CONTROL *ctrl; + Item_Info item; + + svec = work->unknown.field_0C; + rand = 10; + ctrl = &work->control; + svec.vx += GV_RandU( rand ); + svec.vy += 100; + rand = GV_RandU( rand ); + svec.vz += rand; + rand = GV_RandU( 4 ); + + switch ( rand ) + { + case 0: + case 1: + item.type = 4; + item.id = IT_Ration; + item.num = 1; + item.message = s11i_dword_800C33D4[0]; + break; + case 2: + //socom + if ( GM_SocomFlag < 0 ) + { + item.type = 4; + item.id = IT_Ration; + item.num = 1; + item.message = s11i_dword_800C33D4[0]; + } + else + { + //Socom bullets + item.type = 2; + item.id = WP_Socom; + item.num = 12; + item.message = s11i_dword_800C33D4[1]; + } + break; + case 3: + //famas + if ( GM_FamasFlag < 0 ) + { + item.type = 4; + item.id = IT_Ration; + item.num = 1; + item.message = s11i_dword_800C33D4[0]; + } + else + { + item.id = WP_Famas; + item.type = 2; + item.num = 25; + item.message = s11i_dword_800C33D4[2]; + } + break; + } + item.time = 900; + NewItemPut( &ctrl->mov, &svec, &item ); +} + +void ZAKO11F_PutMark_800CD224( Zako11FWork *work, int mark ) +{ + MATRIX *world; + + if (!(work->control.map->index & GM_PlayerMap)) + { + return; + } + + world = &work->body.objs->objs[6].world; + + if (mark == 0) + { + GM_SeSet(&work->control.mov, SE_EXCLAMATION); + } + + if (work->mark_time != 0) + { + GV_DestroyActor(work->mark); + } + + work->mark = AN_Unknown_800CA1EC(world, mark); + work->mark_time = 30; +} + +void ZAKO11F_PutSound_800CD2C0( Zako11FWork *work ) +{ + int action; + int frame; + int step1, step2; + + action = work->unknown.last_set; + frame = work->m_ctrl.info1.frame; + step1 = 160 + (work->param.index % 4) * 2; + step2 = 160 + (work->param.index % 4) * 2 + 1; + + if (action == ACTION1) + { + if (frame == 22) + { + GM_SeSet(&work->control.mov, step2); + } + else if (frame == 11) + { + GM_SeSet(&work->control.mov, step1); + } + } + else if (action == ACTION2) + { + if (frame == 16) + { + GM_SeSet(&work->control.mov, step2); + } + else if (frame == 8) + { + GM_SeSet(&work->control.mov, step1); + } + } +} + +void ZAKO11F_PutBreath_800CD358( Zako11FWork *work ) +{ + int frame; + + if ( Zako11FCommand_800D5AF8.mode == TOP_COMM_ALERT ) + { + return; + } + + if ( !( work->control.map->index & GM_PlayerMap ) ) + { + return; + } + + if ( work->unknown.last_unset == ACTION20 ) + { + frame = work->m_ctrl.info2.frame; + if ( frame == 31 ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } + else if ( work->unknown.last_unset == ACTION22 ) + { + frame = work->m_ctrl.info2.frame; + if ( ( frame == 15 ) || ( frame == 35 ) || ( frame == 50 ) || ( frame == 60 ) || + ( frame == 70 ) || ( frame == 74 ) || ( frame == 78 ) ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } + else if ( work->unknown.last_unset == ACTION19 ) + { + frame = work->m_ctrl.info2.frame; + if ( ( frame == 30 ) || ( frame == 40 ) || ( frame == 50 ) || ( frame == 60 ) || + ( frame == 70 ) || ( frame == 80 ) || ( frame == 90 ) || ( frame == 95 ) || + ( frame == 100 ) || ( frame == 105 ) ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } + else + { + if ( ( GV_Time % 64 ) == ( work->param.index * 16 ) ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } +} + +void ZAKO11F_PutLSight_800CD4A0( Zako11FWork *work ) +{ + if ( work->vision.field_B92 == 2 ) + { + NewLSight( &GM_PlayerPosition, &work->control.mov, 0x00008F ); + } +} + +void ZAKO11F_PutBulletEx_800CD4DC( Zako11FWork *work ) +{ + SVECTOR rot; + MATRIX local; + MATRIX *world; + + rot = DG_ZeroVector; + rot.vz = GV_RandU( 128 ); + + world = &work->body.objs->objs[4].world; + + DG_SetPos( world ); + DG_MovePos( &s11i_800C33F4 ); + DG_RotatePos( &rot ); + + rot.vx = GV_RandS( 16 ) + 1024; + rot.vz = 0; + + DG_RotatePos( &rot ); + ReadRotMatrix( &local ); + + if ( GV_Time & 3 ) + { + NewBulletEx(BULLET_RECOILSPARK, &local, ENEMY_SIDE, 1, 0, 0xA, work->field_B84, 10000, 750); + } + else + { + NewBulletEx(BULLET_RECOILSPARK | BULLET_NO_HZD, &local, ENEMY_SIDE, 1, 0, 0xA, work->field_B84, 10000, 750); + } + + GM_SeSetMode( &work->control.mov, SE_ENEMY_SHOT, GM_SEMODE_BOMB ); + + AN_BulletSmoke( world, 0 ); + AN_FamasFlash( &local ); + + ZAKO11F_ClearPutChar_800CD748( work, ZAKO11F_PutBulletEx_800CD4DC ); +} + +void ZAKO11F_PutBulletEx2_800CD618( Zako11FWork *work ) +{ + SVECTOR rot; + MATRIX local; + MATRIX *world; + + rot.vx = 1024; + rot.vy = 0; + rot.vz = GV_RandS( 16 ); + + world = &work->body.objs->objs[4].world; + + DG_SetPos( world ); + DG_MovePos( &s11i_800C33FC ); + DG_RotatePos( &rot ); + ReadRotMatrix( &local ); + + NewBulletEx(BULLET_FLASH | BULLET_RECOILSPARK | BULLET_BLAST, &local, ENEMY_SIDE, 1, 0, 0x1E, work->field_B86, 40000, 1500); + + GM_SeSetMode( &work->control.mov, SE_ENEMY_SHOT, GM_SEMODE_BOMB ); + + AN_BulletSmoke( world, 0 ); + AN_FamasFlash( &local ); + + ZAKO11F_ClearPutChar_800CD748( work, ZAKO11F_PutBulletEx2_800CD618 ); +} + +int ZAKO11F_SetPutChar_800CD700( Zako11FWork *work, int idx ) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( work->field_B00[ i ] == NULL ) + { + work->field_B00[ i ] = PutFuncList_800C33E0[ idx ]; + return 1; + } + } + + return 0; +} + +int ZAKO11F_ClearPutChar_800CD748( Zako11FWork *work, void *func ) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( work->field_B00[ i ] == func ) + { + work->field_B00[ i ] = NULL; + return 1; + } + } + + return 0; +} + +void ZAKO11F_ExecPutChar_800CD77C( Zako11FWork *work ) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( work->field_B00[i] ) + { + ZAKO11FPUTFUNC func = work->field_B00[i]; + func( work ); + } + } +} diff --git a/components/mgs/mgs/source/animal/zako11f/zako11f.c b/components/mgs/mgs/source/animal/zako11f/zako11f.c new file mode 100644 index 00000000..62c3a2ba --- /dev/null +++ b/components/mgs/mgs/source/animal/zako11f/zako11f.c @@ -0,0 +1,571 @@ +#include "enemy/enemy.h" +#include +#include "mts/mts.h" // for fprintf +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "libhzd/libhzd.h" +#include "strcode.h" + +#include "chara/snake/shadow.h" // for NewShadow2 +#include "enemy/glight.h" // for NewGunLight + +void RootFlagCheck_800C86F0( Zako11FWork *work ) +{ + /* do nothing */ +} + +void s11i_zako11f_800C86F8( DG_OBJS* objs, DG_DEF* def ) +{ + int i; + DG_OBJ *obj; + DG_FreeObjsPacket( objs, 0 ); + DG_FreeObjsPacket( objs, 1 ); + + objs->def = def; + obj = objs->objs; + for ( i = 0 ; i < 16 ; i++ ) + { + obj->model = &def->model[ i ]; + obj->n_packs = def->model[ i ].n_faces; + obj++; + } +} + +extern GM_CameraSystemWork GM_Camera; + +void s11i_zako11f_800C8774( Zako11FWork *work ) +{ + if ( work->visible ) + { + if ( work->param.low_poly == 1 ) + { + if ( GM_GameStatus & (GAME_FLAG_BIT_07 | STATE_BEHIND_CAMERA) || GM_Camera.first_person ) + { + if ( work->has_kmd != work->param.low_poly ) + { + work->has_kmd = work->param.low_poly; + s11i_zako11f_800C86F8( work->body.objs, work->def ); + } + } + else if ( work->has_kmd ) + { + work->has_kmd = 0; + s11i_zako11f_800C86F8( work->body.objs, work->kmd ); + } + } + DG_VisibleObjs( work->body.objs ); + DG_VisibleObjs( work->weapon.objs ); + *work->shadow_enable = 1; + *work->glight_enable = 1; + } + else + { + DG_InvisibleObjs( work->body.objs ); + DG_InvisibleObjs( work->weapon.objs ); + *work->shadow_enable = 0; + *work->glight_enable = 0; + } +} + +extern int ZAKO11F_GameFlag_800D5C4C; +extern ZAKO11F_COMMAND Zako11FCommand_800D5AF8; + +extern void Zako11FPushMove_800CDFAC( Zako11FWork *work ); +extern void Zako11FActionMain_800CDF40( Zako11FWork *work ); + +void ZAKO11FAct_800C88AC( Zako11FWork *work ) +{ + VECTOR vec; + TARGET *trgt; + CONTROL *ctrl; + TARGET *trgt2; + + ctrl = &( work->control ) ; + if (GM_CheckMessage( &( work->actor ) , ctrl->name, HASH_KILL ) ) + { + GV_DestroyActor( &( work->actor ) ); + return; + } + + RootFlagCheck_800C86F0( work ); + if ( !work->faseout ) + { + Zako11FPushMove_800CDFAC( work ); + GM_ActControl( ctrl ); + GM_ActObject2( &( work->body ) ); + GM_ActObject2( &( work->weapon ) ); + + DG_GetLightMatrix2( &( ctrl->mov ), work->light ); + + Zako11FActionMain_800CDF40( work ); + trgt = work->target; + GM_MoveTarget( trgt, &( ctrl->mov ) ); + + GM_PushTarget( trgt ); + + if ( trgt->class & TARGET_TOUCH ) + { + trgt2 = &work->field_94C; + if ( trgt2->class & TARGET_TOUCH ) + { + if ( trgt2->damaged & TARGET_TOUCH ) + { + trgt2->damaged &= ~TARGET_TOUCH; + } + GM_MoveTarget( &( work->field_94C ), &( ctrl->mov ) ); + GM_TouchTarget( &( work->field_94C ) ); + } + } + + vec.vx = vec.vy = vec.vz = work->scale; + ScaleMatrix( &( work->body.objs->world ), &vec ); + } + + s11i_zako11f_800C8774( work ); + + if ( ZAKO11F_GameFlag_800D5C4C == 0xF && Zako11FCommand_800D5AF8.watchers[work->param.index].field_04 == 1 ) + { + GV_DestroyActor( &( work->actor ) ); + } +} + +extern SVECTOR ZAKO11F_TARGET_SIZE_800C365C; +extern SVECTOR ZAKO11F_TARGET_FORCE_800C3664; +extern SVECTOR ZAKO11F_ATTACK_SIZE_800C366C; +extern SVECTOR ZAKO11F_ATTACK_FORCE_800C3674; +extern SVECTOR ZAKO11F_TOUCH_SIZE_800C367C; +extern SVECTOR ZAKO11F_TOUCH_FORCE_800C3684; +extern SVECTOR ZAKO11F_NO_POINT_800C368C; + +extern int ZAKO11F_EYE_LENGTH_800C3694; + +void InitTarget_800C8A10( Zako11FWork *work ) +{ + TARGET *target; + TARGET *target2; + int life; + int faint; + + target = work->target; + life = work->param.life; + faint = work->param.faint; + + GM_SetTarget( target, TARGET_FLAG, ENEMY_SIDE, &ZAKO11F_TARGET_SIZE_800C365C ); + GM_SetPowerTarget( target, POWER_DECREASE, -1, life, faint, &ZAKO11F_TARGET_FORCE_800C3664 ); + GM_SetCaptureTarget( target, -1, faint, NULL, NULL); + + GM_TargetBody( target, &( work->body.objs->objs[1].world ) ); + + target2 = &work->field_904; + GM_SetTarget( target2, TARGET_POWER, PLAYER_SIDE, &ZAKO11F_ATTACK_SIZE_800C366C ); + GM_SetPowerTarget( target2, POWER_CONST | POWER_EXPLODE, 5, 0, 3, &ZAKO11F_ATTACK_FORCE_800C3674 ); + + target2 = &work->field_94C; + GM_SetTarget( target2, ( TARGET_TOUCH ), ENEMY_SIDE, &ZAKO11F_TOUCH_SIZE_800C367C ); + GM_SetPowerTarget( target2, POWER_CONST | POWER_EXPLODE, 5, 0, 0, &ZAKO11F_TOUCH_FORCE_800C3684 ); +} + +void s11i_zako11f_800C8B3C( Zako11FWork* work ) +{ + WatcherUnk *s; + s = &work->unknown; + + GV_ZeroMemory(s, 0x24); + s->field_00 = 0; + s->field_1C = 450; + s->field_1E = 1; + + work->action = 0; + work->action2 = 0; + work->time = 0; + work->time2 = 0; +} + +extern void *NewKogaku2(CONTROL *pCtrl, OBJECT *pObj, int unit); +extern void ZAKO11F_SetPutChar_800CD700( Zako11FWork *work, int put ); + +int s11i_zako11f_800C8B98( Zako11FWork* work, int name, int where ) +{ + int i; + int has_kmd; + char *opt, *opt2; + CONTROL *ctrl; + OBJECT *body; + OBJECT *arm; //? + SVECTOR shadow; + + ctrl = &work->control; + if ( GM_InitControl( ctrl, name, where ) < 0 ) return -1; + + opt = GCL_GetOption( 'p' ); + + GM_ConfigControlString( ctrl, (char*)opt, (char*)GCL_GetOption( 'd' ) ) ; + GM_ConfigControlAttribute( ctrl, 13 ); + GM_ConfigControlInterp( ctrl, 4 ); + + ctrl->seg_flag = 2; + + GM_ConfigControlTrapCheck( ctrl ); + + body = &work->body; + arm = &work->weapon; + + GM_InitObject( body, KMD_IPPANHEI, 0x32D, OAR_ENEMY ) ; + GM_ConfigObjectJoint( body ) ; + GM_ConfigMotionControl( body, &work->m_ctrl, OAR_ENEMY, work->m_segs1, work->m_segs2, ctrl, work->rots ); + GM_ConfigObjectLight( body, work->light ); + + work->param.low_poly = 0; + + opt2 = GCL_GetOption( 'y' ); + if ( opt2 ) { + work->param.low_poly = GCL_StrToInt( (char*)opt2 ); + } + + has_kmd = work->param.low_poly; + if ( has_kmd == 1 ) + { + work->def = body->objs->def; + work->kmd = GV_GetCache( GV_CacheID( HASH_LOPRYHEI, 'k' ) ); + work->has_kmd = has_kmd; + } + + work->hom = GM_AllocHomingTarget( &body->objs->objs[6].world, ctrl ); + GM_InitObject( arm, KMD_FAMAS, 0x6D, 0 ); + GM_ConfigObjectLight( arm, work->light ) ; + GM_ConfigObjectRoot( arm, body, 4 ); + + for ( i = 0 ; i < 0 ; i++ ) + { + work->field_B00[i] = 0; + } + shadow.vy = 6; + shadow.vz = 12; + shadow.pad = 15; + shadow.vx = 0; + + work->shadow = NewShadow2( ctrl, body, shadow, &work->shadow_enable ) ; + work->glight = NewGunLight( &( body->objs->objs[4].world ), &work->glight_enable ) ; + + ZAKO11F_SetPutChar_800CD700( work, 0 ); + s11i_zako11f_800C8B3C( work ); + + return 0; +} + +extern void s11i_zk11fcom_800D0C38( int ); + +void s11i_zako11f_800C8DB8( Zako11FWork* work ) +{ + s11i_zk11fcom_800D0C38( work->param.index ); + GM_FreeControl( &( work->control ) ); + GM_FreeObject( &( work->body ) ); + GM_FreeObject( &( work->weapon ) ); + GM_FreeTarget( work->target ); + GV_DestroyActor( work->glight ); + GV_DestroyActor( work->shadow ); + GM_FreeHomingTarget( work->hom ); +} + +void ZAKO11FDie_800C8E2C( Zako11FWork* work ) +{ + s11i_zako11f_800C8DB8( work ); +} + +int ReadNodes_800C8E4C( Zako11FWork* work ) +{ + int i; + HZD_PAT *patrol; + HZD_PTP *points; + + patrol = work->control.map->hzd->def->routes; + patrol = &patrol[ work->param.root ]; + + work->n_nodes = patrol->n_points; + + if ( work->n_nodes <= 0 ) return -1; + + points = patrol->points; + for ( i = 0 ; i < work->n_nodes ; i++ ) + { + work->nodes[i].vx = points->x; + work->nodes[i].vy = points->y; + work->nodes[i].vz = points->z; + work->nodes[i].pad = points->command; + points++; + } + return 0; +} + +int s11i_zako11f_800C8EE8( char *opt, short* s ) +{ + int i; + unsigned char *res; + + for ( i = 0 ; ( res = GCL_NextStr() ) ; i++ ) + { + *s = GCL_StrToInt( res ); + s++; + } + + return i; +} + +int s11i_zako11f_800C8F40( char *opt, int* l ) +{ + int i; + unsigned char *res; + + for ( i = 0 ; ( res = GCL_NextStr() ) ; i++ ) + { + *l = GCL_StrToInt( res ); + l++; + } + + return i; +} + +int s11i_dword_800C32F0[8] = +{ + 3000, 4000, 4000, 8000, 8000, 8000, 8000, 8000 +}; + +unsigned short s11i_dword_800C3310[8] = +{ + 300, 15, 30, 60, 90, 0, 32001, 30000 +}; + +int s11i_zako11f_800C8F98( Zako11FWork *work ) +{ + int i; + char *opt; + int ret; + + for ( i = 0 ; i < 8 ; i++ ) + { + work->field_BB0[i] = s11i_dword_800C3310[i]; + } + + opt = GCL_GetOption( 't' ); + if ( opt ) + { + ret = s11i_zako11f_800C8F40( opt, &work->field_BB0[1] ); + if ( ret > 4 ) + { + printf( "Err Err Err Set time Over\n" ) ; + return -1; + } + } + + for ( i = 0 ; i < 4 ; i++ ) + { + work->field_BD0[i] = i * 1024; + } + + opt = GCL_GetOption( 'i' ); + + if ( opt ) + { + ret = s11i_zako11f_800C8EE8( opt, work->field_BD0 ); + if ( ret > 4 ) + { + printf( "Err Err Err Set Dir Over\n" ) ; + return -1; + } + } + + return 0; +} + +extern int s11i_zk11fcom_800D0BF4( Zako11FWork *work ); + +void Zako11FGetResources_800C9070( Zako11FWork *work, int name, int where ) +{ + int addr; + char *opt; + int i; + SVECTOR svec; + + s11i_zako11f_800C8B98( work, name, where ) ; + work->param.index = s11i_zk11fcom_800D0BF4( work ) ; + + if ( work->param.index << 24 < 0 ) + { + printf( "Err not enough work !!\n" ) ; + } + + s11i_zako11f_800C8F98( work ) ; + + //root + work->param.root = 0; + opt = GCL_GetOption( 'r' ); + if ( opt ) + { + work->param.root = GCL_StrToInt( ( char* )opt ); + } + + //life + work->param.life = 384; + opt = GCL_GetOption( 'l' ); + if ( opt ) + { + work->param.life = GCL_StrToInt( ( char* )opt ); + } + + //faint + work->param.faint = 7; + opt = GCL_GetOption( 'f' ); + if ( opt ) + { + work->param.faint = GCL_StrToInt( ( char* )opt ); + } + + work->field_B84 = 64; + opt = GCL_GetOption( 'z' ); + if ( opt ) + { + work->field_B84 = GCL_StrToInt( ( char* )opt ); + } + + work->field_B86 = 96; + opt = GCL_GetOption( 'w' ); + if ( opt ) + { + work->field_B86 = GCL_StrToInt( ( char* )opt ); + } + + + work->param.blood = 65; + opt = GCL_GetOption( 'b' ); + if ( opt ) + { + work->param.blood = GCL_StrToInt( ( char* )opt ); + } + + printf( " low[pory=%d\n", work->param.low_poly ) ; + work->param.field_B81 = 0xFF; + + opt = GCL_GetOption('g'); + if ( opt ) + { + work->param.field_B81 = GCL_StrToInt( ( char* )opt ); + } + if ( work->param.blood == 'Z' ) + { + work->param.field_B81 = 0; + } + + work->field_BFC = s11i_dword_800C32F0[ work->param.index ]; + work->field_C00 = work->param.index; + work->param.field_B7C = 0xFF; + + opt = GCL_GetOption( 'n' ); + if ( opt ) + { + GCL_StrToSV( ( char* )opt, (short *)&svec ); + if ( svec.vy < 0x7530 ) + { + work->param.field_B7C = HZD_GetAddress( work->control.map->hzd, &svec, -1 ); + } + else + { + work->param.field_B7C = 0xFF; + } + } + + work->param.area = 'A'; + opt = GCL_GetOption( 'a' ); + if (opt != 0) + { + work->param.area = GCL_StrToInt( ( char* )opt ); + } + + if ( work->param.area == 'S' ) ZAKO11F_SetPutChar_800CD700( work, PUTBREATH ) ; /* 白い息はく */ + work->scale = 4096 ; /* スケール */ + + if ( ( opt = GCL_GetOption( 's' ) ) != NULL ) work->scale += GCL_StrToInt( ( char* )opt ); + work->param.item = 1; + + //fprintf(0,"Life=%d Faint=%d Blood=%c Area=%c \n", + // work->param.life, work->param.faint, work->param.blood,work->param.area); + + if( ReadNodes_800C8E4C( work ) < 0 ) fprintf( 1, "watchar.c : action point Err\n" ); + + /* + 当たりデータを初期化する + */ + if( ( work->target = GM_AllocTarget() ) != NULL ) InitTarget_800C8A10( work ) ; + + /* + 思考ルーチン用データを初期化する + */ + work->think1 = 0 ; + work->think2 = 0 ; + work->think3 = 0 ; + work->think4 = 0 ; + work->count3 = 0 ; + work->l_count = 0 ; + work->t_count = 0 ; + work->mark_time = 0 ; + work->next_node = 0 ; + work->search_flag = 0 ; + work->act_status = 0 ; + work->target_pos = work->nodes[ 0 ] ; + work->target_addr = HZD_GetAddress( work->control.map->hzd, &( work->target_pos ), -1 ) ; + work->target_map = GM_CurrentMap; + work->alert_level = 0; + work->visible = 1; + work->vision.angle = 0x200; + work->vision.facedir = 0; + work->field_B94 = 0; + work->pad.sound = 0; + work->pad.time = 0; + work->vision.length = ZAKO11F_EYE_LENGTH_800C3694; + work->field_BA4 = ZAKO11F_NO_POINT_800C368C; + work->subweapon = NULL; + + work->control.mov = work->nodes[ 0 ] ; + work->field_C40 = 0; + work->param.c_root = work->param.root; + work->param.field_B7F = work->param.field_B7C; + + for ( i = 0 ; i <= 7 ; i++ ) + { + work->modetime[i] = 0; + } + + work->modetime[7] = 7; + work->modetime[4] = -1; + + opt = GCL_GetOption( 'c' ); + if ( opt ) + { + printf( "asiatoooo" ); + work->modetime[7] |= 0x10; + } + + GM_ConfigControlRadarparam( &work->control , 0, 0x200, ZAKO11F_EYE_LENGTH_800C3694, 0 ); + work->start_pos = work->nodes[ 0 ] ; + work->start_map = GM_CurrentMap; + addr = HZD_GetAddress( work->control.map->hzd, &( work->control.mov ), -1 ); + + work->start_addr = addr; + work->field_C10 = addr; + work->field_BF0 = addr; + + work->field_C1C = work->start_pos; +} + +#define EXEC_LEVEL GV_ACTOR_PREV + +void *NewZako11F( int name, int where, int argc, char **argv ) +{ + Zako11FWork *work ; + + work = GV_NewActor( EXEC_LEVEL, sizeof( Zako11FWork ) ) ; + if ( work != NULL ) { + GV_SetNamedActor( &( work->actor ), ZAKO11FAct_800C88AC, ZAKO11FDie_800C8E2C, "zako11f.c" ); + Zako11FGetResources_800C9070( work, name, where ); + } + return (void *)work ; +} \ No newline at end of file diff --git a/components/mgs/mgs/source/animal/zako11f/zk11fact.c b/components/mgs/mgs/source/animal/zako11f/zk11fact.c new file mode 100644 index 00000000..175ba6c3 --- /dev/null +++ b/components/mgs/mgs/source/animal/zako11f/zk11fact.c @@ -0,0 +1,383 @@ +#include "enemy/enemy.h" + +#define SEGMENT_ATR ( HZD_SEG_NO_PLAYER ) + +extern CONTROL *GM_WhereList[96]; + +extern int ZAKO11F_EYE_LENGTH_800C3694; +extern ZAKO11F_COMMAND Zako11FCommand_800D5AF8; + +// in enemy/asiato.c +extern int AsiatoCheck( HZD_HDL *, SVECTOR * ); +extern int SearchNearAsiato( HZD_HDL *, SVECTOR *, short, short, short ); + +void s11i_asiato_800CC39C( Zako11FWork *work ); +void Zako11FThink_800D0BAC( Zako11FWork *work ); +void ZAKO11F_ExecPutChar_800CD77C( Zako11FWork *work ); + +void s11i_asiato_800CD7DC(Zako11FWork *work) +{ + VISION *vision; + RADAR_SIGHT_PARAM *r_param; + + vision = &work->vision; + r_param = &work->control.radar_param; + + r_param->dir = vision->facedir; + r_param->dis = vision->length; + r_param->range = vision->angle * 2; + r_param->r = 0; +} + +void s11i_asiato_800CD808(Zako11FWork *work) +{ + if (work->modetime[7] & 0x4) + { + if (work->target->damaged & (TARGET_POWER | TARGET_CAPTURE)) + { + work->modetime[6] |= 0x4; + } + else if (work->field_94C.damaged & TARGET_TOUCH) + { + if ((GM_PlayerStatus & (PLAYER_CB_BOX | PLAYER_MOVE)) != PLAYER_CB_BOX) + { + work->modetime[6] |= 0xC; + } + } + } +} + +int s11i_asiato_800CD88C(HZD_HDL *hzd, SVECTOR *from, SVECTOR *to) +{ + int to_addr, from_addr; + + from_addr = HZD_GetAddress(hzd, from, -1); + to_addr = HZD_GetAddress(hzd, to, -1); + + from_addr &= 0xFF; + to_addr &= 0xFF; + + return HZD_ZoneDistance(hzd, from_addr, to_addr); +} + +void s11i_asiato_800CD8EC(Zako11FWork *work) +{ + CONTROL *control; + + if ( !( work->modetime[7] & 0x1 ) ) + { + return; + } + + if ( work->act_status & 0x68 ) + { + return; + } + + control = &work->control; + + if ( !( control->map->index & GM_PlayerMap ) ) + { + return; + } + + if ( GM_NoisePower == 0 ) + { + return; + } + + switch ( GM_NoisePower ) + { + case 5: + if ( GV_DiffVec3( &GM_NoisePosition, &control->mov ) < 500 ) + { + break; + } + return; + + case 200: + if ( GV_DiffVec3( &GM_NoisePosition, &control->mov ) < 8000 ) + { + break; + } + return; + + case 255: + break; + + case 100: + if ( GV_DiffVec3( &GM_NoisePosition, &control->mov ) < 8000 && + s11i_asiato_800CD88C( control->map->hzd, &control->mov, &GM_NoisePosition ) < 300 ) + { + work->modetime[6] |= 1; + GM_NoiseLength = 0; + GM_NoisePower = 0; + } + return; + + default: + return; + } + + work->modetime[6] |= 1; +} + +void s11i_asiato_800CDA6C( Zako11FWork *work ) +{ + if ( !( work->modetime[7] & 0x2 ) ) + { + return; + } + + if (work->vision.field_B92 != 2) + { + return; + } + + if ( !( GM_PlayerStatus & PLAYER_CB_BOX ) ) + { + return; + } + + if ( ( GV_DiffVec3( &work->field_BA4, &GM_PlayerPosition ) > 50 ) || ( work->field_BAC != GM_WhereList[0]->rot.vy ) ) + { + if ( Zako11FCommand_800D5AF8.mode == TOP_COMM_ALERT ) + { + return; + } + + work->field_BA4 = GM_PlayerPosition; + work->field_BAC = GM_WhereList[0]->rot.vy; + work->modetime[6] |= 0x2; + } + else if ( GV_DiffVec3( &work->control.mov, &GM_PlayerPosition ) < 1500 ) + { + work->modetime[6] |= 0x40; + } + + work->vision.field_B92 = 0; + work->modetime[6] |= 0x80; +} + +void s11i_asiato_800CDB94( Zako11FWork *work ) +{ + HZD_HDL *hzd; + CONTROL *control; + VISION *vision; + + if ( !( work->modetime[7] & 0x10 ) ) + { + return; + } + + control = &work->control; + if ( work->think2 == 5 ) + { + return; + } + + hzd = work->control.map->hzd; + vision = &work->vision; + if ( !( AsiatoCheck( hzd, &control->mov ) ) ) + { + return; + } + + if ( SearchNearAsiato( hzd, &control->mov, vision->facedir, vision->angle, vision->length ) < 0 ) + { + return; + } + + work->modetime[6] |= 0x10; +} + +void s11i_asiato_800CDC40( Zako11FWork *work ) +{ + work->modetime[6] = 0; + s11i_asiato_800CD8EC( work ); + + if ( Zako11FCommand_800D5AF8.mode != TOP_COMM_ALERT ) + { + s11i_asiato_800CD808( work ); + s11i_asiato_800CDA6C( work ); + s11i_asiato_800CDB94( work ); + } +} + +void s11i_asiato_800CDC94( Zako11FWork *work ) +{ + switch ( work->vision.field_B92 ) + { + case 0: + if ( work->field_B94 != 2 || !( GM_PlayerStatus & PLAYER_INTRUDE ) || work->vision.length == 0 ) + { + work->field_B94 = 0; + work->alert_level -= 4; + } + break; + + case 1: + work->field_B94 = 1; + work->alert_level--; + break; + + case 2: + work->field_B94 = 2; + work->alert_level++; + break; + } + + if ( work->alert_level < 0 ) + { + work->alert_level = 0; + } + else if ( work->alert_level > 255 ) + { + work->alert_level = 255; + } +} + +void s11i_asiato_800CDD64( Zako11FWork *work ) +{ + SVECTOR diff; + SVECTOR *mov; + SVECTOR *pos; + int dir, dis; + int height; + MAP *map; + + mov = &work->control.mov; + pos = &GM_PlayerPosition; + GV_SubVec3( pos, mov, &diff ); + + dir = ratan2( pos->vx - work->control.mov.vx, pos->vz - work->control.mov.vz ) & 0xfff; + work->sn_dir = dir; + dis = GV_VecLen3( &diff ); + + work->sn_dis = dis; + + height = pos->vy - work->control.mov.vy; + if ( height < 0 ) + { + height = work->control.mov.vy - pos->vy; + } + + if ( !( work->control.map->index & GM_PlayerMap ) || GM_PlayerStatus & PLAYER_INTRUDE ) + { + work->vision.field_B92 = 0; + return; + } + + if ( Zako11FCommand_800D5AF8.field_0x0C % Zako11FCommand_800D5AF8.n_watchers == work->param.index ) + { + if ( ZAKO11F_EYE_LENGTH_800C3694 + 2000 < dis ) + { + work->vision.field_B92 = 0; + return; + } + + if ( height > 2000 ) + { + work->vision.field_B92 = 0; + return; + } + + if ( dis >= 500 && GV_DiffDirAbs( work->vision.facedir, dir ) >= work->vision.angle ) + { + work->vision.field_B92 = 0; + return; + } + + map = work->control.map; + if ( HZD_OnlineHazardCheck( map->hzd, pos, mov, HZD_CHK_ALL, SEGMENT_ATR ) ) + { + work->vision.field_B92 = 0; + return; + } + + if ( !( GM_OnlineTargetCheckAny( mov, pos, map->index, &diff ) ) ) + { + if ( work->vision.length < dis ) + { + work->vision.field_B92 = 1; + } + else + { + work->vision.field_B92 = 2; + } + } + else + { + work->vision.field_B92 = 0; + } + } +} + +void Zako11FActionMain_800CDF40( Zako11FWork *work ) +{ + if ( work->unknown.last_set <= ACTION48 ) + { + s11i_asiato_800CD7DC( work ); + s11i_asiato_800CDD64( work ); + s11i_asiato_800CDC40( work ); + s11i_asiato_800CDC94( work ); + Zako11FThink_800D0BAC( work ); + ZAKO11F_ExecPutChar_800CD77C( work ); + } + + s11i_asiato_800CC39C( work ); +} + +void Zako11FPushMove_800CDFAC( Zako11FWork *work ) +{ + TARGET *target; + int ang; + int set; + CONTROL *control; + + target = work->target; + ang = 0; + + if ( !( target->damaged & TARGET_PUSH ) ) + { + return; + } + + GV_AddVec3( &target->offset, &work->control.step, &work->control.step ); + target->damaged &= ~TARGET_PUSH; + + set = work->unknown.last_set; + if ( set != ACTION1 && set != ACTION2 ) + { + return; + } + + if ( target->offset.pad ) + { + if ( GV_Time & 256 ) + { + ang = target->offset.pad * 1024; + if ( !( work->param.index & 1 ) ) + { + ang = ( target->offset.pad + 2 ) * 1024; + } + } + else + { + if ( work->param.index & 1 ) + { + ang = ( target->offset.pad + 2 ) * 1024; + } + else + { + ang = target->offset.pad * 1024; + } + } + + ang &= 0xFFF; + } + + control = &work->control; + control->turn.vy = ang; + control->r_sphere = GV_NearExp2( control->r_sphere, work->unknown.field_1C ); +} diff --git a/components/mgs/mgs/source/animal/zako11f/zk11fcom.c b/components/mgs/mgs/source/animal/zako11f/zk11fcom.c new file mode 100644 index 00000000..a4c55a7e --- /dev/null +++ b/components/mgs/mgs/source/animal/zako11f/zk11fcom.c @@ -0,0 +1,992 @@ +#include + +#include "enemy/enemy.h" +#include "mts/mts.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "strcode.h" + +typedef struct _Zako11FCommanderWork +{ + GV_ACT actor; + int name; + char pad[0x4]; +} Zako11FCommanderWork; + +extern int GM_GameOverTimer; + +extern CONTROL *GM_WhereList[96]; + +extern int s11i_dword_800C3698; +extern SVECTOR ZAKOCOM_PlayerPosition_800D5AF0; +extern ZAKO11F_COMMAND Zako11FCommand_800D5AF8; +extern TOPCOMMAND_STRUCT TOPCOMMAND_800D5C40; +extern int s11i_dword_800D5C48; +extern int ZAKO11F_GameFlag_800D5C4C; +extern int ZAKOCOM_PlayerAddress_800D5C50; +extern int ZAKOCOM_PlayerMap_800D5C54; + +#define EXEC_LEVEL GV_ACTOR_PREV + +int s11i_zk11fcom_800D0BF4( Zako11FWork *watcher ) // SetZako +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( !Zako11FCommand_800D5AF8.watchers[ i ].watcher ) + { + Zako11FCommand_800D5AF8.watchers[ i ].watcher = watcher; + Zako11FCommand_800D5AF8.watchers[ i ].field_04 = 2; + return i; + } + } + + return -1; +} + +void s11i_zk11fcom_800D0C38( int index ) // ClearZako +{ + Zako11FCommand_800D5AF8.watchers[index].watcher = NULL; + Zako11FCommand_800D5AF8.watchers[index].field_04 = 1; +} + +void s11i_zk11fcom_800D0C58( int alert ) +{ + TOPCOMMAND_800D5C40.alert = alert; +} + +void s11i_zk11fcom_800D0C64( int mode ) +{ + TOPCOMMAND_800D5C40.mode = mode; +} + +void s11i_zk11fcom_800D0C70( void ) +{ + Zako11FCommand_800D5AF8.com_addr = ZAKOCOM_PlayerAddress_800D5C50; + Zako11FCommand_800D5AF8.com_pos = ZAKOCOM_PlayerPosition_800D5AF0; + Zako11FCommand_800D5AF8.com_map = ZAKOCOM_PlayerMap_800D5C54; +} + +void s11i_zk11fcom_800D0CBC(void) +{ + Zako11FCommand_800D5AF8.com_addr = HZD_GetAddress( GM_WhereList[ 0 ]->map->hzd, &GM_NoisePosition, -1 ); + Zako11FCommand_800D5AF8.com_pos = GM_NoisePosition; + Zako11FCommand_800D5AF8.com_map = GM_PlayerMap; +} + +int s11i_zk11fcom_800D0D34( int map_id, int val ) +{ + A4_STRUCT *unk = &Zako11FCommand_800D5AF8.field_0x68; + int i; + + if ( unk->map_id == map_id ) + { + for ( i = 0 ; i < unk->n_entry ; i++ ) + { + if ( unk->field_04[ i ].field_00 == val ) + { + return unk->field_04[ i ].field_02; + } + } + + goto not_found; + } + + return 0; + +not_found: + return -1; +} + +int s11i_zk11fcom_800D0DA8(void) +{ + return Zako11FCommand_800D5AF8.field_0x112 > 0; +} + +void s11i_zk11fcom_800D0DB8(void) +{ + Zako11FCommand_800D5AF8.field_0x112 = 5; + Zako11FCommand_800D5AF8.field_0x114++; +} + +void s11i_zk11fcom_800D0DD8(void) +{ + if (Zako11FCommand_800D5AF8.first_damage == 0) + { + Zako11FCommand_800D5AF8.field_0x112 = 5; + Zako11FCommand_800D5AF8.first_damage = 1; + } +} + +int s11i_zk11fcom_800D0E04(char *opt, A4_STRUCT *out) +{ + int count; + + count = 0; + + do + { + out->field_04[count].field_00 = GCL_StrToInt(GCL_NextStr()); + out->field_04[count].field_02 = GCL_StrToInt(GCL_NextStr()); + count++; + } while (GCL_NextStr()); + + return count; +} + +int s11i_zk11fcom_800D0E64(char *opt, short *out) +{ + int count; + char *param; + + count = 0; + + while ((param = GCL_NextStr())) + { + *out++ = GCL_StrToInt(param); + count++; + } + + return count; +} + +void s11i_zk11fcom_800D0EBC( void ) +{ + SVECTOR dest; + int max; + int reset_zone; + int i; + HZD_ZON *zone; + int dist; + + max = 0; + reset_zone = 0; + + for ( i = 0; i < Zako11FCommand_800D5AF8.n_zones ; i++ ) + { + zone = &Zako11FCommand_800D5AF8.map->hzd->def->zones[ Zako11FCommand_800D5AF8.zones[ i ] ]; + + dest.vx = zone->x; + dest.vy = GM_PlayerPosition.vy; + dest.vz = zone->z; + + dist = GV_DiffVec3( &dest, &GM_PlayerPosition ); + if ( max < dist ) + { + max = dist; + reset_zone = i; + } + } + + Zako11FCommand_800D5AF8.reset_zone = reset_zone; +} + +int s11i_zk11fcom_800D0FAC( int n ) +{ + return Zako11FCommand_800D5AF8.field_0x0C % n; +} + +void s11i_zk11fcom_800D0FEC( int *se_id ) +{ + switch ( s11i_zk11fcom_800D0FAC( 2 ) ) + { + case 0: + fprintf( 1, "dareda!!\n" ); + *se_id = VO_ENEMY_WHOSTHAT; + break; + + case 1: + fprintf( 1, "dareda!!\n" ); + *se_id = VO_ENEMY_WHOSTHAT; + break; + } +} + +void s11i_zk11fcom_800D1040( int *se_id ) +{ + switch ( s11i_zk11fcom_800D0FAC( 2 ) ) + { + case 0: + fprintf( 1, "nanno otoda!!\n" ); + *se_id = VO_ENEMY_THATNOISE; + break; + + case 1: + fprintf( 1, "nanno otoda!!\n" ); + *se_id = VO_ENEMY_THATNOISE; + break; + } +} + +void s11i_zk11fcom_800D1094( int *se_id ) +{ + switch ( s11i_zk11fcom_800D0FAC( 2 ) ) + { + case 0: + fprintf( 1, "tadano hakoka\n" ); + *se_id = VO_ENEMY_JUSTABOX; + break; + + case 1: + fprintf( 1, "tadano hakoka\n" ); + *se_id = VO_ENEMY_JUSTABOX; + break; + } +} + +void s11i_zk11fcom_800D10E8( int *se_id ) +{ + switch ( s11i_zk11fcom_800D0FAC( 2 ) ) + { + case 0: + fprintf( 1, "kono asiatoha??\n" ); + *se_id = VO_ENEMY_FOOTPRINTS; + break; + + case 1: + fprintf( 1, "kono asiatoha??\n" ); + *se_id = VO_ENEMY_FOOTPRINTS; + break; + } +} + +void s11i_zk11fcom_800D113C( int *se_id ) +{ + switch ( s11i_zk11fcom_800D0FAC( 2 ) ) + { + case 0: + fprintf( 1, "kinoseika !!\n" ); + *se_id = VO_ENEMY_NOTHINGHERE; + break; + + case 1: + fprintf( 1, "kinoseika !!\n" ); + *se_id = VO_ENEMY_NOTHINGHERE; + break; + } +} + +void s11i_zk11fcom_800D1190( int *se_id ) +{ + switch ( s11i_zk11fcom_800D0FAC( 3 ) ) + { + case 0: + fprintf( 1, "itazo \n" ); + *se_id = VO_ENEMY_LETSGO; + break; + + case 1: + fprintf( 1, "kottida!!\n" ); + *se_id = VO_ENEMY_THISWAY; + break; + + case 2: + fprintf( 1, "kottida!!\n" ); + *se_id = VO_ENEMY_THISWAY; + break; + } +} + +void s11i_zk11fcom_800D1220( int *se_id ) +{ + switch ( s11i_zk11fcom_800D0FAC( 2 ) ) + { + case 0: + fprintf( 1, "haitini modoruzo!!\n" ); + *se_id = VO_ENEMY_RETURN; + break; + + case 1: + fprintf( 1, "haitini modoruzo!!\n" ); + *se_id = VO_ENEMY_RETURN; + break; + } +} + +void s11i_zk11fcom_800D1274( int *se_id ) +{ + switch ( s11i_zk11fcom_800D0FAC( 2 ) ) + { + case 0: + fprintf( 1, "kottida !!\n" ); + *se_id = VO_ENEMY_THISWAY; + break; + + case 1: + *se_id = 0; + break; + } +} + +int s11i_zk11fcom_800D12D8( int se_id ) +{ + switch ( se_id ) + { + case 240: + s11i_zk11fcom_800D0FEC( &se_id ); + break; + + case 241: + s11i_zk11fcom_800D1040( &se_id ); + break; + + case 242: + s11i_zk11fcom_800D1094( &se_id ); + break; + + case 243: + s11i_zk11fcom_800D10E8( &se_id ); + break; + + case 244: + s11i_zk11fcom_800D113C( &se_id ); + break; + + case 245: + s11i_zk11fcom_800D1220( &se_id ); + break; + + case 246: + s11i_zk11fcom_800D1190( &se_id ); + break; + + case 247: + s11i_zk11fcom_800D1274( &se_id ); + break; + } + + return se_id; +} + +void s11i_zk11fcom_800D139C( ZAKO11F_COMMAND *command ) +{ + int sound; + int min; + int i; + Zako11FWork *watcher; + + if ( --s11i_dword_800C3698 < 0 ) + { + s11i_dword_800C3698 = 0; + } + + if ( s11i_dword_800C3698 > 0 ) + { + return; + } + + sound = SE_DUMMY; + min = 100000; + + for ( i = 0 ; i < command->n_watchers ; i++ ) + { + if ( command->watchers[ i ].field_04 == 2 ) + { + watcher = command->watchers[ i ].watcher; + if ( watcher->pad.sound != SE_DUMMY && watcher->sn_dis < min ) + { + sound = watcher->pad.sound; + min = watcher->sn_dis; + } + watcher->pad.sound = SE_DUMMY; + } + } + + if ( sound != SE_DUMMY ) + { + s11i_dword_800C3698 = 30; + GM_SeSet2(0, 48, s11i_zk11fcom_800D12D8( sound ) ); + } +} + +void s11i_zk11fcom_800D1480( WATCHER *watcher, int i ) +{ + switch (watcher->field_08) + { + case 0: + if ( watcher->field_04 == 1 ) + { + watcher->field_08 = 1; + watcher->field_00 = 1; + Zako11FCommand_800D5AF8.field_0x60++; + + // This is probably a shared base struct + Zako11FCommand_800D5AF8.field_0x118 = ((WatcherWork *)watcher->watcher)->control.mov; + } + break; + + case 1: + watcher->field_00--; + if ( watcher->field_00 <= 0 ) + { + watcher->field_08 = 2; + watcher->field_00 = 0; + } + break; + + case 2: + switch ( Zako11FCommand_800D5AF8.mode ) + { + case TOP_COMM_TRAVEL: + if ( Zako11FCommand_800D5AF8.field_0x10C & 1 ) + { + s11i_zk11fcom_800D0EBC(); + watcher->field_04 = 2; + watcher->field_08 = 0; + } + break; + + case TOP_COMM_ALERT: + if ( Zako11FCommand_800D5AF8.field_0x60 < 0 ) + { + s11i_zk11fcom_800D0EBC(); + watcher->field_04 = 2; + watcher->field_08 = 0; + } + break; + } + break; + } +} + +int s11i_zk11fcom_800D15C4( int a0, int a1 ) +{ + return MAX(a1, a0); +} + +void s11i_zk11fcom_800D15DC( int alert, ZAKO11F_COMMAND *command ) +{ + if ( (command->alert - alert) > 4 ) + { + command->alert -= 4; + } + else + { + command->alert = alert; + } + + if ( command->alert < TOPCOMMAND_800D5C40.alert ) + { + command->alert = TOPCOMMAND_800D5C40.alert; + } + + s11i_zk11fcom_800D0C58( 0 ); +} + +void s11i_zk11fcom_800D1640(ZAKO11F_COMMAND *command) +{ + switch (command->mode) + { + case 0: + if (ZAKO11F_GameFlag_800D5C4C == 1 || ZAKO11F_GameFlag_800D5C4C == 2 || Zako11FCommand_800D5AF8.first_damage == 1) + { + printf(" gameflag=%d firstdamage=%d \n", ZAKO11F_GameFlag_800D5C4C, Zako11FCommand_800D5AF8.first_damage); + Zako11FCommand_800D5AF8.first_damage = 2; + + command->alert = 0xFF; + command->mode = 1; + + if (Zako11FCommand_800D5AF8.field_0x124[0] >= 0) + { + GCL_ExecProc(Zako11FCommand_800D5AF8.field_0x124[0], 0); + } + } + break; + + case 1: + if (command->alert <= 0) + { + command->mode = 2; + command->field_0x14 = 0; + } + break; + + case 2: + command->field_0x14--; + break; + } + + TOPCOMMAND_800D5C40.mode = command->mode; +} + +void s11i_zk11fcom_800D1754(Zako11FCommanderWork *work) +{ + int level; + int i; + + level = 0; + + for (i = 0; i < Zako11FCommand_800D5AF8.n_watchers; i++) + { + if (Zako11FCommand_800D5AF8.watchers[i].field_04 == 2) + { + level = s11i_zk11fcom_800D15C4(level, ((WatcherWork *)Zako11FCommand_800D5AF8.watchers[i].watcher)->alert_level); + } + else if (Zako11FCommand_800D5AF8.watchers[i].field_04 == 1) + { + s11i_zk11fcom_800D1480(&Zako11FCommand_800D5AF8.watchers[i], i); + } + } + + s11i_zk11fcom_800D139C(&Zako11FCommand_800D5AF8); + s11i_zk11fcom_800D15DC(level, &Zako11FCommand_800D5AF8); + s11i_zk11fcom_800D1640(&Zako11FCommand_800D5AF8); + + Zako11FCommand_800D5AF8.field_0x10C = 0; + + if (--Zako11FCommand_800D5AF8.field_0x112 < 0) + { + Zako11FCommand_800D5AF8.field_0x112 = 0; + } +} + +void s11i_zk11fcom_800D185C(void) +{ + int lo, hi; + + lo = GM_PlayerAddress & 0xFF; + hi = ( GM_PlayerAddress >> 8 ) & 0xFF; + + if ( lo == hi && lo != 0xFF ) + { + ZAKOCOM_PlayerAddress_800D5C50 = GM_PlayerAddress; + ZAKOCOM_PlayerPosition_800D5AF0 = GM_PlayerPosition; + ZAKOCOM_PlayerMap_800D5C54 = GM_PlayerMap; + } +} + +int s11i_zk11fcom_800D18D0( char *opt ) +{ + int count; + int proc; + + count = 0; + + while (opt) + { + proc = GCL_StrToInt(opt); + opt = GCL_NextStr(); + + GCL_ExecProc(proc, NULL); + count++; + } + + return count; +} + +int s11i_zk11fcom_800D1934(int arg0) +{ + if (GM_PlayerPosition.vx < 4500) + { + return 0; + } + + if (GM_PlayerPosition.vx > 7500) + { + return 0; + } + + if (GM_PlayerPosition.vz < -16000) + { + return 0; + } + + if (arg0 == 1 && GM_PlayerPosition.vz > 6000) + { + return 0; + } + + return 1; +} + +int s11i_zk11fcom_800D1990(int arg0) +{ + if (arg0 == 0) + { + if (GM_PlayerPosition.vz > -15000) + { + return 0; + } + } + else + { + if (GM_PlayerPosition.vz > -13000) + { + return 0; + } + } + + return 1; +} + +void s11i_zk11fcom_800D19D4(void) +{ + int f114; + int i; + + f114 = Zako11FCommand_800D5AF8.field_0x114; + + switch( ZAKO11F_GameFlag_800D5C4C ) + { + case 0: + if (f114 != 0) + { + ZAKO11F_GameFlag_800D5C4C = 2; + Zako11FCommand_800D5AF8.field_0x10 = 0; + + } + else if (s11i_zk11fcom_800D1934(1)) + { + ZAKO11F_GameFlag_800D5C4C = 1; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + break; + + case 1: + if (f114 != 0) + { + ZAKO11F_GameFlag_800D5C4C = 2; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + else if (!s11i_zk11fcom_800D1934(0)) + { + ZAKO11F_GameFlag_800D5C4C = 0; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + else if (s11i_zk11fcom_800D1990(0)) + { + ZAKO11F_GameFlag_800D5C4C = 9; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + break; + + case 2: + if ( Zako11FCommand_800D5AF8.field_0x10 > 150 ) + { + ZAKO11F_GameFlag_800D5C4C = 3; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + break; + + case 3: + if ( f114 > 1 ) + { + ZAKO11F_GameFlag_800D5C4C = 5; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + else if (s11i_zk11fcom_800D1934(0)) + { + if (Zako11FCommand_800D5AF8.field_0x10 > 120 || s11i_zk11fcom_800D1934(1)) + { + ZAKO11F_GameFlag_800D5C4C = 4; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + } + else if (s11i_zk11fcom_800D1990(0)) + { + ZAKO11F_GameFlag_800D5C4C = 9; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + else + { + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + break; + + case 4: + if ( f114 > 1 ) + { + ZAKO11F_GameFlag_800D5C4C = 5; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + else if (!s11i_zk11fcom_800D1934(0)) + { + ZAKO11F_GameFlag_800D5C4C = 3; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + else if (s11i_zk11fcom_800D1990(0)) + { + ZAKO11F_GameFlag_800D5C4C = 9; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + else + { + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + break; + + case 5: + if ( Zako11FCommand_800D5AF8.field_0x10 > 60 ) + { + ZAKO11F_GameFlag_800D5C4C = 6; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + break; + + case 6: + if (f114 > 2) + { + ZAKO11F_GameFlag_800D5C4C = 8; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + else if (Zako11FCommand_800D5AF8.field_0x10 >= 2) + { + ZAKO11F_GameFlag_800D5C4C = 7; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + break; + + case 7: + if (f114 > 2) + { + ZAKO11F_GameFlag_800D5C4C = 8; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + else if (Zako11FCommand_800D5AF8.field_0x10 > 60) + { + ZAKO11F_GameFlag_800D5C4C = 5; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + else if (s11i_zk11fcom_800D1990(0)) + { + ZAKO11F_GameFlag_800D5C4C = 9; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + break; + + case 8: + if (f114 > 2) + { + ZAKO11F_GameFlag_800D5C4C = 15; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + break; + + case 9: + if (!s11i_zk11fcom_800D1990(1)) + { + switch (f114) + { + case 0: + ZAKO11F_GameFlag_800D5C4C = 1; + break; + + case 1: + ZAKO11F_GameFlag_800D5C4C = 4; + break; + + case 2: + ZAKO11F_GameFlag_800D5C4C = 7; + break; + } + + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + + if (Zako11FCommand_800D5AF8.field_0x60 > 2) + { + ZAKO11F_GameFlag_800D5C4C = 8; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + break; + + case 13: + if (Zako11FCommand_800D5AF8.field_0x10 > 60) + { + ZAKO11F_GameFlag_800D5C4C = 0; + Zako11FCommand_800D5AF8.field_0x10 = 0; + } + break; + + case 15: + if (TOPCOMMAND_800D5C40.mode == 2) + { + ZAKO11F_GameFlag_800D5C4C = 16; + + for (i = 0; i < 8; i++) + { + if (Zako11FCommand_800D5AF8.watchers[i].watcher) + { + ZAKO11F_GameFlag_800D5C4C = 15; + } + } + + if (ZAKO11F_GameFlag_800D5C4C == 16 && Zako11FCommand_800D5AF8.field_0x120 > -1) + { + GCL_ExecProc(Zako11FCommand_800D5AF8.field_0x120, NULL); + } + } + break; + + case 16: + break; + } + + Zako11FCommand_800D5AF8.field_0x10++; +} + +/*---------------------------------------------------------------------------*/ + +static void Act(Zako11FCommanderWork *work) +{ + if (GM_CheckMessage(&work->actor, work->name, HASH_KILL)) + { + GV_DestroyActor(&work->actor); + return; + } + + s11i_zk11fcom_800D19D4(); + s11i_zk11fcom_800D185C(); + s11i_zk11fcom_800D1754(work); + + Zako11FCommand_800D5AF8.field_0x0C++; + + if (ZAKO11F_GameFlag_800D5C4C == 16) + { + GV_DestroyActor(&work->actor); + } +} + +static void Die(Zako11FCommanderWork *work) +{ + /* do nothing */ +} + +/*---------------------------------------------------------------------------*/ + +int ZakoCommander_800D1E38(void) +{ + int i; + char *opt; + + for (i = 0; i < 8; i++) + { + Zako11FCommand_800D5AF8.field_0x124[i] = -1; + } + + i = 0; + opt = GCL_GetOption('a'); + while (opt) + { + if (i >= 8) + { + return -1; + } + + Zako11FCommand_800D5AF8.field_0x124[i] = GCL_StrToInt(opt); + opt = GCL_NextStr(); + i++; + } + + return i; +} + +static void GetResources(Zako11FCommanderWork *work, int name, int where) +{ + int i; + char *opt; + + work->name = name; + + ZAKO11F_GameFlag_800D5C4C = 0; + + Zako11FCommand_800D5AF8.field_0x20 = 0; + Zako11FCommand_800D5AF8.alert = 0; + Zako11FCommand_800D5AF8.field_0x10 = 0; + Zako11FCommand_800D5AF8.field_0x60 = 0; + Zako11FCommand_800D5AF8.field_0x112 = 0; + Zako11FCommand_800D5AF8.field_0x114 = 0; + Zako11FCommand_800D5AF8.first_damage = 0; + + for (i = 0; i < 8; i++) + { + Zako11FCommand_800D5AF8.watchers[i].field_00 = 0; + Zako11FCommand_800D5AF8.watchers[i].field_04 = 0; + Zako11FCommand_800D5AF8.watchers[i].field_08 = 0; + Zako11FCommand_800D5AF8.watchers[i].watcher = NULL; + } + + for (i = 0; i < 8; i++) + { + Zako11FCommand_800D5AF8.field_0x40[i] = i; + } + + Zako11FCommand_800D5AF8.field_0x0C = 0; + Zako11FCommand_800D5AF8.mode = 0; + + TOPCOMMAND_800D5C40.mode = 0; + TOPCOMMAND_800D5C40.alert = 0; + + Zako11FCommand_800D5AF8.map = GM_GetMap(where); + + opt = GCL_GetOption('e'); + if (opt) + { + Zako11FCommand_800D5AF8.field_0x120 = GCL_StrToInt(GCL_NextStr()); + } + else + { + Zako11FCommand_800D5AF8.field_0x120 = -1; + } + + ZakoCommander_800D1E38(); + + opt = GCL_GetOption('v'); + if (opt) + { + Zako11FCommand_800D5AF8.n_zones = s11i_zk11fcom_800D0E64(opt, Zako11FCommand_800D5AF8.zones); + } + else + { + Zako11FCommand_800D5AF8.n_zones = 1; + Zako11FCommand_800D5AF8.zones[0] = 0; + } + + for (i = 0; i < Zako11FCommand_800D5AF8.n_zones; i++) + { + fprintf(1, "zone=%d ", Zako11FCommand_800D5AF8.zones[i]); + } + + Zako11FCommand_800D5AF8.reset_zone = 0; + s11i_dword_800D5C48 = 0; + + opt = GCL_GetOption('j'); + if (opt) + { + Zako11FCommand_800D5AF8.field_0x68.n_entry = s11i_zk11fcom_800D0E04(opt, &Zako11FCommand_800D5AF8.field_0x68); + } + else + { + Zako11FCommand_800D5AF8.field_0x68.n_entry = 1; + Zako11FCommand_800D5AF8.field_0x68.field_04[0].field_00 = 0; + Zako11FCommand_800D5AF8.field_0x68.field_04[0].field_02 = 0; + } + + Zako11FCommand_800D5AF8.field_0x68.map_id = where; + Zako11FCommand_800D5AF8.n_watchers = 0; + + opt = GCL_GetOption('n'); + if (opt) + { + Zako11FCommand_800D5AF8.n_watchers = s11i_zk11fcom_800D18D0(opt); + } + + Zako11FCommand_800D5AF8.field_0x00 = 0; + Zako11FCommand_800D5AF8.field_0x04 = 0; + Zako11FCommand_800D5AF8.field_0x110 = 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewZako11FCommander( int name, int where ) +{ + Zako11FCommanderWork *work ; + + work = GV_NewActor( EXEC_LEVEL, sizeof( Zako11FCommanderWork ) ) ; + if ( work != NULL ) { + GV_SetNamedActor( &( work->actor ), Act, Die, "zk11fcom.c" ); + GetResources( work, name, where ); + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/anime/animconv/anime.c b/components/mgs/mgs/source/anime/animconv/anime.c new file mode 100644 index 00000000..f2bc3dfa --- /dev/null +++ b/components/mgs/mgs/source/anime/animconv/anime.c @@ -0,0 +1,730 @@ +#include "anime.h" + +#include +#include +#include +#include +#include + +#include "common.h" +#include "mts/mts.h" // for fprintf +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_AFTER + +#define MAX_LOOP 4 +#define MAX_SCRIPT 4 +#define PRIM_TYPE (DG_PRIM_RECTANGLE | DG_PRIM_POLY_FT4) + +#define SCRIPT_RANDOM 0x80 + +#define MAX_SCRIPTACT 15 + +typedef struct _Unit +{ + int count; + int anime; + SVECTOR speed; + u_char r, g, b; + u_char now_loop; + u_char *data; + u_char *dataptr; + short loop_count[ MAX_LOOP ]; + u_char *loop_s[ MAX_LOOP ]; +} Unit; + +typedef struct _Work +{ + GV_ACT actor; + DG_TEX *tex; + DG_PRIM *prim; + RECT rect; + MATRIX *world; + int map; + u_char which; + short dev_x, dev_y; + short n_anime; + short amb; + short n_verts; + short raise; + SVECTOR *vertices; + Unit unit[ 0 ]; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void TextPack( POLY_FT4 *pack, DG_TEX *tex, int anim, Work *work ) +{ + int ax, ay; + int x, y, w, h; + int amb; + + ax = anim % work->dev_x; + ay = anim / work->dev_x; + + x = tex->off_x; + w = tex->w + 1; + pack->u0 = pack->u2 = x + ( w * ax ) / work->dev_x; + pack->u1 = pack->u3 = x + ( w * ( ax + 1 ) ) / work->dev_x - 1; + + y = tex->off_y; + h = tex->h + 1; + pack->v0 = pack->v1 = y + ( h * ay ) / work->dev_y; + pack->v2 = pack->v3 = y + ( h * ( ay + 1 ) ) / work->dev_y - 1; + + pack->tpage = tex->tpage; + pack->clut = tex->clut; + + amb = work->amb; + if ( ( amb & 0xfffc ) == 0 ) + { + pack->tpage &= ~( 3 << 5 ); + pack->tpage |= work->amb << 5; + } +} + +static void UpdatePack( Work *work, int index ) +{ + Unit *unit; + POLY_FT4 *pack; + + unit = work->unit; + if ( work->which & ( GV_Clock + 1 ) ) + { + pack = (POLY_FT4 *)work->prim->packs[ GV_Clock ] + index; + TextPack( pack, work->tex, unit->anime, work ); + work->which &= ~( GV_Clock + 1 ); + } +} + +static short NextShort( u_char *data, int code ) +{ + short count; + + count = data[ 0 ] * 256 | data[ 1 ]; + if ( code & SCRIPT_RANDOM ) + { + if ( count < 0 ) + { + return -( rand() % -count ); + } + else + { + return rand() % count; + } + } + + return count; +} + +static void SetVec( SVECTOR *vec, u_char *data, int code ) +{ + vec->vx = NextShort( data + 0, code ); + vec->vy = NextShort( data + 2, code ); + vec->vz = NextShort( data + 4, code ); +} + +static void AddVec( SVECTOR *vec, u_char *data, int code ) +{ + vec->vx += NextShort( data + 0, code ); + vec->vy += NextShort( data + 2, code ); + vec->vz += NextShort( data + 4, code ); +} + +/*---------------------------------------------------------------------------*/ +/* Anime Pattern */ + +static int ScriptA( Work *work, int index ) +{ + Unit *unit; + u_char n; + + unit = work->unit + index; + n = unit->dataptr[ 1 ]; + if ( n == 0xff ) + { + unit->anime++; + if ( unit->anime >= work->n_anime ) unit->anime = 0; + } + else if ( n == 0xfe ) + { + unit->anime = rand() % work->n_anime; + } + else + { + unit->anime = n; + } + + work->which = 3; + unit->dataptr += 2; + return 0; +} + +/*---------------------------------------------------------------------------*/ +/* Wait Time */ + +static int ScriptW( Work *work, int index ) +{ + Unit *unit; + + unit = work->unit + index; + unit->count = NextShort( unit->dataptr + 1, unit->dataptr[ 0 ] ); + + unit->dataptr += 3; + return 1; +} + +/*---------------------------------------------------------------------------*/ +/* Position */ + +static int ScriptP( Work *work, int index ) +{ + Unit *unit; + + unit = work->unit + index; + SetVec( work->vertices + index, unit->dataptr + 1, unit->dataptr[ 0 ] ); + + unit->dataptr += 7; + return 0; +} + +/*---------------------------------------------------------------------------*/ +/* Movement */ + +static int ScriptM( Work *work, int index ) +{ + Unit *unit; + + unit = work->unit + index; + AddVec( work->vertices + index, unit->dataptr + 1, unit->dataptr[ 0 ] ); + + unit->dataptr += 7; + return 0; +} + +/*---------------------------------------------------------------------------*/ +/* Speed */ + +static int ScriptS( Work *work, int index ) +{ + Unit *unit; + + unit = work->unit + index; + SetVec( &unit->speed, unit->dataptr + 1, unit->dataptr[ 0 ] ); + + unit->dataptr += 7; + return 0; +} + +/*---------------------------------------------------------------------------*/ +/* Gravity */ + +static int ScriptG( Work *work, int index ) +{ + Unit *unit; + + unit = work->unit + index; + AddVec( &unit->speed, unit->dataptr + 1, unit->dataptr[ 0 ] ); + + unit->dataptr += 7; + return 0; +} + +/*---------------------------------------------------------------------------*/ +/* Semi-transparency */ + +static int ScriptT( Work *work, int index ) +{ + Unit *unit; + int i; + POLY_FT4 *pack; + + unit = work->unit + index; + work->amb = unit->dataptr[ 1 ]; + + if ( !( work->amb & 0xfffc ) ) + { + for ( i = 0; i < 2; i++ ) + { + pack = (POLY_FT4 *)work->prim->packs[ i ]; + pack += index; + setSemiTrans( pack, 1 ); + pack->tpage &= ~( 3 << 5 ); + pack->tpage |= work->amb << 5; + } + } + else + { + for ( i = 0; i < 2; i++ ) + { + pack = (POLY_FT4 *)work->prim->packs[ i ]; + pack += index; + setSemiTrans( pack, 0 ); + } + } + + unit->dataptr += 2; + return 0; +} + +/*---------------------------------------------------------------------------*/ +/* Color */ + +static int ScriptV( Work *work, int index ) +{ + u_char v[3]; + Unit *unit; + u_char *dataptr; + int i; + + unit = work->unit + index; + dataptr = unit->dataptr; + + for ( i = 0; i < 3; i++ ) + { + dataptr++; + v[ i ] = *dataptr; + } + + unit->r += v[ 0 ]; + unit->g += v[ 1 ]; + unit->b += v[ 2 ]; + + unit->dataptr += 4; + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static void ShadePacks( Work *work ) +{ + POLY_FT4 *pack; + Unit *unit; + int i; + + pack = work->prim->packs[ GV_Clock ]; + unit = work->unit; + for ( i = work->n_verts; i > 0; i-- ) + { + setRGB0( pack, unit->r, unit->g, unit->b ); + pack++; + unit++; + } +} + +/*---------------------------------------------------------------------------*/ +/* Point Light */ + +static int ScriptF( Work *work, int index ) +{ + Unit *unit; + short radius; + + unit = work->unit + index; + radius = NextShort( unit->dataptr + 1, 0 ); + DG_SetTmpLight( work->vertices + index, 512, radius ); + + unit->dataptr += 3; + return 0; +} + +/*---------------------------------------------------------------------------*/ +/* Size */ + +static int ScriptZ( Work *work, int index ) +{ + short size[ 2 ]; + Unit *unit; + u_char *dataptr; + int i; + RECT *rect; + + unit = work->unit + index; + dataptr = unit->dataptr + 1; + + for ( i = 0; i < 2; i++ ) + { + size[ i ] = NextShort( dataptr, 0 ); + dataptr += 2; + } + + rect = &work->rect; + rect->w += size[ 0 ]; + rect->h += size[ 1 ]; + rect->x = rect->w / 2; + rect->y = rect->h / 2; + + unit->dataptr += 5; + return 0; +} + +/*---------------------------------------------------------------------------*/ +/* Noise */ + +static int ScriptN( Work *work, int index ) +{ + Unit *unit; + + unit = work->unit + index; + GM_SeSet( work->vertices + index, unit->dataptr[ 1 ] ); + + unit->dataptr += 2; + return 0; +} + +/*---------------------------------------------------------------------------*/ +/* Start Loop (Open?) */ + +static int ScriptO( Work *work, int index ) +{ + Unit *unit; + short n; + int now_loop; + + unit = work->unit + index; + n = NextShort( unit->dataptr + 1, 0 ); + unit->dataptr += 3; + + unit->now_loop++; + now_loop = unit->now_loop; + unit->loop_count[ now_loop ] = n; + unit->loop_s[ now_loop ] = unit->dataptr; + return 0; +} + +/*---------------------------------------------------------------------------*/ +/* End Loop (Close?) */ + +static int ScriptC( Work *work, int index ) +{ + Unit *unit; + int now_loop, count; + + unit = work->unit + index; + now_loop = unit->now_loop; + count = --unit->loop_count[ now_loop ]; + if ( count > 0 ) + { + unit->dataptr = unit->loop_s[ now_loop ]; + } + else if ( count == 0 ) + { + unit->now_loop--; + unit->dataptr++; + } + else + { + unit->dataptr = unit->loop_s[ now_loop ]; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static int ScriptL( Work *work, int index ) +{ + Unit *unit; + + unit = work->unit + index; + unit->dataptr = unit->data; + return 1; +} + +/*---------------------------------------------------------------------------*/ +/* End Script */ + +static int ScriptE( Work *work, int index ) +{ + DG_InvisiblePrim( work->prim ); + GV_DestroyActor( work ); + return 1; +} + +/*---------------------------------------------------------------------------*/ + +static void ApplySpeed( SVECTOR *vert, Unit *unit ) +{ + vert->vx += unit->speed.vx; + vert->vy += unit->speed.vy; + vert->vz += unit->speed.vz; +} + +/*---------------------------------------------------------------------------*/ + +static int ( *ScriptAct[] )( Work *, int ) = { + ScriptA, + ScriptW, + ScriptP, + ScriptM, + ScriptS, + ScriptG, + ScriptT, + ScriptV, + ScriptF, + ScriptZ, + ScriptN, + ScriptO, + ScriptC, + ScriptL, + ScriptE +}; + +/*---------------------------------------------------------------------------*/ + +static void Act( Work *work ) +{ + Unit *unit; + SVECTOR *verts; + int i, n; + + DG_VisiblePrim( work->prim ); + + unit = work->unit; + verts = work->vertices; + for ( i = 0; i < work->n_verts; i++ ) + { + if ( unit->count <= 0 ) + { + do + { + n = *unit->dataptr & 0x7F; + if ( n < 0 || n > MAX_SCRIPTACT ) + { + fprintf( 1, " SCRIPT ACT ERR!! \n" ); + GV_DestroyActor( work ); + break; + } + } while ( !(( *ScriptAct[ n - 1 ] )( work, i )) ); + } + + ApplySpeed( verts, unit ); + UpdatePack( work, i ); + verts++; + unit->count--; + unit++; + } + + ShadePacks( work ); + GM_CurrentMap = work->map; + + if ( work->world != NULL ) + { + DG_SetPos( work->world ); + DG_PutPrim( work->prim ); + } +} + +/*---------------------------------------------------------------------------*/ + +static void Die( Work *work ) +{ + GM_FreePrim( work->prim ); +} + +/*---------------------------------------------------------------------------*/ + +static int MakeBinData( Work *work, ANIMATION *animation ) +{ + int i; + u_char *dataptr, *scriptptr[ MAX_SCRIPT ]; + PRESCRIPT *pre; + int scriptnum; + Unit *unit; + + dataptr = animation->script; + scriptnum = (int)dataptr[ 2 ]; + + dataptr += 3; + for ( i = 0; i < scriptnum; i++ ) + { + int offset; + offset = dataptr[ 0 ] * 256 + dataptr[ 1 ]; + scriptptr[ i ] = animation->script + offset; + dataptr += 2; + } + + pre = animation->pre_script; + unit = work->unit; + for ( i = 0; i < work->n_verts; i++ ) + { + unit->dataptr = unit->data = scriptptr[ pre->scr_num ]; + pre++; + unit++; + } + + return 0; +} + +static void InitPacks( Work *work, u_char v ) +{ + POLY_FT4 *packs[ 2 ]; + DG_PRIM *prim; + DG_TEX *tex; + Unit *unit; + int i, j; + int x_off, y_off; + POLY_FT4 *pack; + int x, y, w, h; + int amb; + + prim = work->prim; + tex = work->tex; + + packs[ 0 ] = prim->packs[ 0 ]; + packs[ 1 ] = prim->packs[ 1 ]; + + DG_RaisePrim( prim, work->raise ); + + unit = work->unit; + for ( i = 0; i < work->n_verts; i++ ) + { + x_off = unit->anime % work->dev_x; + y_off = unit->anime / work->dev_x; + + for ( j = 0; j < 2; j++ ) + { + pack = &packs[j][ i ]; + setPolyFT4( pack ); + + x = tex->off_x; + w = tex->w + 1; + pack->u0 = pack->u2 = x + ( w * x_off ) / work->dev_x; + pack->u1 = pack->u3 = x + ( w * ( x_off + 1 ) ) / work->dev_x - 1; + + y = tex->off_y; + h = tex->h + 1; + pack->v0 = pack->v1 = y + ( h * y_off ) / work->dev_y; + pack->v2 = pack->v3 = y + ( h * ( y_off + 1 ) ) / work->dev_y - 1; + + pack->tpage = tex->tpage; + pack->clut = tex->clut; + + amb = work->amb; + if ( ( amb & 0xfffc ) == 0 ) + { + setSemiTrans( pack, 1 ); + pack->tpage &= ~( 3 << 5 ); + pack->tpage |= work->amb << 5; + } + else + { + setSemiTrans( pack, 0 ); + } + + setRGB0( pack, v, v, v ); + } + + unit++; + } +} + +static int GetResources( Work *work, int map, ANIMATION *animation ) +{ + int n_verts; + PRESCRIPT *pre; + Unit *unit; + RECT *rect; + int i; + + work->which = 3; + work->map = map; + work->dev_x = animation->texdev_x; + work->dev_y = animation->texdev_y; + work->n_anime = animation->n_anime; + work->amb = animation->amb; + work->n_verts = n_verts = animation->n_verts; + work->raise = animation->raise; + + pre = animation->pre_script; + + for ( i = 0; i < n_verts; i++ ) + { + work->vertices[ i ] = pre->pos; + pre++; + } + + rect = &work->rect; + rect->x = animation->size_w / 2; + rect->y = animation->size_h / 2; + rect->w = animation->size_w; + rect->h = animation->size_h; + + work->prim = GM_MakePrim( PRIM_TYPE, n_verts, work->vertices, rect ); + if ( work->prim == NULL ) + { + return -1; + } + + work->tex = DG_GetTexture( animation->tex ); + + unit = work->unit; + pre = animation->pre_script; + + for ( i = 0; i < n_verts; i++ ) + { + unit->count = 0; + unit->anime = pre->s_anim; + unit->speed = pre->speed; + unit->r = unit->g = unit->b = animation->v; + unit->now_loop = -1; + + unit++; + pre++; + } + + InitPacks( work, animation->v ); + DG_InvisiblePrim( work->prim ); + + if ( MakeBinData( work, animation ) < 0 ) + { + return -1; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewAnime( MATRIX *world, int map, ANIMATION *animation ) +{ + int n_verts; + Work *work; + + n_verts = animation->n_verts; + work = GV_NewActor( EXEC_LEVEL, sizeof(Work) + ( sizeof(Unit) + sizeof(SVECTOR) ) * n_verts ); + if ( work != NULL ) + { + work->vertices = (SVECTOR *)(work->unit + n_verts); + GV_SetNamedActor( &work->actor, Act, Die, "anime.c" ); + + if ( GetResources( work, map, animation ) < 0 ) + { + GV_DestroyActor( work ); + return NULL; + } + else + { + work->world = world; + } + } + + return (void *)work; +} + +void *NewAnime2( MATRIX *world, int map, ANIMATION *animation ) +{ + Work *work; + + work = (Work *)NewAnime( NULL, map, animation ); + if ( work != NULL && world != NULL ) + { + work->prim->world = *world; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/anime/animconv/anime.h b/components/mgs/mgs/source/anime/animconv/anime.h new file mode 100644 index 00000000..9cf83e82 --- /dev/null +++ b/components/mgs/mgs/source/anime/animconv/anime.h @@ -0,0 +1,49 @@ +#ifndef __MGS_ANIME_H__ +#define __MGS_ANIME_H__ + +// "It's like one of my Japanese animes..." +// 「これじゃ まるでアニメじゃないか」 + +#include +#include +#include + +typedef struct PRESCRIPT +{ + SVECTOR pos; + SVECTOR speed; + short scr_num; + short s_anim; +} PRESCRIPT; + +typedef struct ANIMATION +{ + u_short tex; + short texdev_x, texdev_y; + short n_anime; + short n_verts; + short raise; + short amb; + short size_w, size_h; + short v; + PRESCRIPT *pre_script; + char *script; +} ANIMATION; + +void *NewAnime( MATRIX *world, int map, ANIMATION *animation ); +void *NewAnime2( MATRIX *world, int map, ANIMATION *animation ); + +// TODO: New header +void *AN_FamasFlash(MATRIX *pMtx); + +void NewAnime_8005DF50(SVECTOR *v1, SVECTOR *v2); +void AN_HeavyFlash(MATRIX *pMtx); +void NewAnime_8005E6A4(SVECTOR *pVec); +void NewAnime_8005DE70(MATRIX *pMtx); +void AN_BulletSmoke(MATRIX *pMtx, int a2); +void AN_SocomFlash(MATRIX *a1, MATRIX *a2, int a3); +void NewAnime_8005DDE0(MATRIX *pMtx); +void AN_RecoilSmoke(SVECTOR *pos); +void NewAnime_8005E090(SVECTOR *pPos); + +#endif // __MGS_ANIME_H__ diff --git a/components/mgs/mgs/source/anime/effect/breath.c b/components/mgs/mgs/source/anime/effect/breath.c new file mode 100644 index 00000000..5fa634fc --- /dev/null +++ b/components/mgs/mgs/source/anime/effect/breath.c @@ -0,0 +1,135 @@ +#include "common.h" +#include "libgv/libgv.h" +#include "anime/animconv/anime.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +static const char anm_breath_data[] = { + 0x00,0x49,0x02,0x00, 0x07,0x00,0x29,0x01, + 0xfe,0x0c,0x00,0x06, 0x08,0x06,0x06,0x06, + 0x01,0xff,0x02,0x00, 0x01,0x0d,0x0c,0x00, + 0x23,0x08,0xff,0xff, 0xff,0x0a,0x00,0x08, + 0x00,0x08,0x01,0xff, 0x02,0x00,0x01,0x0d, + 0x0f,0x0a,0xff,0x38, 0xff,0x38,0x01,0xfe, + 0x0c,0x00,0x06,0x08, 0x06,0x06,0x06,0x01, + 0xff,0x02,0x00,0x01, 0x0d,0x0c,0x00,0x23, + 0x08,0xff,0xff,0xff, 0x0a,0x00,0x06,0x00, + 0x06,0x01,0xff,0x02, 0x00,0x01,0x0d,0x0f +}; + +static ANIMATION anm_breath_form = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 300, // raise + 1, // amb + 500, // size_w + 500, // size_h + 0, // v + NULL, // pre_script + (char *)anm_breath_data // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char anm_unknown_data[] = { + 0x00,0x2c,0x01,0x00, 0x05,0x01,0xfe,0x0c, + 0x00,0x03,0x08,0x06, 0x06,0x06,0x01,0xff, + 0x02,0x00,0x01,0x0d, 0x0c,0x00,0x10,0x08, + 0xff,0xff,0xff,0x0a, 0x00,0x04,0x00,0x04, + 0x01,0xff,0x02,0x00, 0x01,0x0a,0x00,0x04, + 0x00,0x04,0x01,0xff, 0x02,0x00,0x01,0x0d, + 0x0f +}; + +static ANIMATION anm_unknown = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 300, // raise + 1, // amb + 500, // size_w + 500, // size_h + 0, // v + NULL, // pre_script + (char *)anm_unknown_data // script +}; + +/*---------------------------------------------------------------------------*/ + +void AN_Breath( MATRIX *matrix ) +{ + ANIMATION *anm; + PRESCRIPT pre; + SVECTOR *speed; + + pre.pos.vx = matrix->t[0]; + pre.pos.vy = matrix->t[1]; + pre.pos.vz = matrix->t[2]; + + speed = &pre.speed; + speed->vx = 10; + speed->vy = 10; + speed->vz = 0; + + pre.scr_num = 0; + pre.s_anim = 0; + + anm = &anm_breath_form; + anm->pre_script = ⪯ + + NewAnime( NULL, 0, anm ); +} + +void AN_Breath_2( MATRIX *matrix ) +{ + ANIMATION *anm; + PRESCRIPT pre; + SVECTOR *speed; + + pre.pos.vx = matrix->t[0]; + pre.pos.vy = matrix->t[1]; + pre.pos.vz = matrix->t[2]; + + speed = &pre.speed; + speed->vx = 3; + speed->vy = 3; + speed->vz = 0; + + pre.scr_num = 1; + pre.s_anim = 0; + + anm = &anm_breath_form; + anm->pre_script = ⪯ + + NewAnime( NULL, 0, anm ); +} + +void AN_Unknown_800C3B7C( MATRIX *matrix ) +{ + ANIMATION *anm; + PRESCRIPT pre; + SVECTOR *speed; + + pre.pos.vx = matrix->t[0]; + pre.pos.vy = matrix->t[1]; + pre.pos.vz = matrix->t[2]; + + speed = &pre.speed; + speed->vx = GV_RandU( 16 ); + speed->vy = GV_RandS( 16 ) + 20; + speed->vz = GV_RandS( 8 ); + + pre.scr_num = 0; + pre.s_anim = 0; + + anm = &anm_unknown; + anm->pre_script = ⪯ + + NewAnime( NULL, 0, anm ); +} diff --git a/components/mgs/mgs/source/anime/effect/bullet.c b/components/mgs/mgs/source/anime/effect/bullet.c new file mode 100644 index 00000000..3116d85f --- /dev/null +++ b/components/mgs/mgs/source/anime/effect/bullet.c @@ -0,0 +1,787 @@ +#include "common.h" +#include "strcode.h" +#include "anime/animconv/anime.h" +#include "game/game.h" + +const char animation_data_800124A8[] = { + 0x00, 0x8b, 0x03, 0x00, 0x09, 0x00, 0x34, 0x00, 0x64, 0x01, 0xfe, 0x02, 0x00, 0x01, 0x0c, 0x00, + 0x08, 0x01, 0xff, 0x06, 0x00, 0x00, 0xff, 0xec, 0x00, 0x00, 0x02, 0x00, 0x01, 0x0d, 0x0c, 0x00, + 0x04, 0x01, 0xff, 0x06, 0x00, 0x00, 0xff, 0xec, 0x00, 0x00, 0x0a, 0xff, 0xf6, 0xff, 0xf6, 0x02, + 0x00, 0x01, 0x0d, 0x0f, 0x01, 0xfe, 0x0a, 0xff, 0xf6, 0xff, 0xf6, 0x02, 0x00, 0x01, 0x0c, 0x00, + 0x10, 0x01, 0xff, 0x06, 0x00, 0x00, 0xff, 0xec, 0x00, 0x00, 0x02, 0x00, 0x01, 0x0d, 0x0c, 0x00, + 0x04, 0x01, 0xff, 0x06, 0x00, 0x00, 0xff, 0xec, 0x00, 0x00, 0x0a, 0xff, 0xf6, 0xff, 0xf6, 0x02, + 0x00, 0x01, 0x0d, 0x0f, 0x01, 0xfe, 0x0a, 0x00, 0x1e, 0x00, 0x1e, 0x02, 0x00, 0x01, 0x0c, 0x00, + 0x10, 0x01, 0xff, 0x06, 0x00, 0x00, 0xff, 0xec, 0x00, 0x00, 0x02, 0x00, 0x01, 0x0d, 0x0c, 0x00, + 0x04, 0x01, 0xff, 0x06, 0x00, 0x00, 0xff, 0xec, 0x00, 0x00, 0x0a, 0xff, 0xf6, 0xff, 0xf6, 0x02, + 0x00, 0x01, 0x0d, 0x0f +}; + +ANIMATION stru_8009F0D4 = {PCX_WT_SUD11, 2, 2, 4, 1, 0, 5, 50, 50, 250, 0, (char *)animation_data_800124A8}; + +const char animation_data_8001253C[] = { + 0x00, 0x83, 0x04, 0x00, 0x0b, 0x00, 0x33, 0x00, 0x5a, 0x00, 0x74, 0x01, 0xfe, 0x01, 0xff, 0x08, + 0x01, 0x01, 0x01, 0x0c, 0x00, 0x05, 0x08, 0x02, 0x02, 0x02, 0x01, 0xff, 0x02, 0x00, 0x01, 0x0d, + 0x0c, 0x00, 0x14, 0x01, 0xff, 0x0a, 0x00, 0x1e, 0x00, 0x1e, 0x08, 0xfe, 0xfe, 0xfe, 0x02, 0x00, + 0x01, 0x0d, 0x0f, 0x01, 0xfe, 0x0a, 0xfe, 0xd4, 0xfe, 0xd4, 0x0c, 0x00, 0x05, 0x08, 0x02, 0x02, + 0x02, 0x01, 0xff, 0x02, 0x00, 0x01, 0x0d, 0x0c, 0x00, 0x14, 0x01, 0xff, 0x0a, 0x00, 0x1e, 0x00, + 0x1e, 0x08, 0xfe, 0xfe, 0xfe, 0x02, 0x00, 0x01, 0x0d, 0x0f, 0x01, 0xfe, 0x0a, 0xff, 0x9c, 0xff, + 0x9c, 0x0c, 0x00, 0x0e, 0x0a, 0xff, 0xfe, 0xff, 0xfe, 0x08, 0xfe, 0xfe, 0xfe, 0x01, 0xff, 0x02, + 0x00, 0x01, 0x0d, 0x0f, 0x01, 0xfe, 0x0a, 0x01, 0xf4, 0x01, 0xf4, 0x0c, 0x00, 0x1d, 0x0a, 0x00, + 0x14, 0x00, 0x14, 0x08, 0xff, 0xff, 0xff, 0x01, 0xff, 0x02, 0x00, 0x01, 0x0d, 0x0f, 0x00, 0x00 +}; + +ANIMATION stru_8009F0F0 = {PCX_SMOKE, 8, 4, 30, 1, 300, 1, 400, 400, 32, 0, (char *)animation_data_8001253C}; + +const char animation_data_800125CC[] = { + 0x00, 0x1d, 0x03, 0x00, 0x09, 0x00, 0x10, 0x00, 0x1b, 0x09, 0x03, 0xe8, 0x02, 0x00, 0x01, 0x0f, + 0x07, 0x03, 0x0a, 0xff, 0x38, 0xff, 0x38, 0x02, 0x00, 0x01, 0x0f, 0x07, 0x03, 0x0a, 0xfe, 0xfc, + 0xfe, 0xfc, 0x02, 0x00, 0x01, 0x0f, 0x00, 0x00 +}; + +ANIMATION stru_8009F10C = {PCX_SOCOM_F, 2, 2, 4, 1, 300, 1, 400, 400, 128, 0, (char *)animation_data_800125CC}; + +const char animation_data_800125F4[] = { + 0x00, 0x1d, 0x03, 0x00, 0x09, 0x00, 0x10, 0x00, 0x1b, 0x09, 0x03, 0xe8, 0x02, 0x00, 0x01, 0x0f, + 0x07, 0x03, 0x0a, 0xfd, 0xa8, 0xfd, 0xa8, 0x02, 0x00, 0x01, 0x0f, 0x07, 0x03, 0x0a, 0xfc, 0x7c, + 0xfc, 0x7c, 0x02, 0x00, 0x01, 0x0f, 0x00, 0x00 +}; + +ANIMATION stru_8009F128 = {PCX_SOCOM_F, 2, 2, 4, 1, 300, 1, 1400, 1400, 128, 0, (char *)animation_data_800125F4}; + +const char animation_data_8001261C[] = { + 0x00, 0x04, 0x01, 0x00, 0x05, 0x02, 0x00, 0x01, 0x0f, 0x00, 0x00, 0x00 +}; + +ANIMATION stru_8009F144 = {PCX_SOCOM_F, 2, 2, 4, 1, 300, 1, 400, 400, 128, 0, (char *)animation_data_8001261C}; + +const char animation_data_80012628[] = { + 0x00, 0x07, 0x01, 0x00, 0x05, 0x09, 0x03, 0xe8, 0x02, 0x00, 0x01, 0x0f, +}; + +ANIMATION stru_8009F160 = {PCX_FA_FL10, 2, 2, 3, 1, 300, 1, 600, 600, 100, 0, (char *)animation_data_80012628}; + +const char animation_data_80012634[] = { + 0x00, 0xcb, 0x02, 0x00, 0x07, 0x00, 0x79, 0x0c, 0x00, 0x0b, 0x06, 0x00, 0x00, 0xff, 0xec, 0x00, + 0x00, 0x02, 0x00, 0x01, 0x0d, 0x0b, 0x2b, 0x05, 0x00, 0x00, 0x00, 0x14, 0x00, 0x00, 0x86, 0xff, + 0xf6, 0x00, 0x01, 0xff, 0xf6, 0x86, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x0a, 0x0c, 0x00, 0x04, 0x01, + 0xff, 0x02, 0x00, 0x01, 0x0d, 0x06, 0x00, 0x00, 0xff, 0xd8, 0x00, 0x00, 0x0c, 0x00, 0x04, 0x01, + 0xff, 0x02, 0x00, 0x01, 0x0d, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x02, 0x01, + 0xff, 0x0b, 0x2b, 0x02, 0x00, 0x02, 0x0d, 0x0c, 0x00, 0x02, 0x01, 0xff, 0x02, 0x00, 0x02, 0x0d, + 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x01, 0x08, 0xd8, 0xd8, 0xd8, 0x0c, 0x00, 0x04, + 0x08, 0xf0, 0xf0, 0xf0, 0x02, 0x00, 0x01, 0x0d, 0x0f, 0x0c, 0x00, 0x0b, 0x06, 0x00, 0x00, 0xff, + 0xec, 0x00, 0x00, 0x02, 0x00, 0x01, 0x0d, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x86, 0xff, + 0xec, 0x00, 0x01, 0xff, 0xec, 0x86, 0x00, 0x14, 0x00, 0x01, 0x00, 0x14, 0x0c, 0x00, 0x02, 0x01, + 0xff, 0x0b, 0x2b, 0x02, 0x00, 0x01, 0x0d, 0x0c, 0x00, 0x04, 0x01, 0xff, 0x02, 0x00, 0x01, 0x0d, + 0x0c, 0x00, 0x04, 0x01, 0xff, 0x02, 0x00, 0x02, 0x0d, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x07, 0x01, 0x08, 0xd8, 0xd8, 0xd8, 0x0c, 0x00, 0x04, 0x08, 0xf0, 0xf0, 0xf0, 0x02, 0x00, 0x01, + 0x0d, 0x0f, 0x00, 0x00 +}; + +ANIMATION stru_8009F17C = {PCX_MAGAZIN, 2, 2, 4, 1, 200, 5, 100, 100, 128, 0, (char *)animation_data_80012634}; + +const char animation_data_80012708[] = { + 0x00, 0x19, 0x01, 0x00, 0x05, 0x01, 0xfe, 0x0c, 0x00, 0x14, 0x01, 0xff, 0x02, 0x00, 0x01, 0x0d, + 0x0c, 0x00, 0x0a, 0x01, 0xff, 0x08, 0xf9, 0xf9, 0xf9, 0x02, 0x00, 0x01, 0x0d, 0x0f, 0x00, 0x00, +}; + +ANIMATION stru_8009F198 = {PCX_SMOKE, 8, 4, 30, 8, 0, 3, 2200, 2200, 77, 0, (char *)animation_data_80012708}; + +const char animation_data_80012728[] = { + 0x00, 0x23, 0x01, 0x00, 0x05, 0x01, 0xfe, 0x0c, 0x00, 0x0a, 0x01, 0xff, 0x0a, 0x00, 0x50, 0x00, + 0x50, 0x02, 0x00, 0x01, 0x0d, 0x0c, 0x00, 0x05, 0x0a, 0x00, 0x50, 0x00, 0x50, 0x01, 0xff, 0x08, + 0xf2, 0xf2, 0xf2, 0x02, 0x00, 0x01, 0x0d, 0x0f, +}; + +ANIMATION stru_8009F1B4 = {PCX_SMOKE, 8, 4, 30, 1, 0, 1, 1000, 1000, 77, 0, (char *)animation_data_80012728}; + +const char animation_data_80012750[] = { + 0x00, 0x4a, 0x02, 0x00, 0x07, 0x00, 0x20, 0x01, 0xfe, 0x0c, 0x00, 0x05, 0x01, 0xff, 0x02, 0x00, + 0x01, 0x0d, 0x0c, 0x00, 0x0a, 0x01, 0xff, 0x08, 0xf4, 0xf4, 0xf4, 0x02, 0x00, 0x01, 0x0d, 0x0f, + 0x01, 0xfe, 0x0c, 0x00, 0x04, 0x0a, 0x01, 0x2c, 0x01, 0x2c, 0x01, 0xff, 0x02, 0x00, 0x01, 0x0d, + 0x0c, 0x00, 0x0a, 0x0a, 0x00, 0x46, 0x00, 0x46, 0x01, 0xff, 0x02, 0x00, 0x01, 0x0d, 0x0c, 0x00, + 0x0a, 0x01, 0xff, 0x0a, 0x00, 0x50, 0x00, 0x50, 0x08, 0xe7, 0xe7, 0xe7, 0x02, 0x00, 0x01, 0x0d, + 0x0f, 0x00, 0x00, 0x00, +}; + +ANIMATION stru_8009F1D0 = {PCX_SMOKE, 8, 4, 30, 4, 0, 1, 2200, 2200, 128, 0, (char *)animation_data_80012750}; + +const char animation_data_800127A4[] = { + 0x00, 0x27, 0x01, 0x00, 0x05, 0x01, 0xfe, 0x0c, 0x00, 0x05, 0x01, 0xff, 0x0a, 0x00, 0x64, 0x00, + 0x64, 0x08, 0xf6, 0xf6, 0xf6, 0x02, 0x00, 0x01, 0x0d, 0x0c, 0x00, 0x05, 0x01, 0xff, 0x0a, 0x00, + 0xc8, 0x00, 0xc8, 0x08, 0xf3, 0xf3, 0xf3, 0x02, 0x00, 0x01, 0x0d, 0x0f, +}; + +ANIMATION stru_8009F1EC = {PCX_SMOKE, 8, 4, 30, 1, 1000, 3, 400, 400, 128, 0, (char *)animation_data_800127A4}; + +const char animation_data_800127D0[] = { + 0x00, 0x39, 0x01, 0x00, 0x05, 0x01, 0xfe, 0x0c, 0x00, 0x14, 0x01, 0xff, 0x0a, 0x00, 0x01, 0x00, + 0x01, 0x08, 0x05, 0x05, 0x05, 0x02, 0x00, 0x01, 0x0d, 0x0c, 0x00, 0x0c, 0x01, 0xff, 0x0a, 0x00, + 0x05, 0x00, 0x05, 0x08, 0xfb, 0xfb, 0xfb, 0x02, 0x00, 0x01, 0x0d, 0x0c, 0x00, 0x0a, 0x01, 0xff, + 0x0a, 0x00, 0x0a, 0x00, 0x0a, 0x08, 0xfb, 0xfb, 0xfb, 0x02, 0x00, 0x01, 0x0d, 0x0f, 0x00, 0x00 +}; + +ANIMATION stru_8009F208 = {PCX_SMOKE, 8, 4, 30, 1, 1000, 3, 30, 30, 10, 0, (char *)animation_data_800127D0}; + +int dword_8009F224 = 0; + +/*---------------------------------------------------------------------------*/ + +void *AN_FamasFlash(MATRIX *pMtx) +{ + ANIMATION *anm; + PRESCRIPT pre; + int rnd; + + pre.pos.vx = pMtx->t[ 0 ]; pre.pos.vy = pMtx->t[ 1 ]; pre.pos.vz = pMtx->t[ 2 ]; + pre.speed = DG_ZeroVector; + + pre.scr_num = 0; + pre.s_anim = 0; + + rnd = GV_RandU(16); + if (rnd >= 5) + { + pre.s_anim = 2; + } + else if (rnd > 0) + { + pre.s_anim = 1; + } + else + { + pre.s_anim = 0; + } + + anm = &stru_8009F160; + anm->pre_script = ⪯ + + return NewAnime( NULL, 0, anm ); +} + +void AN_BulletSmoke(MATRIX *arg0, int arg1) +{ + PRESCRIPT pre; + MATRIX sp28; + SVECTOR sp48; + int mod, mod2; + ANIMATION *anm; + PRESCRIPT *pPre; + SVECTOR *pSpeed; + int rand; + + pPre = ⪯ + pSpeed = &pre.speed; + + pPre->pos.vx = arg0->t[ 0 ]; + pPre->pos.vy = arg0->t[ 1 ]; + pPre->pos.vz = arg0->t[ 2 ]; + + mod = 3; + mod2 = 5; + + if (((dword_8009F224 % mod) == 0) || (arg1 != 0)) + { + sp48.vx = GV_RandU(32) - 100; + sp48.vy = GV_RandU(16) - 10; + sp48.vz = GV_RandU(16) + 40; + + DG_SetPos(arg0); + DG_MovePos(&sp48); + ReadRotMatrix(&sp28); + + pSpeed->vx = sp28.t[ 0 ] - arg0->t[ 0 ]; + pSpeed->vy = sp28.t[ 1 ] - arg0->t[ 1 ]; + pSpeed->vz = sp28.t[ 2 ] - arg0->t[ 2 ]; + + pre.scr_num = 0; + pre.s_anim = 0; + + anm = &stru_8009F0D4; + anm->pre_script = pPre; + + NewAnime( NULL, 0, anm ); + } + + rand = GV_RandU(8); + + if (((rand > 3) && (GM_PlayerStatus == (PLAYER_MOVE | PLAYER_ATTACK))) || + ((((dword_8009F224 % mod2) == 0) || (arg1 != 0)) && (GM_PlayerStatus != (PLAYER_MOVE | PLAYER_ATTACK)))) + { + pPre = ⪯ + + sp48.vx = 0; + sp48.vy = 0; + sp48.vz = 0; + + DG_SetPos(arg0); + DG_MovePos(&sp48); + ReadRotMatrix(&sp28); + + pPre->pos.vx = sp28.t[ 0 ]; + pPre->pos.vy = sp28.t[ 1 ]; + pPre->pos.vz = sp28.t[ 2 ]; + + sp48.vx = 0; + sp48.vy = -10; + sp48.vz = GV_RandU(8); + + pSpeed = &pre.speed; + + DG_MovePos(&sp48); + ReadRotMatrix(&sp28); + + pSpeed->vx = sp28.t[ 0 ] - pPre->pos.vx; + pSpeed->vy = sp28.t[ 1 ] - pPre->pos.vy; + pSpeed->vz = sp28.t[ 2 ] - pPre->pos.vz; + + pre.scr_num = 0; + pre.s_anim = 0; + + anm = &stru_8009F0F0; + anm->pre_script = ⪯ + + NewAnime( NULL, 0, anm ); + } + +#ifndef VR_EXE + if (dword_8009F224++ >= ((mod2 * mod) - 1)) + { + dword_8009F224 = 0; + } +#endif +} + +void AN_SocomFlash(MATRIX *pMatrix1, MATRIX *pMatrix2, int mode) +{ + PRESCRIPT prescript; + MATRIX m; + SVECTOR move; + int randu; + ANIMATION *anm; + SVECTOR *pos; + SVECTOR *speed; + + pos = &prescript.pos; + + move.vx = 0; + move.vy = -130; + move.vz = 0; + + DG_SetPos(pMatrix1); + DG_MovePos(&move); + ReadRotMatrix(&m); + + pos->vx = m.t[ 0 ]; + pos->vy = m.t[ 1 ]; + pos->vz = m.t[ 2 ]; + + move.vx = GV_RandU(32) - 100; + move.vy = GV_RandU(32) + 15; + move.vz = GV_RandU(64) + 100; + + DG_MovePos(&move); + ReadRotMatrix(&m); + + speed = &prescript.speed; + + speed->vx = m.t[ 0 ] - pos->vx; + speed->vy = m.t[ 1 ] - pos->vy; + speed->vz = m.t[ 2 ] - pos->vz; + prescript.scr_num = 1; + prescript.s_anim = 0; + + anm = &stru_8009F0D4; + stru_8009F0D4.pre_script = &prescript; + + NewAnime(NULL, 0, anm); + + if (mode != 1) + { + prescript.speed = DG_ZeroVector; + + randu = GV_RandU(16); + if (randu >= 7) + { + prescript.s_anim = 3; + } + else if (randu >= 5) + { + prescript.s_anim = 2; + } + else if (randu >= 3) + { + prescript.s_anim = 1; + } + else + { + prescript.s_anim = 0; + } + + anm = &stru_8009F10C; + anm->pre_script = &prescript; + + move.vx = 0; + move.vy = -270; + move.vz = 0; + + DG_SetPos(pMatrix1); + DG_MovePos(&move); + ReadRotMatrix(&m); + + pos->vx = m.t[ 0 ]; + pos->vy = m.t[ 1 ]; + pos->vz = m.t[ 2 ]; + + prescript.scr_num = 0; + NewAnime(NULL, 0, anm); + + move.vx = 0; + move.vy = -370; + move.vz = 0; + + DG_SetPos(pMatrix1); + DG_MovePos(&move); + ReadRotMatrix(&m); + + pos->vx = m.t[ 0 ]; + pos->vy = m.t[ 1 ]; + pos->vz = m.t[ 2 ]; + + prescript.scr_num = 1; + NewAnime(NULL, 0, anm); + + move.vx = 0; + move.vy = -420; + move.vz = 0; + + DG_SetPos(pMatrix1); + DG_MovePos(&move); + ReadRotMatrix(&m); + + pos->vx = m.t[ 0 ]; + pos->vy = m.t[ 1 ]; + pos->vz = m.t[ 2 ]; + + prescript.scr_num = 2; + NewAnime(NULL, 0, anm); + } + else + { + DG_SetPos(pMatrix2); + + move.vx = 0; + move.vy = 0; + move.vz = 0; + + DG_MovePos(&move); + ReadRotMatrix(&m); + + pos->vx = m.t[ 0 ]; + pos->vy = m.t[ 1 ]; + pos->vz = m.t[ 2 ]; + + move.vx = 0; + move.vy = -10; + move.vz = GV_RandU(8); + + DG_MovePos(&move); + ReadRotMatrix(&m); + + speed->vx = m.t[ 0 ] - pos->vx; + speed->vy = m.t[ 1 ] - pos->vy; + speed->vz = m.t[ 2 ] - pos->vz; + + prescript.scr_num = 2; + + anm = &stru_8009F0F0; + anm->pre_script = &prescript; + + NewAnime(NULL, 0, anm); + } + + DG_SetPos(pMatrix1); + + pos = &prescript.pos; + + move.vx = 0; + move.vy = 0; + move.vz = 0; + + DG_MovePos(&move); + ReadRotMatrix(&m); + + pos->vx = m.t[ 0 ]; + pos->vy = m.t[ 1 ]; + pos->vz = m.t[ 2 ]; + + move.vx = 0; + move.vy = 0; + move.vz = GV_RandU(8); + + DG_MovePos(&move); + ReadRotMatrix(&m); + + speed = &prescript.speed; + + speed->vx = m.t[ 0 ] - pos->vx; + speed->vy = m.t[ 1 ] - pos->vy; + speed->vz = m.t[ 2 ] - pos->vz; + prescript.scr_num = 0; + + anm = &stru_8009F0F0; + anm->pre_script = &prescript; + + NewAnime( NULL, 0, anm ); +} + +void NewAnime_8005DDE0(MATRIX *pMtx) +{ + ANIMATION *anm; + PRESCRIPT pre; + + pre.pos.vx = pMtx->t[ 0 ]; + pre.pos.vy = pMtx->t[ 1 ]; + pre.pos.vz = pMtx->t[ 2 ]; + pre.speed = DG_ZeroVector; + + pre.scr_num = GV_RandU(2); + pre.s_anim = 0; + + anm = &stru_8009F17C; + anm->pre_script = ⪯ + + NewAnime( NULL, 0, anm ); +} + +void NewAnime_8005DE70(MATRIX *rotation) +{ + SVECTOR sp10; + SVECTOR sp18; + MATRIX rot; + PRESCRIPT pre; + ANIMATION *anm; + + rot = *rotation; + + sp10.vx = 0; + sp10.vy = -1500; + sp10.vz = 0; + + ApplyMatrixSV(&rot, &sp10, &sp18); + + pre.pos.vx = rotation->t[ 0 ] + sp18.vx; + pre.pos.vy = rotation->t[ 1 ] + sp18.vy; + pre.pos.vz = rotation->t[ 2 ] + sp18.vz; + pre.speed.vx = 0; + pre.speed.vy = 0; + pre.speed.vz = 0; + pre.scr_num = 0; + pre.s_anim = 0; + + anm = &stru_8009F1B4; + anm->pre_script = ⪯ + + NewAnime( NULL, 0, anm ); +} + +void NewAnime_8005DF50(SVECTOR *arg0, SVECTOR *arg1) +{ + SVECTOR sp10; + SVECTOR sp18; + PRESCRIPT pres[4]; + int i; + + sp18.vx = arg1->vx; + sp18.vy = arg1->vy - 448; + sp18.vz = arg1->vz; + + sp10.vx = 0; + sp10.vy = 30; + + for (i = 0; i < 3; i++) + { + sp10.vz = GV_RandU(64) + 150; + pres[ i ].pos = *arg0; + sp18.vy += 128; + + DG_SetPos2(arg0, &sp18); + DG_RotVector(&sp10, &pres[ i ].speed, 1); + + pres[ i ].scr_num = 0; + pres[ i ].s_anim = 0; + } + + pres[3].pos = *arg0; + pres[3].speed = DG_ZeroVector; + pres[3].scr_num = 1; + pres[3].s_anim = 0; + + stru_8009F1D0.pre_script = pres; + NewAnime( NULL, 0, &stru_8009F1D0 ); +} + +void NewAnime_8005E090(SVECTOR *pPos) +{ + SVECTOR axis; + SVECTOR speed_in; + SVECTOR speed_out; + MATRIX rot; + PRESCRIPT pres[8]; + int i; + ANIMATION *anm; + + axis.vx = 0; + axis.vz = 0; + speed_in.vx = 0; + speed_in.vy = 0; + + for (i = 0; i < 8; i++) + { + axis.vy = i * 512; + speed_in.vz = GV_RandS(16) + 40; + + pres[ i ].s_anim = 0; + pres[ i ].pos = *pPos; + pres[ i ].scr_num = 0; + + RotMatrixYXZ(&axis, &rot); + ApplyMatrixSV(&rot, &speed_in, &speed_out); + + pres[ i ].speed.vx = speed_out.vx; + pres[ i ].speed.vy = speed_out.vy; + pres[ i ].speed.vz = speed_out.vz; + } + + anm = &stru_8009F198; + anm->pre_script = pres; + + NewAnime( NULL, 0, anm ); +} + +void NewAnime_8005E1A0(MATRIX *arg0) +{ + PRESCRIPT pre; + int rnd; + ANIMATION *anm; + + pre.pos.vx = arg0->t[ 0 ]; + pre.pos.vy = arg0->t[ 1 ]; + pre.pos.vz = arg0->t[ 2 ]; + pre.speed = DG_ZeroVector; + pre.scr_num = 0; + pre.s_anim = 0; + + rnd = GV_RandU(16); + + if (rnd >= 5) + { + pre.s_anim = 2; + } + else if (rnd > 0) + { + pre.s_anim = 1; + } + else + { + pre.s_anim = 0; + } + + anm = &stru_8009F10C; + anm->pre_script = ⪯ + + NewAnime( NULL, 0, anm ); +} + +void NewAnime_8005E258(MATRIX *pMatrix) +{ + SVECTOR vec1, vec2; + MATRIX m; + PRESCRIPT prescript; + + m = *pMatrix; + vec1.vx = 0; + vec1.vy = 0; + vec1.vz = 0; + ApplyMatrixSV(&m, &vec1, &vec2); + prescript.pos.vx = pMatrix->t[ 0 ] + vec2.vx; + prescript.pos.vy = pMatrix->t[ 1 ] + vec2.vy; + prescript.pos.vz = pMatrix->t[ 2 ] + vec2.vz; + prescript.speed.vx = 0; + prescript.speed.vy = 0; + prescript.speed.vz = 0; + prescript.scr_num = 0; + prescript.s_anim = 0; + stru_8009F1B4.pre_script = &prescript; + NewAnime(NULL, 0, &stru_8009F1B4); +} + +void AN_HeavyFlash(MATRIX *rotation) +{ + PRESCRIPT pre; + SVECTOR translation; + MATRIX rot; + int rnd; + PRESCRIPT *pPre; + ANIMATION *anm; + + pPre = ⪯ + pPre->speed = DG_ZeroVector; + + rnd = GV_RandU(16); + + if (rnd >= 7) + { + pre.s_anim = 3; + } + else if (rnd >= 5) + { + pre.s_anim = 2; + } + else if (rnd >= 3) + { + pre.s_anim = 1; + } + else + { + pre.s_anim = 0; + } + + anm = &stru_8009F128; + anm->pre_script = ⪯ + + translation.vx = 0; + translation.vy = 0; + translation.vz = 300; + + DG_SetPos(rotation); + DG_MovePos(&translation); + ReadRotMatrix(&rot); + + pPre->pos.vx = rot.t[ 0 ]; + pPre->pos.vy = rot.t[ 1 ]; + pPre->pos.vz = rot.t[ 2 ]; + + pre.scr_num = 0; + NewAnime( NULL, 0, anm ); + + translation.vx = 0; + translation.vy = 0; + translation.vz = 750; + + DG_SetPos(rotation); + DG_MovePos(&translation); + ReadRotMatrix(&rot); + + pPre->pos.vx = rot.t[ 0 ]; + pPre->pos.vy = rot.t[ 1 ]; + pPre->pos.vz = rot.t[ 2 ]; + + pre.scr_num = 1; + NewAnime( NULL, 0, anm ); + + translation.vx = 0; + translation.vy = 0; + translation.vz = 990; + + DG_SetPos(rotation); + DG_MovePos(&translation); + ReadRotMatrix(&rot); + + pPre->pos.vx = rot.t[ 0 ]; + pPre->pos.vy = rot.t[ 1 ]; + pPre->pos.vz = rot.t[ 2 ]; + + pre.scr_num = 2; + NewAnime( NULL, 0, anm ); +} + +void AN_RecoilSmoke(SVECTOR *pos) +{ + ANIMATION *anm; + PRESCRIPT pre = {{ 0 }}; + + pre.pos = *pos; + + anm = &stru_8009F1EC; + anm->pre_script = ⪯ + + NewAnime( NULL, 0, anm ); +} + +void NewAnime_8005E574(MATRIX *pMtx) +{ + ANIMATION *anm; + SVECTOR vec; + PRESCRIPT pre; + + anm = &stru_8009F10C; + anm->pre_script = ⪯ + + pre.speed = DG_ZeroVector; + pre.s_anim = GV_RandU(4); + + vec.vx = 0; vec.vy = -270; vec.vz = 0; + DG_SetPos(pMtx); + DG_PutVector(&vec, &pre.pos, 1); + pre.scr_num = 0; + NewAnime( NULL, 0, anm ); + + vec.vx = 0; vec.vy = -370; vec.vz = 0; + DG_SetPos(pMtx); + DG_PutVector(&vec, &pre.pos, 1); + pre.scr_num = 1; + NewAnime( NULL, 0, anm ); + + vec.vx = 0; vec.vy = -420; vec.vz = 0; + DG_SetPos(pMtx); + DG_PutVector(&vec, &pre.pos, 1); + pre.scr_num = 2; + NewAnime( NULL, 0, anm ); +} + +void NewAnime_8005E6A4(SVECTOR *pos) +{ + ANIMATION *anm; + SVECTOR rot; + SVECTOR speed; + PRESCRIPT pre; + + rot = DG_ZeroVector; + rot.vy = GV_Time * 128; + + pre.pos = *pos; + + speed.vx = 0; + speed.vy = GV_RandU(2) + 10; + speed.vz = 2; + + DG_SetPos2(&DG_ZeroVector, &rot); + DG_PutVector(&speed, &pre.speed, 1); + + pre.scr_num = 0; + + anm = &stru_8009F208; + anm->pre_script = ⪯ + + NewAnime( 0, 0, anm ); +} + +void NewAnime_8005E774(SVECTOR *pos) +{ + ANIMATION *anm; + PRESCRIPT pre; + + anm = &stru_8009F144; + + pre.pos = *pos; + pre.speed.vx = 0; + pre.speed.vy = 0; + pre.speed.vz = 0; + + pre.scr_num = 0; + pre.s_anim = GV_RandU(4); + + anm->pre_script = ⪯ + + NewAnime( 0, 0, anm ); +} diff --git a/components/mgs/mgs/source/anime/effect/fog.c b/components/mgs/mgs/source/anime/effect/fog.c new file mode 100644 index 00000000..cd905c96 --- /dev/null +++ b/components/mgs/mgs/source/anime/effect/fog.c @@ -0,0 +1,47 @@ +#include "common.h" +#include "libdg/libdg.h" +#include "anime/animconv/anime.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +static const char anm_fog_data[] = { + 0x00,0x1c,0x01,0x00, 0x05,0x01,0xfe,0x04, + 0x00,0x00,0x01,0x90, 0x00,0x00,0x0c,0x00, + 0x09,0x01,0xff,0x0a, 0x00,0x14,0x00,0x14, + 0x08,0xe7,0xe7,0xe7, 0x02,0x00,0x01,0x0d, + 0x0f, +}; + +static ANIMATION anm_fog_form = { + PCX_PCH_FOG, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 800, // raise + 3, // amb + 600, // size_w + 600, // size_h + 250, // v + NULL, // pre_script + (char *)anm_fog_data // script +}; + +/*---------------------------------------------------------------------------*/ + +void AN_Fog( SVECTOR *pos ) +{ + ANIMATION *anm; + PRESCRIPT pre; + + pre.pos = *pos; + pre.speed = DG_ZeroVector; + pre.s_anim = 0; + + anm = &anm_fog_form; + anm->pre_script = ⪯ + pre.scr_num = 0; + + NewAnime( NULL, 0, anm ); +} diff --git a/components/mgs/mgs/source/anime/effect/m1e1.c b/components/mgs/mgs/source/anime/effect/m1e1.c new file mode 100644 index 00000000..510bc76c --- /dev/null +++ b/components/mgs/mgs/source/anime/effect/m1e1.c @@ -0,0 +1,251 @@ +#include "common.h" +#include "libdg/libdg.h" +#include "anime/animconv/anime.h" +#include "strcode.h" + +const char animation_data_8001345C[] = { + 0x00, 0x27, 0x01, 0x00, 0x05, 0x01, 0xfe, 0x0c, + 0x00, 0x05, 0x01, 0xff, 0x0a, 0x00, 0x64, 0x00, + 0x64, 0x08, 0xf1, 0xf1, 0xf1, 0x02, 0x00, 0x01, + 0x0d, 0x0c, 0x00, 0x05, 0x01, 0xff, 0x0a, 0x01, + 0x2c, 0x01, 0x2c, 0x08, 0xe2, 0xe2, 0xe2, 0x02, + 0x00, 0x01, 0x0d, 0x0f +}; + +ANIMATION stru_8009F73C = {PCX_SMOKE, 8, 4, 30, 1, 1000, 3, 500, 500, 255, NULL, (char *)animation_data_8001345C}; + +const char animation_data_80013488[] = { + 0x00, 0x4a, 0x02, 0x00, 0x07, 0x00, 0x20, 0x01, + 0xfe, 0x0c, 0x00, 0x05, 0x01, 0xff, 0x02, 0x00, + 0x01, 0x0d, 0x0c, 0x00, 0x0a, 0x01, 0xff, 0x08, + 0xfa, 0xfa, 0xfa, 0x02, 0x00, 0x01, 0x0d, 0x0f, + 0x01, 0xfe, 0x0c, 0x00, 0x04, 0x0a, 0x01, 0x2c, + 0x01, 0x2c, 0x01, 0xff, 0x02, 0x00, 0x01, 0x0d, + 0x0c, 0x00, 0x0a, 0x0a, 0x00, 0x46, 0x00, 0x46, + 0x01, 0xff, 0x02, 0x00, 0x01, 0x0d, 0x0c, 0x00, + 0x0a, 0x01, 0xff, 0x0a, 0x00, 0x50, 0x00, 0x50, + 0x08, 0xf0, 0xf0, 0xf0, 0x02, 0x00, 0x01, 0x0d, + 0x0f, 0x00, 0x00, 0x00 +}; + +ANIMATION stru_8009F758 = {PCX_SMOKE, 8, 4, 30, 3, 0, 1, 1000, 1000, 64, NULL, (char *)animation_data_80013488}; + +const char animation_data_800134DC[] = { + 0x00, 0x2c, 0x01, 0x00, 0x05, 0x01, 0xfe, 0x0c, + 0x00, 0x05, 0x01, 0xff, 0x02, 0x00, 0x01, 0x0d, + 0x0c, 0x00, 0x05, 0x01, 0xff, 0x0a, 0x00, 0x05, + 0x00, 0x05, 0x02, 0x00, 0x01, 0x0d, 0x0c, 0x00, + 0x0f, 0x01, 0xff, 0x08, 0xf0, 0xf0, 0xf0, 0x0a, + 0x00, 0x05, 0x00, 0x05, 0x02, 0x00, 0x01, 0x0d, + 0x0f, 0x00, 0x00, 0x00 +}; + +ANIMATION stru_8009F774 = {PCX_SMOKE, 8, 4, 30, 8, 0, 3, 2200, 2200, 255, NULL, (char *)animation_data_800134DC}; + +const char animation_data_80013510[] = { + 0x00, 0x49, 0x03, 0x00, 0x09, 0x00, 0x1e, 0x00, + 0x38, 0x01, 0xfe, 0x0c, 0x00, 0x04, 0x02, 0x00, + 0x01, 0x0d, 0x0c, 0x00, 0x04, 0x08, 0xec, 0xec, + 0xec, 0x02, 0x00, 0x01, 0x0d, 0x0f, 0x01, 0xfe, + 0x0a, 0xfc, 0x18, 0xfc, 0x18, 0x0c, 0x00, 0x04, + 0x02, 0x00, 0x01, 0x0d, 0x0c, 0x00, 0x04, 0x08, + 0xec, 0xec, 0xec, 0x02, 0x00, 0x01, 0x0d, 0x0f, + 0x01, 0xfe, 0x0a, 0xfb, 0x50, 0xfb, 0x50, 0x0c, + 0x00, 0x04, 0x02, 0x00, 0x01, 0x0d, 0x0c, 0x00, + 0x04, 0x08, 0xec, 0xec, 0xec, 0x02, 0x00, 0x01, + 0x0d, 0x0f, 0x00, 0x00 +}; + +ANIMATION stru_8009F790 = {PCX_CANON_SEQ, 2, 2, 4, 1, 300, 1, 5000, 5000, 128, NULL, (char *)animation_data_80013510}; + +void AN_CaterpillerSmoke(SVECTOR *pos) +{ + ANIMATION *anm; + PRESCRIPT pre = {{ 0 }}; + + pre.pos = *pos; + + anm = &stru_8009F73C; + anm->pre_script = ⪯ + + NewAnime( NULL, 0, anm ); +} + +void demothrd_2_8007DA94(SVECTOR *pPosition, SVECTOR *pRotation) +{ + SVECTOR rotation; + SVECTOR speed; + PRESCRIPT pres[3]; + int i; + ANIMATION *anm; + + rotation.vx = 0; + rotation.vy = pRotation->vy - 512; + rotation.vz = 0; + + for (i = 0; i < 2; i++) + { + pres[i].pos = *pPosition; + pres[i].pos.vy += 400; + + speed.vx = 0; + speed.vy = 30; + speed.vz = 200; + + rotation.vy += 341; + + DG_SetPos2(&DG_ZeroVector, &rotation); + DG_PutVector(&speed, &speed, 1); + + pres[i].speed = speed; + pres[i].scr_num = 0; + pres[i].s_anim = 0; + } + + anm = &stru_8009F758; + anm->pre_script = pres; + + pres[2].pos = *pPosition; + pres[2].pos.vy += 400; + pres[2].speed = DG_ZeroVector; + pres[2].scr_num = 1; + pres[2].s_anim = 0; + + NewAnime( NULL, 0, anm ); +} + +void sub_8007DC24(SVECTOR *pPosition) +{ + SVECTOR rotation; + SVECTOR speed_in; + SVECTOR speed_out; + PRESCRIPT pres[8]; + int i; + ANIMATION *anm; + + rotation = DG_ZeroVector; + rotation.vy += GV_RandU(512); + + speed_in = DG_ZeroVector; + + for (i = 0; i < 8; i++) + { + pres[i].pos = *pPosition; + pres[i].pos.vy += 500; + + speed_in.vz = 100; + rotation.vy += 512; + + DG_SetPos2(&DG_ZeroVector, &rotation); + DG_PutVector(&speed_in, &speed_out, 1); + + pres[i].speed = speed_out; + pres[i].scr_num = 0; + } + + anm = &stru_8009F774; + anm->pre_script = pres; + + NewAnime( NULL, 0, anm ); +} + +void sub_8007DD80(short param_1, SVECTOR *pPos) +{ + int i; + SVECTOR vec1; + SVECTOR vec2; + SVECTOR speed_tmp; + SVECTOR speed; + PRESCRIPT prescript[8]; + + vec1 = DG_ZeroVector; + vec2 = DG_ZeroVector; + vec1.vy = param_1 - 512; + speed_tmp = DG_ZeroVector; + for (i = 0; i < 8; i++) + { + prescript[i].pos = *pPos; + prescript[i].pos.vy -= 1000; + speed_tmp.vz = GV_RandU(64) + 100; + vec1.vy += 0x80; + vec2.vy = vec1.vy + GV_RandS(0x40); + DG_SetPos2(&DG_ZeroVector, &vec2); + DG_PutVector(&speed_tmp, &speed, 1); + prescript[i].speed = speed; + prescript[i].scr_num = 0; + } + + stru_8009F774.pre_script = prescript; + NewAnime(0, 0, &stru_8009F774); +} + +void sub_8007DF10(SVECTOR *pRotation, SVECTOR *pTranslation) +{ + ANIMATION *anm; + SVECTOR vin[3]; + SVECTOR vout[3]; + PRESCRIPT pre; + + pre.speed = DG_ZeroVector; + + vin[0] = DG_ZeroVector; + vin[0].vz = 500; + + vin[1] = DG_ZeroVector; + vin[1].vz = 2000; + + vin[2] = DG_ZeroVector; + vin[2].vz = 3000; + + DG_SetPos2(pTranslation, pRotation); + DG_PutVector(vin, vout, 3); + + anm = &stru_8009F790; + anm->pre_script = ⪯ + + pre.pos = vout[0]; + pre.scr_num = 0; + NewAnime( NULL, 0, anm ); + + pre.pos = vout[1]; + pre.scr_num = 1; + NewAnime( NULL, 0, anm ); + + pre.pos = vout[2]; + pre.scr_num = 2; + NewAnime( NULL, 0, anm ); +} + +void sub_8007E0AC(int y, SVECTOR *pPosition) +{ + ANIMATION *anm; + SVECTOR rotation; + SVECTOR vin; + PRESCRIPT pre[8]; + int i; + + rotation.vx = 0; + rotation.vy = y; + rotation.vz = 0; + + vin = DG_ZeroVector; + + for ( i = 0; i < 8; i++ ) + { + pre[i].pos = *pPosition; + + vin.vx = GV_RandU(64); + vin.vz = GV_RandU(64); + + rotation.vz += 512; + + DG_SetPos2(&DG_ZeroVector, &rotation); + DG_PutVector(&vin, &pre[i].speed, 1); + pre[i].scr_num = 0; + } + + anm = &stru_8009F774; + anm->pre_script = pre; + + NewAnime( NULL, 0, anm ); +} diff --git a/components/mgs/mgs/source/anime/effect/mark.c b/components/mgs/mgs/source/anime/effect/mark.c new file mode 100644 index 00000000..88da9af1 --- /dev/null +++ b/components/mgs/mgs/source/anime/effect/mark.c @@ -0,0 +1,386 @@ +#include "common.h" +#include "libdg/libdg.h" +#include "anime/animconv/anime.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +static const char anm_mark_data_800DFEB4[] = { + 0x01,0x56,0x04,0x00, 0x0b,0x00,0x5e,0x00, + 0xb1,0x01,0x09,0x01, 0x00,0x0c,0x00,0x04, + 0x0a,0x00,0x64,0x00, 0x64,0x08,0x32,0x19, + 0x14,0x02,0x00,0x01, 0x0d,0x0a,0x00,0x64, + 0x00,0x64,0x08,0x32, 0x96,0xaa,0x02,0x00, + 0x03,0x0a,0xff,0x9c, 0xff,0x9c,0x08,0xce, + 0x6a,0x56,0x02,0x00, 0x16,0x0a,0x01,0x2c, + 0xfe,0x98,0x08,0xce, 0xe7,0xec,0x02,0x00, + 0x01,0x0a,0x01,0x2c, 0xff,0xe2,0x08,0xce, + 0xe7,0xec,0x02,0x00, 0x01,0x0a,0x01,0x2c, + 0xff,0xf4,0x02,0x00, 0x01,0x0a,0xfa,0xba, + 0x00,0xf0,0x02,0x00, 0x01,0x0f,0x01,0x00, + 0x0c,0x00,0x04,0x0a, 0x00,0x32,0x00,0x32, + 0x08,0x32,0x19,0x14, 0x02,0x00,0x01,0x0d, + 0x0a,0x00,0x32,0x00, 0x32,0x08,0x32,0x96, + 0xaa,0x02,0x00,0x03, 0x0a,0xff,0xce,0xff, + 0xce,0x08,0xce,0x6a, 0x56,0x02,0x00,0x16, + 0x0a,0x00,0x96,0xff, 0x4c,0x08,0xce,0xe7, + 0xec,0x02,0x00,0x01, 0x0a,0x00,0x96,0xff, + 0xf1,0x08,0xce,0xe7, 0xec,0x02,0x00,0x01, + 0x0a,0x00,0x96,0xff, 0xfa,0x02,0x00,0x01, + 0x0a,0xfd,0x5d,0x00, 0x78,0x02,0x00,0x01, + 0x0f,0x0a,0xff,0xd8, 0xff,0xd8,0x01,0x00, + 0x0c,0x00,0x04,0x0a, 0x00,0x19,0x00,0x19, + 0x08,0x32,0x19,0x14, 0x02,0x00,0x01,0x0d, + 0x0a,0x00,0x19,0x00, 0x19,0x08,0x32,0x96, + 0xaa,0x02,0x00,0x03, 0x0a,0xff,0xe7,0xff, + 0xe7,0x08,0xce,0x6a, 0x56,0x02,0x00,0x16, + 0x0a,0x00,0x4b,0xff, 0xa6,0x08,0xce,0xe7, + 0xec,0x02,0x00,0x01, 0x0a,0x00,0x4b,0xff, + 0xf9,0x08,0xce,0xe7, 0xec,0x02,0x00,0x01, + 0x0a,0x00,0x4b,0xff, 0xf9,0x02,0x00,0x01, + 0x0a,0xfe,0xd4,0x00, 0x3c,0x02,0x00,0x01, + 0x0f,0x0a,0xff,0xc4, 0xff,0xc4,0x01,0x00, + 0x0c,0x00,0x04,0x0a, 0x00,0x0a,0x00,0x0a, + 0x08,0x32,0x19,0x14, 0x02,0x00,0x01,0x0d, + 0x0a,0x00,0x0a,0x00, 0x0a,0x08,0x32,0x96, + 0xaa,0x02,0x00,0x03, 0x0a,0xff,0xf6,0xff, + 0xf6,0x08,0xce,0x6a, 0x56,0x02,0x00,0x16, + 0x0a,0x00,0x1e,0xff, 0xdc,0x08,0xce,0xe7, + 0xec,0x02,0x00,0x01, 0x0a,0x00,0x1e,0xff, + 0xfe,0x08,0xce,0xe7, 0xec,0x02,0x00,0x01, + 0x0a,0x00,0x1e,0xff, 0xfe,0x02,0x00,0x01, + 0x0a,0xff,0xb0,0x00, 0x0a,0x02,0x00,0x01, + 0x0f +}; + +static ANIMATION anm_mark_form_800C3418 = { + PCX_B_MARK, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 500, // raise + 1, // amb + 80, // size_w + 80, // size_h + 0, // v + NULL, // pre_script + (char *)anm_mark_data_800DFEB4 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char anm_mark_data_800E0018[] = { + 0x00,0xf9,0x03,0x00, 0x09,0x00,0x5c,0x00, + 0xaf,0x01,0x00,0x0c, 0x00,0x04,0x0a,0x00, + 0x64,0x00,0x64,0x08, 0x2d,0x23,0x14,0x02, + 0x00,0x01,0x0d,0x0a, 0x00,0x64,0x00,0x64, + 0x08,0x46,0x6e,0xaa, 0x02,0x00,0x03,0x0a, + 0xff,0x9c,0xff,0x9c, 0x08,0xba,0x92,0x56, + 0x02,0x00,0x16,0x0a, 0x01,0x2c,0xfe,0x98, + 0x08,0xd3,0xdd,0xec, 0x02,0x00,0x01,0x0a, + 0x01,0x2c,0xff,0xe2, 0x08,0xd3,0xdd,0xec, + 0x02,0x00,0x01,0x0a, 0x01,0x2c,0xff,0xf4, + 0x02,0x00,0x01,0x0a, 0xfa,0xba,0x00,0xf0, + 0x02,0x00,0x01,0x0f, 0x01,0x00,0x0c,0x00, + 0x04,0x0a,0x00,0x32, 0x00,0x32,0x08,0x2d, + 0x23,0x14,0x02,0x00, 0x01,0x0d,0x0a,0x00, + 0x32,0x00,0x32,0x08, 0x46,0x6e,0xaa,0x02, + 0x00,0x03,0x0a,0xff, 0xce,0xff,0xce,0x08, + 0xba,0x92,0x56,0x02, 0x00,0x16,0x0a,0x00, + 0x96,0xff,0x4c,0x08, 0xd3,0xdd,0xec,0x02, + 0x00,0x01,0x0a,0x00, 0x96,0xff,0xf1,0x08, + 0xd3,0xdd,0xec,0x02, 0x00,0x01,0x0a,0x00, + 0x96,0xff,0xfa,0x02, 0x00,0x01,0x0a,0xfd, + 0x5d,0x00,0x78,0x02, 0x00,0x01,0x0f,0x01, + 0x00,0x0c,0x00,0x04, 0x0a,0x00,0x19,0x00, + 0x19,0x08,0x32,0x19, 0x14,0x02,0x00,0x01, + 0x0d,0x0a,0x00,0x19, 0x00,0x19,0x08,0x32, + 0x96,0xaa,0x02,0x00, 0x03,0x0a,0xff,0xe7, + 0xff,0xe7,0x08,0xce, 0x6a,0x56,0x02,0x00, + 0x16,0x0a,0x00,0x4b, 0xff,0xa6,0x08,0xce, + 0xe7,0xec,0x02,0x00, 0x01,0x0a,0x00,0x4b, + 0xff,0xf9,0x08,0xce, 0xe7,0xec,0x02,0x00, + 0x01,0x0a,0x00,0x4b, 0xff,0xf9,0x02,0x00, + 0x01,0x0a,0xfe,0xd4, 0x00,0x3c,0x02,0x00, + 0x01,0x0f +}; + +static ANIMATION anm_mark_form_800C3434 = { + PCX_Q_MARK, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 500, // raise + 1, // amb + 80, // size_w + 80, // size_h + 0, // v + NULL, // pre_script + (char *)anm_mark_data_800E0018 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char anm_mark_data_800E011C[] = { + 0x00,0xf9,0x03,0x00, 0x09,0x00,0x5c,0x00, + 0xaf,0x01,0x00,0x0c, 0x00,0x04,0x0a,0x00, + 0x64,0x00,0x64,0x08, 0x19,0x26,0x2d,0x02, + 0x00,0x01,0x0d,0x0a, 0x00,0x64,0x00,0x64, + 0x08,0x96,0x62,0x46, 0x02,0x00,0x03,0x0a, + 0xff,0x9c,0xff,0x9c, 0x08,0x6a,0x9e,0xba, + 0x02,0x00,0x16,0x0a, 0x01,0x2c,0xfe,0x98, + 0x08,0xe7,0xda,0xd3, 0x02,0x00,0x01,0x0a, + 0x01,0x2c,0xff,0xe2, 0x08,0xe7,0xda,0xd3, + 0x02,0x00,0x01,0x0a, 0x01,0x2c,0xff,0xf4, + 0x02,0x00,0x01,0x0a, 0xfa,0xba,0x00,0xf0, + 0x02,0x00,0x01,0x0f, 0x01,0x00,0x0c,0x00, + 0x04,0x0a,0x00,0x32, 0x00,0x32,0x08,0x2d, + 0x23,0x14,0x02,0x00, 0x01,0x0d,0x0a,0x00, + 0x32,0x00,0x32,0x08, 0x46,0x6e,0xaa,0x02, + 0x00,0x03,0x0a,0xff, 0xce,0xff,0xce,0x08, + 0xba,0x92,0x56,0x02, 0x00,0x16,0x0a,0x00, + 0x96,0xff,0x4c,0x08, 0xd3,0xdd,0xec,0x02, + 0x00,0x01,0x0a,0x00, 0x96,0xff,0xf1,0x08, + 0xd3,0xdd,0xec,0x02, 0x00,0x01,0x0a,0x00, + 0x96,0xff,0xfa,0x02, 0x00,0x01,0x0a,0xfd, + 0x5d,0x00,0x78,0x02, 0x00,0x01,0x0f,0x01, + 0x00,0x0c,0x00,0x04, 0x0a,0x00,0x19,0x00, + 0x19,0x08,0x32,0x19, 0x14,0x02,0x00,0x01, + 0x0d,0x0a,0x00,0x19, 0x00,0x19,0x08,0x32, + 0x96,0xaa,0x02,0x00, 0x03,0x0a,0xff,0xe7, + 0xff,0xe7,0x08,0xce, 0x6a,0x56,0x02,0x00, + 0x16,0x0a,0x00,0x4b, 0xff,0xa6,0x08,0xce, + 0xe7,0xec,0x02,0x00, 0x01,0x0a,0x00,0x4b, + 0xff,0xf9,0x08,0xce, 0xe7,0xec,0x02,0x00, + 0x01,0x0a,0x00,0x4b, 0xff,0xf9,0x02,0x00, + 0x01,0x0a,0xfe,0xd4, 0x00,0x3c,0x02,0x00, + 0x01,0x0f, +}; + +static ANIMATION anm_mark_form_800C3450 = { + PCX_Q_MARK, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 500, // raise + 1, // amb + 80, // size_w + 80, // size_h + 0, // v + NULL, // pre_script + (char *)anm_mark_data_800E011C // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char anm_mark_data_800E0220[] = { + 0x00,0x04,0x01,0x00, 0x05,0x02,0x00,0x01, + 0x0f +}; + +static ANIMATION anm_mark_form_800C346C = { + PCX_HEART, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 0, // raise + 1, // amb + 70, // size_w + 70, // size_h + 255, // v + NULL, // pre_script + (char *)anm_mark_data_800E0220 // script +}; + +/*---------------------------------------------------------------------------*/ + +static unsigned short mark_tex_list[] = { + PCX_B_MARK, // "!" Bikkuri mark + PCX_Q_MARK, // "?" Question mark + PCX_HEART // "❤" Heart +}; +#define TEX_B_MARK 0 +#define TEX_Q_MARK 1 +#define TEX_HEART 2 + +/*---------------------------------------------------------------------------*/ + +void *AN_Unknown_800CA1EC( MATRIX *mat, int mark ) +{ + SVECTOR *pos; + PRESCRIPT pre; + ANIMATION *anim; + + anim = 0; + pos = &pre.pos; + pos->vx = 0; + pos->vy = 800; + pos->vz = 0; + + pre.speed = DG_ZeroVector; + pre.scr_num = 0; + pre.s_anim = 0; + + switch ( mark ) + { + case 0: + anm_mark_form_800C3418.tex = mark_tex_list[TEX_B_MARK]; + anim = &anm_mark_form_800C3418; + break; + case 1: + anm_mark_form_800C3418.tex = mark_tex_list[TEX_Q_MARK]; + anim = &anm_mark_form_800C3418; + break; + case 2: + anm_mark_form_800C3434.tex = mark_tex_list[TEX_B_MARK]; + anim = &anm_mark_form_800C3434; + break; + case 3: + anm_mark_form_800C3434.tex = mark_tex_list[TEX_Q_MARK]; + anim = &anm_mark_form_800C3434; + break; + case 4: + anm_mark_form_800C3450.tex = mark_tex_list[TEX_B_MARK]; + anim = &anm_mark_form_800C3450; + break; + case 5: + anm_mark_form_800C3450.tex = mark_tex_list[TEX_Q_MARK]; + anim = &anm_mark_form_800C3450; + break; + case 6: + anm_mark_form_800C3418.tex = mark_tex_list[TEX_HEART]; + anim = &anm_mark_form_800C3418; + pre.scr_num = 1; + break; + } + + anim->pre_script = ⪯ + return NewAnime( mat, 0, anim ); +} + +void *AN_Unknown_800CA320( MATRIX *mat, int mark ) +{ + SVECTOR *pos; + PRESCRIPT pre; + ANIMATION *anim; + + anim = 0; + pos = &pre.pos; + pos->vx = 0; + pos->vy = 600; + pos->vz = 0; + + pre.speed = DG_ZeroVector; + pre.scr_num = 1; + pre.s_anim = 0; + + switch ( mark ) + { + case 0: + anm_mark_form_800C3418.tex = mark_tex_list[TEX_B_MARK]; + anim = &anm_mark_form_800C3418; + break; + case 1: + anm_mark_form_800C3418.tex = mark_tex_list[TEX_Q_MARK]; + anim = &anm_mark_form_800C3418; + break; + case 2: + anm_mark_form_800C3434.tex = mark_tex_list[TEX_B_MARK]; + anim = &anm_mark_form_800C3434; + break; + case 3: + anm_mark_form_800C3434.tex = mark_tex_list[TEX_Q_MARK]; + anim = &anm_mark_form_800C3434; + break; + case 4: + anm_mark_form_800C3450.tex = mark_tex_list[TEX_B_MARK]; + anim = &anm_mark_form_800C3450; + break; + case 5: + anm_mark_form_800C3450.tex = mark_tex_list[TEX_Q_MARK]; + anim = &anm_mark_form_800C3450; + break; + case 6: + anm_mark_form_800C3418.tex = mark_tex_list[TEX_HEART]; + anim = &anm_mark_form_800C3418; + pre.scr_num = 2; + break; + } + + anim->pre_script = ⪯ + return NewAnime( mat, 0, anim ); +} + +void *AN_Unknown_800CA458( MATRIX *mat, int mark ) +{ + SVECTOR *pos; + PRESCRIPT pre; + ANIMATION *anim; + + anim = 0; + pos = &pre.pos; + pos->vx = 0; + pos->vy = 120; + pos->vz = 0; + + pre.speed = DG_ZeroVector; + pre.scr_num = 2; + pre.s_anim = 0; + + switch ( mark ) + { + case 0: + anm_mark_form_800C3418.tex = mark_tex_list[TEX_B_MARK]; + anim = &anm_mark_form_800C3418; + pre.scr_num = 3; + break; + case 1: + anm_mark_form_800C3418.tex = mark_tex_list[TEX_Q_MARK]; + anim = &anm_mark_form_800C3418; + break; + case 2: + anm_mark_form_800C3434.tex = mark_tex_list[TEX_B_MARK]; + anim = &anm_mark_form_800C3434; + break; + case 3: + anm_mark_form_800C3434.tex = mark_tex_list[TEX_Q_MARK]; + anim = &anm_mark_form_800C3434; + break; + case 4: + anm_mark_form_800C3450.tex = mark_tex_list[TEX_B_MARK]; + anim = &anm_mark_form_800C3450; + break; + case 5: + anm_mark_form_800C3450.tex = mark_tex_list[TEX_Q_MARK]; + anim = &anm_mark_form_800C3450; + break; + case 6: + anm_mark_form_800C3418.tex = mark_tex_list[TEX_HEART]; + anim = &anm_mark_form_800C3418; + pre.scr_num = 3; + break; + } + + anim->pre_script = ⪯ + return NewAnime( mat, 0, anim ); +} + +void AN_Unknown_800CA594( SVECTOR *pos ) +{ + ANIMATION *anm; + PRESCRIPT pre; + + pre.pos = *pos; + pre.speed = DG_ZeroVector; + pre.scr_num = 0; + pre.s_anim = 0; + + anm = &anm_mark_form_800C346C; + anm->pre_script = ⪯ + + NewAnime( NULL, 0, anm ); +} diff --git a/components/mgs/mgs/source/anime/effect/rcm.c b/components/mgs/mgs/source/anime/effect/rcm.c new file mode 100644 index 00000000..7f1e4e56 --- /dev/null +++ b/components/mgs/mgs/source/anime/effect/rcm.c @@ -0,0 +1,265 @@ +#include "common.h" +#include "libdg/libdg.h" +#include "anime/animconv/anime.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +static const char anim_data_800E46F8[] = { + 0x00,0x1f,0x04,0x00, 0x0b,0x00,0x0f,0x00, + 0x18,0x00,0x21,0x02, 0x00,0x01,0x0f,0x0a, + 0xff,0xce,0xff,0xce, 0x02,0x00,0x01,0x0f, + 0x0a,0xff,0x9c,0xff, 0x9c,0x02,0x00,0x01, + 0x0f,0x0a,0xff,0x6a, 0xff,0x6a,0x02,0x00, + 0x01,0x0f,0x00,0x00 +}; + +static ANIMATION anm_800C3C04 = { + PCX_RCM_L, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 230, // raise + 1, // amb + 300, // size_w + 300, // size_h + 255, // v + NULL, // pre_script + (char *)anim_data_800E46F8 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char anim_data_800E4724[] = { + 0x00,0x10,0x01,0x00, 0x05,0x02,0x00,0x01, + 0x08,0xf0,0xf0,0xf0, 0x0a,0x03,0xe8,0x03, + 0xe8,0x02,0x00,0x01, 0x0f,0x00,0x00,0x00 +}; + +static ANIMATION anm_800C3C20 = { + PCX_RCM_L, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 230, // raise + 1, // amb + 1000, // size_w + 1000, // size_h + 32, // v + NULL, // pre_script + (char *)anim_data_800E4724 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char anim_data_800E473C[] = { + 0x00,0x08,0x01,0x00, 0x05,0x0c,0x00,0x3c, + 0x02,0x00,0x01,0x0d, 0x0f,0x00,0x00,0x00 +}; + +static ANIMATION anm_800C3C3C = { + PCX_RCM_L, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 500, // raise + 1, // amb + 200, // size_w + 200, // size_h + 255, // v + NULL, // pre_script + (char *)anim_data_800E473C // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char anim_data_800E474C[] = { + 0x00,0x0d,0x02,0x00, 0x07,0x00,0x0b,0x02, + 0x00,0x01,0x0f,0x0a, 0xfe,0x70,0xfe,0x70, + 0x02,0x00,0x01,0x0f +}; + +static ANIMATION anm_800C3C58 = { + PCX_RCM_L, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 500, // raise + 1, // amb + 2000, // size_w + 2000, // size_h + 64, // v + NULL, // pre_script + (char *)anim_data_800E474C // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char anim_data_800E4760[] = { + 0x00,0x04,0x01,0x00, 0x05,0x02,0x00,0x01, + 0x0f,0x00,0x00,0x00 +}; + +static ANIMATION anm_800C3C74 = { + PCX_RCM_L, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 0, // raise + 0, // amb + 20, // size_w + 20, // size_h + 255, // v + NULL, // pre_script + (char *)anim_data_800E4760 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char anim_data_800E476C[] = { + 0x00,0x13,0x01,0x00, 0x05,0x08,0xc8,0xc8, + 0xff,0x02,0x00,0x01, 0x0c,0x00,0x1f,0x08, + 0xfa,0xfa,0xf8,0x02, 0x00,0x01,0x0d,0x0f +}; + +static ANIMATION anm_800C3C90 = { + PCX_W_BONBORI, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 230, // raise + 1, // amb + 0, // size_w + 0, // size_h + 255, // v + NULL, // pre_script + (char *)anim_data_800E476C // script +}; + +/*---------------------------------------------------------------------------*/ + +void s01a_object_800D93BC(MATRIX *world) +{ + PRESCRIPT prescript; + SVECTOR *pos, *speed; + + pos = &prescript.pos; + + pos->vx = world->t[0]; + pos->vy = world->t[1]; + pos->vz = world->t[2]; + + speed = &prescript.speed; + + speed->vx = 0; + speed->vy = 0; + speed->vz = 0; + + prescript.scr_num = 1; + prescript.s_anim = 0; + + anm_800C3C04.pre_script = &prescript; + NewAnime(NULL, NULL, &anm_800C3C04); +} + +void s01a_object_800D9424(SVECTOR *pos, short scr_num) +{ + PRESCRIPT prescript; + + prescript.pos = *pos; + prescript.speed = DG_ZeroVector; + prescript.scr_num = scr_num; + prescript.s_anim = 0; + + anm_800C3C04.pre_script = &prescript; + NewAnime(NULL, NULL, &anm_800C3C04); +} + +void s01a_object_800D94A8(SVECTOR *pos) +{ + PRESCRIPT prescript; + + prescript.pos = *pos; + prescript.speed = DG_ZeroVector; + prescript.scr_num = 0; + prescript.s_anim = 0; + + anm_800C3C3C.pre_script = &prescript; + NewAnime(NULL, NULL, &anm_800C3C3C); +} + +void s01a_object_800D952C(SVECTOR *pos, short scr_num) +{ + PRESCRIPT prescript; + + prescript.pos = *pos; + prescript.speed = DG_ZeroVector; + prescript.scr_num = scr_num; + prescript.s_anim = 0; + + anm_800C3C58.pre_script = &prescript; + NewAnime(NULL, NULL, &anm_800C3C58); +} + +void s01a_object_800D95B0(SVECTOR *pos) +{ + PRESCRIPT prescript; + + prescript.pos = *pos; + prescript.speed = DG_ZeroVector; + prescript.scr_num = 0; + prescript.s_anim = 0; + + anm_800C3C74.pre_script = &prescript; + NewAnime(NULL, NULL, &anm_800C3C74); +} + +void s01a_object_800D9634(SVECTOR *pos) +{ + PRESCRIPT prescript; + + prescript.pos = *pos; + prescript.speed = DG_ZeroVector; + prescript.scr_num = 0; + prescript.s_anim = 0; + + anm_800C3C20.pre_script = &prescript; + NewAnime(NULL, NULL, &anm_800C3C20); +} + +void s01a_object_800D96B8(MATRIX *world, int arg1, char arg2, char arg3, char arg4, char arg5, char arg6, char arg7, char arg8, char *ptr) +{ + ANIMATION anm; + PRESCRIPT pre; + + memset(&pre, 0, sizeof(PRESCRIPT)); + + pre.pos.vx = world->t[0]; + pre.pos.vy = world->t[1]; + pre.pos.vz = world->t[2]; + + anm = anm_800C3C90; + + memcpy(ptr, anm.script, 32); + + anm.script = ptr; + anm.pre_script = ⪯ + anm.size_w = arg1; + anm.size_h = arg1; + + anm.script[6] = arg2; + anm.script[7] = arg3; + anm.script[8] = arg4; + anm.script[14] = arg5; + anm.script[16] = arg6; + anm.script[17] = arg7; + anm.script[18] = arg8; + + NewAnime(NULL, 0, &anm); +} diff --git a/components/mgs/mgs/source/anime/effect/rcm2.c b/components/mgs/mgs/source/anime/effect/rcm2.c new file mode 100644 index 00000000..9b63dd9e --- /dev/null +++ b/components/mgs/mgs/source/anime/effect/rcm2.c @@ -0,0 +1,135 @@ +#include "common.h" +#include "libdg/libdg.h" +#include "anime/animconv/anime.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +static const char anim_data_800D13CC[] = { + 0x00,0x23,0x02,0x00, 0x07,0x00,0x1a,0x02, + 0x00,0x01,0x08,0x6a, 0x6a,0x6a,0x0c,0x00, + 0x0a,0x08,0xf6,0xf6, 0xf6,0x02,0x00,0x01, + 0x0d,0x0f,0x08,0x6a, 0x6a,0x6a,0x0c,0x00, + 0x05,0x08,0xec,0xec, 0xec,0x02,0x00,0x01, + 0x0d,0x0f +}; + +static ANIMATION anm_800C32F0 = { + PCX_RCM_L, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 250, // raise + 1, // amb + 40, // size_w + 40, // size_h + 255, // v + NULL, // pre_script + (char *)anim_data_800D13CC // script +}; +static ANIMATION anm_800C330C = { + PCX_RCM_L, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 2500, // raise + 1, // amb + 40, // size_w + 40, // size_h + 255, // v + NULL, // pre_script + (char *)anim_data_800D13CC // script +}; + +/*---------------------------------------------------------------------------*/ + +void s08c_800C4194(MATRIX *pos) +{ + PRESCRIPT prescript; + MATRIX mat; + SVECTOR svec; + SVECTOR *pos2, *speed; + + svec.vx = 0; + svec.vy = 18; + svec.vz = 130; + + speed = &prescript.speed; + speed->vx = 0; + speed->vy = 0; + speed->vz = 0; + DG_SetPos(pos); + DG_MovePos(&svec); + ReadRotMatrix(&mat); + + pos2 = &prescript.pos; + pos2->vx = mat.t[0]; + pos2->vy = mat.t[1]; + pos2->vz = mat.t[2]; + + prescript.scr_num = 0; + prescript.s_anim = 0; + + anm_800C32F0.pre_script = &prescript; + NewAnime(NULL, NULL, &anm_800C32F0); +} + +void s08c_800C4220(MATRIX *pos) +{ + PRESCRIPT prescript; + MATRIX mat; + SVECTOR svec; + SVECTOR *pos2, *speed; + + svec.vx = 0; + svec.vy = 18; + svec.vz = 130; + + speed = &prescript.speed; + speed->vx = 0; + speed->vy = 0; + speed->vz = 0; + DG_SetPos(pos); + DG_MovePos(&svec); + ReadRotMatrix(&mat); + + pos2 = &prescript.pos; + pos2->vx = mat.t[0]; + pos2->vy = mat.t[1]; + pos2->vz = mat.t[2]; + + prescript.scr_num = 1; + prescript.s_anim = 0; + + anm_800C32F0.pre_script = &prescript; + NewAnime(NULL, NULL, &anm_800C32F0); +} + +void s08c_800C42B0(SVECTOR *pos) +{ + PRESCRIPT prescript; + MATRIX mat; + SVECTOR svec; + SVECTOR *speed; + + prescript.pos.vx = pos->vx; + prescript.pos.vy = pos->vy; + prescript.pos.vz = pos->vz; + + svec.vx = 0; + svec.vy = 18; + svec.vz = 130; + + speed = &prescript.speed; + speed->vx = 0; + speed->vy = 0; + speed->vz = 0; + + prescript.scr_num = 0; + prescript.s_anim = 0; + + anm_800C330C.pre_script = &prescript; + NewAnime(NULL, NULL, &anm_800C330C); +} diff --git a/components/mgs/mgs/source/anime/effect/sleep.c b/components/mgs/mgs/source/anime/effect/sleep.c new file mode 100644 index 00000000..ce3cfb6e --- /dev/null +++ b/components/mgs/mgs/source/anime/effect/sleep.c @@ -0,0 +1,68 @@ +#include "common.h" +#include "libdg/libdg.h" +#include "anime/animconv/anime.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +static const char anm_sleep_data[] = { + 0x00,0x96,0x03,0x00, 0x09,0x00,0x3B,0x00, + 0x6d,0x08,0x00,0x02, 0x1e,0x02,0x00,0x01, + 0x0c,0x00,0x19,0x08, 0x04,0x06,0x06,0x0a, + 0x00,0x04,0x00,0x04, 0x04,0x00,0x04,0x00, + 0x14,0x00,0x00,0x02, 0x00,0x01,0x0d,0x0c, + 0x00,0x0f,0x0a,0x00, 0x04,0x00,0x04,0x04, + 0x00,0x04,0x00,0x14, 0x00,0x00,0x02,0x00, + 0x01,0x0d,0x0f,0x08, 0x00,0x02,0x1e,0x02, + 0x00,0x0b,0x0c,0x00, 0x19,0x08,0x04,0x06, + 0x06,0x0a,0x00,0x04, 0x00,0x04,0x04,0x00, + 0x04,0x00,0x14,0x00, 0x00,0x02,0x00,0x01, + 0x0d,0x0c,0x00,0x0f, 0x0a,0x00,0x04,0x00, + 0x04,0x04,0x00,0x04, 0x00,0x14,0x00,0x00, + 0x02,0x00,0x01,0x0d, 0x0f,0x08,0x00,0x02, + 0x1e,0x02,0x00,0x15, 0x0c,0x00,0x19,0x08, + 0x04,0x06,0x06,0x0a, 0x00,0x04,0x00,0x04, + 0x04,0x00,0x04,0x00, 0x14,0x00,0x00,0x02, + 0x00,0x01,0x0d,0x0c, 0x00,0x0f,0x0a,0x00, + 0x04,0x00,0x04,0x04, 0x00,0x04,0x00,0x14, + 0x00,0x00,0x02,0x00, 0x01,0x0d,0x0f +}; + +static ANIMATION anm_sleep_form = { + PCX_ZZZ, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 0, // raise + 1, // amb + 2, // size_w + 2, // size_h + 0, // v + NULL, // pre_script + (char *)anm_sleep_data // script +}; + +/*---------------------------------------------------------------------------*/ + +void AN_Sleep( SVECTOR *pos ) +{ + ANIMATION *anm; + PRESCRIPT pre; + + pre.pos = *pos; + pre.speed = DG_ZeroVector; + pre.s_anim = 0; + + anm = &anm_sleep_form; + anm->pre_script = ⪯ + + pre.scr_num = 0; + NewAnime( NULL, 0, anm ); + + pre.scr_num = 1; + NewAnime( NULL, 0, anm ); + + pre.scr_num = 2; + NewAnime( NULL, 0, anm ); +} diff --git a/components/mgs/mgs/source/anime/effect/smoke.c b/components/mgs/mgs/source/anime/effect/smoke.c new file mode 100644 index 00000000..933d6723 --- /dev/null +++ b/components/mgs/mgs/source/anime/effect/smoke.c @@ -0,0 +1,209 @@ +#include "common.h" +#include "libdg/libdg.h" +#include "anime/animconv/anime.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +static const char anm_piyopiyo_data[] = { + 0x02,0x97,0x03,0x00, 0x09,0x00,0xe6,0x01, + 0xc3,0x08,0x00,0xfc, 0xde,0x04,0x00,0x64, + 0x01,0xf4,0xff,0x53, 0x0c,0x00,0x03,0x02, + 0x00,0x01,0x0d,0x0c, 0x00,0x03,0x0a,0xfe, + 0x70,0x00,0x00,0x04, 0xff,0x9c,0x00,0x00, + 0xff,0xe5,0x0c,0x00, 0x03,0x02,0x00,0x01, + 0x0d,0x04,0xff,0x9c, 0x00,0x00,0x00,0x1b, + 0x0c,0x00,0x03,0x02, 0x00,0x01,0x0d,0x0a, + 0x01,0x90,0x00,0x00, 0x04,0xff,0xb7,0x00, + 0x00,0x00,0x49,0x0c, 0x00,0x03,0x02,0x00, + 0x01,0x0d,0x04,0xff, 0xe5,0x00,0x00,0x00, + 0x64,0x0c,0x00,0x03, 0x02,0x00,0x01,0x0d, + 0x0a,0xfe,0x70,0x00, 0x00,0x04,0x00,0x1b, + 0x00,0x00,0x00,0x64, 0x0c,0x00,0x03,0x02, + 0x00,0x01,0x0d,0x04, 0x00,0x49,0x00,0x00, + 0x00,0x49,0x0c,0x00, 0x03,0x02,0x00,0x01, + 0x0d,0x0a,0x01,0x90, 0x00,0x00,0x04,0x00, + 0x64,0x00,0x00,0x00, 0x1b,0x0c,0x00,0x03, + 0x02,0x00,0x01,0x0d, 0x04,0x00,0x64,0x00, + 0x00,0xff,0xe5,0x0c, 0x00,0x03,0x02,0x00, + 0x01,0x0d,0x0a,0xfe, 0x70,0x00,0x00,0x04, + 0x00,0x49,0x00,0x00, 0xff,0xb7,0x0c,0x00, + 0x03,0x02,0x00,0x01, 0x0d,0x04,0x00,0x1b, + 0x00,0x00,0xff,0x9c, 0x0c,0x00,0x03,0x02, + 0x00,0x01,0x0d,0x0a, 0x01,0x90,0x00,0x00, + 0x04,0xff,0xe5,0x00, 0x00,0xff,0x9c,0x0c, + 0x00,0x03,0x02,0x00, 0x01,0x0d,0x04,0xff, + 0xb7,0x00,0x00,0xff, 0xb7,0x0c,0x00,0x03, + 0x02,0x00,0x01,0x0d, 0x0d,0x0f,0x08,0x00, + 0xfc,0xde,0x04,0xff, 0x38,0x01,0xf4,0x00, + 0x00,0x0c,0x00,0x03, 0x02,0x00,0x01,0x0d, + 0x0c,0x00,0x03,0x0a, 0xfe,0x70,0x00,0x00, + 0x04,0x00,0x1b,0x00, 0x00,0x00,0x64,0x0c, + 0x00,0x03,0x02,0x00, 0x01,0x0d,0x04,0x00, + 0x49,0x00,0x00,0x00, 0x49,0x0c,0x00,0x03, + 0x02,0x00,0x01,0x0d, 0x0a,0x01,0x90,0x00, + 0x00,0x04,0x00,0x64, 0x00,0x00,0x00,0x1b, + 0x0c,0x00,0x03,0x02, 0x00,0x01,0x0d,0x04, + 0x00,0x64,0x00,0x00, 0xff,0xe5,0x0c,0x00, + 0x03,0x02,0x00,0x01, 0x0d,0x0a,0xfe,0x70, + 0x00,0x00,0x04,0x00, 0x49,0x00,0x00,0xff, + 0xb7,0x0c,0x00,0x03, 0x02,0x00,0x01,0x0d, + 0x04,0x00,0x1b,0x00, 0x00,0xff,0x9c,0x0c, + 0x00,0x03,0x02,0x00, 0x01,0x0d,0x0a,0x01, + 0x90,0x00,0x00,0x04, 0xff,0xe5,0x00,0x00, + 0xff,0x9c,0x0c,0x00, 0x03,0x02,0x00,0x01, + 0x0d,0x04,0xff,0xb7, 0x00,0x00,0xff,0xb7, + 0x0c,0x00,0x03,0x02, 0x00,0x01,0x0d,0x0a, + 0xfe,0x70,0x00,0x00, 0x04,0xff,0x9c,0x00, + 0x00,0xff,0xe5,0x0c, 0x00,0x03,0x02,0x00, + 0x01,0x0d,0x04,0xff, 0x9c,0x00,0x00,0x00, + 0x1b,0x0c,0x00,0x03, 0x02,0x00,0x01,0x0d, + 0x0a,0x01,0x90,0x00, 0x00,0x04,0xff,0xb7, + 0x00,0x00,0x00,0x49, 0x0c,0x00,0x03,0x02, + 0x00,0x01,0x0d,0x04, 0xff,0xe5,0x00,0x00, + 0x00,0x64,0x0c,0x00, 0x03,0x02,0x00,0x01, + 0x0d,0x0d,0x0f,0x08, 0x00,0xfc,0xde,0x04, + 0x00,0x64,0x01,0xf4, 0x00,0xad,0x0c,0x00, + 0x03,0x02,0x00,0x01, 0x0d,0x0c,0x00,0x03, + 0x0a,0xfe,0x70,0x00, 0x00,0x04,0x00,0x49, + 0x00,0x00,0xff,0xb7, 0x0c,0x00,0x03,0x02, + 0x00,0x01,0x0d,0x04, 0x00,0x1b,0x00,0x00, + 0xff,0x9c,0x0c,0x00, 0x03,0x02,0x00,0x01, + 0x0d,0x0a,0x01,0x90, 0x00,0x00,0x04,0xff, + 0xe5,0x00,0x00,0xff, 0x9c,0x0c,0x00,0x03, + 0x02,0x00,0x01,0x0d, 0x04,0xff,0xb7,0x00, + 0x00,0xff,0xb7,0x0c, 0x00,0x03,0x02,0x00, + 0x01,0x0d,0x0a,0xfe, 0x70,0x00,0x00,0x04, + 0xff,0x9c,0x00,0x00, 0xff,0xe5,0x0c,0x00, + 0x03,0x02,0x00,0x01, 0x0d,0x04,0xff,0x9c, + 0x00,0x00,0x00,0x1b, 0x0c,0x00,0x03,0x02, + 0x00,0x01,0x0d,0x0a, 0x01,0x90,0x00,0x00, + 0x04,0xff,0xb7,0x00, 0x00,0x00,0x49,0x0c, + 0x00,0x03,0x02,0x00, 0x01,0x0d,0x04,0xff, + 0xe5,0x00,0x00,0x00, 0x64,0x0c,0x00,0x03, + 0x02,0x00,0x01,0x0d, 0x0a,0xfe,0x70,0x00, + 0x00,0x04,0x00,0x1b, 0x00,0x00,0x00,0x64, + 0x0c,0x00,0x03,0x02, 0x00,0x01,0x0d,0x04, + 0x00,0x49,0x00,0x00, 0x00,0x49,0x0c,0x00, + 0x03,0x02,0x00,0x01, 0x0d,0x0a,0x01,0x90, + 0x00,0x00,0x04,0x00, 0x64,0x00,0x00,0x00, + 0x1b,0x0c,0x00,0x03, 0x02,0x00,0x01,0x0d, + 0x04,0x00,0x64,0x00, 0x00,0xff,0xe5,0x0c, + 0x00,0x03,0x02,0x00, 0x01,0x0d,0x0d,0x0f +}; + +static ANIMATION anm_piyopiyo_form = { + PCX_HOSI, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 0, // raise + 1, // amb + 200, // size_w + 200, // size_h + 128, // v + NULL, // pre_script + (char *)anm_piyopiyo_data // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char s00a_dword_800E054C[] = { + 0x00,0x57,0x01,0x00, 0x05,0x0c,0x00,0x1c, + 0x0a,0x00,0x05,0x00, 0x05,0x04,0x00,0x00, + 0x00,0x0a,0x00,0x00, 0x02,0x00,0x01,0x0d, + 0x0c,0x00,0x04,0x0c, 0x00,0x05,0x0a,0xff, + 0xf6,0xff,0xf6,0x04, 0x00,0x00,0x00,0x0a, + 0x00,0x00,0x02,0x00, 0x01,0x0d,0x02,0x00, + 0x01,0x0c,0x00,0x05, 0x0a,0x00,0x0a,0x00, + 0x0a,0x04,0x00,0x00, 0x00,0x0a,0x00,0x00, + 0x02,0x00,0x01,0x0d, 0x02,0x00,0x01,0x0d, + 0x0c,0x00,0x0e,0x0a, 0xff,0xf6,0xff,0xf6, + 0x04,0x00,0x00,0x00, 0x0a,0x00,0x00,0x02, + 0x00,0x01,0x0d,0x0f +}; + +static ANIMATION s00a_dword_800C34C8 = { + 0x4184, // tex + 1, // texdev_x + 1, // texdev_y + 1, // n_anime + 1, // n_verts + 0, // raise + 5, // amb + 10, // size_w + 10, // size_h + 128, // v + NULL, // pre_script + (char *)s00a_dword_800E054C // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char s00a_dword_800E05A8[] = { + 0x00,0x41,0x01,0x00, 0x05,0x01,0xfe,0x0c, + 0x00,0x1c,0x01,0xff, 0x0a,0x00,0x02,0x00, + 0x0a,0x04,0x00,0x00, 0x00,0x0a,0x00,0x00, + 0x02,0x00,0x01,0x0d, 0x0c,0x00,0x04,0x0c, + 0x00,0x05,0x01,0xff, 0x04,0x00,0x00,0x00, + 0x0a,0x00,0x00,0x02, 0x00,0x01,0x0d,0x0d, + 0x0c,0x00,0x0e,0x01, 0xff,0x0a,0xff,0xfc, + 0xff,0xec,0x04,0x00, 0x00,0x00,0x0a,0x00, + 0x00,0x02,0x00,0x01, 0x0d,0x0f +}; + +static ANIMATION s00a_dword_800C34E4 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 0, // raise + 1, // amb + 10, // size_w + 10, // size_h + 255, // v + NULL, // pre_script + (char *)s00a_dword_800E05A8 // script +}; + +/*---------------------------------------------------------------------------*/ + +void AN_Piyopiyo( SVECTOR *pos ) +{ + ANIMATION *anm; + PRESCRIPT pre; + + pre.pos = *pos; + pre.speed = DG_ZeroVector; + pre.s_anim = 0; + + anm = &anm_piyopiyo_form; + anm->pre_script = ⪯ + + pre.scr_num = 0; + NewAnime( NULL, 0, anm ); + + pre.scr_num = 1; + NewAnime( NULL, 0, anm ); + + pre.scr_num = 2; + NewAnime( NULL, 0, anm ); +} + +void s00a_command_800CA758( SVECTOR* pos ) +{ + ANIMATION *anm; + PRESCRIPT pre; + + pre.pos = *pos; + pre.speed = DG_ZeroVector; + pre.s_anim = 0; + + anm = &s00a_dword_800C34E4; + anm->pre_script = ⪯ + pre.scr_num = 0; + + NewAnime( NULL, 0, anm ); +} diff --git a/components/mgs/mgs/source/anime/effect/smoke2.c b/components/mgs/mgs/source/anime/effect/smoke2.c new file mode 100644 index 00000000..b6e3faed --- /dev/null +++ b/components/mgs/mgs/source/anime/effect/smoke2.c @@ -0,0 +1,123 @@ +#include "common.h" +#include "libgv/libgv.h" +#include "anime/animconv/anime.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +static const char s03c_dword_800D7B34[] = { + 0x00,0x15,0x01,0x00, 0x05,0x01,0xfe,0x0c, + 0x00,0x07,0x0a,0x00, 0xc8,0x00,0xc8,0x01, + 0xff,0x08,0xf8,0xf8, 0xf8,0x02,0x00,0x01, + 0x0d,0x0f +}; + +static ANIMATION s03c_dword_800C33DC = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 0, // raise + 2, // amb + 1500, // size_w + 1500, // size_h + 64, // v + NULL, // pre_script + (char *)s03c_dword_800D7B34 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char s03c_dword_800D7B50[] = { + 0x00,0x36,0x02,0x00, 0x07,0x00,0x1c,0x01, + 0xfe,0x0c,0x00,0x07, 0x0a,0x01,0x2c,0x01, + 0x2c,0x01,0xff,0x08, 0xf8,0xf8,0xf8,0x02, + 0x00,0x01,0x0d,0x0f, 0x01,0xfe,0x0a,0x01, + 0xf4,0x01,0xf4,0x08, 0xbe,0xbe,0xbe,0x02, + 0x00,0x01,0x0c,0x00, 0x0f,0x0a,0x00,0xc8, + 0x00,0xc8,0x01,0xff, 0x08,0xf0,0xf0,0xf0, + 0x02,0x00,0x01,0x0d, 0x0f +}; + +static ANIMATION s03c_dword_800C33F8 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 0, // raise + 1, // amb + 800, // size_w + 800, // size_h + 64, // v + NULL, // pre_script + (char *)s03c_dword_800D7B50 // script +}; + +/*---------------------------------------------------------------------------*/ + +void AN_Unknown_800CCA40(SVECTOR *pos) +{ + PRESCRIPT prescript; + SVECTOR *prepos, *speed; + ANIMATION *anime; + + prepos = &prescript.pos; + prepos->vx = pos->vx + GV_RandS(512) - 64; + prepos->vy = pos->vy + GV_RandS(256) - 128; + prepos->vz = pos->vz; + + speed = &prescript.speed; + speed->vx = GV_RandS(64); + speed->vy = GV_RandU(128) + 64; + speed->vz = 0; + + prescript.scr_num = 0; + prescript.s_anim = 0; + + anime = &s03c_dword_800C33DC; + anime->pre_script = &prescript; + NewAnime(NULL, NULL, anime); + + if (!(GV_Time & 3)) + { + prepos->vx = pos->vx + GV_RandS(512) - 64; + prepos->vy = pos->vy + GV_RandS(256) - 128; + prepos->vz = pos->vz; + + speed->vx = GV_RandS(64); + speed->vy = GV_RandS(128) + 64; + speed->vz = 0; + + prescript.scr_num = 0; + prescript.s_anim = 0; + + anime = &s03c_dword_800C33F8; + anime->pre_script = &prescript; + NewAnime(NULL, NULL, anime); + } +} + +void AN_Unknown_800CCB84(SVECTOR *pos) +{ + PRESCRIPT prescript; + SVECTOR *prepos, *speed; + + prepos = &prescript.pos; + prepos->vx = pos->vx + GV_RandS(2048); + prepos->vy = pos->vy; + prepos->vz = pos->vz + GV_RandS(2048); + + speed = &prescript.speed; + speed->vx = 0; + speed->vy = GV_RandU(128) + 64; + speed->vz = 0; + + prescript.scr_num = 1; + prescript.s_anim = 0; + + s03c_dword_800C33F8.pre_script = &prescript; + + NewAnime(NULL, NULL, &s03c_dword_800C33F8); +} diff --git a/components/mgs/mgs/source/anime/effect/smoke3.c b/components/mgs/mgs/source/anime/effect/smoke3.c new file mode 100644 index 00000000..6eab4d0d --- /dev/null +++ b/components/mgs/mgs/source/anime/effect/smoke3.c @@ -0,0 +1,302 @@ +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "anime/animconv/anime.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +static const char s13a_dword_800E31C8[] = { + 0x00,0x15,0x01,0x00, 0x05,0x01,0xfe,0x0c, + 0x00,0x0a,0x01,0xff, 0x0a,0x00,0x06,0x00, + 0x06,0x08,0xfa,0xfa, 0xfa,0x02,0x00,0x01, + 0x0d,0x0f,0x00,0x00 +}; + +static ANIMATION anm_800C370C = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 8, // n_verts + 500, // raise + 3, // amb + 300, // size_w + 300, // size_h + 64, // v + NULL, // pre_script + (char *)s13a_dword_800E31C8 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char s13a_dword_800E31E4[] = { + 0x00,0x90,0x04,0x00, 0x0b,0x00,0x2c,0x00, + 0x51,0x00,0x76,0x0c, 0x00,0x05,0x01,0xff, + 0x0a,0x00,0x32,0x00, 0x32,0x02,0x00,0x01, + 0x0d,0x0c,0x00,0x0a, 0x01,0xff,0x0a,0x00, + 0x64,0x00,0x64,0x08, 0xf4,0xf4,0xf4,0x02, + 0x00,0x01,0x0d,0x0f, 0x08,0x00,0xc0,0xc0, + 0x0c,0x00,0x05,0x01, 0xff,0x0a,0x00,0x32, + 0x00,0x32,0x02,0x00, 0x01,0x0d,0x0c,0x00, + 0x0a,0x01,0xff,0x0a, 0x00,0x64,0x00,0x64, + 0x08,0xf4,0xfa,0xfa, 0x02,0x00,0x01,0x0d, + 0x0f,0x08,0xe0,0x00, 0xe0,0x0c,0x00,0x05, + 0x01,0xff,0x0a,0x00, 0x32,0x00,0x32,0x02, + 0x00,0x01,0x0d,0x0c, 0x00,0x0a,0x01,0xff, + 0x0a,0x00,0x64,0x00, 0x64,0x08,0xf7,0xf4, + 0xf7,0x02,0x00,0x01, 0x0d,0x0f,0x08,0xe0, + 0xe0,0x00,0x0c,0x00, 0x05,0x01,0xff,0x0a, + 0x00,0x32,0x00,0x32, 0x02,0x00,0x01,0x0d, + 0x0c,0x00,0x0a,0x01, 0xff,0x0a,0x00,0x64, + 0x00,0x64,0x08,0xf7, 0xf7,0xf4,0x02,0x00, + 0x01,0x0d,0x0f,0x00 +}; + +static ANIMATION anm_800C3728 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 500, // raise + 1, // amb + 300, // size_w + 300, // size_h + 128, // v + NULL, // pre_script + (char *)s13a_dword_800E31E4 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char s13a_dword_800E3280[] = { + 0x00,0x25,0x01,0x00, 0x05,0x08,0xff,0xff, + 0xff,0x0c,0x00,0x05, 0x01,0xff,0x0a,0x00, + 0x32,0x00,0x32,0x02, 0x00,0x01,0x0d,0x0c, + 0x00,0x0a,0x01,0xff, 0x0a,0x00,0x64,0x00, + 0x64,0x08,0xf4,0xf4, 0xf4,0x02,0x00,0x01, + 0x0d,0x0f,0x00,0x00 +}; + +static ANIMATION anm_800C3744 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 500, // raise + 1, // amb + 300, // size_w + 300, // size_h + 0, // v + NULL, // pre_script + (char *)s13a_dword_800E3280 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char s13a_dword_800E32AC[] = { + 0x00,0x13,0x01,0x00, 0x05,0x0c,0x00,0x09, + 0x01,0xff,0x0a,0x00, 0x78,0x00,0x78,0x08, + 0xe7,0xe7,0xe7,0x02, 0x00,0x01,0x0d,0x0f +}; + +static ANIMATION anm_800C3760 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 500, // raise + 3, // amb + 300, // size_w + 300, // size_h + 255, // v + NULL, // pre_script + (char *)s13a_dword_800E32AC // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char s13a_dword_800E32C4[] = { + 0x00,0x36,0x02,0x00, 0x07,0x00,0x1c,0x01, + 0xfe,0x0c,0x00,0x07, 0x0a,0x01,0x2c,0x01, + 0x2c,0x01,0xff,0x08, 0xf8,0xf8,0xf8,0x02, + 0x00,0x01,0x0d,0x0f, 0x01,0xfe,0x0a,0x01, + 0xf4,0x01,0xf4,0x08, 0xbe,0xbe,0xbe,0x02, + 0x00,0x01,0x0c,0x00, 0x0f,0x0a,0x00,0xc8, + 0x00,0xc8,0x01,0xff, 0x08,0xf0,0xf0,0xf0, + 0x02,0x00,0x01,0x0d, 0x0f,0x00,0x00,0x00 +}; + +static ANIMATION anm_800C377C = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 0, // raise + 1, // amb + 800, // size_w + 800, // size_h + 64, // v + NULL, // pre_script + (char *)s13a_dword_800E32C4 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char s13a_dword_800E3304[] = { + 0x00,0x15,0x01,0x00, 0x05,0x01,0xfe,0x0c, + 0x00,0x07,0x0a,0xff, 0xe2,0xff,0xe2,0x01, + 0xff,0x08,0xf8,0xf8, 0xf8,0x02,0x00,0x01, + 0x0d,0x0f +}; + +static ANIMATION anm_800C3798 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 0, // raise + 1, // amb + 400, // size_w + 400, // size_h + 32, // v + NULL, // pre_script + (char *)s13a_dword_800E3304 // script +}; + +/*---------------------------------------------------------------------------*/ + +void AN_Unknown_800DC4B4(SVECTOR *pos, int ang) +{ + SVECTOR rot; + SVECTOR speed; + char unused[0x20]; + PRESCRIPT pre[8]; + ANIMATION *anm; + int i; + + rot.vx = 0; + rot.vz = 0; + + for (i = 0; i < 8; i++, pos++) + { + pre[i].pos = *pos; + pre[i].scr_num = 0; + pre[i].s_anim = 0; + + speed.vx = 0; + speed.vy = 0; + speed.vz = GV_RandU(32) + 50; + + rot.vy = ang + GV_RandS(32); + + DG_SetPos2(&DG_ZeroVector, &rot); + DG_PutVector(&speed, &pre[i].speed, 1); + } + + anm = &anm_800C370C; + anm->pre_script = pre; + NewAnime(NULL, 0, anm); +} + +void AN_Unknown_800DC5B4(SVECTOR *pos, SVECTOR *speed, int script) +{ + PRESCRIPT pre; + SVECTOR *ppos; + ANIMATION *anm; + + ppos = &pre.pos; + ppos->vx = pos->vx + GV_RandS(32); + ppos->vy = pos->vy + GV_RandS(32); + ppos->vz = pos->vz + GV_RandS(32); + pre.speed = *speed; + pre.scr_num = script; + pre.s_anim = GV_RandU(1024) % 30; + + anm = &anm_800C3728; + anm->pre_script = ⪯ + NewAnime(NULL, 0, anm); +} + +void AN_Unknown_800DC6AC(SVECTOR *pos, SVECTOR *speed, char r, char g, char b) +{ + PRESCRIPT pre; + SVECTOR *ppos; + ANIMATION *anm; + + ppos = &pre.pos; + ppos->vx = pos->vx + GV_RandS(32); + ppos->vy = pos->vy + GV_RandS(32); + ppos->vz = pos->vz + GV_RandS(32); + pre.scr_num = 0; + pre.speed = *speed; + pre.s_anim = GV_RandU(1024) % 30; + + anm = &anm_800C3744; + anm->pre_script = ⪯ + anm->script[6] = r; + anm->script[7] = g; + anm->script[8] = b; + anm->script[34] = -((r - 1) / 10); + anm->script[35] = -((g - 1) / 10); + anm->script[36] = -((b - 1) / 10); + NewAnime(NULL, 0, anm); +} + +void AN_Unknown_800DC860(SVECTOR *pos, SVECTOR *speed) +{ + PRESCRIPT pre; + SVECTOR *ppos; + ANIMATION *anm; + + ppos = &pre.pos; + ppos->vx = pos->vx + GV_RandS(32); + ppos->vy = pos->vy + GV_RandS(32); + ppos->vz = pos->vz + GV_RandS(32); + pre.speed = *speed; + pre.scr_num = 0; + pre.s_anim = GV_RandU(1024) % 30; + + anm = &anm_800C3760; + anm->pre_script = ⪯ + NewAnime(NULL, 0, &anm_800C3760); +} + +void AN_Unknown_800DC94C(SVECTOR *pos) +{ + PRESCRIPT pre; + SVECTOR *speed; + ANIMATION *anm; + + pre.pos = *pos; + speed = &pre.speed; + speed->vx = 0; + speed->vy = GV_RandU(128) + 64; + speed->vz = 0; + pre.scr_num = 1; + pre.s_anim = 0; + + anm = &anm_800C377C; + anm->pre_script = ⪯ + NewAnime(NULL, 0, anm); +} + +void AN_Unknown_800DC9C8(SVECTOR *pos) +{ + PRESCRIPT pre; + ANIMATION *anm; + + pre.pos = *pos; + pre.speed = DG_ZeroVector; + pre.scr_num = 0; + pre.s_anim = 0; + + anm = &anm_800C3798; + anm->pre_script = ⪯ + NewAnime(NULL, 0, anm); +} diff --git a/components/mgs/mgs/source/anime/effect/smoke4.c b/components/mgs/mgs/source/anime/effect/smoke4.c new file mode 100644 index 00000000..d09dde5a --- /dev/null +++ b/components/mgs/mgs/source/anime/effect/smoke4.c @@ -0,0 +1,51 @@ +#include "common.h" +#include "libdg/libdg.h" +#include "anime/animconv/anime.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +static const char anm_panel_smoke_data[] = { + 0x00,0x33,0x01,0x00, 0x05,0x01,0xfe,0x0c, + 0x00,0x04,0x01,0xff, 0x0a,0x00,0x0a,0x00, + 0x0a,0x02,0x00,0x01, 0x0d,0x0c,0x00,0x20, + 0x01,0xff,0x04,0x00, 0x00,0x00,0x14,0x00, + 0x00,0x02,0x00,0x01, 0x0d,0x0c,0x00,0x2f, + 0x08,0xff,0xff,0xff, 0x01,0xff,0x0a,0x00, + 0x0a,0x00,0x0a,0x02, 0x00,0x01,0x0d,0x0f +}; + +static ANIMATION anm_panel_smoke = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 255, // raise + 1, // amb + 100, // size_w + 100, // size_h + 80, // v + NULL, // pre_script + (char *)anm_panel_smoke_data // script +}; + +/*---------------------------------------------------------------------------*/ + +void AN_PanelSmoke(SVECTOR *pos) +{ + PRESCRIPT sp10; + + sp10.pos = *pos; + + sp10.speed = DG_ZeroVector; + sp10.speed.vx = GV_RandU(4) - 2; + sp10.speed.vy = GV_RandU(4) - 2; + sp10.speed.vz = GV_RandU(4) - 2; + + sp10.s_anim = 0; + anm_panel_smoke.pre_script = &sp10; + sp10.scr_num = 0; + + NewAnime(NULL, NULL, &anm_panel_smoke); +} diff --git a/components/mgs/mgs/source/anime/effect/smoke_ln.c b/components/mgs/mgs/source/anime/effect/smoke_ln.c new file mode 100644 index 00000000..451527f5 --- /dev/null +++ b/components/mgs/mgs/source/anime/effect/smoke_ln.c @@ -0,0 +1,618 @@ +#include "common.h" +#include "libdg/libdg.h" +#include "anime/animconv/anime.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +static const char d16e_dword_800D291C[] = { + 0x00,0x15,0x01,0x00, 0x05,0x01,0xfe,0x0c, + 0x00,0x0a,0x0a,0x00, 0xc8,0x00,0xc8,0x01, + 0xff,0x08,0xe7,0xe7, 0xe7,0x02,0x00,0x01, + 0x0d,0x0f +}; + +static ANIMATION d16e_dword_800C3520 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 1000, // raise + 3, // amb + 4000, // size_w + 4000, // size_h + 255, // v + NULL, // pre_script + (char *)d16e_dword_800D291C // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char d16e_dword_800D2938[] = { + 0x00,0x15,0x01,0x00, 0x05,0x01,0xfe,0x0c, + 0x00,0x07,0x0a,0x00, 0xc8,0x00,0xc8,0x01, + 0xff,0x08,0xfc,0xfc, 0xfc,0x02,0x00,0x01, + 0x0d,0x0f +}; + +static ANIMATION d16e_dword_800C353C = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 1000, // raise + 3, // amb + 3000, // size_w + 3000, // size_h + 32, // v + NULL, // pre_script + (char *)d16e_dword_800D2938 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char d16e_dword_800D2954[] = { + 0x00,0x3a,0x01,0x00, 0x05,0x01,0xfe,0x0c, + 0x00,0x07,0x04,0x00, 0x00,0x00,0x96,0x00, + 0x00,0x0a,0x01,0x2c, 0x01,0x2c,0x02,0x00, + 0x01,0x04,0x00,0x00, 0x00,0x96,0x00,0x00, + 0x0a,0x01,0x2c,0x01, 0x2c,0x02,0x00,0x01, + 0x04,0x00,0x00,0x00, 0x96,0x00,0x00,0x0a, + 0x01,0x2c,0x01,0x2c, 0x01,0xff,0x08,0xf0, + 0xf0,0xf0,0x02,0x00, 0x01,0x0d,0x0f +}; + +static ANIMATION d16e_dword_800C3558 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 1000, // raise + 2, // amb + 1500, // size_w + 1500, // size_h + 128, // v + NULL, // pre_script + (char *)d16e_dword_800D2954 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char d16e_dword_800D2994[] = { + 0x00,0xa7,0x04,0x00, 0x0b,0x00,0x31,0x00, + 0x5c,0x00,0x87,0x01, 0xfe,0x0c,0x00,0x07, + 0x02,0x00,0x01,0x0a, 0x00,0x32,0x00,0x32, + 0x01,0xff,0x02,0x00, 0x01,0x0d,0x0c,0x00, + 0x07,0x01,0xff,0x02, 0x00,0x01,0x01,0xff, + 0x08,0xff,0xff,0xff, 0x02,0x00,0x01,0x0d, + 0x0f,0x0a,0x01,0xf4, 0x01,0xf4,0x01,0xfe, + 0x0c,0x00,0x07,0x02, 0x00,0x01,0x0a,0x00, + 0x64,0x00,0x64,0x01, 0xff,0x02,0x00,0x01, + 0x0d,0x0c,0x00,0x07, 0x01,0xff,0x02,0x00, + 0x01,0x01,0xff,0x08, 0xff,0xff,0xff,0x02, + 0x00,0x01,0x0d,0x0f, 0x0a,0x03,0xe8,0x03, + 0xe8,0x01,0xfe,0x0c, 0x00,0x07,0x02,0x00, + 0x01,0x0a,0x00,0xc8, 0x00,0x64,0x01,0xff, + 0x02,0x00,0x01,0x0d, 0x0c,0x00,0x07,0x01, + 0xff,0x02,0x00,0x01, 0x01,0xff,0x08,0xff, + 0xff,0xff,0x02,0x00, 0x01,0x0d,0x0f,0x0a, + 0x07,0xd0,0x07,0xd0, 0x01,0xfe,0x0c,0x00, + 0x07,0x02,0x00,0x01, 0x0a,0x01,0x90,0x01, + 0x90,0x01,0xff,0x02, 0x00,0x01,0x0d,0x0c, + 0x00,0x07,0x01,0xff, 0x02,0x00,0x01,0x01, + 0xff,0x08,0xff,0xff, 0xff,0x02,0x00,0x01, + 0x0d,0x0f +}; + +static ANIMATION d16e_dword_800C3574 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 1000, // raise + 2, // amb + 500, // size_w + 500, // size_h + 8, // v + NULL, // pre_script + (char *)d16e_dword_800D2994 // script +}; +static ANIMATION d16e_dword_800C3590 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 1000, // raise + 2, // amb + 500, // size_w + 500, // size_h + 16, // v + NULL, // pre_script + (char *)d16e_dword_800D2994 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char d16e_dword_800D2A48[] = { + 0x00,0x3a,0x01,0x00, 0x05,0x01,0xfe,0x0c, + 0x00,0x07,0x04,0x00, 0x00,0x00,0x96,0x00, + 0x00,0x0a,0x01,0x2c, 0x01,0x2c,0x02,0x00, + 0x01,0x04,0x00,0x00, 0x00,0x96,0x00,0x00, + 0x0a,0x01,0x2c,0x01, 0x2c,0x02,0x00,0x01, + 0x04,0x00,0x00,0x00, 0x96,0x00,0x00,0x0a, + 0x01,0x2c,0x01,0x2c, 0x01,0xff,0x08,0xfc, + 0xfc,0xfc,0x02,0x00, 0x01,0x0d,0x0f +}; + +static ANIMATION d16e_dword_800C35AC = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 3000, // raise + 2, // amb + 1000, // size_w + 1000, // size_h + 32, // v + NULL, // pre_script + (char *)d16e_dword_800D2A48 // script +}; +static ANIMATION d16e_dword_800C35C8 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 3000, // raise + 2, // amb + 1000, // size_w + 1000, // size_h + 32, // v + NULL, // pre_script + (char *)d16e_dword_800D2A48 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char d16e_dword_800D2A88[] = { + 0x00,0xab,0x04,0x00, 0x0b,0x00,0x32,0x00, + 0x5e,0x00,0x8a,0x01, 0xfe,0x0c,0x00,0x0f, + 0x0a,0x00,0x32,0x00, 0x32,0x01,0xff,0x02, + 0x00,0x01,0x0d,0x0c, 0x00,0x1e,0x01,0xff, + 0x02,0x00,0x01,0x0d, 0x0c,0x00,0x0f,0x01, + 0xff,0x08,0xf0,0xf0, 0xf0,0x02,0x00,0x01, + 0x0d,0x0f,0x0a,0x00, 0xfa,0x00,0xfa,0x01, + 0xfe,0x0c,0x00,0x0f, 0x0a,0x00,0x46,0x00, + 0x46,0x01,0xff,0x02, 0x00,0x01,0x0d,0x0c, + 0x00,0x1e,0x01,0xff, 0x02,0x00,0x01,0x0d, + 0x0c,0x00,0x0f,0x01, 0xff,0x08,0xf0,0xf0, + 0xf0,0x02,0x00,0x01, 0x0d,0x0f,0x0a,0x01, + 0xf4,0x01,0xf4,0x01, 0xfe,0x0c,0x00,0x0f, + 0x0a,0x00,0x5a,0x00, 0x5a,0x01,0xff,0x02, + 0x00,0x01,0x0d,0x0c, 0x00,0x1e,0x01,0xff, + 0x02,0x00,0x01,0x0d, 0x0c,0x00,0x0f,0x01, + 0xff,0x08,0xf0,0xf0, 0xf0,0x02,0x00,0x01, + 0x0d,0x0f,0x0a,0x02, 0xee,0x02,0xee,0x01, + 0xfe,0x0c,0x00,0x0f, 0x0a,0x00,0x6e,0x00, + 0x6e,0x01,0xff,0x02, 0x00,0x01,0x0d,0x0c, + 0x00,0x1e,0x01,0xff, 0x02,0x00,0x01,0x0d, + 0x0c,0x00,0x0f,0x01, 0xff,0x08,0xf0,0xf0, + 0xf0,0x02,0x00,0x01, 0x0d,0x0f +}; + +static ANIMATION d16e_dword_800C35E4 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 1000, // raise + 3, // amb + 500, // size_w + 500, // size_h + 255, // v + NULL, // pre_script + (char *)d16e_dword_800D2A88 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char d16e_dword_800D2B40[] = { + 0x00,0xab,0x04,0x00, 0x0b,0x00,0x32,0x00, + 0x5e,0x00,0x8a,0x01, 0xfe,0x0c,0x00,0x0f, + 0x0a,0x00,0x32,0x00, 0x32,0x01,0xff,0x02, + 0x00,0x01,0x0d,0x0c, 0x00,0x1e,0x01,0xff, + 0x02,0x00,0x01,0x0d, 0x0c,0x00,0x0f,0x01, + 0xff,0x08,0xff,0xff, 0xff,0x02,0x00,0x01, + 0x0d,0x0f,0x0a,0x00, 0xfa,0x00,0xfa,0x01, + 0xfe,0x0c,0x00,0x0f, 0x0a,0x00,0x46,0x00, + 0x46,0x01,0xff,0x02, 0x00,0x01,0x0d,0x0c, + 0x00,0x1e,0x01,0xff, 0x02,0x00,0x01,0x0d, + 0x0c,0x00,0x0f,0x01, 0xff,0x08,0xff,0xff, + 0xff,0x02,0x00,0x01, 0x0d,0x0f,0x0a,0x01, + 0xf4,0x01,0xf4,0x01, 0xfe,0x0c,0x00,0x0f, + 0x0a,0x00,0x5a,0x00, 0x5a,0x01,0xff,0x02, + 0x00,0x01,0x0d,0x0c, 0x00,0x1e,0x01,0xff, + 0x02,0x00,0x01,0x0d, 0x0c,0x00,0x0f,0x01, + 0xff,0x08,0xff,0xff, 0xff,0x02,0x00,0x01, + 0x0d,0x0f,0x0a,0x02, 0xee,0x02,0xee,0x01, + 0xfe,0x0c,0x00,0x0f, 0x0a,0x00,0x6e,0x00, + 0x6e,0x01,0xff,0x02, 0x00,0x01,0x0d,0x0c, + 0x00,0x1e,0x01,0xff, 0x02,0x00,0x01,0x0d, + 0x0c,0x00,0x0f,0x01, 0xff,0x08,0xff,0xff, + 0xff,0x02,0x00,0x01, 0x0d,0x0f +}; + +static ANIMATION d16e_dword_800C3600 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 1000, // raise + 2, // amb + 500, // size_w + 500, // size_h + 16, // v + NULL, // pre_script + (char *)d16e_dword_800D2B40 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char d16e_dword_800D2BF8[] = { + 0x00,0xbb,0x04,0x00, 0x0b,0x00,0x36,0x00, + 0x66,0x00,0x96,0x08, 0xff,0xff,0xff,0x01, + 0xfe,0x0c,0x00,0x0f, 0x0a,0x00,0x32,0x00, + 0x32,0x01,0xff,0x02, 0x00,0x01,0x0d,0x0c, + 0x00,0x1e,0x01,0xff, 0x02,0x00,0x01,0x0d, + 0x0c,0x00,0x0f,0x01, 0xff,0x08,0xf0,0xf0, + 0xf0,0x02,0x00,0x01, 0x0d,0x0f,0x08,0xff, + 0xff,0xff,0x0a,0x00, 0xfa,0x00,0xfa,0x01, + 0xfe,0x0c,0x00,0x0f, 0x0a,0x00,0x46,0x00, + 0x46,0x01,0xff,0x02, 0x00,0x01,0x0d,0x0c, + 0x00,0x1e,0x01,0xff, 0x02,0x00,0x01,0x0d, + 0x0c,0x00,0x0f,0x01, 0xff,0x08,0xf0,0xf0, + 0xf0,0x02,0x00,0x01, 0x0d,0x0f,0x08,0xff, + 0xff,0xff,0x0a,0x01, 0xf4,0x01,0xf4,0x01, + 0xfe,0x0c,0x00,0x0f, 0x0a,0x00,0x5a,0x00, + 0x5a,0x01,0xff,0x02, 0x00,0x01,0x0d,0x0c, + 0x00,0x1e,0x01,0xff, 0x02,0x00,0x01,0x0d, + 0x0c,0x00,0x0f,0x01, 0xff,0x08,0xf0,0xf0, + 0xf0,0x02,0x00,0x01, 0x0d,0x0f,0x08,0xff, + 0xff,0xff,0x0a,0x02, 0xee,0x02,0xee,0x01, + 0xfe,0x0c,0x00,0x0f, 0x0a,0x00,0x6e,0x00, + 0x6e,0x01,0xff,0x02, 0x00,0x01,0x0d,0x0c, + 0x00,0x1e,0x01,0xff, 0x02,0x00,0x01,0x0d, + 0x0c,0x00,0x0f,0x01, 0xff,0x08,0xf0,0xf0, + 0xf0,0x02,0x00,0x01, 0x0d,0x0f +}; + +static ANIMATION d16e_dword_800C361C = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 1000, // raise + 3, // amb + 500, // size_w + 500, // size_h + 0, // v + NULL, // pre_script + (char *)d16e_dword_800D2BF8 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char d16e_dword_800D2CC0[] = { + 0x00,0x87,0x04,0x00, 0x0b,0x00,0x29,0x00, + 0x4c,0x00,0x6f,0x01, 0xfe,0x0c,0x00,0x07, + 0x0a,0x00,0x64,0x00, 0x64,0x01,0xff,0x02, + 0x00,0x01,0x0d,0x0c, 0x00,0x03,0x01,0xff, + 0x08,0xc0,0xc0,0xc0, 0x02,0x00,0x01,0x0d, + 0x0f,0x0a,0x00,0xfa, 0x00,0xfa,0x01,0xfe, + 0x0c,0x00,0x07,0x0a, 0x00,0x96,0x00,0x96, + 0x01,0xff,0x02,0x00, 0x01,0x0d,0x0c,0x00, + 0x03,0x01,0xff,0x08, 0xc0,0xc0,0xc0,0x02, + 0x00,0x01,0x0d,0x0f, 0x0a,0x01,0xf4,0x01, + 0xf4,0x01,0xfe,0x0c, 0x00,0x07,0x0a,0x00, + 0xc8,0x00,0xc8,0x01, 0xff,0x02,0x00,0x01, + 0x0d,0x0c,0x00,0x03, 0x01,0xff,0x08,0xc0, + 0xc0,0xc0,0x02,0x00, 0x01,0x0d,0x0f,0x0a, + 0x02,0xee,0x02,0xee, 0x01,0xfe,0x0c,0x00, + 0x07,0x0a,0x00,0xfa, 0x00,0xfa,0x01,0xff, + 0x02,0x00,0x01,0x0d, 0x0c,0x00,0x03,0x01, + 0xff,0x08,0xc0,0xc0, 0xc0,0x02,0x00,0x01, + 0x0d,0x0f +}; + +static ANIMATION d16e_dword_800C3638 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 1000, // raise + 3, // amb + 500, // size_w + 500, // size_h + 255, // v + NULL, // pre_script + (char *)d16e_dword_800D2CC0 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char d16e_dword_800D2D54[] = { + 0x00,0x87,0x04,0x00, 0x0b,0x00,0x29,0x00, + 0x4c,0x00,0x6f,0x01, 0xfe,0x0c,0x00,0x07, + 0x0a,0x00,0x64,0x00, 0x64,0x01,0xff,0x02, + 0x00,0x01,0x0d,0x0c, 0x00,0x03,0x01,0xff, + 0x08,0xfc,0xfc,0xfc, 0x02,0x00,0x01,0x0d, + 0x0f,0x0a,0x00,0xfa, 0x00,0xfa,0x01,0xfe, + 0x0c,0x00,0x07,0x0a, 0x00,0x96,0x00,0x96, + 0x01,0xff,0x02,0x00, 0x01,0x0d,0x0c,0x00, + 0x03,0x01,0xff,0x08, 0xfc,0xfc,0xfc,0x02, + 0x00,0x01,0x0d,0x0f, 0x0a,0x01,0xf4,0x01, + 0xf4,0x01,0xfe,0x0c, 0x00,0x07,0x0a,0x00, + 0xc8,0x00,0xc8,0x01, 0xff,0x02,0x00,0x01, + 0x0d,0x0c,0x00,0x03, 0x01,0xff,0x08,0xfc, + 0xfc,0xfc,0x02,0x00, 0x01,0x0d,0x0f,0x0a, + 0x02,0xee,0x02,0xee, 0x01,0xfe,0x0c,0x00, + 0x07,0x0a,0x00,0xfa, 0x00,0xfa,0x01,0xff, + 0x02,0x00,0x01,0x0d, 0x0c,0x00,0x03,0x01, + 0xff,0x08,0xfc,0xfc, 0xfc,0x02,0x00,0x01, + 0x0d,0x0f +}; + +static ANIMATION d16e_dword_800C3654 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 1000, // raise + 2, // amb + 500, // size_w + 500, // size_h + 16, // v + NULL, // pre_script + (char *)d16e_dword_800D2D54 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char d16e_dword_800D2DE8[] = { + 0x00,0x2a,0x01,0x00, 0x05,0x01,0xfe,0x0c, + 0x00,0x02,0x02,0x00, 0x01,0x0d,0x0a,0x03, + 0xe8,0x03,0xe8,0x0c, 0x00,0x07,0x0a,0x00, + 0x64,0x00,0x64,0x01, 0xff,0x02,0x00,0x01, + 0x0d,0x0c,0x00,0x03, 0x01,0xff,0x08,0xc0, + 0xc0,0xc0,0x02,0x00, 0x01,0x0d,0x0f +}; + +static ANIMATION d16e_dword_800C3670 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 1000, // raise + 3, // amb + 1, // size_w + 1, // size_h + 255, // v + NULL, // pre_script + (char *)d16e_dword_800D2DE8 // script +}; + +/*---------------------------------------------------------------------------*/ + +void AN_Smoke_800CE08C(SVECTOR *pos) +{ + PRESCRIPT pre; + ANIMATION *anm; + + memset(&pre, 0, sizeof(PRESCRIPT)); + pre.pos = *pos; + + anm = &d16e_dword_800C3520; + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); +} + +void AN_Smoke_800CE0F8(SVECTOR *pos) +{ + PRESCRIPT pre; + ANIMATION *anm; + + memset(&pre, 0, sizeof(PRESCRIPT)); + pre.pos = *pos; + + anm = &d16e_dword_800C3558; + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); +} + +void AN_Smoke_800CE164(SVECTOR *pos, SVECTOR *speed, int index, int script) +{ + PRESCRIPT pre; + ANIMATION *anm; + + memset(&pre, 0, sizeof(PRESCRIPT)); + pre.pos = *pos; + pre.speed = *speed; + pre.scr_num = script; + + anm = NULL; + + switch (index) + { + case 0: + anm = &d16e_dword_800C3574; + break; + + case 1: + anm = &d16e_dword_800C3590; + break; + } + + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); +} + +void AN_Smoke_800CE240(SVECTOR *pos) +{ + PRESCRIPT pre; + ANIMATION *anm; + + memset(&pre, 0, sizeof(PRESCRIPT)); + pre.pos = *pos; + + anm = &d16e_dword_800C35AC; + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); + + anm = &d16e_dword_800C353C; + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); +} + +void AN_Smoke_800CE2C4(SVECTOR *pos, SVECTOR *speed, int index, int script, char r, char g, char b) +{ + PRESCRIPT pre; + ANIMATION *anm; + + memset(&pre, 0, sizeof(PRESCRIPT)); + pre.pos = *pos; + pre.speed = *speed; + pre.scr_num = script; + + anm = NULL; + + switch (index) + { + case 0: + anm = &d16e_dword_800C35E4; + break; + + case 1: + anm = &d16e_dword_800C3600; + break; + + case 2: + anm = &d16e_dword_800C361C; + + anm->script[12] = r; + anm->script[13] = g; + anm->script[14] = b; + + anm->script[55] = r; + anm->script[56] = g; + anm->script[57] = b; + + anm->script[108] = r; + anm->script[109] = g; + anm->script[110] = b; + + anm->script[151] = r; + anm->script[152] = g; + anm->script[153] = b; + + r = -((r - 1) / 15); + g = -((g - 1) / 15); + b = -((b - 1) / 15); + + anm->script[46] = r; + anm->script[47] = g; + anm->script[48] = b; + + anm->script[94] = r; + anm->script[95] = g; + anm->script[96] = b; + + anm->script[142] = r; + anm->script[143] = g; + anm->script[144] = b; + + anm->script[190] = r; + anm->script[191] = g; + anm->script[192] = b; + break; + } + + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); +} + +void AN_Smoke_800CE55C(SVECTOR *pos) +{ + PRESCRIPT pre; + ANIMATION *anm; + + memset(&pre, 0, sizeof(PRESCRIPT)); + pre.pos = *pos; + + anm = &d16e_dword_800C35C8; + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); +} + +void AN_Smoke_800CE5C8(SVECTOR *pos, SVECTOR *speed, int index, int script) +{ + PRESCRIPT pre; + ANIMATION *anm; + + memset(&pre, 0, sizeof(PRESCRIPT)); + pre.pos = *pos; + pre.speed = *speed; + pre.scr_num = script; + + anm = NULL; + + switch (index) + { + case 0: + anm = &d16e_dword_800C3638; + break; + + case 1: + anm = &d16e_dword_800C3654; + break; + } + + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); +} + +void AN_Smoke_800CE6A4(SVECTOR *pos) +{ + PRESCRIPT pre; + ANIMATION *anm; + + memset(&pre, 0, sizeof(PRESCRIPT)); + pre.pos = *pos; + pre.speed = DG_ZeroVector; + pre.scr_num = 0; + pre.s_anim = 0; + + anm = &d16e_dword_800C3670; + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); +} diff --git a/components/mgs/mgs/source/anime/effect/socom.c b/components/mgs/mgs/source/anime/effect/socom.c new file mode 100644 index 00000000..a26a02b5 --- /dev/null +++ b/components/mgs/mgs/source/anime/effect/socom.c @@ -0,0 +1,483 @@ +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "anime/animconv/anime.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +static const char anim_data_800CC124[] = { + 0x00,0x1d,0x03,0x00, 0x09,0x00,0x10,0x00, + 0x1b,0x09,0x0b,0xb8, 0x02,0x00,0x01,0x0f, + 0x07,0x03,0x0a,0xff, 0x38,0xff,0x38,0x02, + 0x00,0x01,0x0f,0x07, 0x03,0x0a,0xfe,0x98, + 0xfe,0xfc,0x02,0x00, 0x01,0x0f +}; + +static ANIMATION anm_800C3B78 = { + PCX_SOCOM_F, // tex + 2, // texdev_x + 2, // texdev_y + 4, // n_anime + 1, // n_verts + 300, // raise + 1, // amb + 800, // size_w + 800, // size_h + 128, // v + NULL, // pre_script + (char *)anim_data_800CC124 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char anim_data_800CC14C[] = { + 0x00,0x13,0x01,0x00, 0x05,0x0c,0x00,0x07, + 0x01,0xff,0x0a,0x00, 0x46,0x00,0x46,0x08, + 0xf0,0xf0,0xf0,0x02, 0x00,0x01,0x0d,0x0f +}; + +static ANIMATION anm_800C3B94 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 500, // raise + 1, // amb + 400, // size_w + 400, // size_h + 128, // v + NULL, // pre_script + (char *)anim_data_800CC14C // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char anim_data_800CC164[] = { + 0x00,0x5B,0x04,0x00, 0x0b,0x00,0x1e,0x00, + 0x36,0x00,0x4e,0x0c, 0x00,0x07,0x01,0xff, + 0x0a,0x00,0xc8,0x00, 0xc8,0x08,0xf0,0xf0, + 0xf0,0x02,0x00,0x01, 0x0d,0x0f,0x0a,0x01, + 0x90,0x01,0x90,0x0c, 0x00,0x07,0x01,0xff, + 0x0a,0x01,0x2c,0x01, 0x2c,0x08,0xf0,0xf0, + 0xf0,0x02,0x00,0x01, 0x0d,0x0f,0x0a,0x03, + 0x20,0x03,0x20,0x0c, 0x00,0x07,0x01,0xff, + 0x0a,0x01,0x90,0x01, 0x90,0x08,0xf0,0xf0, + 0xf0,0x02,0x00,0x01, 0x0d,0x0f,0x0a,0x04, + 0xb0,0x04,0xb0,0x0c, 0x00,0x07,0x01,0xff, + 0x0a,0x01,0xf4,0x01, 0xf4,0x08,0xf0,0xf0, + 0xf0,0x02,0x00,0x01, 0x0d,0x0f +}; + +static ANIMATION anm_800C3BB0 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 500, // raise + 1, // amb + 400, // size_w + 400, // size_h + 128, // v + NULL, // pre_script + (char *)anim_data_800CC164 // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char anim_data_800CC1CC[] = { + 0x00,0x6b,0x04,0x00, 0x0b,0x00,0x22,0x00, + 0x3e,0x00,0x5a,0x08, 0xff,0xff,0xff,0x0c, + 0x00,0x07,0x01,0xff, 0x0a,0x00,0xc8,0x00, + 0xc8,0x08,0xf0,0xf0, 0xf0,0x02,0x00,0x01, + 0x0d,0x0f,0x08,0xff, 0xff,0xff,0x0a,0x01, + 0x90,0x01,0x90,0x0c, 0x00,0x07,0x01,0xff, + 0x0a,0x01,0x2c,0x01, 0x2c,0x08,0xf0,0xf0, + 0xf0,0x02,0x00,0x01, 0x0d,0x0f,0x08,0xff, + 0xff,0xff,0x0a,0x03, 0x20,0x03,0x20,0x0c, + 0x00,0x07,0x01,0xff, 0x0a,0x01,0x90,0x01, + 0x90,0x08,0xf0,0xf0, 0xf0,0x02,0x00,0x01, + 0x0d,0x0f,0x08,0xff, 0xff,0xff,0x0a,0x04, + 0xb0,0x04,0xb0,0x0c, 0x00,0x07,0x01,0xff, + 0x0a,0x01,0xf4,0x01, 0xf4,0x08,0xf0,0xf0, + 0xf0,0x02,0x00,0x01, 0x0d,0x0f +}; + +static ANIMATION anm_800C3BCC = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 500, // raise + 1, // amb + 400, // size_w + 400, // size_h + 0, // v + NULL, // pre_script + (char *)anim_data_800CC1CC // script +}; + +/*---------------------------------------------------------------------------*/ + +static const char anim_data_800CC244[] = { + 0x00,0x5B,0x04,0x00, 0x0b,0x00,0x1e,0x00, + 0x36,0x00,0x4e,0x0c, 0x00,0x07,0x01,0xff, + 0x0a,0x00,0xc8,0x00, 0xc8,0x08,0xfc,0xfc, + 0xfc,0x02,0x00,0x01, 0x0d,0x0f,0x0a,0x01, + 0x90,0x01,0x90,0x0c, 0x00,0x07,0x01,0xff, + 0x0a,0x01,0x2c,0x01, 0x2c,0x08,0xfc,0xfc, + 0xfc,0x02,0x00,0x01, 0x0d,0x0f,0x0a,0x03, + 0x20,0x03,0x20,0x0c, 0x00,0x07,0x01,0xff, + 0x0a,0x01,0x90,0x01, 0x90,0x08,0xfc,0xfc, + 0xfc,0x02,0x00,0x01, 0x0d,0x0f,0x0a,0x04, + 0xb0,0x04,0xb0,0x0c, 0x00,0x07,0x01,0xff, + 0x0a,0x01,0xf4,0x01, 0xf4,0x08,0xfc,0xfc, + 0xfc,0x02,0x00,0x01, 0x0d,0x0f +}; + +static ANIMATION anm_800C3BE8 = { + PCX_SMOKE, // tex + 8, // texdev_x + 4, // texdev_y + 30, // n_anime + 1, // n_verts + 500, // raise + 2, // amb + 400, // size_w + 400, // size_h + 32, // v + NULL, // pre_script + (char *)anim_data_800CC244 // script +}; + +/*---------------------------------------------------------------------------*/ + +extern ANIMATION stru_8009F0D4; +extern ANIMATION stru_8009F0F0; + +void AN_Unknown_800D6898(MATRIX *pos) +{ + PRESCRIPT pre; + SVECTOR *pPos; + SVECTOR *pSpeed; + ANIMATION *anm; + SVECTOR sp28; + MATRIX sp30; + int rnd; + + pPos = &pre.pos; + pSpeed = &pre.speed; + + pre.speed = DG_ZeroVector; + + rnd = GV_RandU(16); + if (rnd >= 7) + { + pre.s_anim = 3; + } + else if (rnd >= 5) + { + pre.s_anim = 2; + } + else if (rnd >= 3) + { + pre.s_anim = 1; + } + else + { + pre.s_anim = 0; + } + + anm = &anm_800C3B78; + anm->pre_script = ⪯ + + pPos->vx = pos->t[0]; + pPos->vy = pos->t[1]; + pPos->vz = pos->t[2]; + pre.scr_num = 0; + + NewAnime(NULL, 0, anm); + + sp28.vx = 0; + sp28.vy = 0; + sp28.vz = 420; + + DG_SetPos(pos); + DG_MovePos(&sp28); + ReadRotMatrix(&sp30); + + pPos->vx = sp30.t[0]; + pPos->vy = sp30.t[1]; + pPos->vz = sp30.t[2]; + pre.scr_num = 2; + + NewAnime(NULL, 0, anm); + + if (GV_RandU(16) >= 5) + { + sp28.vx = 0; + sp28.vy = 0; + sp28.vz = -800; + + DG_SetPos(pos); + DG_MovePos(&sp28); + ReadRotMatrix(&sp30); + + pPos->vx = sp30.t[0]; + pPos->vy = sp30.t[1]; + pPos->vz = sp30.t[2]; + + sp28.vx = GV_RandU(32) - 100; + sp28.vy = GV_RandU(32) + 15; + sp28.vz = GV_RandU(64) + 100; + + DG_MovePos(&sp28); + ReadRotMatrix(&sp30); + + pSpeed->vx = pPos->vx - sp30.t[0]; + pSpeed->vy = pPos->vy - sp30.t[1]; + pSpeed->vz = sp30.t[2] - pPos->vz; + pre.scr_num = 2; + pre.s_anim = 0; + + anm = &stru_8009F0D4; + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); + } + + if (GV_RandU(16) >= 9) + { + sp28.vx = 0; + sp28.vy = 0; + sp28.vz = -500; + + DG_SetPos(pos); + DG_MovePos(&sp28); + ReadRotMatrix(&sp30); + + pPos->vx = sp30.t[0]; + pPos->vy = sp30.t[1]; + pPos->vz = sp30.t[2]; + + sp28.vx = 0; + sp28.vy = 0; + sp28.vz = -GV_RandU(32); + + DG_MovePos(&sp28); + ReadRotMatrix(&sp30); + + pSpeed->vx = pPos->vx - sp30.t[0]; + pSpeed->vy = pPos->vy - sp30.t[1]; + pSpeed->vz = pPos->vz - sp30.t[2]; + pre.s_anim = 0; + pre.scr_num = 3; + + anm = &stru_8009F0F0; + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); + } +} + +void AN_Unknown_800D6BCC(SVECTOR *pos, SVECTOR *rot) +{ + PRESCRIPT pre; + SVECTOR *pPos; + SVECTOR *pSpeed; + ANIMATION *anm; + SVECTOR sp28[4]; + SVECTOR sp48[6]; // The stack has an extra 32 unused bytes + int rnd; + + pPos = &pre.pos; + pSpeed = &pre.speed; + + sp28[0].vx = 0; + sp28[0].vy = 0; + sp28[0].vz = 250; + + sp28[1].vx = 0; + sp28[1].vy = 0; + sp28[1].vz = 420; + + sp28[2].vx = -100; + sp28[2].vy = 0; + sp28[2].vz = -800; + + sp28[3].vx = 0; + sp28[3].vy = 0; + sp28[3].vz = -500; + + DG_SetPos2(pos, rot); + DG_PutVector(sp28, sp28, 4); + + sp48[0].vx = -100 - GV_RandU(32); + sp48[0].vy = GV_RandU(32) + 15; + sp48[0].vz = GV_RandS(4); + + sp48[1].vx = 0; + sp48[1].vy = 0; + sp48[1].vz = -GV_RandU(32); + + DG_SetPos2(&DG_ZeroVector, rot); + DG_PutVector(sp48, sp48, 2); + + pre.speed = DG_ZeroVector; + + rnd = GV_RandU(16); + if (rnd >= 7) + { + pre.s_anim = 3; + } + else if (rnd >= 5) + { + pre.s_anim = 2; + } + else if (rnd >= 3) + { + pre.s_anim = 1; + } + else + { + pre.s_anim = 0; + } + + anm = &anm_800C3B78; + anm->pre_script = ⪯ + + *pPos = *pos; + pre.scr_num = 0; + + NewAnime(NULL, 0, anm); + + *pPos = sp28[1]; + pre.scr_num = 2; + + NewAnime(NULL, 0, anm); + + if (GV_RandU(16) >= 5) + { + *pPos = sp28[2]; + *pSpeed = sp48[0]; + pre.scr_num = 2; + pre.s_anim = 0; + + anm = &stru_8009F0D4; + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); + } + + if (GV_RandU(16) >= 9) + { + *pPos = sp28[3]; + *pSpeed = sp48[1]; + pre.s_anim = 0; + pre.scr_num = 3; + + anm = &stru_8009F0F0; + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); + } +} + +void AN_Unknown_800D6EB0(SVECTOR *pos) +{ + PRESCRIPT pre; + ANIMATION *anm; + + pre.pos = *pos; + pre.speed = DG_ZeroVector; + pre.scr_num = 0; + pre.s_anim = GV_RandU(1024) % 30; + + anm = &anm_800C3B94; + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); +} + +void AN_Unknown_800D6F6C(SVECTOR *pos) +{ + PRESCRIPT pre; + ANIMATION *anm; + + pre.pos = *pos; + pre.speed = DG_ZeroVector; + pre.scr_num = 0; + pre.s_anim = GV_RandU(1024) % 30; + + anm = &anm_800C3BB0; + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); +} + +void AN_Unknown_800D7028(SVECTOR *pos, int num) +{ + PRESCRIPT pre; + ANIMATION *anm; + + pre.pos = *pos; + pre.speed = DG_ZeroVector; + pre.scr_num = num; + pre.s_anim = GV_RandU(1024) % 30; + + anm = &anm_800C3BE8; + anm->pre_script = ⪯ + + NewAnime(NULL, 0, anm); +} + +void AN_Unknown_800D70E4(SVECTOR *pos, char r, char g, char b) +{ + PRESCRIPT pre; + ANIMATION *anm; + + pre.pos = *pos; + pre.speed = DG_ZeroVector; + pre.scr_num = 0; + pre.s_anim = GV_RandU(1024) % 30; + + anm = &anm_800C3BCC; + anm->pre_script = ⪯ + + anm->script[12] = r; + anm->script[13] = g; + anm->script[14] = b; + + anm->script[35] = r; + anm->script[36] = g; + anm->script[37] = b; + + anm->script[63] = r; + anm->script[64] = g; + anm->script[65] = b; + + anm->script[91] = r; + anm->script[92] = g; + anm->script[93] = b; + + anm->script[26] = -((r - 1) / 7); + anm->script[27] = -((g - 1) / 7); + anm->script[28] = -((b - 1) / 7); + + anm->script[54] = -((r - 1) / 7); + anm->script[55] = -((g - 1) / 7); + anm->script[56] = -((b - 1) / 7); + + anm->script[82] = -((r - 1) / 7); + anm->script[83] = -((g - 1) / 7); + anm->script[84] = -((b - 1) / 7); + + anm->script[110] = -((r - 1) / 7); + anm->script[111] = -((g - 1) / 7); + anm->script[112] = -((b - 1) / 7); + + NewAnime(NULL, 0, anm); +} diff --git a/components/mgs/mgs/source/bullet/amissile.c b/components/mgs/mgs/source/bullet/amissile.c new file mode 100644 index 00000000..6f849ddc --- /dev/null +++ b/components/mgs/mgs/source/bullet/amissile.c @@ -0,0 +1,479 @@ +#include "amissile.h" +#include "blast.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "anime/animconv/anime.h" +#include "game/game.h" +#include "sound/g_sound.h" +#include "strcode.h" + +extern TARGET *target_800BDF00; +extern BLAST_DATA blast_data_8009F4B8[8]; +extern GM_SnakeCameraWork GM_SnakeCamera; + +/*---------------------------------------------------------------------------*/ +// Anti-Air Missile (Stinger) + +#define EXEC_LEVEL GV_ACTOR_AFTER + +#define STINGER_MSG 0x57f8 // GV_StrCode("stinger") +#define STINGER_MODEL 0x76ab // GV_StrCode("stn_fr") + +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE \ + | DG_FLAG_GBOUND | DG_FLAG_ONEPIECE \ + | DG_FLAG_AMBIENT | DG_FLAG_IRTEXTURE ) + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + OBJECT_NO_ROTS body; + MATRIX light[2]; + MATRIX rotation; + int field_120; + int field_124; + int field_128; + SVECTOR field_12C_svector; + DG_PRIM *prim; + RECT prim_rect; + SVECTOR vertices[4]; +} Work; + +/*---------------------------------------------------------------------------*/ + +int amissile_alive_8009F490 = FALSE; +SVECTOR svector_8009F494 = {0, 0, 0, 0}; +int dword_8009F49C = 0; +SVECTOR svector_8009F4A0 = {0, -800, 0, 0}; +SVECTOR svector_8009F4A8 = {0, -350, 0, 0}; +SVECTOR svector_8009F4B0 = {0, -100, -70, 0}; + +/*---------------------------------------------------------------------------*/ + +static void amissile_loader_helper_8006D1F4(POLY_FT4 *poly, DG_TEX *tex) +{ + int i; + char rgb; + int x, y, w, h; + + for (i = 0; i < 4; poly++, i++) + { + rgb = 128 - 32 * i; + + setPolyFT4(poly); + setSemiTrans(poly, 1); + setRGB0(poly, rgb, rgb, rgb); + + x = tex->off_x; + w = tex->w + 1; + poly->u0 = poly->u2 = x + w / 2; + poly->u1 = poly->u3 = x + w - 1; + + y = tex->off_y; + h = tex->h + 1; + poly->v0 = poly->v1 = y + h / 2; + poly->v2 = poly->v3 = y + h - 1; + + poly->tpage = tex->tpage; + poly->clut = tex->clut; + } +} + +static void amissile_act_helper_8006D2A0(Work *work, SVECTOR input) +{ + SVECTOR position = work->control.mov; + SVECTOR result; + SVECTOR rotation; + + SVECTOR *out = &work->vertices[3]; + int i; + + GV_SubVec3(&input, &position, &result); + + for (i = 0; i < 4; i++) + { + GV_AddVec3(&result, out - 1, out); + out--; + } + + rotation = DG_ZeroVector; + rotation.vy = 500; + + DG_RotVector(&rotation, &work->vertices[0], 1); +} + +static void amissile_act_helper_8006D37C(Work *work) +{ + SVECTOR diff; + SVECTOR result; + SVECTOR *target_pos; + int dir; + int temp_v0; + int temp_s1; + + if (work->field_120 < 5) + { + DG_RotVector(&svector_8009F4A8, &work->control.step, 1); + return; + } + + if (work->field_120 < 14) + { + DG_RotVector(&svector_8009F4B0, &work->control.step, 1); + return; + } + + if (--work->field_124 >= 0) + { + svector_8009F4A0.vz = 11; + } + else + { + svector_8009F4A0.vz = 0; + } + + DG_RotVector(&svector_8009F4A0, &work->control.step, 1); + + if (!target_800BDF00) + { + return; + } + + target_pos = &target_800BDF00->center; + diff.vx = target_pos->vx / 8 - work->control.mov.vx / 8; + diff.vy = target_pos->vy / 8 - work->control.mov.vy / 8; + diff.vz = target_pos->vz / 8 - work->control.mov.vz / 8; + + dir = GV_VecDir2(&diff); + temp_v0 = GV_DiffDirS(work->control.turn.vy, dir); + + if (abs(temp_v0) > 1024) + { + return; + } + + temp_s1 = 30 - work->field_120 / 3; + + if (abs(temp_v0) < temp_s1) + { + work->control.turn.vy += temp_v0; + } + else if (temp_v0 > 0) + { + work->control.turn.vy += temp_s1; + } + else + { + work->control.turn.vy -= temp_s1; + } + + result.vx = diff.vy; + result.vz = SquareRoot0(diff.vx * diff.vx + diff.vz * diff.vz); + + dir = -GV_VecDir2(&result); + temp_v0 = GV_DiffDirS(work->control.turn.vx, (dir - 1024) & 4095); + + if (abs(temp_v0) < temp_s1) + { + work->control.turn.vx += temp_v0; + } + else if (temp_v0 > 0) + { + work->control.turn.vx += temp_s1; + } + else + { + work->control.turn.vx -= temp_s1; + } +} + +static int CheckMessage(void) +{ +#ifdef VR_EXE + GV_MSG *msg; + int len; + + len = GV_ReceiveMessage(STINGER_MSG, &msg); + if (len > 0) + { + for (len--; len >= 0; msg++, len--) + { + if (msg->message[0] == HASH_KILL) + { + return 1; + } + } + } +#endif + return 0; +} + +static void Act(Work *work) +{ + MATRIX rotation; + SVECTOR position; + SVECTOR addition; +#ifdef VR_EXE + SVECTOR rotator; +#endif + + CONTROL *control; + int result; + BLAST_DATA *blast; + + control = &work->control; + position = control->mov; + + GM_ActControl(control); + amissile_act_helper_8006D37C(work); + GM_ActObject2((OBJECT *)&work->body); + + work->prim->world.t[0] = work->control.mov.vx; + work->prim->world.t[1] = work->control.mov.vy; + work->prim->world.t[2] = work->control.mov.vz; + + DG_GetLightMatrix2(&control->mov, work->light); + + if (work->field_120 >= 15) + { + amissile_act_helper_8006D2A0(work, position); + } + + amissile_alive_8009F490 = TRUE; + svector_8009F494 = control->mov; + + if (work->field_120 == 0) + { + DG_VisiblePrim(work->prim); + ReadRotMatrix(&work->rotation); + NewAnime_8005DE70(&work->rotation); + work->field_128 = 4; + } + + if (work->field_120 == 14) + { + ReadRotMatrix(&work->rotation); + NewAnime_8005DE70(&work->rotation); + GM_SeSet2(0, 63, SE_MISSILE_BOOST); + work->prim_rect.x = work->prim_rect.y = 1030; + work->prim_rect.w = work->prim_rect.h = 2060; + work->field_128 = 12; + GM_ConfigControlHazard(control, 100, 100, 100); + } + + if (--work->field_128 > 0) + { + GM_SnakeCamera.position.vy += GV_RandS(512) * work->field_128 / 8; + + if (work->field_120 >= 14) + { + work->prim_rect.x = work->prim_rect.y = work->prim_rect.y - 65; + work->prim_rect.w = work->prim_rect.h = work->prim_rect.h - 130; + } + } + + // probably an inline + control = &work->control; + GV_AddVec3(&control->mov, &control->step, &addition); + + result = CheckMessage(); + + // this is probably also an inline + if (work->control.n_touches <= 0 && !work->control.grounded) + { + if (++work->field_120 != 90 && + !GM_OnlineTargetCheck(&control->mov, &addition, work->control.map->index, &addition, 1) && + !dword_8009F49C) + { + if (!result) + { + if (abs(work->control.mov.vx) <= 30000 && + abs(work->control.mov.vy) <= 30000 && + abs(work->control.mov.vz) <= 30000) + { + return; + } + } + } + } + +#ifdef VR_EXE + rotator = work->control.mov; + + if (rotator.vx > 30000) + { + rotator.vx = 30000; + } + else if (rotator.vx < -30000) + { + rotator.vx = -30000; + } + + if (rotator.vy > 30000) + { + rotator.vy = 30000; + } + else if (rotator.vy < -30000) + { + rotator.vy = -30000; + } + + if (rotator.vz > 30000) + { + rotator.vz = 30000; + } + else if (rotator.vz < -30000) + { + rotator.vz = -30000; + } + + DG_SetPos2(&rotator, &work->control.rot); +#endif + + if (!result) + { + ReadRotMatrix(&rotation); + + if (GM_GameStatus & (STATE_PADRELEASE | STATE_PADDEMO | STATE_DEMO) || GM_PlayerStatus & PLAYER_PAD_OFF) + { + blast = &blast_data_8009F4B8[7]; +#ifdef VR_EXE + if ((GM_GameStatus & STATE_PADDEMO) && + !(GM_PlayerStatus & PLAYER_PAD_OFF) && + !(GM_GameStatus & STATE_PADRELEASE)) + { + blast = &blast_data_8009F4B8[3]; + } +#endif + } + else + { + blast = &blast_data_8009F4B8[3]; + } + + NewBlast(&rotation, blast); + } + + amissile_alive_8009F490 = FALSE; + GV_DestroyActor(&work->actor); +} + +static void Die(Work *work) +{ + GM_FreeControl(&work->control); + GM_FreeObject((OBJECT *)&work->body); + GM_FreePrim(work->prim); + + if (target_800BDF00) + { + target_800BDF00 = 0; + } + + amissile_alive_8009F490 = FALSE; +} + +static int GetResources(Work *work, MATRIX *world, int side) +{ + CONTROL *control = &work->control; + OBJECT_NO_ROTS *body; + DG_OBJS *objs; + RECT *rect; + DG_PRIM *prim; + SVECTOR vector; + int i; + DG_TEX *tex; + + if (GM_InitControl(control, 0, 0) < 0) + { + return -1; + } + + GM_ConfigControlMatrix(control, world); + GM_ConfigControlHazard(control, 100, 50, 50); + + body = &work->body; + + control->skip_flag |= CTRL_SKIP_NEAR_CHECK; + control->seg_flag = 8; + + GM_InitObjectNoRots(body, STINGER_MODEL, BODY_FLAG, 0); + + objs = body->objs; + + if (!objs) + { + return -1; + } + + objs->world = *world; + GM_ConfigObjectLight((OBJECT *)body, work->light); + + body->objs->objs[0].raise = -500; + + rect = &work->prim_rect; + rect->x = rect->y = 30; + rect->w = rect->h = 60; + + work->prim = prim = GM_MakePrim(DG_PRIM_RECTANGLE | DG_PRIM_POLY_FT4, 4, work->vertices, rect); + + if (!prim) + { + return -1; + } + + DG_SetPos2(&control->mov, &control->rot); + + vector.vz = 0; + vector.vx = 0; + vector.vy = 500; + + for (i = 0; i < 4; i++) + { + DG_RotVector(&vector, &work->vertices[i], 1); + vector.vy += 2000; + } + + tex = DG_GetTexture(PCX_SOCOM_F); + if (!tex) + { + return -1; + } + + amissile_loader_helper_8006D1F4(prim->packs[0], tex); + amissile_loader_helper_8006D1F4(prim->packs[1], tex); + + prim->world = DG_ZeroMatrix; + DG_InvisiblePrim(prim); + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewAMissile(MATRIX *world, int side) +{ + Work *work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + + if (work) + { + GV_SetNamedActor(&work->actor, &Act, &Die, "amissile.c"); + + if (GetResources(work, world, side) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + work->field_124 = 30; + work->field_120 = 0; + work->field_128 = 0; + work->field_12C_svector = DG_ZeroVector; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/bullet/amissile.h b/components/mgs/mgs/source/bullet/amissile.h new file mode 100644 index 00000000..d25a2810 --- /dev/null +++ b/components/mgs/mgs/source/bullet/amissile.h @@ -0,0 +1,12 @@ +#ifndef __MGS_BULLET_AMISSILE_H__ +#define __MGS_BULLET_AMISSILE_H__ + +#include +#include +#include + +// Anti-Air Missile (Stinger) + +void *NewAMissile(MATRIX *world, int side); + +#endif // __MGS_BULLET_AMISSILE_H__ diff --git a/components/mgs/mgs/source/bullet/bakudan.c b/components/mgs/mgs/source/bullet/bakudan.c new file mode 100644 index 00000000..9bb370d9 --- /dev/null +++ b/components/mgs/mgs/source/bullet/bakudan.c @@ -0,0 +1,294 @@ +#include "bakudan.h" +#include "blast.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "chara/snake/sna_init.h" +#include "game/game.h" +#include "linkvar.h" +#include "sound/g_sound.h" + +extern HITTABLE GM_C4Datas[C4_COUNT]; +extern unsigned short GM_ItemTypes[]; +extern BLAST_DATA blast_data_8009F4B8[8]; + +/*---------------------------------------------------------------------------*/ +// C4 bomb (armed) + +#define EXEC_LEVEL GV_ACTOR_AFTER + +#define C4BOMB_MODEL 0xf83d // GV_StrCode("c4_bomb") + +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE \ + | DG_FLAG_GBOUND | DG_FLAG_ONEPIECE ) + +/*---------------------------------------------------------------------------*/ + +int bakudan_count_8009F42C = 0; +int time_last_press_8009F430 = 0; +int dword_8009F434 = 0; // unused variable + +// default orientation for C4 object when placed on a moving target +// (probably to keep it upright because the 3d model is not oriented correctly) +STATIC SVECTOR model_orientation_8009F438 = {3072, 0, 0, 0}; + +/** + * @brief Main function for the C4 actor. Handles the logic for the C4's behavior. + * + * @param work Pointer to the BakudanWork structure. + */ +static void Act(BakudanWork *work) +{ + MATRIX rotation; + CONTROL *control; + GV_PAD *pad; + MATRIX *world; + TARGET *target; +#ifdef VR_EXE + int cond; +#endif + // if invalid game status, destroy the actor + if (GM_GameStatus < 0) + { + GV_DestroyActor(&work->actor); + return; + } + + control = &work->control; + pad = &GV_PadData[0]; + + if (GM_PlayerStatus & PLAYER_SECOND_CONTROLLER) + { + pad = &GV_PadData[1]; + } + + work->active_pad = pad; + GM_ActControl(control); + + world = work->transform; + + // if the C4 is placed on a moving target + if (world) + { + DG_RotatePos(&model_orientation_8009F438); + // get the target where the C4 is placed + target = GM_C4Datas[work->c4_index].data; + work->map_index = target->map; + + // if the target is not alive, destroy the actor + if (!target->body) + { + GV_DestroyActor(&work->actor); + return; + } + } + + GM_CurrentMap = work->map_index; + + GM_ActObject2((OBJECT *)&work->kmd); + DG_GetLightMatrix(&control->mov, work->light_mtx); + +#ifdef VR_EXE + // VR executable for some reason assigns the result + // of the condition below to a temporary variable!? + cond = 0; +#endif + if (((work->active_pad->press & PAD_CIRCLE) && /* if the circle button was pressed */ + (time_last_press_8009F430 != GV_Time) && /* and it's not on the same frame */ + (GM_CurrentMap & GM_PlayerMap) && /* and the player is on the same map as the C4 */ + !(GM_GameStatus & STATE_PADRELEASE) && /* and the player has not released the button */ + !(GM_PlayerStatus & PLAYER_PAD_OFF) && /* and pad input isn't disabled */ + !(GM_ItemTypes[GM_CurrentItemId + 1] & 2)) || /* and the player hasn't equipped an incompatible item */ + dword_8009F434) +#ifdef VR_EXE + { + cond = 1; + } + if (cond) +#endif + { + work->detonator_btn_pressed = 1; + + if (work->active_pad->press & PAD_CIRCLE) + { + GM_SeSetMode(&GM_PlayerPosition, SE_C4_SWITCH, GM_SEMODE_BOMB); + } + + time_last_press_8009F430 = GV_Time; + } + // keep track of the consecutive frames the circle button is pressed + if (work->detonator_btn_pressed) + { + work->detonator_frames_count++; + } + + // detonate the C4 after 3 frames + if (work->detonator_frames_count >= 3) + { + ReadRotMatrix(&rotation); + NewBlast(&rotation, &blast_data_8009F4B8[1]); + HZD_ExecLeaveEvent(work->control.map->hzd, &work->control.evt); + GV_DestroyActor(&work->actor); + } + else if (world) + { + // update the C4 position and rotation to follow the target + DG_SetPos(world); + DG_PutVector(work->position, &control->mov, 1); + DG_MatrixRotYXZ(world, &control->rot); + } +} + +/** + * @brief Cleanup function for the C4 actor. + * Frees resources and updates the actor list. + * + * @param work Pointer to the BakudanWork structure. + */ +static void Die(BakudanWork *work) +{ + GM_FreeControl(&work->control); + GM_ClearBulName(work->control.name); + GM_FreeObject((OBJECT *)&work->kmd); + + if (work->c4_index >= 0) + { + GM_C4Datas[work->c4_index].actor = NULL; + bakudan_count_8009F42C--; + } +} + +/** + * @brief Find the next free slot in the GM_C4Datas array. + * + * @return int Index of the next free slot, or -1 if no free slot is available. + */ +static int GetNextC4Data( void ) +{ + int i; + for (i = 0; i < C4_COUNT; i++) + { + if (!GM_C4Datas[i].actor) + { + return i; + } + } + return -1; +} + +/** + * @brief Helper function to initialize a C4 actor. + * + * @param work Pointer to the BakudanWork structure. + * @param world Transform matrix. + * @param pos Position vector. + * @param attached 1 if the C4 is placed on a moving target, 0 otherwise. + * @param data Pointer to the target (wall/floor or moving target). + * Used to update the C4 position and rotation and to delete the + * C4 when the target is dead) + * + * @return int 0 on success, -1 on failure. + */ +static int GetResources(BakudanWork *work, MATRIX *world, SVECTOR *pos, int attached, void *data) +{ + CONTROL *control = &work->control; + OBJECT_NO_ROTS *object; + int nextItem; + HITTABLE *item; + + work->map_index = GM_CurrentMap = GM_PlayerMap; + + if (GM_InitControl(control, GM_Next_BulName_8004FBA0(), 0) < 0) + { + return -1; + } + + GM_ConfigControlHazard(control, 0, 0, 0); + GM_ConfigControlMatrix(control, world); + + if (attached == 1) + { + // initial position and rotation + work->transform = world; + work->position = pos; + } + else + { + work->transform = NULL; + } + + object = &work->kmd; + GM_InitObjectNoRots(object, C4BOMB_MODEL, BODY_FLAG, 0); + + if (!object->objs) + { + return -1; + } + + object->objs->world = *world; + GM_ConfigObjectLight((OBJECT *)object, work->light_mtx); + object->objs->objs[0].raise = 200; + + work->c4_index = nextItem = GetNextC4Data(); + + if (nextItem < 0) + { + return -1; + } + + item = &GM_C4Datas[nextItem]; + item->actor = &work->actor; + item->control = control; + item->data = data; + + bakudan_count_8009F42C++; + return 0; +} + +/*---------------------------------------------------------------------------*/ +/** + * @brief Construct a new C4 actor + * + * @param world Transform matrix. + * @param pos Position vector. + * @param attached 1 if the C4 is placed on a moving target, 0 otherwise. + * @param unused + * @param data Pointer to the target (used to update the C4 position, + * rotation and to delete the C4 when the target is dead). + * + * @returns The C4 actor's work area. + */ +void *NewBakudan(MATRIX *world, SVECTOR *pos, int attached, int unused, void *data) +{ + BakudanWork *work; + + // if there are already 16 C4s, don't create a new one + if (bakudan_count_8009F42C == C4_COUNT) + { + return NULL; + } + + work = GV_NewActor(EXEC_LEVEL, sizeof(BakudanWork)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "bakudan.c"); + if (GetResources(work, world, pos, attached, data) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + work->detonator_frames_count = 0; + work->detonator_btn_pressed = 0; + } +#ifdef VR_EXE + if (time_last_press_8009F430 > GV_Time) + { + time_last_press_8009F430 = 0; + } +#endif + return (void *)work; +} diff --git a/components/mgs/mgs/source/bullet/bakudan.h b/components/mgs/mgs/source/bullet/bakudan.h new file mode 100644 index 00000000..6164fa1b --- /dev/null +++ b/components/mgs/mgs/source/bullet/bakudan.h @@ -0,0 +1,32 @@ +#ifndef __MGS_BULLET_BAKUDAN_H__ +#define __MGS_BULLET_BAKUDAN_H__ + +#include +#include +#include + +#include "libgv/libgv.h" +#include "game/game.h" + +// C4 bomb (armed) + +typedef struct BakudanWork +{ + GV_ACT actor; + CONTROL control; + OBJECT_NO_ROTS kmd; + MATRIX light_mtx[2]; + MATRIX *transform; // transform matrix (assigned if the c4 is placed on a moving target) + SVECTOR *position; // position (assigned if the c4 is placed on a moving target) + int detonator_btn_pressed; // 1 if the detonator button is pressed, 0 otherwise + int detonator_frames_count; // number of actor actions to wait before detonate + GV_PAD *active_pad; // pointer to the currently active gamepad + int c4_index; // the index in the GM_C4Datas array + int map_index; // the current map where the c4 is placed +} BakudanWork; + +#define C4_COUNT 16 + +void *NewBakudan(MATRIX *world, SVECTOR *pos, int attached, int unused, void *data); + +#endif // __MGS_BULLET_BAKUDAN_H__ diff --git a/components/mgs/mgs/source/bullet/blast.c b/components/mgs/mgs/source/bullet/blast.c new file mode 100644 index 00000000..94977479 --- /dev/null +++ b/components/mgs/mgs/source/bullet/blast.c @@ -0,0 +1,477 @@ +#include "blast.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "anime/animconv/anime.h" +#include "game/game.h" +#include "sound/g_sound.h" +#include "linkvar.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_AFTER + +typedef struct _Work +{ + GV_ACT actor; + int map; + SVECTOR pos; + DG_PRIM *prim; + int unused1; + int unused2; + int time; + TARGET target; +} Work; + +/*---------------------------------------------------------------------------*/ + +BLAST_DATA blast_data_8009F4B8[8] = { + { 0x100, 5, 0x3E8, 0x7D0, WP_Grenade }, + { 0x100, 5, 0x3E8, 0x7D0, WP_C4 }, + { 0x100, 5, 0x3E8, 0x7D0, WP_Claymore }, + { 0x100, 5, 0x3E8, 0x7D0, WP_Stinger }, + { 0x100, 5, 0x3E8, 0x7D0, WP_Nikita }, + { 0x100, 5, 0x3E8, 0x7D0, WP_None }, + { 0x400, 5, 0x3E8, 0x7D0, WP_Max }, + { 0, 0, 1, 1, WP_None } +}; + +/*---------------------------------------------------------------------------*/ + +STATIC SVECTOR svector_8009F558[2] = { +#ifndef VR_EXE + {100, 0, 0, 0}, +#else + {50, 0, 0, 0}, +#endif + {2000, 2000, 4000, 4000} +}; + +static void Act(Work *work) +{ + int time; + + GM_CurrentMap = work->map; + + time = ++work->time; + + if (time == 1) + { + AN_Blast_Single(&work->pos); + } + + if (time == 2) + { + sub_8007913C(); + } + + if (time >= 30) + { + GV_DestroyActor(&work->actor); + } +} + +static void Die(Work *blast) +{ + if (blast->time < 2) + { + sub_8007913C(); + } + + GM_FreePrim(blast->prim); +} + +/*---------------------------------------------------------------------------*/ + +static void InitBlastTarget(BLAST_DATA *blast_data, Work *work, int side) +{ + TARGET *target = &work->target; + SVECTOR size; + + size.vx = size.vy = size.vz = blast_data->field_8_z; + + if ( side ) + { + GM_SetTarget(target, 4, NO_SIDE, &size); + } + else + { + GM_SetTarget(target, 4, ENEMY_SIDE, &size); + } + + GM_SetPowerTarget(target, POWER_CONST | POWER_EXPLODE, 2, blast_data->field_0 >> 1, blast_data->field_4, svector_8009F558); + + target->weapon = blast_data->weapon; + + GM_MoveTarget(&work->target, &work->pos); + GM_PowerTarget(target); + + size.vx = size.vy = size.vz = blast_data->field_C; + svector_8009F558[0].vx = 50; + + if ( side ) + { + GM_SetTarget(target, 4, NO_SIDE, &size); + } + else + { + GM_SetTarget(target, 4, ENEMY_SIDE, &size); + } + + GM_SetPowerTarget(target, POWER_CONST | POWER_EXPLODE, 2, blast_data->field_0 >> 1, 3, svector_8009F558); + + target->weapon = blast_data->weapon; + + GM_MoveTarget(&work->target, &work->pos); + GM_PowerTarget(target); + + if ( GM_lpfnBombExplosion && GM_lpfnBombExplosion(&work->target, GM_uBombHoming) ) + { + ++work->time; + } + else + { + GM_SeSet(&work->pos, SE_EXPLOSION); + } +} + +static int GetResources(BLAST_DATA *blast_data, Work *work, MATRIX *world, int side) +{ + work->time = 0; + work->map = GM_CurrentMap; + work->pos.vx = world->t[0]; + work->pos.vy = world->t[1]; + work->pos.vz = world->t[2]; + InitBlastTarget(blast_data, work, side); + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewBlast(MATRIX *world, BLAST_DATA *blast_data) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "blast.c"); + GM_ClaymoreMap = GM_CurrentMap; + + if (GetResources(blast_data, work, world, 1) < 0) + { + + GV_DestroyActor(&work->actor); + return NULL; + } + + GM_SetNoise(255, 32, &work->pos); + + sub_800790E8(); + } + + return (void *)work; +} + +void *NewBlast2(MATRIX *world, BLAST_DATA *blast_data, int doSound, int side) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "blast.c"); + GM_ClaymoreMap = GM_CurrentMap; + if (GetResources(blast_data, work, world, side) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + if (doSound) + { + GM_SetNoise(255, 32, &work->pos); + } + + sub_800790E8(); + } + return (void *)work; +} + +/*---------------------------------------------------------------------------*/ + +STATIC const unsigned char blast_anim_data_single[64] = { + 0x00,0x3b,0x01,0x00, 0x05,0x01,0x00,0x02, 0x00,0x01,0x0c,0x00, 0x02,0x01,0xff,0x0a, + 0x04,0xb0,0x04,0xb0, 0x02,0x00,0x01,0x0d, 0x0c,0x00,0x06,0x01, 0xff,0x02,0x00,0x01, + 0x0d,0x0c,0x00,0x07, 0x01,0xff,0x0a,0x00, 0x64,0x00,0x64,0x08, 0xf8,0xf8,0xf8,0x02, + 0x00,0x01,0x0a,0x00, 0x50,0x00,0x50,0x08, 0xf8,0xf8,0xf8,0x02, 0x00,0x01,0x0d,0x0f +}; +STATIC const unsigned char blast_anim_data_random[64] = { + 0x00,0x3b,0x01,0x00, 0x05,0x01,0x00,0x02, 0x00,0x01,0x0c,0x00, 0x04,0x01,0xff,0x0a, + 0x04,0xb0,0x04,0xb0, 0x02,0x00,0x01,0x0d, 0x0c,0x00,0x0c,0x01, 0xff,0x02,0x00,0x01, + 0x0d,0x0c,0x00,0x08, 0x01,0xff,0x0a,0x00, 0x64,0x00,0x64,0x08, 0xf8,0xf8,0xf8,0x02, + 0x00,0x01,0x0a,0x00, 0x50,0x00,0x50,0x08, 0xf8,0xf8,0xf8,0x02, 0x00,0x01,0x0d,0x0f +}; +STATIC const unsigned char blast_anim_data_mini[64] = { + 0x00,0x3b,0x01,0x00, 0x05,0x01,0x00,0x02, 0x00,0x01,0x0c,0x00, 0x02,0x01,0xff,0x0a, + 0x02,0x58,0x02,0x58, 0x02,0x00,0x01,0x0d, 0x0c,0x00,0x06,0x01, 0xff,0x02,0x00,0x01, + 0x0d,0x0c,0x00,0x07, 0x01,0xff,0x0a,0x00, 0x32,0x00,0x32,0x08, 0xf8,0xf8,0xf8,0x02, + 0x00,0x01,0x0a,0x00, 0x28,0x00,0x28,0x08, 0xf8,0xf8,0xf8,0x02, 0x00,0x01,0x0d,0x0f +}; +STATIC const unsigned char blast_anim_data_minimini[64] = { + 0x00,0x3b,0x01,0x00, 0x05,0x01,0x00,0x02, 0x00,0x01,0x0c,0x00, 0x02,0x01,0xff,0x0a, + 0x00,0xc8,0x00,0xc8, 0x02,0x00,0x01,0x0d, 0x0c,0x00,0x06,0x01, 0xff,0x02,0x00,0x01, + 0x0d,0x0c,0x00,0x07, 0x01,0xff,0x0a,0x00, 0x14,0x00,0x14,0x08, 0xf8,0xf8,0xf8,0x02, + 0x00,0x01,0x0a,0x00, 0x14,0x00,0x14,0x08, 0xf8,0xf8,0xf8,0x02, 0x00,0x01,0x0d,0x0f +}; +STATIC const unsigned char blast_anim_data_high[290] = { + 0x01,0x17,0x04,0x00, 0x0b,0x00,0x4b,0x00, 0x90,0x00,0xd9,0x01, 0x00,0x02,0x00,0x01, + 0x0c,0x00,0x02,0x01, 0xff,0x0a,0x04,0xb0, 0x04,0xb0,0x02,0x00, 0x01,0x0d,0x0c,0x00, + 0x06,0x01,0xff,0x0a, 0x00,0xc8,0x00,0xc8, 0x02,0x00,0x01,0x0d, 0x0c,0x00,0x07,0x01, + 0xff,0x0a,0x00,0x64, 0x00,0x64,0x08,0xf8, 0xf8,0xf8,0x02,0x00, 0x01,0x0a,0x00,0x50, + 0x00,0x50,0x08,0xf8, 0xf8,0xf8,0x02,0x00, 0x01,0x0d,0x0f,0x01, 0x00,0x02,0x00,0x01, + 0x01,0x00,0x02,0x00, 0x01,0x0c,0x00,0x02, 0x01,0xff,0x0a,0x03, 0xe8,0x03,0xe8,0x02, + 0x00,0x01,0x0d,0x0c, 0x00,0x06,0x01,0xff, 0x0a,0x00,0xb4,0x00, 0xb4,0x02,0x00,0x01, + 0x0d,0x0c,0x00,0x07, 0x01,0xff,0x0a,0x00, 0x64,0x00,0x64,0x08, 0xf8,0xf8,0xf8,0x02, + 0x00,0x01,0x0a,0x00, 0x50,0x00,0x50,0x08, 0xf8,0xf8,0xf8,0x02, 0x00,0x01,0x0d,0x0f, + 0x0c,0x00,0x02,0x01, 0x00,0x02,0x00,0x01, 0x0d,0x01,0x00,0x02, 0x00,0x01,0x0c,0x00, + 0x02,0x01,0xff,0x0a, 0x03,0x20,0x03,0x20, 0x02,0x00,0x01,0x0d, 0x0c,0x00,0x06,0x01, + 0xff,0x0a,0x00,0xa0, 0x00,0xa0,0x02,0x00, 0x01,0x0d,0x0c,0x00, 0x07,0x01,0xff,0x0a, + 0x00,0x64,0x00,0x64, 0x08,0xf8,0xf8,0xf8, 0x02,0x00,0x01,0x0a, 0x00,0x50,0x00,0x50, + 0x08,0xf8,0xf8,0xf8, 0x02,0x00,0x01,0x0d, 0x0f,0x0c,0x00,0x03, 0x01,0x00,0x02,0x00, + 0x01,0x0d,0x01,0x00, 0x02,0x00,0x01,0x0c, 0x00,0x02,0x01,0xff, 0x0a,0x02,0x58,0x02, + 0x58,0x02,0x00,0x01, 0x0d,0x0c,0x00,0x06, 0x01,0xff,0x0a,0x00, 0x8c,0x00,0x8c,0x02, + 0x00,0x01,0x0d,0x0c, 0x00,0x07,0x01,0xff, 0x0a,0x00,0x64,0x00, 0x64,0x08,0xf8,0xf8, + 0xf8,0x02,0x00,0x01, 0x0a,0x00,0x50,0x00, 0x50,0x08,0xf8,0xf8, 0xf8,0x02,0x00,0x01, + 0x0d,0x0f +}; + +ANIMATION blast_anim_single = { PCX_BOMB1_FL, 4, 4, 16, 1, 2000, 1, 1000, 1000, 128, NULL, (void *)blast_anim_data_single }; +ANIMATION blast_anim_random = { PCX_BOMB1_FL, 4, 4, 16, 1, 2000, 1, 1000, 1000, 128, NULL, (void *)blast_anim_data_random }; +ANIMATION blast_anim_mini = { PCX_BOMB1_FL, 4, 4, 16, 1, 2000, 1, 500, 500, 128, NULL, (void *)blast_anim_data_mini }; +ANIMATION blast_anim_minimini = { PCX_BOMB1_FL, 4, 4, 16, 1, 2000, 1, 200, 200, 128, NULL, (void *)blast_anim_data_minimini }; +ANIMATION blast_anim_high = { PCX_BOMB1_FL, 4, 4, 16, 1, 2000, 1, 1000, 1000, 128, NULL, (void *)blast_anim_data_high }; + +void AN_Blast_Single(SVECTOR *pos) +{ + ANIMATION *anm; + PRESCRIPT pre; + + pre.pos = *pos; + pre.speed = DG_ZeroVector; + pre.s_anim = 0; + pre.scr_num = 0; + + anm = &blast_anim_single; + anm->pre_script = ⪯ + + NewAnime( NULL, 0, anm ); +} + +void AN_Blast_Mini(SVECTOR *pos) +{ + ANIMATION *anm; + PRESCRIPT pre; + + pre.pos = *pos; + pre.speed = DG_ZeroVector; + + pre.s_anim = 0; + + anm = &blast_anim_mini; + anm->pre_script = ⪯ + + pre.scr_num = 0; + NewAnime( NULL, 0, anm ); +} + +void AN_Blast_Minimini(SVECTOR *pos) +{ + ANIMATION *anm; + PRESCRIPT pre; + + pre.pos = *pos; + pre.speed = DG_ZeroVector; + + pre.s_anim = 0; + + anm = &blast_anim_minimini; + anm->pre_script = ⪯ + + pre.scr_num = 0; + NewAnime( NULL, 0, anm ); +} + +void AN_Blast_Rand(SVECTOR *pos) +{ + PRESCRIPT prescript; + PRESCRIPT *prescript_ptr; + MATRIX *m; + int map; + int randu; + ANIMATION *anm; + + prescript.pos = *pos; + prescript.pos.vx += GV_RandS(128); + prescript.pos.vy += GV_RandS(128); + prescript.pos.vz += GV_RandS(128); + + prescript.speed = DG_ZeroVector; + prescript.speed.vy += GV_RandS(64); + + prescript.s_anim = 0; + + randu = GV_RandU(4); + if (randu == 0) + { + anm = &blast_anim_single; + } + else + { + anm = &blast_anim_random; + } + + prescript_ptr = &prescript; + map = 0; + m = NULL; + // Silly code just to get the compiler to emit + // an unnecessary move and get a match... + if (randu == 0) + { + m = NULL; + anm->pre_script = prescript_ptr; + } + else + { + anm->pre_script = prescript_ptr; + } + + prescript.scr_num = 0; + NewAnime(m, map, anm); +} + +void AN_Blast_high(SVECTOR *pos) +{ + PRESCRIPT pre; + ANIMATION *anm; + + pre.pos = *pos; + pre.pos.vy += 600; + pre.speed = DG_ZeroVector; + pre.speed.vy += 200; + pre.s_anim = 0; + + anm = &blast_anim_high; + anm->pre_script = ⪯ + + pre.scr_num = 0; + NewAnime( NULL, 0, anm ); + + pre.pos = *pos; + pre.pos.vy += 400; + pre.speed = DG_ZeroVector; + pre.speed.vy += 150; + pre.s_anim = 0; + + anm = &blast_anim_high; + anm->pre_script = ⪯ + + pre.scr_num = 1; + NewAnime( NULL, 0, anm ); + + pre.pos = *pos; + pre.pos.vy += 200; + pre.speed = DG_ZeroVector; + pre.speed.vy += 100; + pre.s_anim = 0; + + anm = &blast_anim_high; + anm->pre_script = ⪯ + + pre.scr_num = 2; + NewAnime( NULL, 0, anm ); + + pre.pos = *pos; + pre.speed = DG_ZeroVector; + pre.speed.vy += 50; + pre.s_anim = 0; + + anm = &blast_anim_high; + anm->pre_script = ⪯ + + pre.scr_num = 3; + NewAnime( NULL, 0, anm ); +} + +void AN_Blast_high2(SVECTOR *pos, SVECTOR *offset) +{ + PRESCRIPT pre; + ANIMATION *anm; + + pre.pos = *pos; + pre.pos.vx += offset->vx; + pre.pos.vy += offset->vy; + pre.pos.vz += offset->vz; + pre.speed = DG_ZeroVector; + pre.speed.vx += offset->vx / 3; + pre.speed.vy += offset->vy / 3; + pre.speed.vz += offset->vz / 3; + pre.s_anim = 0; + + anm = &blast_anim_high; + anm->pre_script = ⪯ + + pre.scr_num = 0; + NewAnime( NULL, 0, anm ); + + pre.pos = *pos; + pre.pos.vx += (offset->vx * 3) >> 2; + pre.pos.vy += (offset->vy * 3) >> 2; + pre.pos.vz += (offset->vz * 3) >> 2; + pre.speed = DG_ZeroVector; + pre.speed.vx += ((offset->vx / 3) * 3) >> 2; + pre.speed.vy += ((offset->vy / 3) * 3) >> 2; + pre.speed.vz += ((offset->vz / 3) * 3) >> 2; + pre.s_anim = 0; + + anm = &blast_anim_high; + anm->pre_script = ⪯ + + pre.scr_num = 1; + NewAnime( NULL, 0, anm ); + + pre.pos = *pos; + pre.pos.vx += offset->vx >> 1; + pre.pos.vy += offset->vy >> 1; + pre.pos.vz += offset->vz >> 1; + pre.speed = DG_ZeroVector; + pre.speed.vx += (offset->vx / 3) >> 1; + pre.speed.vy += (offset->vy / 3) >> 1; + pre.speed.vz += (offset->vz / 3) >> 1; + pre.s_anim = 0; + + anm = &blast_anim_high; + anm->pre_script = ⪯ + + pre.scr_num = 2; + NewAnime( NULL, 0, anm ); + + pre.pos = *pos; + pre.speed = DG_ZeroVector; + pre.speed.vx += (offset->vx / 3) >> 2; + pre.speed.vy += (offset->vy / 3) >> 2; + pre.speed.vz += (offset->vz / 3) >> 2; + pre.s_anim = 0; + + anm = &blast_anim_high; + anm->pre_script = ⪯ + + pre.scr_num = 3; + NewAnime( NULL, 0, anm ); +} diff --git a/components/mgs/mgs/source/bullet/blast.h b/components/mgs/mgs/source/bullet/blast.h new file mode 100644 index 00000000..e722a420 --- /dev/null +++ b/components/mgs/mgs/source/bullet/blast.h @@ -0,0 +1,29 @@ +#ifndef __MGS_BULLET_BLAST_H__ +#define __MGS_BULLET_BLAST_H__ + +#include +#include +#include + +#include "libdg/libdg.h" +#include "libgv/libgv.h" + +typedef struct BLAST_DATA +{ + int field_0; // hp + int field_4; + int field_8_z; // size + int field_C; // also size? + int weapon; +} BLAST_DATA; + +void *NewBlast( MATRIX *world, BLAST_DATA *blast_data ); +void *NewBlast2( MATRIX *world, BLAST_DATA *blast_data, int doSound, int side ); + +void AN_Blast_Single( SVECTOR *pos ); +void AN_Blast_Mini( SVECTOR *pos ); +void AN_Blast_Minimini( SVECTOR *pos ); +void AN_Blast_high( SVECTOR *pos ); +void AN_Blast_high2( SVECTOR *pos, SVECTOR *offset ); + +#endif // __MGS_BULLET_BLAST_H__ diff --git a/components/mgs/mgs/source/bullet/bullet.h b/components/mgs/mgs/source/bullet/bullet.h new file mode 100644 index 00000000..c0499745 --- /dev/null +++ b/components/mgs/mgs/source/bullet/bullet.h @@ -0,0 +1,11 @@ +#ifndef __MGS_BULLET_H__ +#define __MGS_BULLET_H__ + +#include "blast.h" +#include "bakudan.h" +#include "jirai.h" +#include "tenage.h" +#include "rmissile.h" +#include "amissile.h" + +#endif // __MGS_BULLET_H__ diff --git a/components/mgs/mgs/source/bullet/jirai.c b/components/mgs/mgs/source/bullet/jirai.c new file mode 100644 index 00000000..ca9a94e5 --- /dev/null +++ b/components/mgs/mgs/source/bullet/jirai.c @@ -0,0 +1,676 @@ +#include "jirai.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "libhzd/libhzd.h" +#include "game/game.h" +#include "linkvar.h" +#include "okajima/claymore.h" +#include "chara/snake/sna_init.h" +#include "sound/g_sound.h" + +extern HITTABLE GM_ClayDatas[8]; + +/*---------------------------------------------------------------------------*/ +// Claymore (armed) + +#define CLAYMORE_NAME GV_StrCode("claymore") +#define CLAYMORE_MODEL GV_StrCode("claymore") + +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE | \ + DG_FLAG_GBOUND | DG_FLAG_ONEPIECE | \ + DG_FLAG_AMBIENT | DG_FLAG_IRTEXTURE ) + +int dword_8009F440 = 0; +int dword_8009F444 = 0; +int counter_8009F448 = 0; +SVECTOR svec_8009F44C = {-625, -250, 750, 0}; +SVECTOR svec_8009F454 = {-500, -250, 750, 0}; +SVECTOR svec_8009F45C = {500, 200, 500, 0}; +SVECTOR svec_8009F464 = {300, 200, 300, 0}; + +/*---------------------------------------------------------------------------*/ + +static MATRIX *jirai_loader_helper_8006A798(MATRIX *arg0, MATRIX *arg1, HZD_FLR *floor) +{ + MATRIX mtx1; + MATRIX mtx2; + + HZD_FLR *flr; + + int var_a2; + int var_a0; + int var_v1; + + if (!floor) + { + *arg0 = *arg1; + } + else + { + flr = (HZD_FLR *)((int)floor | 0x80000000); // cached + + var_a2 = flr->p1.h * 16; + var_v1 = flr->p2.h * 16; + var_a0 = flr->p3.h * 16; + + if (var_a0 < 0) + { + var_a2 = -var_a2; + var_a0 = -var_a0; + var_v1 = -var_v1; + } + + mtx1.m[2][0] = 0; + mtx1.m[0][1] = var_a2; + mtx1.m[1][0] = -var_a2; + mtx1.m[0][0] = var_a0; + mtx1.m[1][1] = var_a0; + mtx1.m[2][1] = var_v1; + mtx1.m[0][2] = 0; + mtx1.m[2][2] = var_a0; + mtx1.m[1][2] = -var_v1; + + mtx2 = *arg1; + MulMatrix2(&mtx1, &mtx2); + *arg0 = mtx2; + } + + return arg0; +} + +static int jirai_act_helper_8006A8F4(JiraiWork *work) +{ + int local_10E; + CONTROL *p_control; + SVECTOR v; + + if (!(GM_PlayerStatus & PLAYER_GROUND)) + { + return 0; + } + + local_10E = work->field_10E; + p_control = &work->control; + + if (local_10E == TRUE) + { + return 0; + } + GV_SubVec3(&p_control->mov, &GM_PlayerPosition, &v); + return GV_VecLen3(&v) < 800; +} + +static void JiraiDisplayText(JiraiWork *work, int arg1) +{ + SVECTOR vec; + MATRIX *matrix; + int temp_a2; + int sound; + int r, g, b; + const char *text; + + matrix = &DG_Chanl(0)->eye_inv; + + gte_SetRotMatrix(matrix); + gte_SetTransMatrix(matrix); + gte_ldv0(&work->control.mov); + gte_rtps(); + gte_stsxy(&vec); + + if ((work->field_130 < 16) && (arg1 == 0)) + { + vec.vy -= work->field_130; + } + else + { + vec.vy -= 16; + } + + if (arg1 == 0) + { + temp_a2 = work->field_130; + + if (temp_a2 == 1) + { + if (GM_Weapons[WP_Claymore] >= GM_WeaponsMax[WP_Claymore]) + { + sound = SE_ITEM_FULL; + work->field_150 = temp_a2; + } + else + { + work->control.radar_atr = RADAR_OFF; + sound = SE_ITEM_GET; + + if (GM_Weapons[WP_Claymore] < 0) + { + GM_Weapons[WP_Claymore] = 0; + } + + GM_Weapons[WP_Claymore]++; + + work->field_140 = temp_a2; + work->field_150 = 0; + } + + GM_SeSetMode(&work->control.mov, sound, GM_SEMODE_BOMB); + } + + if (work->field_150 != 0) + { + r = 255; + g = 48; + b = 48; + } + else + { + r = 200; + g = 200; + b = 200; + } + + MENU_Color(r, g, b); + text = "CLAYMORE"; + } + else + { + text = "FULL"; + MENU_Color(255, 48, 48); + } + + if (work->control.map->index & GM_PlayerMap) + { + MENU_Locate(vec.vx + 160, vec.vy + 104, 0x12); + MENU_Printf(text); + + MENU_Color(1, 1, 1); + MENU_Locate(vec.vx + 161, vec.vy + 105, 0x12); + MENU_Printf(text); + + MENU_ResetText(); + } +} + +static void Act(JiraiWork *work) +{ + TARGET target; + CONTROL *control; + TARGET *target2; + int f130; + GV_ACT *claymore; + + if (GM_GameStatus < 0) + { + GV_DestroyActor(&work->actor); + return; + } + + control = &work->control; + DG_GetLightMatrix2(&control->mov, work->light); + + if (work->field_134_gcl_arg == 2) + { + if (++work->field_130 < 45) + { + JiraiDisplayText(work, 0); + + if ((work->field_140 != 0) && (work->field_130 == 16)) + { + DG_InvisibleObjs(work->body.objs); + } + } + else if (work->field_140 != 0) + { + HZD_ExecLeaveEvent(work->control.map->hzd, &work->control.evt); + GV_DestroyActor(&work->actor); + } + else + { + work->field_130 = 0; + work->field_134_gcl_arg = 3; + } + } + else if (work->field_134_gcl_arg == 3) + { + if ((++work->field_130 < 45) || jirai_act_helper_8006A8F4(work)) + { + JiraiDisplayText(work, 1); + } + else + { + work->field_134_gcl_arg = 0; + work->field_130 = 0; + } + } + + if (work->field_134_gcl_arg >= 2) + { + work->target->damaged &= ~TARGET_PUSH; + return; + } + + target2 = work->target; + + GM_ActControl(control); + GM_SetCurrentMap(work->map); + GM_ActObject2((OBJECT *)&work->body); + + f130 = work->field_130; + + if (f130 < 16) + { + work->field_130++; + } + + if ((f130 > 15) && (f130 < 64)) + { + if (f130 & 1) + { + DG_InvisibleObjs(work->body.objs); + } + else + { + DG_VisibleObjs(work->body.objs); + } + + if (++work->field_130 == 64) + { + DG_InvisibleObjs(work->body.objs); + } + } + + if (GM_GameStatus & STATE_THERMG) + { + DG_VisibleObjs(work->body.objs); + } + else if (work->field_130 == 64) + { + DG_InvisibleObjs(work->body.objs); + } + + if (GM_CurrentItemId == IT_MineDetector) + { + GM_ConfigControlAttribute(control, 0x202D); + } + else + { + GM_ConfigControlAttribute(control, 0); + } + + if ( +#ifdef VR_EXE + (GM_GameStatus & (STATE_PADRELEASE | STATE_DEMO)) +#else + (GM_GameStatus & (STATE_PADRELEASE | STATE_PADDEMO | STATE_DEMO)) +#endif + || (GM_PlayerStatus & PLAYER_PAD_OFF)) + { + target2->class &= ~TARGET_PUSH; + target2->damaged &= ~TARGET_PUSH; + } + else + { + target2->class |= TARGET_PUSH; + } + + if (((target2->damaged & TARGET_PUSH) || (dword_8009F444 != 0)) && (work->field_10E == FALSE)) + { + if (dword_8009F440 == 1) + { + work->field_134_gcl_arg = dword_8009F440; + GV_DestroyActor(&work->actor); + return; + } + + if ((target2->push_side & PLAYER_SIDE) && (GM_PlayerStatus & (PLAYER_INVINCIBLE | PLAYER_GROUND))) + { + target2->damaged &= ~TARGET_PUSH; + dword_8009F444 = 0; + return; + } + + if (dword_8009F444 != 0) + { + work->field_144_vec.vy += 2048; + } + + dword_8009F440 = 1; + work->field_10E = TRUE; + + DG_VisibleObjs(work->body.objs); + + sub_800790E8(); + +#ifdef VR_EXE + work->field_154 = 1; +#endif + + GM_SetTarget(&target, TARGET_POWER, NO_SIDE, &target2->size); + GM_SetPowerTarget(&target, POWER_DECREASE, 2, 128, 0, &DG_ZeroVector); + GM_MoveTarget(&target, &target2->center); + + GM_PowerTarget(&target); + HZD_ExecLeaveEvent(work->control.map->hzd, &work->control.evt); + } + + if (work->field_10E == TRUE) + { + if (work->field_10C == 7) + { + sub_8007913C(); + } + + claymore = NewClaymore(&work->control.mov, &work->field_144_vec, 2, work->field_10C); + + if (!claymore) + { + work->field_10C = 0; + work->field_10E = FALSE; + } + else + { + work->field_10C--; + } + + if ((work->field_10C <= 0) || !claymore) + { + work->field_134_gcl_arg = 1; + GV_DestroyActor(&work->actor); + return; + } + } + + if (jirai_act_helper_8006A8F4(work)) + { + work->field_134_gcl_arg = 2; + DG_VisibleObjs(work->body.objs); + work->field_130 = 0; + } +} + +/*---------------------------------------------------------------------------*/ + +// A different version of ExecProc is used here, which checks for a proccess ID less than zero. +#define ExecProc(proc_id, mode) \ +{ \ + GCL_ARGS args; \ + long data; \ + \ + if (proc_id < 0) goto skip; \ + \ + args.argc = 1; \ + args.argv = &data; \ + data = mode; \ + do {} while (0); \ + GCL_ExecProc(proc_id, &args); \ +skip: \ +} + +static void Die(JiraiWork *work) +{ +#ifdef VR_EXE + if (work->field_154) +#endif + { + sub_8007913C(); + } + GM_FreeControl(&work->control); + GM_FreeObject((OBJECT *)&work->body); + GM_FreeTarget(work->target); + + if (work->field_13C_idx >= 0) + { + GM_ClearBulName(work->control.name); + GM_ClayDatas[work->field_13C_idx].actor = NULL; + counter_8009F448--; + } + + ExecProc(work->field_138_gcl, work->field_134_gcl_arg); + + dword_8009F444 = 0; + dword_8009F440 = 0; +} + +/*---------------------------------------------------------------------------*/ + +static int InitJiraiTarget(JiraiWork *work, MATRIX *pMtx, int a3) +{ + TARGET *target; + SVECTOR v12; + SVECTOR *v8; + + target = GM_AllocTarget(); + work->target = target; + if (!target) + { + return -1; + } + + if (a3) + { + v12 = svec_8009F45C; + } + else + { + v12 = svec_8009F464; + } + + if (a3) + { + v8 = &svec_8009F454; + } + else + { + v8 = &svec_8009F44C; + } + + if (GCL_GetOption('t')) + { + GCL_StrToSV(GCL_NextStr(), (short *)&v12); + } + GM_SetTarget(target, 9, NO_SIDE, &v12); + target->flag |= 2; + DG_SetPos(pMtx); + DG_PutVector(v8, &v12, 1); + GM_MoveTarget(target, &v12); + work->field_10C = 8; + work->field_10E = FALSE; + return 0; +} + +static int GetNextClayData(void) +{ + int i; + for (i = 0; i < 8; i++) + { + if (!GM_ClayDatas[i].actor) + { + return i; + } + } + return -1; +} + +static int GetResources(JiraiWork *work, MATRIX *world, HZD_FLR *floor) +{ + int map; + CONTROL *control; + HITTABLE *hittable; + MATRIX matrix; + RADAR_SIGHT_PARAM *r_param; + OBJECT_NO_ROTS *obj; + + map = GM_PlayerMap; + control = &work->control; + work->field_138_gcl = -1; + work->field_13C_idx = -1; + GM_CurrentMap = map; + work->map = map; + if (GM_InitControl(control, GM_Next_BulName_8004FBA0(), 0) < 0) + { + return -1; + } + + GM_ConfigControlHazard(control, 0, 0, 0); + jirai_loader_helper_8006A798(&matrix, world, floor); + GM_ConfigControlMatrix(control, world); + work->field_144_vec.vy = ratan2(-matrix.m[0][0], -matrix.m[2][0]) & 4095; + work->field_144_vec.vx = ratan2(matrix.m[1][0], 4096) & 4095; + work->field_144_vec.vz = 0; + GM_ConfigControlAttribute(control, 0); + obj = &work->body; + GM_InitObjectNoRots(obj, CLAYMORE_MODEL, BODY_FLAG, 0); + if (!obj->objs) + { + return -1; + } + + DG_SetPos2(&control->mov, &work->control.rot); + DG_PutObjs(obj->objs); + GM_ConfigObjectLight((OBJECT *)obj, work->light); + + work->field_130 = 0; + work->field_138_gcl = -1; + work->field_134_gcl_arg = 0; + work->field_140 = 0; + + if (InitJiraiTarget(work, world, 0) < 0) + { + return -1; + } + + work->field_13C_idx = GetNextClayData(); + if (work->field_13C_idx < 0) + { + return -1; + } + + hittable = &GM_ClayDatas[work->field_13C_idx]; + hittable->actor = &work->actor; + hittable->control = control; + hittable->data = floor; + + r_param = &work->control.radar_param; + r_param->dis = 2000; + r_param->range = 1024; + ++counter_8009F448; + r_param->dir = GM_PlayerControl->rot.vy; + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewJirai(MATRIX *world, HZD_FLR *floor) +{ + JiraiWork *work; + + if (counter_8009F448 == 8) + { + return NULL; + } + + work = GV_NewActor(GV_ACTOR_USER, sizeof(JiraiWork)); + if (work) + { + work->field_104_vec = GM_PlayerControl->rot; + GV_SetNamedActor(&work->actor, Act, Die, "jirai.c"); + + if (GetResources(work, world, floor) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + + return (void *)work; +} + +/*---------------------------------------------------------------------------*/ + +static int GetResources2(JiraiWork *work, MATRIX *world, int map) +{ + MATRIX matrix; + CONTROL *ctrl; + RADAR_SIGHT_PARAM *r_param; + OBJECT_NO_ROTS *obj; + + work->map = map; + + ctrl = &work->control; + if (GM_InitControl(ctrl, CLAYMORE_NAME, map) < 0) + { + return -1; + } + + GM_ConfigControlString(ctrl, GCL_GetOption('p'), GCL_GetOption('d')); + GM_ConfigControlHazard(ctrl, 0, -2, -2); + GM_ConfigControlAttribute(ctrl, 0); + + work->field_144_vec = ctrl->rot; + obj = &work->body; + GM_InitObjectNoRots(obj, CLAYMORE_MODEL, BODY_FLAG, 0); + GM_ConfigObjectLight((OBJECT *)obj, work->light); + + work->field_104_vec = ctrl->rot; + + ctrl->rot.vx = ctrl->turn.vx = -1024; + ctrl->rot.vy = ctrl->turn.vy += 1024; + ctrl->mov.vy += 500; + + DG_SetPos2(&ctrl->mov, &ctrl->rot); + + ReadRotMatrix(&matrix); + + if (InitJiraiTarget(work, &matrix, 1) < 0) + { + return -1; + } + + ctrl->mov.vy -= 500; + DG_InvisibleObjs(obj->objs); + work->field_130 = 64; + + if (GCL_GetOption('e')) + { + work->field_138_gcl = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->field_138_gcl = -1; + } + + work->field_134_gcl_arg = 0; + work->field_13C_idx = -1; + work->field_140 = 0; + + r_param = &ctrl->radar_param; + r_param->dis = 2000; + r_param->range = 1024; + r_param->dir = ctrl->rot.vy - 1024; + + return 0; +} + +void *NewScenarioJirai(MATRIX *world, int map) +{ + JiraiWork *work; + + work = GV_NewActor(GV_ACTOR_AFTER, sizeof(JiraiWork)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "jirai.c"); + if (GetResources2(work, world, map) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/bullet/jirai.h b/components/mgs/mgs/source/bullet/jirai.h new file mode 100644 index 00000000..0ca45d0c --- /dev/null +++ b/components/mgs/mgs/source/bullet/jirai.h @@ -0,0 +1,48 @@ +#ifndef __MGS_BULLET_JIRAI_H__ +#define __MGS_BULLET_JIRAI_H__ + +#include +#include +#include + +#include "libgv/libgv.h" +#include "libhzd/libhzd.h" +#include "game/game.h" + +// Claymore (armed) + +typedef struct JiraiWork +{ + GV_ACT actor; + CONTROL control; + OBJECT_NO_ROTS body; + MATRIX light[2]; + TARGET *target; + SVECTOR field_104_vec; + short field_10C; + short field_10E; + int field_110; + int field_114; + int field_118; + int field_11C; + int field_120; + int field_124; + int field_128; + int field_12C; + int field_130; + long field_134_gcl_arg; + int field_138_gcl; + int field_13C_idx; + int field_140; + SVECTOR field_144_vec; + int map; + int field_150; +#ifdef VR_EXE + int field_154; +#endif +} JiraiWork; + +void *NewJirai(MATRIX *world, HZD_FLR *floor); +void *NewScenarioJirai(MATRIX *world, int map); + +#endif // __MGS_BULLET_JIRAI_H__ diff --git a/components/mgs/mgs/source/bullet/rmissile.c b/components/mgs/mgs/source/bullet/rmissile.c new file mode 100644 index 00000000..0e6d9e58 --- /dev/null +++ b/components/mgs/mgs/source/bullet/rmissile.c @@ -0,0 +1,1011 @@ +#include "rmissile.h" +#include "blast.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libhzd/libhzd.h" +#include "takabe/prim.h" +#include "game/game.h" +#include "linkvar.h" +#include "menu/menuman.h" +#include "thing/sgtrect3.h" +#include "thing/sight.h" +#include "sound/g_sound.h" +#include "strcode.h" + +extern BLAST_DATA blast_data_8009F4B8[8]; +extern TARGET *target_800BDF00; +extern int dword_8009F604; +extern int dword_800BDEF8[]; +extern int GM_CameraTrackSave; +extern SVECTOR GM_CameraRotateSave; +extern int GM_event_camera_flag; +extern GM_CameraSystemWork GM_Camera; +extern GM_SnakeCameraWork GM_SnakeCamera; +extern CAMERA GM_CameraList[8]; + +/*---------------------------------------------------------------------------*/ +// RC Missile (Nikita) + +#define EXEC_LEVEL GV_ACTOR_AFTER + +#define NIKITA_NAME 0x50ae // GV_StrCode("RCM") +#define NIKITA_SIGHT 0x15a9 // GV_StrCode("nikita") +#define NIKITA_MSG 0x15a9 // GV_StrCode("nikita") +#define NIKITA_MODEL 0x9a90 // GV_StrCode("nik_mis") + +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE | \ + DG_FLAG_GBOUND | DG_FLAG_ONEPIECE | \ + DG_FLAG_AMBIENT | DG_FLAG_IRTEXTURE ) + +#define SEGMENT_ATR (HZD_SEG_NO_MISSILE) + +#define MISSILE_SPEED_SLOW 50 +#define MISSILE_SPEED_FAST 200 + +#define FUEL_BAR_XOFFSET 31 +#define FUEL_BAR_YOFFSET 39 +#define FUEL_BAR_HEIGHT 10 + +#define COLOR_LIGHT_GREEN MAKE_RGB0(158,184,138) +#define COLOR_DARK_GREEN MAKE_RGB0( 93,135, 90) +#define COLOR_LIGHT_RED MAKE_RGB0(212, 78, 78) +#define COLOR_DARK_RED MAKE_RGB0(128, 26, 12) + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + OBJECT_NO_ROTS object; + MATRIX light[2]; + SVECTOR field_100_svector; // work->control.mov + SVECTOR field_108_svector; // work->control.mov + char field_110; + char boost_delay; + char field_112; + char fpv_mode; + char hud_delay; + char field_115; + char field_116; + char field_117; + short field_118; + short speed; + int field_11C; + TARGET target; + short enemy_time; + short cam_interp; + SVECTOR cam_pos; + POLY_F4 *fuel_bar[2]; + GM_CameraSystemWork saved_camera; + GM_SnakeCameraWork saved_camera_unk; + CAMERA saved_camera_list[8]; + int saved_camera_track; + SVECTOR saved_camera_rotate; + int saved_camera_event; + DG_PRIM *flame; + DG_TEX *flame_tex; + char field_2E0_pad[4]; + SVECTOR flame_vec[8]; + int flame_delay; +} Work; + +/*---------------------------------------------------------------------------*/ + +int dword_8009F46C = 0; // FIXME: this is referenced as an array in several places +int dword_8009F470 = 0; +int dword_8009F474 = 0; +SVECTOR svector_8009F478 = {0, 0, 0, 0}; +int dword_8009F480 = 0; +short Nik_Blast = 0; +SVECTOR svector_8009F488 = {100, 100, 100, 0}; + +/*---------------------------------------------------------------------------*/ + +static void SaveCameraState(Work *work) +{ + GV_CopyMemory(&GM_Camera, &work->saved_camera, sizeof(work->saved_camera)); + GV_CopyMemory(&GM_SnakeCamera, &work->saved_camera_unk, sizeof(work->saved_camera_unk)); + GV_CopyMemory(GM_CameraList, &work->saved_camera_list, sizeof(work->saved_camera_list)); + GV_CopyMemory(&GM_CameraRotateSave, &work->saved_camera_rotate, sizeof(work->saved_camera_rotate)); + + work->saved_camera_track = GM_CameraTrackSave; + work->saved_camera_event = GM_event_camera_flag; +} + +static void ResetCameraState(Work *work) +{ + GV_CopyMemory(&work->saved_camera, &GM_Camera, sizeof(work->saved_camera)); + GV_CopyMemory(&work->saved_camera_unk, &GM_SnakeCamera, sizeof(work->saved_camera_unk)); + GV_CopyMemory(&work->saved_camera_list, &GM_CameraList, sizeof(work->saved_camera_list)); + GV_CopyMemory(&work->saved_camera_rotate, &GM_CameraRotateSave, sizeof(work->saved_camera_rotate)); + + GM_CameraTrackSave = work->saved_camera_track; + GM_event_camera_flag = work->saved_camera_event; + + sub_800309B4(2, 30); + GM_CameraEventReset(); +} + +static void rmissile_8006B924(Work *work) +{ + CONTROL *ctrl; + + ctrl = &work->control; + ctrl->skip_flag |= CTRL_SKIP_TRAP; + + ResetCameraState(work); + GM_GameStatus &= ~GAME_FLAG_BIT_07; + HZD_ExecLeaveEvent(ctrl->map->hzd, &ctrl->evt); + + ctrl = GM_PlayerControl; + + if (ctrl) + { + HZD_ReExecEvent(ctrl->map->hzd, &ctrl->evt, 0x102); + } +} + +static void SetFirstPersonView(Work *work) +{ + Nik_Blast = 1; + + if (dword_8009F604 != NIKITA_SIGHT) + { + NewSight(NIKITA_SIGHT, NIKITA_SIGHT, &Nik_Blast, IT_Scope, NULL); + } + + GM_Camera.first_person = 1; + + if (work->fpv_mode == FALSE) + { + work->fpv_mode = TRUE; + dword_800BDEF8[0] = COLOR_DARK_GREEN; + dword_800BDEF8[1] = COLOR_DARK_RED; + NewSgtRect3(&Nik_Blast, 1, dword_800BDEF8, 0); + GM_SeSet2(0, 0x3F, SE_ITEM_OPENWINDOW); + } +} + +static void DrawEnemyText(Work *work) +{ + if (!target_800BDF00) + { + work->enemy_time = 0; + return; + } + + work->enemy_time++; + + if (work->enemy_time <= 16) + { + return; + } + + if (work->enemy_time == 32) + { + work->enemy_time = 0; + } + + if (!(GM_PlayerStatus & PLAYER_NOT_SIGHT)) + { + MENU_Color(158, 184, 138); + MENU_Locate(116, 98, 0); + MENU_Printf("ENEMY\n"); + MENU_ResetText(); + } +} + +static void DrawMissileHud(Work *work) +{ + int width; + OT_TYPE *ot; + POLY_F4 *bar; + CONTROL ctrl; + + width = ((1000 - work->field_118) * 60) / 1000; + + ot = DG_ChanlOTag(1); + bar = work->fuel_bar[GV_Clock]; + + if (width > 60) + { + width = 60; + } + + /* update fuel gauge */ + bar->x3 = width + FUEL_BAR_XOFFSET; + bar->x1 = width + FUEL_BAR_XOFFSET; + + if (GM_PlayerStatus & PLAYER_NOT_SIGHT) + { + return; + } + + if (width < 15) + { + /* low fuel remaining */ + setRGB0(bar, 212, 78, 78); + + if (!(work->field_118 % 15)) + { + GM_SeSet2(0, 63, SE_SIGNAL02); + } + } + + addPrim(ot, bar); + + /* print missile XYZ position */ + ctrl = work->control; + + MENU_Locate(8, 136, 0); + MENU_Printf("%d", ctrl.mov.vx); + + MENU_Locate(8, 144, 0); + MENU_Printf("%d", ctrl.mov.vy); + + MENU_Locate(8, 152, 0); + MENU_Printf("%d", ctrl.mov.vz); + + MENU_ResetText(); +} + +static void rmissile_act_helper_8006BD24(Work *work, int pad_status) +{ + if (work->field_112 || work->field_117 || work->field_110) + { + return; + } + + if (pad_status & PAD_TRIANGLE) + { + SetFirstPersonView(work); + + GM_SnakeCamera.rotate2.vx = 0; + GM_SnakeCamera.rotate2.vy = work->control.rot.vy; + GM_SnakeCamera.rotate2.vz = 0; + + DG_InvisibleObjs(work->object.objs); + + work->field_11C = -2; + + if (work->hud_delay < 16) + { + work->hud_delay++; + return; + } + + DrawMissileHud(work); + DrawEnemyText(work); + return; + } + + if (work->fpv_mode) + { + work->field_115 = 5; + } + + work->hud_delay = 0; + work->fpv_mode = FALSE; + + GM_Camera.first_person = 0; + Nik_Blast = 0; + + if (work->field_115) + { + work->field_115--; + + if (work->field_115 == 0) + { + DG_VisibleObjs(work->object.objs); + } + } +} + +static void SetMissileDir3P(Work *work, int pad_dir) +{ + if (GM_PlayerStatus & PLAYER_PAD_OFF) + { + return; + } + + if (pad_dir >= 0 && work->control.turn.vy != pad_dir) + { + work->boost_delay = 30; + work->control.turn.vy = pad_dir; + } +} + +static void SetMissileDir1P(Work *work, int pad_status) +{ + if (GM_PlayerStatus & PLAYER_PAD_OFF) + { + return; + } + + if (pad_status & PAD_LEFT) + { + work->boost_delay = 30; + work->control.turn.vy += 64; + work->control.turn.vy &= 0x0FFF; + } + + if (pad_status & PAD_RIGHT) + { + work->boost_delay = 30; + work->control.turn.vy -= 64; + work->control.turn.vy &= 0x0FFF; + } +} + +static void rmissile_act_helper_8006BEEC(Work *work) +{ + if (work->field_117) + { + return; + } + + if (GM_PlayerStatus & (PLAYER_DAMAGED | PLAYER_GAME_OVER) || + dword_8009F474 == 1 || GM_GameStatus & (STATE_CHAFF | STATE_JAMMING)) + { + work->field_117 = 1; + work->cam_interp = 15; + work->cam_pos = work->control.mov; + work->fpv_mode = FALSE; + + GM_Camera.first_person = 0; + Nik_Blast = 0; + + if (!work->field_112) + { + DG_VisibleObjs(work->object.objs); + } + + dword_8009F474 = 0; + rmissile_8006B924(work); + } +} + +static void rmissile_act_helper_8006BFD4(Work *work) +{ + if (work->boost_delay) + { + work->boost_delay--; + + if (work->boost_delay == 0) + { + GM_SeSet2(0, 63, SE_MISSILE_BOOST); + work->field_116 = 12; + } + + work->speed = MISSILE_SPEED_SLOW; + + if (!work->field_112) + { + work->field_11C = 0; + } + } + else + { + if (work->field_116) + { + work->field_116--; + GM_SnakeCamera.position.vy += GV_RandS(512) * work->field_116 / 32; + } + + work->speed = MISSILE_SPEED_FAST; + + if (!work->field_112) + { + work->field_11C = 8; + } + } +} + +static int CheckMessage(void) +{ + GV_MSG *msg; + int len; + + len = GV_ReceiveMessage(NIKITA_MSG, &msg); + if (len > 0) + { + for (len--; len >= 0; msg++, len--) + { + if (msg->message[0] == HASH_KILL) + { + return 1; + } + } + } + return 0; +} + +static void rmissile_act_helper_8006C114(Work *work) +{ + SVECTOR *position; + int result; + MATRIX rotation; + BLAST_DATA *blast; + + if (work->field_112) + { + return; + } + + position = &work->control.mov; + result = CheckMessage(); + + if (work->control.n_touches <= 0 && !(work->control.grounded & 2)) + { + if (position->vy - work->field_108_svector.vy < 200) + { + if (++work->field_118 != 1000 && !GM_PowerTarget(&work->target) && !dword_8009F480 && !result) + { + if (!HZD_NearHazardCheck(work->control.map->hzd, position, 250, HZD_CHK_ALL, SEGMENT_ATR)) + { + if (abs(position->vx) <= 30000 && + abs(position->vy) <= 30000 && + abs(position->vz) <= 30000) + { + return; + } + } + } + } + } + + if (GM_Camera.first_person && !work->field_117) + { + GM_Camera.first_person = 0; + Nik_Blast = 0; + } + + work->fpv_mode = FALSE; + dword_8009F480 = 0; + work->field_112 = 1; + work->field_118 = result ? 28 : 0; + work->control.step = DG_ZeroVector; + DG_InvisibleObjs(work->object.objs); + work->object.objs->group_id = 0; + work->field_11C = -2; + + if (!result) + { + DG_SetPos2(position, &work->control.rot); + ReadRotMatrix(&rotation); + + GM_CurrentMap = work->control.map->index; + + if (GM_GameStatus & (STATE_PADRELEASE | STATE_PADDEMO | STATE_DEMO)) + { + blast = &blast_data_8009F4B8[7]; + #ifdef VR_EXE + if ((GM_GameStatus & STATE_PADDEMO) && + !(GM_PlayerStatus & PLAYER_PAD_OFF) && + !(GM_GameStatus & STATE_PADRELEASE)) + { + blast = &blast_data_8009F4B8[4]; + } + #endif + } + else + { + blast = &blast_data_8009F4B8[4]; + } + NewBlast(&rotation, blast); + } +} + +static void rmissile_act_helper_8006C37C(Work *work) +{ + SVECTOR vecs[8]; + POLY_FT4 *poly; + DG_TEX *tex; + int i; + int inc; + int offx; + int offy; + int width; + int height; + int y; + int rand1; + int rand2; + + if (work->field_11C > 0) + { + inc = 80; + y = 120; + } + else + { + inc = 64; + y = 120; + } + + poly = work->flame->packs[GV_Clock]; + + for (i = 0; i < 8; i++, poly++) + { + if (work->field_11C && (i == 0)) + { + poly->tpage &= ~0x0060; + poly->tpage |= 0x0020; + } + else + { + poly->tpage |= 0x60; + } + + vecs[i].vx = 0; + + if (work->field_11C) + { + vecs[i].vy = 150 + i * inc; + } + else + { + vecs[i].vy = 250 + i * inc; + } + + vecs[i].vz = 0; + + work->flame_vec[i].pad = y; + y = 15 * y / 16; + + + rand1 = GV_RandU(4) & 1; + rand2 = GV_RandU(4); + + tex = work->flame_tex; + + offx = work->flame_tex->off_x; + width = work->flame_tex->w + 1; + + poly->u3 = offx + (width << rand1) / 2 - 1; + poly->u1 = offx + (width << rand1) / 2 - 1; + + poly->u2 = offx + ((width * rand1) >> 1); + poly->u0 = offx + ((width * rand1) >> 1); + + + offy = work->flame_tex->off_y; + height = work->flame_tex->h + 1; + + poly->v1 = offy + (height * (rand2 / 2)) / 2; + poly->v0 = offy + (height * (rand2 / 2)) / 2; + + poly->v3 = offy + (height * (rand2 / 2 + 1)) / 2 - 1; + poly->v2 = offy + (height * (rand2 / 2 + 1)) / 2 - 1; + + setRGB0(poly, 128u - i * 16, 128u - i * 16, 128u - i * 16); + } + + DG_SetPos2(&work->control.mov, &work->control.rot); + DG_PutVector(vecs, work->flame_vec, 8); +} + +static void Act(Work *work) +{ + GV_PAD *pad; + BLAST_DATA *blast; + SVECTOR vector; + SVECTOR vector2; + MATRIX rotation; + + pad = &GV_PadData[0]; + + if (GM_PlayerStatus & PLAYER_SECOND_CONTROLLER) + { + pad = &GV_PadData[1]; + } + + if (work->flame_delay-- > 0 || (work->field_11C < 0 && work->field_11C > -3)) + { + DG_InvisiblePrim(work->flame); + } + else + { + DG_VisiblePrim(work->flame); + rmissile_act_helper_8006C37C(work); + } + + if (work->field_110 && !work->field_112) + { + if (--work->field_110 != 0) + { + if (work->field_118 != 0) + { + vector2 = work->control.mov; + GV_NearExp2V(&vector2.vx, &work->field_100_svector.vx, 3); + GV_SubVec3(&vector2, &work->control.mov, &work->control.step); + } + } + else + { + DG_VisibleObjs(work->object.objs); + work->target.side = 0; + GV_SubVec3(&work->field_100_svector, &work->control.mov, &work->control.step); + } + } + + if (GM_CurrentWeaponId != 3) + { + /* player has unequipped the Nikita launcher */ + if (!work->field_112) + { + GM_CurrentMap = work->control.map->index; + DG_SetPos2(&work->control.mov, &work->control.rot); + ReadRotMatrix(&rotation); + + if (GM_GameStatus & (STATE_PADRELEASE | STATE_PADDEMO | STATE_DEMO) + || !GM_SnakeCurrentHealth || GM_GameOverTimer) + { + blast = &blast_data_8009F4B8[7]; + #ifdef VR_EXE + if ((GM_GameStatus & STATE_PADDEMO) && + !(GM_PlayerStatus & PLAYER_PAD_OFF) && + !(GM_GameStatus & STATE_PADRELEASE)) + { + blast = &blast_data_8009F4B8[4]; + } + #endif + } + else + { + blast = &blast_data_8009F4B8[4]; + } + + NewBlast(&rotation, blast); + } + + dword_8009F480 = 0; + + if (GM_Camera.first_person && GM_PlayerBody) + { + DG_InvisibleObjs(GM_PlayerBody->objs); + } + + work->field_11C = -1; + GV_DestroyActor(&work->actor); + } + else + { + rmissile_act_helper_8006BEEC(work); + rmissile_act_helper_8006C114(work); + + if (!work->field_117) + { + GM_SnakeCamera.position = work->control.mov; + } + else + { + vector = work->cam_pos; + GV_NearTimeV(&work->cam_pos.vx, &GM_PlayerPosition.vx, work->cam_interp, 3); + GM_SnakeCamera.position = work->cam_pos; + + if (work->cam_interp > 0) + { + work->cam_interp--; + } + } + + dword_8009F46C = !work->field_117; + + if (work->field_112) + { + if (++work->field_118 == 30) + { + GV_DestroyActor(&work->actor); + } + } + else + { + GM_SetNoise(5, 2, &work->control.mov); + work->field_108_svector = work->control.mov; + + GM_ActControl(&work->control); + svector_8009F478 = vector2 = work->control.mov; + + GM_ActObject2((OBJECT *)&work->object); + DG_GetLightMatrix2(&vector2, work->light); + + if (!work->field_117 && !work->field_110) + { + if (GM_PlayerBody) + { + DG_VisibleObjs(GM_PlayerBody->objs); + } + + if (work->fpv_mode == FALSE) + { + SetMissileDir3P(work, pad->dir); + } + else + { + SetMissileDir1P(work, pad->status); + } + } + + rmissile_act_helper_8006BFD4(work); + + if (1000 - work->field_118 < 100) + { + work->control.rot.vy += GV_RandS(32) * (work->field_118 - 900) / 32; + work->field_11C = GV_RandU(2); + } + + if (work->field_117 == 1) + { + work->control.rot.vy += GV_RandS(128); + } + + GV_DirVec2(work->control.rot.vy, work->speed, &work->control.step); + rmissile_act_helper_8006BD24(work, pad->status); + GM_MoveTarget(&work->target, &vector2); + } + } +} + +/*---------------------------------------------------------------------------*/ + +static void Die(Work *work) +{ + GM_FreeControl(&work->control); + GM_FreeObject((OBJECT *)&work->object); + + if (work->fuel_bar[0]) + { + GV_DelayedFree(work->fuel_bar[0]); + dword_8009F46C = 0; + } + else + { + dword_8009F46C = 0; + } + + dword_8009F470 = 0; + Nik_Blast = 0; + + GM_FreePrim(work->flame); + + if (!work->field_117) + { + rmissile_8006B924(work); + } +} + +/*---------------------------------------------------------------------------*/ + +static int InitTarget(Work *work, int side) +{ + TARGET *target = &work->target; + + GM_SetTarget(target, TARGET_POWER, side, &svector_8009F488); + GM_SetPowerTarget(target, POWER_ONCE, -1, 1, 0, &DG_ZeroVector); + GM_MoveTarget(target, &work->control.mov); + return 0; +} + +static int InitFuelBar(Work *work) +{ + POLY_F4 *poly1; + POLY_F4 *poly2; + + work->fuel_bar[0] = poly1 = GV_Malloc(sizeof(POLY_F4) * 2); + + if (poly1 == NULL) + { + return -1; + } + + work->fuel_bar[1] = &poly1[1]; + + LSTORE(COLOR_LIGHT_GREEN, &poly1[0].r0); + LSTORE(COLOR_LIGHT_GREEN, &poly1[1].r0); + + poly2 = &poly1[1]; + + SetPolyF4(poly1); + SetPolyF4(poly2); + SetSemiTrans(poly1, 1); + SetSemiTrans(poly2, 1); + + /* left side screen coordinates */ + poly2->x2 = FUEL_BAR_XOFFSET; + poly2->x0 = FUEL_BAR_XOFFSET; + poly1->x2 = FUEL_BAR_XOFFSET; + poly1->x0 = FUEL_BAR_XOFFSET; + + /* top side screen coordinates */ + poly2->y1 = FUEL_BAR_YOFFSET; + poly2->y0 = FUEL_BAR_YOFFSET; + poly1->y1 = FUEL_BAR_YOFFSET; + poly1->y0 = FUEL_BAR_YOFFSET; + + /* bottom side screen coordinates */ + poly2->y3 = (FUEL_BAR_YOFFSET + FUEL_BAR_HEIGHT); + poly2->y2 = (FUEL_BAR_YOFFSET + FUEL_BAR_HEIGHT); + poly1->y3 = (FUEL_BAR_YOFFSET + FUEL_BAR_HEIGHT); + poly1->y2 = (FUEL_BAR_YOFFSET + FUEL_BAR_HEIGHT); + + return 0; +} + +static inline void set_poly_tex(POLY_FT4 *poly, DG_TEX *tex) +{ + int u, v, w, h; + + setPolyFT4(poly); + setSemiTrans(poly, 1); + + u = tex->off_x; + w = (tex->w + 1) / 2; + poly->u2 = u; + poly->u0 = u; + u += w; + poly->u3 = u - 1; + poly->u1 = u - 1; + + v = tex->off_y; + h = (tex->h + 1) / 2; + poly->v1 = v; + poly->v0 = v; + v += h; + poly->v3 = v - 1; + poly->v2 = v - 1; + + poly->tpage = tex->tpage; + poly->clut = tex->clut; +} + +static void SetPolyTexture(POLY_FT4 *poly, DG_TEX *tex, int n_poly) +{ + set_poly_tex(poly, tex); + poly->tpage = (poly->tpage & ~0x0060) | 0x20; + + setRGB0(poly, 0, 0, 0); + + poly++; + n_poly--; + + while (n_poly-- != 0) + { + set_poly_tex(poly, tex); + poly->tpage |= 0x0060; + + setRGB0(poly, 0, 0, 0); + + poly++; + } +} + +static void InitMissileFlame(Work *work) +{ + DG_TEX *tex; + SVECTOR *vec; + DG_PRIM *prim; + int count; + + tex = DG_GetTexture(GV_StrCode("socom_f")); + + work->flame_tex = tex; + prim = work->flame = Takabe_MakeIndividualRect3DPrim(8, work->flame_vec); + + SetPolyTexture(prim->packs[0], tex, 8); + SetPolyTexture(prim->packs[1], tex, 8); + + prim->world = DG_ZeroMatrix; + prim->raise = 100; + + vec = work->flame_vec; + for (count = 8; count > 0; vec++, count--) + { + vec->pad = 0; + } +} + +static inline int GetEight(void) +{ + return 8; // what??? +} + +static int GetResources(Work *work, MATRIX *world, int side) +{ + CONTROL *ctrl; + OBJECT_NO_ROTS *object; + + dword_8009F46C = 1; + dword_8009F470 = 1; + + SaveCameraState(work); + + ctrl = &work->control; + + if (GM_InitControl(ctrl, NIKITA_NAME, 0) < 0) + { + return -1; + } + + GM_ConfigControlMatrix(ctrl, world); + + work->field_100_svector = ctrl->mov; + work->field_110 = 8; + ctrl->mov = GM_PlayerPosition; + work->field_108_svector = GM_PlayerPosition; + svector_8009F478 = GM_PlayerPosition; + + GM_ConfigControlHazard(ctrl, 400, 0xC8, 0xC8); + ctrl->seg_flag = GetEight(); + GM_ConfigControlTrapCheck(ctrl); + + object = &work->object; + ctrl->turn.vz = 0; + ctrl->rot.vz = 0; + ctrl->skip_flag |= CTRL_SKIP_NEAR_CHECK; + + GM_InitObjectNoRots(object, NIKITA_MODEL, BODY_FLAG, 0); + + if (!object->objs) + { + return -1; + } + + GM_ConfigObjectLight((OBJECT *)object, work->light); + + if (InitTarget(work, side) < 0) + { + return -1; + } + + if (InitFuelBar(work) < 0) + { + return -1; + } + + DG_InvisibleObjs(work->object.objs); + work->field_11C = -1; + work->flame_delay = 10; + InitMissileFlame(work); + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewRMissile(MATRIX *world, int side) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "rmissile.c"); + + if (GetResources(work, world, side) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + work->boost_delay = 30; + work->field_112 = 0; + work->fpv_mode = FALSE; + work->field_117 = 0; + work->hud_delay = 0; + work->field_115 = 0; + work->field_116 = 0; + dword_8009F480 = 0; + work->field_118 = 0; + work->speed = 0; + work->field_11C = 0; + work->enemy_time = 0; + Nik_Blast = 0; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/bullet/rmissile.h b/components/mgs/mgs/source/bullet/rmissile.h new file mode 100644 index 00000000..bef14a64 --- /dev/null +++ b/components/mgs/mgs/source/bullet/rmissile.h @@ -0,0 +1,12 @@ +#ifndef __MGS_BULLET_RMISSILE_H__ +#define __MGS_BULLET_RMISSILE_H__ + +#include +#include +#include + +// RC Missile (Nikita) + +void *NewRMissile(MATRIX *world, int side); + +#endif // __MGS_BULLET_RMISSILE_H__ diff --git a/components/mgs/mgs/source/bullet/tenage.c b/components/mgs/mgs/source/bullet/tenage.c new file mode 100644 index 00000000..8f76637f --- /dev/null +++ b/components/mgs/mgs/source/bullet/tenage.c @@ -0,0 +1,329 @@ +#include "tenage.h" + +#include +#include +#include + +#include "common.h" +#include "chara/snake/sna_init.h" +#include "bullet/blast.h" +#include "game/game.h" +#include "okajima/chafgrnd.h" +#include "okajima/stngrnd.h" +#include "weapon/weapon.h" +#include "sound/g_sound.h" +#include "strcode.h" + +extern int dword_800BDD28; +extern BLAST_DATA blast_data_8009F4B8[8]; +extern CONTROL *tenage_ctrls_800BDD30[16]; +extern int tenage_ctrls_count_800BDD70; + +/*---------------------------------------------------------------------------*/ +// 手投げ弾 (en: hand grenade) + +#define GRENADE_MODEL 0x3b88 // GV_StrCode("grenade") + +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE \ + | DG_FLAG_GBOUND | DG_FLAG_ONEPIECE ) + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + OBJECT_NO_ROTS object; + MATRIX light[2]; + int fuse_time; + int bounces; + SVECTOR step; + int type; + int homing_arg3; + int do_sound; + int side; + int control_index; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void Act(Work *work) +{ + MATRIX rotation; + SVECTOR vec; + CONTROL *control; +#ifdef VR_EXE + int vy, level; +#endif + + work->control.step = work->step; + + control = &work->control; + + if (GM_lpfnBombHoming) + { + GM_lpfnBombHoming(control, work->fuse_time, &work->homing_arg3); + } + + if ((work->control.mov.pad != 0) || (GM_GameStatus < 0)) + { + GV_DestroyActor(&work->actor); + return; + } + + work->step = work->control.step; + +#ifdef VR_EXE + vy = control->step.vy; + control->step.vy = 0; +#endif + + GM_ActControl(control); + +#ifdef VR_EXE + vy = vy + control->mov.vy; + level = control->levels[0] + 100; + if (level >= vy) + { + control->grounded |= 1; + vy = level; + } + else + { + level = control->levels[1] - 100; + if (level <= vy) + { + control->grounded |= 2; + vy = level; + } + } + control->mov.vy = vy; + DG_SetPos2(&control->mov, &control->rot); +#endif + + GM_ActObject2((OBJECT *)&work->object); + DG_GetLightMatrix(&control->mov, work->light); + + if (!(GM_GameStatus & (STATE_PADRELEASE | STATE_PADDEMO | STATE_DEMO)) && !(GM_PlayerStatus & PLAYER_PAD_OFF)) + { + work->fuse_time--; + } +#ifdef VR_EXE + else if ((GM_GameStatus & (STATE_PADRELEASE | STATE_PADDEMO)) == STATE_PADDEMO && !(GM_PlayerStatus & PLAYER_PAD_OFF)) + { + work->fuse_time--; + } +#endif + + if (work->fuse_time <= 0 || dword_800BDD28 == 1) + { + ReadRotMatrix(&rotation); + + GM_uBombHoming = work->homing_arg3; + + switch (work->type) + { + case GRD_STUN: + NewStanBlast(&rotation); + break; + + case GRD_CHAFF: + NewChaffGrd(&rotation); + break; + + case GRD_TBOMB: + NewBlast2(&rotation, &blast_data_8009F4B8[6], work->do_sound, work->side); + break; + + default: + NewBlast2(&rotation, &blast_data_8009F4B8[0], work->do_sound, work->side); + break; + } + + HZD_ExecLeaveEvent(work->control.map->hzd, &work->control.evt); + GV_DestroyActor(&work->actor); + + GM_uBombHoming = 0; + return; + } + + if (control->grounded && work->step.vy <= 0) + { + work->step.vy = -work->step.vy / 8; + work->step.vz /= 4; + work->step.vx /= 4; + + if (work->step.vy < 5) + { + work->step = DG_ZeroVector; + } + + if (++work->bounces < 3) + { + if (!GM_lpfnBombBound || !GM_lpfnBombBound(0, control, &work->homing_arg3)) + { + GM_SeSet(&control->mov, SE_GRENADE_HIT); + } + + if (work->do_sound != 0) + { + GM_SetNoise(128, 12, &control->mov); + } + } + } + else + { + work->step.vy -= 16; + } + + if (control->n_touches > 0 && GM_CheckControlTouches(control, 300)) + { + HZD_GetNormal(control->segs[0], &vec); + DG_ReflectVector(&vec, &work->step, &work->step); + + work->step.vx /= 4; + work->step.vz /= 4; + + if (++work->bounces < 3) + { + if (!GM_lpfnBombBound || !GM_lpfnBombBound(1, control, &work->homing_arg3)) + { + GM_SeSet(&control->mov, SE_GRENADE_HIT); + } + } + } +} + +static void Die(Work *work) +{ + GM_FreeControl(&work->control); + GM_ClearBulName(work->control.name); + GM_FreeObject((OBJECT *)&work->object); + + if (work->control_index >= 0) + { + tenage_ctrls_800BDD30[work->control_index] = 0; + tenage_ctrls_count_800BDD70--; + } +} + +static int GetNextControl(void) +{ + int i; + + for (i = 0; i < (int)(sizeof(tenage_ctrls_800BDD30) / sizeof(CONTROL *)); i++) + { + if (tenage_ctrls_800BDD30[i] == 0) + { + return i; + } + } + + return -1; +} + +static int GetResources(Work *work, SVECTOR *pos, SVECTOR *step, int type, int model, int int_5, int side) +{ + CONTROL *control; + + control = &work->control; + if (GM_InitControl(control, GM_Next_BulName_8004FBA0(), 0) >= 0) + { + if (int_5 != 0) + { + GM_ConfigControlHazard(control, 100, 50, 50); + } + else + { + GM_ConfigControlHazard(control, 100, -1, -1); + } + control->seg_flag = 4; + if (side == PLAYER_SIDE) + { + control->mov = GM_PlayerPosition; + GM_ConfigControlTrapCheck(control); + GM_ActControl(control); + } + GM_ConfigControlVector(control, pos, (SVECTOR *)&DG_ZeroVector); + work->step = *step; + GM_InitObjectNoRots(&work->object, model, BODY_FLAG, 0); + if (work->object.objs) + { + DG_SetPos2(&control->mov, &control->rot); + DG_PutObjs((work->object).objs); + GM_ConfigObjectLight((OBJECT *)&work->object, work->light); + + work->control_index = GetNextControl(); + if (work->control_index >= 0) + { + tenage_ctrls_800BDD30[work->control_index] = control; + control->mov.pad = 0; + tenage_ctrls_count_800BDD70 = tenage_ctrls_count_800BDD70 + 1; + return 0; + } + } + } + return -1; +} + +/*---------------------------------------------------------------------------*/ + +void *NewTenage(SVECTOR *pos, SVECTOR *step, int fuse_time, int type, int model) +{ + Work *work; + + if (tenage_ctrls_count_800BDD70 == 16) + { + return NULL; + } + + work = GV_NewActor(GV_ACTOR_USER, sizeof(Work)); + + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "tenage.c"); + + if (GetResources(work, pos, step, type, model, 1, PLAYER_SIDE) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + work->fuse_time = fuse_time; + work->bounces = 0; + work->type = type; + work->do_sound = TRUE; + work->side = PLAYER_SIDE; + } + + return (void *)work; +} + +void *NewTenage2(SVECTOR *pos, SVECTOR *step, int fuse_time) +{ + return NewTenage(pos, step, fuse_time, GRD_GRENADE, GRENADE_MODEL); +} + +// enemy's grenades, probably +void *NewTenage3(SVECTOR *pos, SVECTOR *step, int fuse_time, int type, int model, int do_sound, int player_side) +{ + Work *work; + + work = GV_NewActor(GV_ACTOR_AFTER, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "tenage.c"); + + if (GetResources(work, pos, step, type, model, 0, ENEMY_SIDE) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + work->fuse_time = fuse_time; + work->bounces = 0; + work->type = type; + work->do_sound = do_sound & 1; + work->side = player_side & 1; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/bullet/tenage.h b/components/mgs/mgs/source/bullet/tenage.h new file mode 100644 index 00000000..36fa1058 --- /dev/null +++ b/components/mgs/mgs/source/bullet/tenage.h @@ -0,0 +1,14 @@ +#ifndef __MGS_BULLET_TENAGE_H__ +#define __MGS_BULLET_TENAGE_H__ + +#include +#include +#include + +// 手投げ弾 (en: hand grenade) + +void *NewTenage(SVECTOR *pos, SVECTOR *step, int fuse_time, int type, int model); +void *NewTenage2(SVECTOR *pos, SVECTOR *step, int fuse_time); +void *NewTenage3(SVECTOR *pos, SVECTOR *step, int fuse_time, int type, int model, int do_sound, int player_side); + +#endif // __MGS_BULLET_TENAGE_H__ diff --git a/components/mgs/mgs/source/chara/hind/hind_bul.c b/components/mgs/mgs/source/chara/hind/hind_bul.c new file mode 100644 index 00000000..4e7c8d6f --- /dev/null +++ b/components/mgs/mgs/source/chara/hind/hind_bul.c @@ -0,0 +1,334 @@ +#include "game/game.h" +#include "okajima/spark.h" + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + TARGET target; + int dist; + int hurt; + int map; + SVECTOR pos; + DG_PRIM *prim; + SVECTOR vertices[16]; +} Work; + +static SVECTOR target_size = {280, 280, 280}; +static int SECTION(".bss") bul_damage; + +static int CheckTarget(Work *work) +{ + SVECTOR mov; + SVECTOR out; + SVECTOR diff; + int hit; + + if (work->hurt == 0) + { + return 0; + } + + mov = work->control.mov; + GV_SubVec3(&work->pos, &mov, &diff); + GV_LenVec3(&diff, &out, GV_VecLen3(&diff), 2000); + GV_AddVec3(&mov, &out, &out); + + hit = GM_OnlineTargetCheck(&mov, &out, work->map, &out, ENEMY_SIDE); + if (hit) + { + GM_MoveTarget(&work->target, &out); + GM_PowerTarget(&work->target); + } + + return hit; +} + +static void Move(Work *work) +{ + SVECTOR rot; + SVECTOR step; + + rot = DG_ZeroVector; + rot.vy = GV_VecDir2(&work->control.step); + + step.vx = work->control.step.vy; + step.vz = work->control.step.vz; + rot.vx = GV_VecDir2(&step); + + RotMatrixYXZ(&rot, &work->prim->world); + + work->prim->world.t[0] = work->control.mov.vx; + work->prim->world.t[1] = work->control.mov.vy; + work->prim->world.t[2] = work->control.mov.vz; +} + +static void Act(Work *work) +{ + MATRIX mat; + SVECTOR rot; + VECTOR scale; + + GM_ActControl(&work->control); + + work->dist -= 2000; + work->control.mov.vy += work->control.step.vy; + + Move(work); + + if (CheckTarget(work)) + { + GV_DestroyActor(work); + return; + } + + if (work->dist <= 0) + { + rot.vx = -1024; + rot.vz = 0; + rot.vy = 0; + RotMatrixYXZ(&rot, &mat); + + mat.t[0] = work->pos.vx; + mat.t[1] = work->pos.vy; + mat.t[2] = work->pos.vz; + + GM_SeSet(&work->pos, 181); + + scale.vx = scale.vy = scale.vz = 8192; + ScaleMatrix(&mat, &scale); + NewSpark(&mat, 1); + + GV_DestroyActor(work); + } +} + +static void Die(Work *work) +{ + GM_FreeControl(&work->control); + GM_FreePrim(work->prim); +} + +static int CalcEndpoint(Work *work, SVECTOR *trg) +{ + SVECTOR from; + SVECTOR to; + SVECTOR diff; + int hit; + + hit = 0; + + from = work->control.mov; + to = *trg; + + if (HZD_OnlineHazardCheck(work->control.map->hzd, &from, &to, HZD_CHK_F_SEGMENT, HZD_SEG_NO_PLAYER)) + { + HZD_GetOnlinePoint(&to); + hit = 1; + work->pos = to; + } + + if (!hit) + { + work->pos = *trg; + GV_SubVec3(trg, &work->control.mov, &diff); + return GV_VecLen3(&diff); + } + + GV_SubVec3(&to, &from, &from); + return GV_VecLen3(&from); +} + +static int InitPrim(Work *work) +{ + SVECTOR *vert; + DG_PRIM *prim; + DG_TEX *tex; + int i, j; + POLY_FT4 *pack; + + vert = work->vertices; + + work->prim = prim = GM_MakePrim(DG_PRIM_POLY_FT4, 4, vert, NULL); + if (prim == NULL) + { + return -1; + } + + tex = DG_GetTexture(GV_StrCode("bullet")); + if (tex == NULL) + { + return -1; + } + + for (i = 0; i < 2; i++) + { + pack = prim->packs[i]; + for (j = 0; j < 4; j++) + { + setPolyFT4(pack); + setRGB0(pack, 36, 36, 12); + setSemiTrans(pack, 1); + DG_SetPacketTexture4(pack, tex); + pack->tpage |= (1 << 5); + pack++; + } + } + + prim->world = DG_ZeroMatrix; + + for (i = 0; i < 16; i++, vert++) + { + if (i < 4) + { + if (i & 2) + { + vert->vz = 1250; + } + else + { + vert->vz = 0; + } + + if (i & 1) + { + vert->vx = 16; + } + else + { + vert->vx = -16; + } + + vert->vy = 0; + } + else if (i < 8) + { + if (i & 2) + { + vert->vz = 1250; + } + else + { + vert->vz = 0; + } + + if (i & 1) + { + vert->vx = -16; + } + else + { + vert->vx = 16; + } + + vert->vy = 0; + } + else if (i < 12) + { + if (i & 2) + { + vert->vz = 1250; + } + else + { + vert->vz = 0; + } + + if (i & 1) + { + vert->vy = -16; + } + else + { + vert->vy = 16; + } + + vert->vx = 0; + } + else + { + if (i & 2) + { + vert->vz = 1250; + } + else + { + vert->vz = 0; + } + + if (i & 1) + { + vert->vy = 16; + } + else + { + vert->vy = -16; + } + + vert->vx = 0; + } + } + + return 0; +} + +static int GetResources(Work *work, SVECTOR *pos, SVECTOR *trg) +{ + SVECTOR diff; + CONTROL *control; + SVECTOR *step; + int len; + + control = &work->control; + if (GM_InitControl(control, 0, 0) < 0) + { + return -1; + } + + control->mov = *pos; + control->rot = control->turn = DG_ZeroVector; + + GM_ConfigControlHazard(control, 0, -2, -2); + + GM_SetTarget(&work->target, TARGET_POWER, ENEMY_SIDE, &target_size); + GM_SetPowerTarget(&work->target, POWER_ONCE, 1, bul_damage, 0, &DG_ZeroVector); + + step = &work->control.step; + GV_SubVec3(trg, &control->mov, &diff); + len = GV_VecLen3(&diff); + + work->dist = CalcEndpoint(work, trg); + GV_LenVec3(&diff, step, len, 2000); + + work->map = GM_CurrentMap; + + if (InitPrim(work) < 0) + { + return -1; + } + + return 0; +} + +void *NewHindBullet(SVECTOR *pos, SVECTOR *trg, int hurt, int damage) +{ + Work *work; + + work = GV_NewActor(GV_ACTOR_USER, sizeof(Work)); + if (work == NULL) + { + return NULL; + } + + GV_SetNamedActor(work, Act, Die, "hind_bul.c"); + if (GetResources(work, pos, trg) < 0) + { + GV_DestroyActor(work); + return NULL; + } + + bul_damage = damage; + work->hurt = hurt; + + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/hind2/hd_bul2.c b/components/mgs/mgs/source/chara/hind2/hd_bul2.c new file mode 100644 index 00000000..d07c2a94 --- /dev/null +++ b/components/mgs/mgs/source/chara/hind2/hd_bul2.c @@ -0,0 +1,373 @@ +#include "hd_bul2.h" + +#include "libdg/libdg.h" +#include "libgv/libgv.h" +#include "game/game.h" +#include "linkvar.h" +#include "okajima/spark.h" + +typedef struct _HdBul2Work +{ + GV_ACT actor; + CONTROL control; + TARGET target; + int fE4; + int enable; + int map; + SVECTOR pos; + DG_PRIM *prim; + SVECTOR vertices[16]; +} HdBul2Work; + +SVECTOR s11i_800C32C8 = {200, 200, 200, 0}; + +#define EXEC_LEVEL GV_ACTOR_USER + +#define SEGMENT_ATR ( HZD_SEG_NO_PLAYER ) + +int HdBul2_800C5920( HdBul2Work *work ) +{ + SVECTOR mov; + SVECTOR scaled; + SVECTOR diff; + int hit; + + if ( !work->enable ) + { + return 0; + } + + mov = work->control.mov; + + GV_SubVec3( &work->pos, &mov, &diff ); + GV_LenVec3( &diff, &scaled, GV_VecLen3( &diff ), 2000 ); + GV_AddVec3( &mov, &scaled, &scaled ); + + hit = GM_OnlineTargetCheck( &mov, &scaled, work->map, &scaled, ENEMY_SIDE ); + if ( hit != 0 ) + { + GM_MoveTarget( &work->target, &scaled ); + GM_PowerTarget( &work->target ); + } + + return hit; +} + +void HdBul2_800C5A14( HdBul2Work *work ) +{ + SVECTOR step; + SVECTOR rot; + + step = work->control.step; + rot = DG_ZeroVector; + + rot.vy = ratan2( step.vx, step.vz ); + rot.vx = -ratan2( step.vy, SquareRoot0( step.vx * step.vx + step.vz * step.vz ) ); + + RotMatrixYXZ( &rot, &work->prim->world ); + + work->prim->world.t[ 0 ] = work->control.mov.vx; + work->prim->world.t[ 1 ] = work->control.mov.vy; + work->prim->world.t[ 2 ] = work->control.mov.vz; +} + +void HdBul2Act( HdBul2Work *work ) +{ + MATRIX world; + SVECTOR rot; + VECTOR scale; + POLY_FT4 *poly; + int i; + + GM_ActControl( &work->control ); + + work->fE4 -= 2000; + work->control.mov.vy += work->control.step.vy; + + HdBul2_800C5A14( work ); + + if ( !HdBul2_800C5920( work ) ) + { + if ( work->fE4 <= 0 ) + { + rot.vx = -1024; + rot.vy = rot.vz = 0; + RotMatrixYXZ( &rot, &world ); + + world.t[ 0 ] = work->pos.vx; + world.t[ 1 ] = work->pos.vy; + world.t[ 2 ] = work->pos.vz; + + GM_SeSet( &work->pos, 178 ); + + scale.vx = scale.vy = scale.vz = 8192; + ScaleMatrix( &world, &scale ); + + NewSpark( &world, 1 ); + } + else + { + return; + } + } + + poly = (POLY_FT4 *)work->prim->packs[ GV_Clock ]; + for ( i = 0; i < 4; i++ ) + { + setRGB0( poly, 0, 0, 0 ); + poly++; + } + + GV_DestroyActor( &work->actor ); +} + +void HdBul2Die( HdBul2Work *work ) +{ + GM_FreeControl( &work->control ); + GM_FreePrim( work->prim ); +} + +int HdBul2_800C5C68( HdBul2Work *work, SVECTOR *arg1 ) +{ + SVECTOR sp18; + SVECTOR sp20; + SVECTOR sp28; + int hit; + int len; + + hit = 0; + + sp18 = work->control.mov; + sp20 = *arg1; + + if ( HZD_OnlineHazardCheck( work->control.map->hzd, &sp18, &sp20, HZD_CHK_FIX, SEGMENT_ATR ) ) + { + HZD_GetOnlinePoint( &sp20 ); + hit = 1; + work->pos = sp20; + } + + if ( hit != 0 ) + { + GV_SubVec3( &sp20, &sp18, &sp18 ); + len = GV_VecLen3( &sp18 ); + } + else + { + work->pos = *arg1; + GV_SubVec3( arg1, &work->control.mov, &sp28 ); + len = GV_VecLen3( &sp28 ); + } + + return len; +} + +int HdBul2_800C5DB4( HdBul2Work *work ) +{ + SVECTOR *vertices; + DG_PRIM *prim; + DG_TEX *tex; + int i, j; + POLY_FT4 *poly; + int rnd; + + vertices = work->vertices; + prim = work->prim = GM_MakePrim( DG_PRIM_POLY_FT4, 4, vertices, NULL ); + if ( !prim ) + { + return -1; + } + + tex = DG_GetTexture( GV_StrCode( "bullet" ) ); + if ( !tex ) + { + return -1; + } + + for ( i = 0; i < 2; i++ ) + { + poly = (POLY_FT4 *)prim->packs[ i ]; + + for ( j = 0; j < 4; j++ ) + { + setPolyFT4( poly ); + + rnd = GV_RandU( 128 ); + poly->r0 = rnd - 32; + poly->g0 = ( ( rnd - 32 ) / 3 ) * 2; + poly->b0 = ( ( rnd - 32 ) / 3 ) * 2; + + setSemiTrans( poly, 1 ); + DG_SetPacketTexture4( poly, tex ); + poly->tpage |= 0x20; + poly++; + } + } + + prim->world = DG_ZeroMatrix; + + for ( i = 0; i < 16; i++, vertices++ ) + { + if ( i < 4 ) + { + if ( i & 2 ) + { + vertices->vz = 750; + } + else + { + vertices->vz = 0; + } + + if ( i & 1 ) + { + vertices->vx = 16; + } + else + { + vertices->vx = -16; + } + + vertices->vy = 0; + } + else if ( i < 8 ) + { + if ( i & 2 ) + { + vertices->vz = 750; + } + else + { + vertices->vz = 0; + } + + if ( i & 1 ) + { + vertices->vx = -16; + } + else + { + vertices->vx = 16; + } + + vertices->vy = 0; + } + else if ( i < 12 ) + { + if ( i & 2 ) + { + vertices->vz = 750; + } + else + { + vertices->vz = 0; + } + + if ( i & 1 ) + { + vertices->vy = -16; + } + else + { + vertices->vy = 16; + } + + vertices->vx = 0; + } + else + { + if ( i & 2 ) + { + vertices->vz = 750; + } + else + { + vertices->vz = 0; + } + + if ( i & 1 ) + { + vertices->vy = 16; + } + else + { + vertices->vy = -16; + } + + vertices->vx = 0; + } + } + + return 0; +} + +int HdBul2GetResources( HdBul2Work *work, SVECTOR *arg1, SVECTOR *arg2 ) +{ + SVECTOR diff; + CONTROL *control; + TARGET *target; + int len; + int len2; + SVECTOR *step; + + control = &work->control; + + if ( GM_InitControl( control, 0, 0 ) < 0 ) + { + return -1; + } + + work->control.mov = *arg1; + work->control.rot = work->control.turn = DG_ZeroVector; + + GM_ConfigControlHazard( control, 0, -2, -2 ); + + target = &work->target; + GM_SetTarget( target, TARGET_POWER, ENEMY_SIDE, &s11i_800C32C8 ); + GM_SetPowerTarget( target, POWER_ONCE, 1, GM_SnakeMaxHealth / 8, 0, &DG_ZeroVector ); + + step = &work->control.step; + GV_SubVec3( arg2, &control->mov, &diff ); + + len = GV_VecLen3( &diff ); + + len2 = HdBul2_800C5C68( work, arg2 ); + if ( len2 < 0 ) + { + return -1; + } + + work->fE4 = len2; + + GV_LenVec3( &diff, step, len, 2000 ); + + work->map = GM_CurrentMap; + + if ( HdBul2_800C5DB4( work ) < 0 ) + { + return -1; + } + + return 0; +} + +void *NewHdBul2( SVECTOR *arg0, SVECTOR *arg1, int enable ) +{ + HdBul2Work *work; + + work = GV_NewActor( EXEC_LEVEL, sizeof(HdBul2Work) ); + if ( work ) + { + GV_SetNamedActor( &work->actor, HdBul2Act, HdBul2Die, "hd_bul2.c" ); + + if ( HdBul2GetResources( work, arg0, arg1 ) >= 0 ) + { + work->enable = enable; + return work; + } + + GV_DestroyActor( &work->actor ); + } + + return NULL; +} diff --git a/components/mgs/mgs/source/chara/hind2/hd_bul2.h b/components/mgs/mgs/source/chara/hind2/hd_bul2.h new file mode 100644 index 00000000..6a4aa002 --- /dev/null +++ b/components/mgs/mgs/source/chara/hind2/hd_bul2.h @@ -0,0 +1,10 @@ +#ifndef __MGS_CHARA_HD_BUL2_H__ +#define __MGS_CHARA_HD_BUL2_H__ + +#include +#include +#include + +void *NewHdBul2( SVECTOR *arg0, SVECTOR *arg1, int enable ); + +#endif // __MGS_CHARA_HD_BUL2_H__ diff --git a/components/mgs/mgs/source/chara/hind2/hind2.c b/components/mgs/mgs/source/chara/hind2/hind2.c new file mode 100644 index 00000000..aa5a279f --- /dev/null +++ b/components/mgs/mgs/source/chara/hind2/hind2.c @@ -0,0 +1,742 @@ +#include "hind2.h" +#include "hd_bul2.h" // for NewHdBul2 + +#include +#include "common.h" +#include "libdg/libdg.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "linkvar.h" +#include "strcode.h" + +typedef struct _Hind2Work +{ + GV_ACT actor; + CONTROL control; + OBJECT body; + MATRIX light[2]; + DG_PRIM *prim; + SVECTOR vertices[1]; + RECT prim_rect; + DG_TEX *tex; + char pad1[0x4]; + int name; + short f1E0[2]; + VECTOR f1E4; + SVECTOR f1F4; + unsigned int f1FC; + int f200; + char pad2[0x10]; + VECTOR f214; + int f224; + SVECTOR f228; + int f230; + int f234; + SVECTOR adjust[10]; + int f288; + SVECTOR f28C; + char pad3[0x10]; + int f2A4; + int f2A8; +} Hind2Work; + +SVECTOR s11i_800C32C0 = { 0, -2728, 5587, 0 }; + +#define EXEC_LEVEL GV_ACTOR_USER +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_GBOUND | DG_FLAG_SHADE ) + +void Hind2_800C43F0( SVECTOR *a, SVECTOR *b, VECTOR *out ) +{ + out->vx = a->vx - b->vx; + out->vy = a->vy - b->vy; + out->vz = a->vz - b->vz; +} + +void Hind2_800C4430( Hind2Work *work ) +{ + OBJECT *body; + int val; + + body = &work->body; + + val = body->rots[ 1 ].vy; + val = ( val + work->f1E0[ 0 ] ) & 4095; + body->rots[ 1 ].vy = val; + + val = body->rots[ 2 ].vx; + val = ( val + work->f1E0[ 1 ] ) & 4095; + body->rots[ 2 ].vx = val; +} + +void Hind2_800C4468( Hind2Work *work, int arg1 ) +{ + if ( GV_Time & 3 ) + { + return; + } + + if ( arg1 == 0 ) + { + GM_SeSetMode( &work->f28C, 181, GM_SEMODE_BOMB ); + } + else if ( arg1 == 1 ) + { + GM_SeSet2( 0, 8, 181 ); + } +} + +void Hind2_800C44C4( Hind2Work *work ) +{ + VECTOR diff; + MATRIX rot; + SVECTOR pos; + SVECTOR pos_world; + SVECTOR *mov; + + mov = &work->control.mov; + + work->control.mov.vx = work->f1E4.vx; + work->control.mov.vy = work->f1E4.vy; + work->control.mov.vz = work->f1E4.vz; + + Hind2_800C43F0( &GM_PlayerPosition, mov, &diff ); + + work->control.turn.vy = ratan2( diff.vx, diff.vz ); + work->control.turn.vx = -ratan2( diff.vy, SquareRoot0( diff.vx * diff.vx + diff.vz * diff.vz ) ); + + pos = work->control.mov; + + ReadRotMatrix( &rot ); + DG_SetPos2( &pos, &work->control.rot ); + DG_RotVector( &work->f1F4, &pos_world, 1 ); + GV_SubVec3( &pos, &pos_world, mov ); + DG_SetPos( &rot ); +} + +void Hind2_800C45E4( Hind2Work *work, SVECTOR arg1 ) +{ + MATRIX sp10; + SVECTOR sp30; + DG_PRIM *prim; + DG_TEX *tex; + int rand_w, rand_h; + int x, y, w, h; + POLY_FT4 *poly; + + prim = work->prim; + + DG_InvisiblePrim( prim ); + + if ( !( work->f200 & 0x1 ) ) + { + DG_MovePos( &work->f1F4 ); + ReadRotMatrix( &sp10 ); + + DG_VisiblePrim( prim ); + DG_PutPrim( prim ); + + rand_w = GV_RandU( 1024 ) % 2; + rand_h = GV_RandU( 1024 ) % 2; + + tex = work->tex; + x = tex->off_x; + w = tex->w; + y = tex->off_y; + h = tex->h; + + poly = (POLY_FT4 *)prim->packs[ GV_Clock ]; + + poly->u0 = poly->u2 = x + ( w * rand_w ) / 2; + poly->u1 = poly->u3 = x + ( w * ( rand_w + 1 ) ) / 2 - 1; + + poly->v0 = poly->v1 = y + ( h * rand_h ) / 2; + poly->v2 = poly->v3 = y + ( h * ( rand_h + 1 ) ) / 2 - 1; + + sp30.vx = sp10.t[ 0 ]; + sp30.vy = sp10.t[ 1 ]; + sp30.vz = sp10.t[ 2 ]; + + NewHdBul2( &sp30, &arg1, 1 ); + GM_SeSetMode( &work->f28C, 177, GM_SEMODE_BOMB ); + } +} + +void Hind2_800C47A4( Hind2Work *work ) +{ + work->control.mov.vx += work->f230; + work->f1E4.vx += work->f230; +} + +void Hind2_800C47C8( Hind2Work *work ) +{ + int f224; + + f224 = work->f224; + work->control.mov.vx += ( rsin( f224 * 23 ) - rsin( ( f224 - 1 ) * 23 ) ) * 2; + work->control.mov.vy += ( rsin( f224 * 64 ) - rsin( ( f224 - 1 ) * 64 ) ); + work->control.mov.vz += ( rsin( f224 * 32 ) - rsin( ( f224 - 1 ) * 32 ) ) * 2; +} + +void Hind2_800C4898( Hind2Work *work, int scale ) +{ + work->f214.vx = scale; + work->f214.vy = scale; + work->f214.vz = scale; + ScaleMatrix( &work->body.objs->world, &work->f214 ); +} + +int Hind2_800C48CC( Hind2Work *work ) +{ + SVECTOR diff; + int ret; + + ret = 0; + + GV_SubVec3( &GM_PlayerPosition, &work->control.mov, &diff ); + + if ( GV_VecLen3( &diff ) >= 10000 ) + { + ret = 1; + } + + if ( GM_PlayerPosition.vz >= -10000 ) + { + ret = 2; + } + + if ( GM_PlayerPosition.vx <= -7250 ) + { + ret = 4; + } + + if ( work->f1FC == 6 && GM_PlayerPosition.vz <= -10000 ) + { + ret = 3; + } + + if ( work->f1FC == 7 && GM_PlayerPosition.vx >= -7250 ) + { + ret = 5; + } + + return ret; +} + +static inline void Hind2PlaySound( Hind2Work *work, int val ) +{ + printf( "%d\n", val ); + + if ( GM_GameOverTimer == 0 ) + { + switch ( val ) + { + case 0: + GM_SeSetMode( &work->f28C, 187, GM_SEMODE_BOMB ); + break; + + case 1: + GM_SeSetMode( &work->f28C, 188, GM_SEMODE_BOMB ); + break; + + case 2: + GM_SeSetMode( &work->f28C, 189, GM_SEMODE_BOMB ); + break; + + case 3: + GM_SeSetMode( &work->f28C, 190, GM_SEMODE_BOMB ); + break; + } + + work->f2A4 = 1; + } +} + +void Hind2_800C499C( Hind2Work *work ) +{ + SVECTOR sp10; + int scale; + int seg; + int rnd; + + switch ( work->f1FC ) + { + case 0: + Hind2_800C4468( work, 1 ); + + if ( GM_PlayerPosition.vz <= -15000 && work->f234 != 0 ) + { + DG_VisibleObjs( work->body.objs ); + work->f1FC = 1; + GM_SeSetMode( &work->f28C, 180, GM_SEMODE_BOMB ); + work->f200 = 0; + Hind2_800C4898( work, 2925 ); + Hind2_800C44C4( work ); + GCL_ExecProc( work->f288, NULL ); + } + break; + + case 1: + Hind2_800C4430( work ); + Hind2_800C4468( work, 0 ); + work->f1E4.vy += 500; + + scale = ( ( work->f200 * 2 + 100 ) * 4096 ) / 140; + Hind2_800C4898( work, scale ); + Hind2_800C44C4( work ); + + if ( work->f200 > 19 ) + { + work->f1FC = 2; + work->f200 = 0; + } + break; + + case 2: + Hind2_800C4430( work ); + Hind2_800C4468( work, 0 ); + Hind2_800C44C4( work ); + Hind2_800C47C8( work ); + DG_InvisiblePrim( work->prim ); + + if ( work->f200 >= 60 ) + { + work->f1FC = 3; + work->f228 = GM_PlayerPosition; + work->f200 = 0; + + if ( work->f2A4 != 0 ) + { + work->f2A4 = 0; + break; + } + + rnd = GV_RandU( 4 ); + Hind2PlaySound( work, rnd ); + } + break; + + case 3: + Hind2_800C4430( work ); + Hind2_800C4468( work, 0 ); + Hind2_800C44C4( work ); + Hind2_800C47C8( work ); + + sp10.vx = work->f228.vx - ( work->f200 - 15 ) * 150; + sp10.vy = work->f228.vy; + sp10.vz = work->f228.vz; + Hind2_800C45E4( work, sp10 ); + + if ( work->f200 > 30 || GM_PlayerPosition.vx <= -7500 ) + { + work->f1FC = 4; + work->f200 = 0; + } + break; + + case 4: + Hind2_800C4430( work ); + Hind2_800C4468( work, 0 ); + Hind2_800C44C4( work ); + Hind2_800C47C8( work ); + DG_InvisiblePrim( work->prim ); + + seg = Hind2_800C48CC( work ); + switch ( seg ) + { + case 0: + work->f1FC = 2; + work->f200 = 0; + break; + + case 1: + work->f1FC = 5; + work->f230 = ( GM_PlayerPosition.vx - work->control.mov.vx ) / 100; + work->f200 = 0; + break; + + case 2: + work->f1FC = 6; + work->f200 = 0; + break; + + case 4: + work->f1FC = 7; + work->f200 = 0; + break; + } + break; + + case 5: + Hind2_800C4430( work ); + Hind2_800C4468( work, 0 ); + Hind2_800C44C4( work ); + Hind2_800C47A4( work ); + Hind2_800C47C8( work ); + DG_InvisiblePrim( work->prim ); + + if ( work->f200 >= 80 ) + { + work->f1FC = 3; + work->f228 = GM_PlayerPosition; + work->f200 = 0; + + if ( work->f2A4 != 0 ) + { + work->f2A4 = 0; + break; + } + + rnd = GV_RandU( 4 ); + Hind2PlaySound( work, rnd ); + } + break; + + case 6: + Hind2_800C4430( work ); + Hind2_800C4468( work, 0 ); + Hind2_800C44C4( work ); + Hind2_800C47C8( work ); + DG_InvisiblePrim( work->prim ); + + seg = Hind2_800C48CC( work ); + if ( seg == 3 ) + { + work->f1FC = 3; + work->f228 = GM_PlayerPosition; + work->f200 = 0; + + if ( work->f2A4 != 0 ) + { + work->f2A4 = 0; + break; + } + + rnd = GV_RandU( 4 ); + Hind2PlaySound( work, rnd ); + } + break; + + case 7: + Hind2_800C4430( work ); + Hind2_800C4468( work, 1 ); + Hind2_800C44C4( work ); + Hind2_800C47C8( work ); + DG_InvisiblePrim( work->prim ); + + seg = Hind2_800C48CC( work ); + if ( seg == 5 ) + { + work->f1FC = 2; + work->f228 = GM_PlayerPosition; + work->f200 = 0; + } + break; + + case 8: + Hind2_800C4430( work ); + Hind2_800C4468( work, 1 ); + work->f1E4.vy -= 300; + scale = ( ( 140 - work->f200 * 2 ) * 4096 ) / 140; + Hind2_800C44C4( work ); + Hind2_800C4898( work, scale ); + DG_InvisiblePrim( work->prim ); + + if ( work->f200 > 29 ) + { + work->f1FC = 9; + work->f200 = 0; + DG_InvisibleObjs( work->body.objs ); + GM_ConfigControlAttribute( &work->control, 0 ); + } + + if ( work->f200 == 2 ) + { + GM_SeSetMode( &work->f28C, 186, GM_SEMODE_BOMB ); + } + break; + + case 9: + break; + } + + if ( work->f1FC >= 2 && work->f1FC != 8 && work->f1FC != 9 && GM_CurrentWeaponId == WP_Stinger ) + { + work->f1FC = 8; + work->f200 = 0; + DG_InvisiblePrim( work->prim ); + } + +} + +void Hind2_800C5188( Hind2Work *work ) +{ + SVECTOR sp10; + VECTOR sp18; + int ang; + + if ( work->f1FC == 3 ) + { + sp10.vx = work->f228.vx - ( work->f200 - 15 ) * 150; + sp10.vy = work->f228.vy; + sp10.vz = work->f228.vz; + + Hind2_800C43F0( &sp10, &work->control.mov, &sp18 ); + + ang = ratan2( sp18.vx, sp18.vz ) - work->control.turn.vy; + if ( ang < 0 ) + { + ang += 4096; + } + + work->adjust[ 4 ].vy = ang; + } + else if ( work->adjust[ 4 ].vy <= 2048 ) + { + work->adjust[ 4 ].vy -= work->adjust[ 4 ].vy / 2; + } + else + { + work->adjust[ 4 ].vy += ( 4096 - work->adjust[ 4 ].vy ) / 2; + } +} + +void Hind2_800C5274( Hind2Work *work ) +{ + int n_msgs; + GV_MSG *msg; + + if ( work->control.n_msg == 0 ) + { + return; + } + + n_msgs = work->control.n_msg; + msg = &work->control.msg[ n_msgs ] - 1; + + for ( ; n_msgs > 0; n_msgs--, msg-- ) + { + switch ( msg->message[ 0 ] ) + { + case 0x7075: + work->f2A8 = 1; + printf( "up message\n" ); + break; + + case HASH_KILL: + printf( "die message\n" ); + GV_DestroyActor( &work->actor ); + break; + } + } +} + +void Hind2Act( Hind2Work *work ) +{ + CONTROL *control; + OBJECT *body; + + control = &work->control; + body = &work->body; + + GM_ActControl( control ); + GM_ActObject2( body ); + + DG_GetLightMatrix( &control->mov, work->light ); + + work->f28C.vx = ( GM_PlayerPosition.vx * 3 + work->control.mov.vx ) / 4; + work->f28C.vy = ( GM_PlayerPosition.vy * 3 + work->control.mov.vy ) / 4; + work->f28C.vz = ( GM_PlayerPosition.vz * 3 + work->control.mov.vz ) / 4; + + Hind2_800C5274( work ); + + if ( work->f2A8 > 0 && work->f2A8 < 35 && ++work->f2A8 == 35 ) + { + work->f234 = 1; + } + + Hind2_800C499C( work ); + Hind2_800C5188( work ); + + work->f200++; + work->f224++; + + work->control.mov.vy += work->control.step.vy; +} + +void Hind2Die( Hind2Work *work ) +{ + GM_FreeControl( &work->control ); + GM_FreeObject( &work->body ); + GM_FreePrim( work->prim ); +} + +int Hind2_800C54C8( Hind2Work *work ) +{ + RECT *prim_rect; + DG_PRIM *prim; + DG_TEX *tex; + int i; + POLY_FT4 *poly; + int x, y, w, h; + + prim_rect = &work->prim_rect; + prim_rect->x = 400; + prim_rect->y = 400; + prim_rect->w = 800; + prim_rect->h = 800; + + work->vertices[ 0 ] = DG_ZeroVector; + + work->prim = prim = GM_MakePrim( DG_PRIM_RECTANGLE | DG_PRIM_POLY_FT4, 1, work->vertices, prim_rect ); + if ( !prim ) + { + return -1; + } + + tex = work->tex = DG_GetTexture( GV_StrCode( "socom_f" ) ); + + for ( i = 0; i < 2; i++ ) + { + poly = (POLY_FT4 *)prim->packs[ i ]; + + setPolyFT4( poly ); + setRGB0( poly, 128, 128, 128 ); + setSemiTrans( poly, 1 ); + + x = tex->off_x; + w = tex->w + 1; + poly->u0 = poly->u2 = x; + poly->u1 = poly->u3 = x + w / 2 - 1; + + y = tex->off_y; + h = tex->h + 1; + poly->v0 = poly->v1 = y; + poly->v2 = poly->v3 = y + h / 2 - 1; + + poly->tpage = tex->tpage; + poly->clut = tex->clut; + + poly->tpage = ( poly->tpage & ~0x60 ) | 0x20; + } + + DG_InvisiblePrim( prim ); + return 0; +} + +int Hind2GetResources( Hind2Work *work, int name, int map ) +{ + CONTROL *control; + char *pos; + char *dir; + char *model; + OBJECT *body; + int i; + + control = &work->control; + + if ( GM_InitControl( control, name, map ) < 0 ) + { + return -1; + } + + pos = GCL_GetOption( 'p' ); + dir = GCL_GetOption( 'd' ); + GM_ConfigControlString( control, pos, dir ); + + GM_ConfigControlAttribute( control, 1 ); + GM_ConfigControlTrapCheck( control ); + GM_ConfigControlHazard( control, 0, -2, -2 ); + + control->seg_flag = 2; + + model = GCL_GetOption( 'm' ); + if ( !model ) + { + return -1; + } + + work->f1E4.vx = work->control.mov.vx; + work->f1E4.vy = work->control.mov.vy; + work->f1E4.vz = work->control.mov.vz; + + body = &work->body; + GM_InitObject( body, GCL_StrToInt( model ), BODY_FLAG, 0 ); + GM_ConfigObjectJoint( body ); + GM_ConfigObjectLight( body, work->light ); + + DG_InvisibleObjs( body->objs ); + + if ( GCL_GetOption( 'w' ) ) + { + work->f1E0[ 0 ] = GCL_StrToInt( GCL_NextStr() ); + work->f1E0[ 1 ] = GCL_StrToInt( GCL_NextStr() ); + } + else + { + work->f1E0[ 0 ] = 0; + work->f1E0[ 1 ] = 0; + } + + if ( GCL_GetOption( 'g' ) ) + { + GCL_StrToSV( GCL_NextStr(), (short *)&work->f1F4 ); + } + else + { + work->f1F4 = s11i_800C32C0; + } + + if ( GCL_GetOption( 'e' ) ) + { + work->f288 = GCL_StrToInt( GCL_NextStr() ); + } + else + { + work->f288 = -1; + } + + work->f1FC = 0; + work->f200 = 0; + work->f224 = 0; + work->f234 = 0; + work->f2A4 = 1; + work->f2A8 = 0; + + for ( i = 0; i < 10; i++ ) + { + work->adjust[ i ].vx = 0; + work->adjust[ i ].vy = 0; + work->adjust[ i ].vz = 0; + } + + GM_ConfigMotionAdjust( &work->body, work->adjust ); + + if ( Hind2_800C54C8( work ) < 0 ) + { + return -1; + } + + return 0; +} + +void *NewHind2( int name, int where ) +{ + Hind2Work *work; + + work = GV_NewActor( EXEC_LEVEL, sizeof(Hind2Work) ); + if ( work ) + { + GV_SetNamedActor( &work->actor, Hind2Act, Hind2Die, "hind2.c" ); + + if ( Hind2GetResources( work, name, where ) >= 0 ) + { + work->name = name; + return work; + } + + GV_DestroyActor( &work->actor ); + } + + return NULL; +} diff --git a/components/mgs/mgs/source/chara/hind2/hind2.h b/components/mgs/mgs/source/chara/hind2/hind2.h new file mode 100644 index 00000000..c1b35b89 --- /dev/null +++ b/components/mgs/mgs/source/chara/hind2/hind2.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_HIND2_H__ +#define __MGS_CHARA_HIND2_H__ + +void *NewHind2( int name, int where ); + +#endif // __MGS_CHARA_HIND2_H__ diff --git a/components/mgs/mgs/source/chara/others/belong.c b/components/mgs/mgs/source/chara/others/belong.c new file mode 100644 index 00000000..ab39e0d3 --- /dev/null +++ b/components/mgs/mgs/source/chara/others/belong.c @@ -0,0 +1,146 @@ +#include "belong.h" + +#include +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "strcode.h" + +extern CONTROL *GM_WhereList[96]; +extern int GM_N_WhereList; + +/*---------------------------------------------------------------------------*/ + +typedef struct _Work +{ + GV_ACT actor; + CONTROL *control; + OBJECT *field_24; + OBJECT_NO_ROTS field_28; + int field_4C; + int name; + int field_54; +} Work; + +#define EXEC_LEVEL GV_ACTOR_USER + +/*---------------------------------------------------------------------------*/ + +static void CheckMessage(Work *work) +{ + GV_MSG *msg; + int count; + int i; + + i = count = GV_ReceiveMessage(work->name, &msg); + if (count <= 0) + { + return; + } + + for (--i; i >= 0; i--, msg++) + { + switch (msg->message[0]) + { + case HASH_KILL: + work->field_54 = 1; + GV_DestroyActor(&work->actor); + break; + case HASH_ON: + work->field_54 = 0; + DG_VisibleObjs(work->field_28.objs); + break; + case HASH_OFF: + DG_InvisibleObjs(work->field_28.objs); + work->field_54 = 1; + break; + } + } +} + +static void Act(Work *work) +{ + CheckMessage(work); + if (work->field_54 != 1) + { + GM_CurrentMap = work->control->map->index; + DG_GroupObjsEx(work->field_28.objs); + if (work->field_24->objs->flag & DG_FLAG_INVISIBLE) + { + DG_InvisibleObjs(work->field_28.objs); + } + else + { + DG_VisibleObjs(work->field_28.objs); + } + } +} + +static void Die(Work *work) +{ + GM_FreeObject((OBJECT *)&work->field_28); +} + +static int GetResources(Work *work, int name, int where) +{ + CONTROL **whereListIter; + CONTROL *control; + OBJECT *parent_obj; + OBJECT_NO_ROTS *obj; + int name_opt; + int model; + int num_parent; + int i; + + model = GCL_StrToInt(GCL_GetOption('m')); + num_parent = work->field_4C = GCL_StrToInt(GCL_GetOption('u')); + name_opt = GCL_StrToInt(GCL_GetOption('c')); + work->control = NULL; + work->field_24 = NULL; + + whereListIter = GM_WhereList; + parent_obj = NULL; + for (i = GM_N_WhereList - 1; i >= 0; i--) + { + control = *whereListIter++; + + if (control->name == name_opt) + { + work->control = control; + parent_obj = work->field_24 = (OBJECT *)(control + 1); + break; + } + } + obj = &work->field_28; + if (work->control == NULL) + { + printf("name %d is not exist\n", name_opt); + return -1; + } + GM_InitObjectNoRots(obj, model, 0x6D, 0); + GM_ConfigObjectRoot((OBJECT *)obj, parent_obj, num_parent); + work->name = name; + work->field_54 = 0; + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewBelong(int name, int where, int argc, char **argv) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "belong.c"); + if (GetResources(work, name, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/others/belong.h b/components/mgs/mgs/source/chara/others/belong.h new file mode 100644 index 00000000..8a2b7cb2 --- /dev/null +++ b/components/mgs/mgs/source/chara/others/belong.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_BELONG_H__ +#define __MGS_CHARA_BELONG_H__ + +void *NewBelong(int name, int where, int argc, char **argv); + +#endif // __MGS_CHARA_BELONG_H__ diff --git a/components/mgs/mgs/source/chara/others/fonttext.c b/components/mgs/mgs/source/chara/others/fonttext.c new file mode 100644 index 00000000..3d8e9d89 --- /dev/null +++ b/components/mgs/mgs/source/chara/others/fonttext.c @@ -0,0 +1,151 @@ +#include "fonttext.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "menu/menuman.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +typedef struct _Work +{ + GV_ACT actor; + int map; + int flags; + int name; + SVECTOR color; + SVECTOR pos; + char *text; + RECT offset; +} Work; + +/*---------------------------------------------------------------------------*/ + +int fonttext_kill_name = 0; + +static int CheckMessage( Work *work, int name ) +{ + GV_MSG *msg; + int count; + + count = GV_ReceiveMessage( name, &msg ); + if ( count > 0 ) + { + for ( count--; count >= 0; count--, msg++ ) + { + if ( msg->message[0] == HASH_KILL ) + { + return 1; + } + + if ( msg->message[0] == 0xB84C ) + { + work->offset.w = msg->message[1]; + work->offset.h = msg->message[2]; + } + } + } + + return 0; +} + +static void Act( Work *work ) +{ + int x, y; + + if ( CheckMessage( work, work->name ) || fonttext_kill_name == work->name ) + { + fonttext_kill_name = 0; + GV_DestroyActor( &( work->actor ) ); + return; + } + + x = work->offset.x; + y = work->offset.y; + + MENU_Locate( work->pos.vx + x, work->pos.vy + y, work->flags ); + MENU_Color( work->color.vx, work->color.vy, work->color.vz ); + MENU_Printf( "%s", work->text ); + + work->offset.x += work->offset.w; + work->offset.y += work->offset.h; +} + +static void Die( Work *work ) +{ + /* do nothing */ +} + +static int GetResources( Work *work ) +{ + SVECTOR pos; + int flags; + + if ( !GCL_GetOption( 't' ) ) + { + return -1; + } + + work->text = GCL_GetString( GCL_NextStr() ); + + flags = 0; + if ( GCL_GetOption( 'f' ) ) + { + flags = GCL_GetNextInt() ? 16 : 0; + } + + if ( GCL_GetOption( 'l' ) ) + { + GCL_GetNextSV( (short *)&pos ); + } + else + { + pos = DG_ZeroVector; + } + + work->pos = pos; + + if ( GCL_GetOption( 'c' ) ) + { + GCL_GetNextSV( (short *)&work->color ); + } + + if ( GCL_GetOption( 's' ) ) + { + flags |= 0x20 | ( GCL_GetNextInt() << 8 ); + } + + work->flags = flags; + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewFontText( int name, int where ) +{ + Work *work; + + work = GV_NewActor( GV_ACTOR_USER, sizeof( Work ) ); + if ( work != NULL ) + { + GV_SetNamedActor( &( work->actor ), Act, Die, "fonttext.c" ); + + if ( GetResources( work ) < 0 ) + { + GV_DestroyActor( &( work->actor ) ); + return NULL; + } + + work->map = where; + work->name = name; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/others/fonttext.h b/components/mgs/mgs/source/chara/others/fonttext.h new file mode 100644 index 00000000..44e45023 --- /dev/null +++ b/components/mgs/mgs/source/chara/others/fonttext.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_FONTTEXT_H__ +#define __MGS_CHARA_FONTTEXT_H__ + +void *NewFontText( int name, int where ); + +#endif // __MGS_CHARA_FONTTEXT_H__ diff --git a/components/mgs/mgs/source/chara/others/intr_cam.c b/components/mgs/mgs/source/chara/others/intr_cam.c new file mode 100644 index 00000000..fe2b2a4b --- /dev/null +++ b/components/mgs/mgs/source/chara/others/intr_cam.c @@ -0,0 +1,120 @@ +#include "intr_cam.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "strcode.h" + +extern GM_SnakeCameraWork GM_SnakeCamera; + +/*---------------------------------------------------------------------------*/ + +typedef struct _Work +{ + GV_ACT actor; + int name; + int state; + int interp; + SVECTOR pos; + SVECTOR eye; +} Work; + +#define EXEC_LEVEL GV_ACTOR_AFTER2 + +/*---------------------------------------------------------------------------*/ + +static void CheckMessage(Work *work) +{ + GV_MSG *msgs; + int count; + + count = GV_ReceiveMessage(work->name, &msgs); + if (count <= 0) + { + return; + } + + while (--count >= 0) + { + switch (msgs->message[0]) + { + case HASH_KILL: + work->state = 3; + break; + + case HASH_ON: + work->state = 1; + work->eye = GM_SnakeCamera.position; + break; + + case HASH_OFF: + work->state = 2; + break; + } + msgs++; + } +} + +static void Act(Work *work) +{ + int interp; + + CheckMessage(work); + if (!(GM_PlayerStatus & PLAYER_INTRUDE)) + { + work->interp = 8; + return; + } + + if (work->state >= 2) + { + if (work->state == 3) + { + GV_DestroyActor(&work->actor); + } + work->interp = 8; + return; + } + + interp = work->interp; + if (work->interp > 0) + { + work->interp--; + } + GV_NearTimeV(&work->eye.vx, &work->pos.vx, interp, 3); + GM_SnakeCamera.position = work->eye; +} + +static void Die(Work *work) +{ +} + +static int GetResources(Work *work) +{ + GCL_GetOption('p'); + GCL_StrToSV(GCL_NextStr(), (short *)&work->pos); + work->state = 2; + work->interp = 8; + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewIntrudeCamera(int name, int where, int argc, char **argv) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "intr_cam.c"); + if (GetResources(work) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + work->name = name; + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/others/intr_cam.h b/components/mgs/mgs/source/chara/others/intr_cam.h new file mode 100644 index 00000000..3891c9b3 --- /dev/null +++ b/components/mgs/mgs/source/chara/others/intr_cam.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_INTR_CAM_H__ +#define __MGS_CHARA_INTR_CAM_H__ + +void *NewIntrudeCamera(int name, int where, int argc, char **argv); + +#endif // __MGS_CHARA_INTR_CAM_H__ diff --git a/components/mgs/mgs/source/chara/others/motse.c b/components/mgs/mgs/source/chara/others/motse.c new file mode 100644 index 00000000..ab2d48a2 --- /dev/null +++ b/components/mgs/mgs/source/chara/others/motse.c @@ -0,0 +1,89 @@ +#include "motse.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "game/game.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_AFTER + +typedef struct MotseElem +{ + int param1; + int sound; +} MotseElem; + +typedef struct _Work +{ + GV_ACT actor; + int m_opt; + int n_elems; + MotseElem *elems; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void Act(Work *work) +{ + MotseElem *elem; + int i; + + if ((GM_PlayerStatus & PLAYER_ACT_ONLY) && GM_PlayerAction == work->m_opt) + { + for (i = 0, elem = work->elems; i < work->n_elems; i++, elem++) + { + if (elem->param1 == dword_800AB9D4) + { + GM_SeSetMode(&GM_PlayerControl->mov, elem->sound, GM_SEMODE_BOMB); + return; + } + } + } +} + +static void Die(Work *work) +{ + GV_DelayedFree(work->elems); +} + +static int GetResources(Work *work, int name) +{ + int i, count; + unsigned char *param; + MotseElem *elems; + + work->m_opt = GCL_StrToInt(GCL_GetOption('m')); + work->n_elems = count = GCL_StrToInt(GCL_GetOption('n')); + work->elems = elems = GV_Malloc(count * sizeof(MotseElem)); + + GCL_GetOption('s'); + + for (i = 0; (param = GCL_NextStr()) && i != count; i++, elems++) + { + elems->param1 = GCL_StrToInt(param); + elems->sound = GCL_StrToInt(GCL_NextStr()); + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewMotionSoundEffect(int name, int where, int argc, char **argv) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "motse.c"); + if (GetResources(work, name) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/others/motse.h b/components/mgs/mgs/source/chara/others/motse.h new file mode 100644 index 00000000..70222f70 --- /dev/null +++ b/components/mgs/mgs/source/chara/others/motse.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_MOTSE_H__ +#define __MGS_CHARA_MOTSE_H__ + +void *NewMotionSoundEffect(int name, int where, int argc, char **argv); + +#endif // __MGS_CHARA_MOTSE_H__ diff --git a/components/mgs/mgs/source/chara/rope/dsmoke2.c b/components/mgs/mgs/source/chara/rope/dsmoke2.c new file mode 100644 index 00000000..892243de --- /dev/null +++ b/components/mgs/mgs/source/chara/rope/dsmoke2.c @@ -0,0 +1,483 @@ +#include "linkvar.h" +#include "game/game.h" +#include "libgcl/libgcl.h" + +#define EXEC_LEVEL GV_ACTOR_AFTER + +typedef struct _Work +{ + GV_ACT actor; + int name; + TARGET target; + SVECTOR start; + SVECTOR end; + SVECTOR size; + SVECTOR field_84; + char pad1[0x8]; + int n_vecs; + SVECTOR vecs[3]; + u_short map; + short field_B2; + short field_B4; + short field_B6; + u_short field_B8; + u_short field_BA; + u_short field_BC; + short field_BE; + SVECTOR field_C0[16]; + SVECTOR field_140[16]; + RECT field_1C0[16]; + DG_PRIM *field_240[16]; + DG_TEX *tex; + u_short tx; + u_short ty; + u_short tw; + u_short th; +} Work; + +static int dsmoke2_life = 36; + +void AN_DamageSmoke1( SVECTOR * ); + +int s11d_dsmoke2_800CC08C(Work *work, SVECTOR *pos) +{ + int yh, yl; + + if (work->start.vy > work->end.vy) + { + yh = work->start.vy + 2000; + yl = work->end.vy - 2000; + } + else + { + yh = work->end.vy + 2000; + yl = work->start.vy - 2000; + } + + if ((pos->vy > yh) || (pos->vy < yl)) + { + return 0; + } + + return 1; +} + +void s11d_dsmoke2_800CC0E0(Work *work, SVECTOR *arg1, SVECTOR *size) +{ + SVECTOR sp18; + SVECTOR sp20; + SVECTOR *pos; + SVECTOR *vec; + int map; + int hit; + int count; + + pos = &GM_PlayerPosition; + vec = work->vecs; + map = work->map; + hit = 0; + count = work->n_vecs; + while (--count >= 0) + { + GV_AddVec3(vec, arg1, &sp18); + GV_AddVec3(&sp18, size, &sp20); + + hit = GM_OnlineTargetCheck(&sp18, &sp20, map, &sp20, ENEMY_SIDE); + if (hit) + { + break; + } + + vec++; + } + + if (hit) + { + GM_MoveTarget(&work->target, pos); + GM_PowerTarget(&work->target); + } +} + +void s11d_dsmoke2_800CC1DC(Work *work) +{ + SVECTOR sp10; + SVECTOR size; + u_short index; + u_short temp_s3; + u_short temp_s2; + + temp_s3 = work->field_B4; + temp_s2 = work->field_BA; + + if (++work->field_BA > temp_s3) + { + work->field_BA = 0; + work->field_B8 = 0; + work->field_B2 = 0; + return; + } + + if (temp_s2 == 0) + { + GM_SeSet(&work->start, 186); + } + + if (temp_s2 < 16) + { + DG_VisiblePrim(work->field_240[temp_s2]); + } + + if (temp_s2 >= (temp_s3 - 16)) + { + index = temp_s2 - (temp_s3 - 16); + DG_InvisiblePrim(work->field_240[index]); + } + + if (s11d_dsmoke2_800CC08C(work, &GM_PlayerPosition)) + { + if (temp_s2 < 16) + { + size.vx = temp_s2 * work->field_84.vx; + size.vy = temp_s2 * work->field_84.vy; + size.vz = temp_s2 * work->field_84.vz; + } + else + { + size = work->size; + } + + if (temp_s2 >= (temp_s3 - 16)) + { + index = temp_s2 - (temp_s3 - 16); + DG_InvisiblePrim(work->field_240[index]); + + sp10.vx = index * work->field_84.vx; + sp10.vy = index * work->field_84.vy; + sp10.vz = index * work->field_84.vz; + } + else + { + sp10 = DG_ZeroVector; + } + + GV_SubVec3(&size, &sp10, &size); + s11d_dsmoke2_800CC0E0(work, &sp10, &size); + } +} + +void s11d_dsmoke2_800CC418(Work *work) +{ + int index; + + index = work->field_B8; + if (index >= 16) + { + work->field_BE = 16; + } + else + { + work->field_BE++; + DG_VisiblePrim(work->field_240[index]); + } + + work->field_B8++; + + if (s11d_dsmoke2_800CC08C(work, &GM_PlayerPosition)) + { + s11d_dsmoke2_800CC0E0(work, &DG_ZeroVector, &work->size); + } +} + +void s11d_dsmoke2_800CC4B8(Work *work) +{ + int i; + DG_PRIM *prim; + POLY_FT4 *pack; + int x, y, w, h; + int xs, ys; + + x = work->tx; + y = work->ty; + h = work->th; + w = work->tw; + + for (i = 0; i < 16; i++) + { + prim = work->field_240[i]; + pack = prim->packs[GV_Clock]; + + if (prim->type & DG_PRIM_INVISIBLE) + { + continue; + } + + xs = GV_RandU(1024) % 7; + ys = GV_RandU(1024) % 4; + + pack->u0 = pack->u2 = x + (w * xs) / 8; + pack->u1 = pack->u3 = x + (w * (xs + 1)) / 8 - 1; + + pack->v0 = pack->v1 = y + (h * ys) / 4; + pack->v2 = pack->v3 = y + (h * (ys + 1)) / 4 - 1; + } +} + +void s11d_dsmoke2_800CC648(Work *work) +{ + GM_CurrentMap = work->map; + + if (work->field_BC != 0) + { + work->field_BC--; + return; + } + + if (work->field_B4 >= 0) + { + if (work->field_B2 == 0) + { + if (work->field_B8 == work->field_B6) + { + work->field_B2 = 1; + } + + if ((work->field_B8 >= (work->field_B6 - 24)) && (work->field_B8 < (work->field_B6 - 18))) + { + if (work->field_B8 == work->field_B6 - 24) + { + GM_SeSet(&work->start, 176); + } + + AN_DamageSmoke1(&work->start); + } + + work->field_B8++; + } + else + { + s11d_dsmoke2_800CC1DC(work); + } + } + else + { + s11d_dsmoke2_800CC418(work); + } + + s11d_dsmoke2_800CC4B8(work); +} + +void s11d_dsmoke2_800CC730(Work *work) +{ + int i; + + for (i = 0; i < 16; i++) + { + GM_FreePrim(work->field_240[i]); + } +} + +int s11d_dsmoke2_800CC794(Work *work) +{ + DG_TEX *tex; + SVECTOR *vert; + SVECTOR *sp18; + RECT *rect; + int shade; + int i, j; + int height; + int rad; + DG_PRIM *prim; + POLY_FT4 *pack; + int x, y, w, h; + + tex = work->tex = DG_GetTexture(GV_StrCode("smoke")); + work->tx = tex->off_x; + work->ty = tex->off_y; + work->tw = tex->w + 1; + work->th = tex->h + 1; + if (tex == NULL) + { + return -1; + } + + vert = work->field_C0; + sp18 = work->field_140; + rect = work->field_1C0; + shade = 96; + + for (i = 0; i < 16; i++, vert++, rect++, sp18++) + { + if (i < 8) + { + height = i * 30 + 750; + } + else + { + height = (i - 8) * 120 + 1000; + } + + rad = GV_VecLen3(&work->field_84) / 2; + rect->x = rad * 3; + rect->w = rect->x * 2; + rect->y = height / 2; + rect->h = height; + + vert->vx = work->start.vx + work->field_84.vx * i + GV_RandS(64); + vert->vy = work->start.vy + work->field_84.vy * i + GV_RandS(64); + vert->vz = work->start.vz + work->field_84.vz * i + GV_RandS(64); + + *sp18 = *vert; + + work->field_240[i] = prim = GM_MakePrim(0x412, 1, vert, rect); + if (prim == NULL) + { + return -1; + } + + shade -= 5; + + prim->raise = 500; + DG_InvisiblePrim(prim); + + for (j = 0; j < 2; j++) + { + pack = prim->packs[j]; + setPolyFT4(pack); + setSemiTrans(pack, 1); + setRGB0(pack, shade, shade, shade); + + x = tex->off_x; + w = tex->w; + pack->u0 = pack->u2 = x; + pack->u1 = pack->u3 = x + (w + 1) / 8 - 1; + + y = tex->off_y; + h = tex->h; + pack->v0 = pack->v1 = y; + pack->v2 = pack->v3 = y + (h + 1) / 4 - 1; + + pack->tpage = tex->tpage; + pack->clut = tex->clut; + + pack->tpage = (pack->tpage & ~0x60) | (1 << 5); + } + } + + return 0; +} + +int s11d_dsmoke2_800CCAB4(Work *work) +{ + SVECTOR size; + int difficulty; + int i; + + GCL_StrToSV(GCL_GetOption('s'), (short *)&work->start); + GCL_StrToSV(GCL_GetOption('e'), (short *)&work->end); + + GCL_GetOption('i'); + work->field_B4 = GCL_StrToInt(GCL_NextStr()); + work->field_B6 = GCL_StrToInt(GCL_NextStr()); + work->field_BC = GCL_StrToInt(GCL_GetOption('d')); + work->field_B8 = 0; + work->field_BA = 0; + + GV_SubVec3(&work->end, &work->start, &work->size); + + work->field_84.vx = work->size.vx / 16; + work->field_84.vy = work->size.vy / 16; + work->field_84.vz = work->size.vz / 16; + + difficulty = GM_DifficultyFlag; + if (difficulty < DIFFICULTY_EASY) + { + difficulty = DIFFICULTY_EASY; + } + + if (GCL_GetOption('x')) + { + for (i = 0; i < difficulty; i++) + { + GCL_GetNextInt(); + } + + dsmoke2_life = GCL_GetNextInt(); + } + + size.vx = size.vy = size.vz = 250; + GM_SetTarget(&work->target, TARGET_POWER, ENEMY_SIDE, &size); + GM_SetPowerTarget(&work->target, POWER_CONST, 2, dsmoke2_life, -1, &DG_ZeroVector); + + work->field_B2 = 0; + work->field_BE = 0; + return 0; +} + +void s11d_dsmoke2_800CCC60(Work *work) +{ + int count; + SVECTOR *vec; + char *res; + + count = 0; + + if (GCL_GetOption('b')) + { + vec = work->vecs; + + while ((res = GCL_NextStr())) + { + if (count == 3) + { + break; + } + + GCL_StrToSV(res, (short *)vec); + vec++; + count++; + } + } + + work->n_vecs = count; +} + +int DSGetResources(Work *work, int name, int where) +{ + GM_CurrentMap = where; + + if (s11d_dsmoke2_800CCAB4(work) < 0) + { + return -1; + } + + s11d_dsmoke2_800CCC60(work); + + if (s11d_dsmoke2_800CC794(work) < 0) + { + return -1; + } + + work->name = name; + work->map = GM_CurrentMap; + return 0; +} + +void *NewDamageSmoke2(int name, int where) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(work, s11d_dsmoke2_800CC648, s11d_dsmoke2_800CC730, "dsmoke2.c"); + + if (DSGetResources(work, name, where) >= 0) + { + return work; + } + + GV_DestroyActor(work); + } + + return NULL; +} diff --git a/components/mgs/mgs/source/chara/rope/landing.c b/components/mgs/mgs/source/chara/rope/landing.c new file mode 100644 index 00000000..9234ccf4 --- /dev/null +++ b/components/mgs/mgs/source/chara/rope/landing.c @@ -0,0 +1,353 @@ +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "game/vibrate.h" // for NewPadVibration +#include "chara/snake/shadow.h" // for NewShadow +#include "takabe/cineutil.h" // for OpenCinemaScreen, etc. + +extern GM_SnakeCameraWork GM_SnakeCamera; + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_USER + +#define NUM_JOINT 16 +#define MOTION_DATA GV_StrCode("sne_11d2") +#define BODY_MODEL GV_StrCode("snake") +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE | DG_FLAG_GBOUND ) + +/*---------------------------------------------------------------------------*/ + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + OBJECT body; + MOTION_CONTROL m_ctrl; + MOTION_SEGMENT m_segs1[NUM_JOINT + 1]; + MOTION_SEGMENT m_segs2[NUM_JOINT + 1]; + SVECTOR rots[NUM_JOINT]; + SVECTOR adjust[NUM_JOINT]; + MATRIX light[2]; + DG_PRIM *shadow_prim; + SVECTOR shadow_vertices[4]; + void *shadow; + DG_PRIM *prim; + SVECTOR vertices[32]; + int level; + int state; + int commence; + int proc_id; +} Work; + +static u_char s11d_dword_800C3348[] = {0x7F, 0x02, 0x00, 0x00}; +static u_char s11d_dword_800C334C[] = {0xFF, 0x02, 0x00, 0x00}; + +/*---------------------------------------------------------------------------*/ + +static void Act(Work *work) +{ + int state; + + GM_ActMotion(&work->body); + GM_ActControl(&work->control); + work->control.mov.vy = work->body.height + work->level; + GM_ActObject(&work->body); + DG_GetLightMatrix(&work->control.mov, work->light); + + GM_SnakeCamera.position = work->control.mov; + GM_SnakeCamera.rotate2 = work->control.rot; + GM_SnakeCamera.track = 4000; + + work->control.height = work->body.height; + + state = work->state; + if (state >= 0) + { + work->state++; + + if (state == 0) + { + GM_GameStatus |= ( STATE_PAUSE_ONLY | STATE_PADRELEASE ); + GCL_ExecProc(work->commence, NULL); + } + + work->shadow_prim->world.t[0] = work->control.mov.vx; + work->shadow_prim->world.t[1] = work->control.mov.vy; + work->shadow_prim->world.t[2] = work->control.mov.vz; + + if (state > 16) + { + work->shadow_prim->world.t[1] = work->level; + } + else + { + work->shadow_prim->world.t[1] = -13500; + } + + if (state < 16) + { + work->prim->world.t[0] = work->body.objs->objs[4].world.t[0]; + work->prim->world.t[1] = work->body.objs->objs[4].world.t[1]; + work->prim->world.t[2] = work->body.objs->objs[4].world.t[2]; + } + + if (state == 8) + { + GM_SeSet2(0, 63, 128); + } + + if (state == 24) + { + GM_SeSet2(0, 63, 129); + NewPadVibration(s11d_dword_800C3348, 1); + NewPadVibration(s11d_dword_800C334C, 2); + } + + if (work->body.is_end) + { + work->m_ctrl.info1.field_14 = 0; + work->m_ctrl.interp = 15; + GCL_ExecProc(work->proc_id, NULL); + work->state = -1; + } + } +} + +/*---------------------------------------------------------------------------*/ + +static void Die(Work *work) +{ + CloseCinemaScreen(); + + GM_FreeControl(&work->control); + GM_FreeObject(&work->body); + GM_FreePrim(work->shadow_prim); + GM_FreePrim(work->prim); + + if (work->shadow) + { + GV_DestroyOtherActor(work->shadow); + } + + GM_GameStatus &= ~( STATE_PAUSE_ONLY | STATE_PADRELEASE ); +} + +/*---------------------------------------------------------------------------*/ + +static int InitShadow(Work *work) +{ + DG_PRIM *prim; + DG_TEX *tex; + int i; + POLY_FT4 *packs; + SVECTOR *vert; + + work->shadow_prim = prim = GM_MakePrim(DG_PRIM_POLY_FT4, 1, work->shadow_vertices, NULL); + if (prim == NULL) + { + return -1; + } + + tex = DG_GetTexture(GV_StrCode("shadow")); + if (tex == NULL) + { + return -1; + } + + for (i = 0; i < 2; i++) + { + packs = prim->packs[i]; + setPolyFT4(packs); + setSemiTrans(packs, 1); + setRGB0(packs, 72, 72, 72); + DG_SetPacketTexture4(packs, tex); + } + + vert = work->shadow_vertices; + for (i = 0; i < 4; i++) + { + *vert = DG_ZeroVector; + + if (i & 1) + { + vert->vx = 400; + } + else + { + vert->vx = -400; + } + + if (i & 2) + { + vert->vz = 400; + } + else + { + vert->vz = -400; + } + + + vert++; + } + + prim->world = DG_ZeroMatrix; + prim->raise = 2000; + return 0; +} + +static int InitWire(Work *work) +{ + SVECTOR *vert; + DG_PRIM *prim; + DG_TEX *tex; + int i, j, k; + POLY_FT4 *packs; + int height; + + vert = work->vertices; + + work->prim = prim = GM_MakePrim(DG_PRIM_POLY_FT4, 8, work->vertices, NULL); + if (prim == NULL) + { + return -1; + } + + prim->group_id = 0; + + tex = DG_GetTexture(GV_StrCode("wire")); + if (tex == NULL) + { + return -1; + } + + for (i = 0; i < 2; i++) + { + packs = prim->packs[i]; + height = -1000; + + for (j = 0; j < 8; j++, packs++) + { + setPolyFT4(packs); + setSemiTrans(packs, 1); + setRGB0(packs, 48, 48, 48); + DG_SetPacketTexture4(packs, tex); + + if (i == 0) + { + for (k = 0; k < 4; k++) + { + vert->vz = 0; + vert->vy = (k & 2) ? height : height - 500; + vert->vx = (k & 1) ? 34 : -34; + vert++; + } + + height += 500; + } + } + } + + prim->world = DG_ZeroMatrix; + return 0; +} + +static int GetResources(Work *work, int name, int where) +{ + SVECTOR indices; + CONTROL *control; + OBJECT *body; + + control = &work->control; + if (GM_InitControl(control, name, where) < 0) + { + return -1; + } + + GM_ConfigControlString(control, GCL_GetOption('p'), GCL_GetOption('d')); + GM_ConfigControlHazard(control, 0, -2, -2); + + body = &work->body; + GM_InitObject(body, BODY_MODEL, BODY_FLAG, MOTION_DATA); + + GM_ConfigObjectJoint(body); + GM_ConfigMotionControl(body, &work->m_ctrl, MOTION_DATA, work->m_segs1, work->m_segs2, control, work->rots); + GM_ConfigObjectLight(body, work->light); + GM_ConfigObjectAction(body, 0, 0, 0); + + if (GCL_GetOption('e')) + { + work->proc_id = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->proc_id = -1; + } + + if (GCL_GetOption('c')) + { + work->commence = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->commence = -1; + } + + if (GCL_GetOption('l')) + { + work->level = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->level = -16500; + } + + if (InitShadow(work) < 0) + { + return -1; + } + + if (InitWire(work) < 0) + { + return -1; + } + + indices.vx = 0; + indices.vy = 6; + indices.vz = 12; + indices.pad = 15; + work->shadow = NewShadow(control, body, indices); + + work->state = 0; + + OpenCinemaScreen(1, 30000); + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewLanding(int name, int where) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(work, Act, Die, "landing.c"); + + if (GetResources(work, name, where) >= 0) + { + return work; + } + + GV_DestroyActor(work); + } + + return NULL; +} diff --git a/components/mgs/mgs/source/chara/rope/landing.h b/components/mgs/mgs/source/chara/rope/landing.h new file mode 100644 index 00000000..9a514516 --- /dev/null +++ b/components/mgs/mgs/source/chara/rope/landing.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_LANDING_H__ +#define __MGS_CHARA_LANDING_H__ + +void *NewLanding(int name, int where); + +#endif // __MGS_CHARA_LANDING_H__ diff --git a/components/mgs/mgs/source/chara/rope/pipe.c b/components/mgs/mgs/source/chara/rope/pipe.c new file mode 100644 index 00000000..31d53876 --- /dev/null +++ b/components/mgs/mgs/source/chara/rope/pipe.c @@ -0,0 +1,291 @@ +#include "pipe.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "game/vibrate.h" // for NewPadVibration +#include "bullet/blast.h" // for NewBlast + +extern BLAST_DATA blast_data_8009F4B8[8]; // in bullet/blast.c + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_AFTER + +#define MODEL_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE | DG_FLAG_GBOUND | DG_FLAG_ONEPIECE ) + +typedef struct _Work +{ + GV_ACT actor; + OBJECT_NO_ROTS object; + MATRIX light[2]; + TARGET *target; + SVECTOR pos; + u_short models[2]; + int where; + int counter; + int counter2; +} Work; + +/*---------------------------------------------------------------------------*/ + +unsigned char pipe_vibration1_800C3360[] = {0x7F, 0x02, 0x00, 0x00}; +unsigned char pipe_vibration2_800C3364[] = {0xAF, 0x04, 0x41, 0x04, 0x00, 0x00, 0x00, 0x00}; + +static int GetRaise(DG_MDL *mdl) +{ + unsigned int flags; + int raise; + + flags = mdl->flags; + raise = 0; + if (flags & 0x300) + { + raise = (4 - ((flags >> 12) & 3)) * 250; + if (!(flags & 0x100)) + { + raise = -raise; + } + } + return raise; +} + +static int SwitchModel(Work *work) +{ + DG_MDL *mdl, *mdl2; + DG_OBJ *obj; + DG_OBJS *objs; + int count; + + objs = work->object.objs; + count = objs->n_models; + obj = objs->objs; + for (; count > 0; count--, obj++) + { + DG_FreeObjPacket(obj, 0); + DG_FreeObjPacket(obj, 1); + } + + objs->def = GV_GetCache(GV_CacheID(work->models[1], 'k')); + + count = objs->def->n_models; + mdl = objs->def->model; + mdl2 = &objs->def->model[0]; + obj = &objs->objs[0]; + for (; count > 0; obj++, mdl2++, mdl++, count--) + { + obj->model = mdl; + + if (mdl2->extend < 0) + { + obj->extend = NULL; + } + else + { + obj->extend = &objs->objs[mdl2->extend]; + } + + obj->raise = GetRaise(mdl); + obj->n_packs = mdl2->n_faces; + } + + return 0; +} + +static void Destroy(Work *work) +{ + MATRIX mat; + + if (work->target->damaged & TARGET_POWER) + { + mat = DG_ZeroMatrix; + + mat.t[0] = work->pos.vx; + mat.t[1] = work->pos.vy; + mat.t[2] = work->pos.vz; + + NewBlast(&mat, &blast_data_8009F4B8[1]); + NewPadVibration(pipe_vibration1_800C3360, 1); + NewPadVibration(pipe_vibration2_800C3364, 2); + + GM_FreeTarget(work->target); + work->target = NULL; + work->counter2 = 1; + SwitchModel(work); + work->counter = 0; + } +} + +static void Act(Work *work) +{ + MATRIX mat; + + GM_CurrentMap = work->where; + if (work->counter < 4) + { + work->counter++; + DG_GetLightMatrix2(&work->pos, work->light); + } + + if (work->counter2 == 0) + { + Destroy(work); + } + else if (work->counter2 < 16) + { + work->counter2++; + if (work->counter2 == 8 || work->counter2 == 16) + { + mat = DG_ZeroMatrix; + + mat.t[0] = work->pos.vx; + mat.t[1] = work->pos.vy; + mat.t[2] = work->pos.vz; + + mat.t[0] += GV_RandS(2048); + mat.t[1] += GV_RandS(2048); + mat.t[2] += GV_RandS(2048); + + NewBlast(&mat, &blast_data_8009F4B8[1]); + NewPadVibration(pipe_vibration1_800C3360, 1); + NewPadVibration(pipe_vibration2_800C3364, 2); + } + } +} + +static void Die(Work *work) +{ + GM_FreeObject((OBJECT *)&work->object); + + if (work->target != NULL) + { + GM_FreeTarget(work->target); + } +} + +static int InitTarget(Work *work) +{ + SVECTOR size, pos; + TARGET *target; + + size.vx = 1500; + size.vy = 750; + size.vz = 4000; + + GCL_StrToSV(GCL_GetOption('t'), (short *)&pos); + work->pos = pos; + + work->target = target = GM_AllocTarget(); + if (target == NULL) + { + return -1; + } + + GM_SetTarget(target, ( TARGET_POWER | TARGET_SEEK ), PLAYER_SIDE, &size); + GM_SetPowerTarget(target, POWER_DECREASE, 0, 1, -1, &DG_ZeroVector); + GM_MoveTarget(target, &work->pos); + + work->counter = 0; + return 0; +} + +static void GetInts(Work *work) +{ + int i; + short *out; + char *res; + + if (!GCL_GetOption('m')) + { + return; + } + + i = 0; + out = work->models; + while ((res = GCL_NextStr())) + { + if (i == 2) + { + break; + } + + *out++ = GCL_StrToInt(res); + i++; + } +} + +static int InitObject(Work *work) +{ + SVECTOR dir, pos; + MATRIX world; + OBJECT_NO_ROTS *object; + int i; + + GetInts(work); + + object = &work->object; + GM_InitObjectNoRots(object, work->models[0], MODEL_FLAG, 0); + GM_ConfigObjectLight((OBJECT *)object, work->light); + + GCL_StrToSV(GCL_GetOption('d'), (short *)&dir); + GCL_StrToSV(GCL_GetOption('p'), (short *)&pos); + DG_SetPos2(&pos, &dir); + + ReadRotMatrix(&world); + work->object.objs->world = world; + + object->map_name = GM_CurrentMap; + for (i = 0; i < object->objs->n_models; i++) + { + object->objs->objs[i].raise = 240; + } + + return 0; +} + +static int GetResources(Work *work, int name, int where) +{ + GM_CurrentMap = where; + + if (InitTarget(work) < 0) + { + return -1; + } + + if (InitObject(work) < 0) + { + return -1; + } + + work->counter2 = 0; + work->where = GM_CurrentMap; + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewPipe(int name, int where, int argc, char **argv) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work == NULL) + { + return NULL; + } + + GV_SetNamedActor(&work->actor, Act, Die, "pipe.c"); + if (GetResources(work, name, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/rope/pipe.h b/components/mgs/mgs/source/chara/rope/pipe.h new file mode 100644 index 00000000..f5cd54a2 --- /dev/null +++ b/components/mgs/mgs/source/chara/rope/pipe.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_PIPE_H__ +#define __MGS_CHARA_PIPE_H__ + +void *NewPipe(int name, int where, int argc, char **argv); + +#endif // __MGS_CHARA_PIPE_H__ diff --git a/components/mgs/mgs/source/chara/snake/afterse.c b/components/mgs/mgs/source/chara/snake/afterse.c new file mode 100644 index 00000000..bc1cc457 --- /dev/null +++ b/components/mgs/mgs/source/chara/snake/afterse.c @@ -0,0 +1,48 @@ +#include "afterse.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "game/game.h" + +typedef struct _Work +{ + GV_ACT actor; + short sound; + short time; +} Work; + +#define EXEC_LEVEL GV_ACTOR_AFTER + +static void Act(Work *work) +{ + if ( --work->time == 0 ) + { + // Play the sound + GM_SeSetMode(&GM_PlayerPosition, work->sound, GM_SEMODE_BOMB); + + // Allow soliders/chavs/whatever to hear it + GM_SetNoise(100, 16, &GM_PlayerPosition); + + // Die now we've done our stuff + GV_DestroyActor(&work->actor); + } +} + +void *NewAfterSe(short sound, short time) +{ + Work *work; + + if (sound != 0 && time != 0) + { + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, NULL, "afterse.c"); + work->sound = sound; + work->time = time; + return (void *)work; + } + } + + return NULL; +} diff --git a/components/mgs/mgs/source/chara/snake/afterse.h b/components/mgs/mgs/source/chara/snake/afterse.h new file mode 100644 index 00000000..02f5d106 --- /dev/null +++ b/components/mgs/mgs/source/chara/snake/afterse.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_AFTERSE_H__ +#define __MGS_CHARA_AFTERSE_H__ + +void *NewAfterSe(short sound, short time); + +#endif // __MGS_CHARA_AFTERSE_H__ diff --git a/components/mgs/mgs/source/chara/snake/breath.c b/components/mgs/mgs/source/chara/snake/breath.c new file mode 100644 index 00000000..5dc60344 --- /dev/null +++ b/components/mgs/mgs/source/chara/snake/breath.c @@ -0,0 +1,89 @@ +#include "breath.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "strcode.h" + +extern void AN_Breath( MATRIX *world ); + +#define EXEC_LEVEL GV_ACTOR_USER +#define DEFAULT_TIME 64 + +typedef struct _Work +{ + GV_ACT actor; + u_short name; + u_short visible; + int time; +} Work; + +static void Act( Work *work ) +{ + GV_MSG *msg; + int n_msgs; + OBJECT *body; + + n_msgs = GV_ReceiveMessage( work->name, &msg ); + + for (; n_msgs > 0 ; --n_msgs, ++msg ) + { + switch ( msg->message[0] ) + { + case HASH_ENTER: + work->visible = TRUE; + break; + default: + work->visible = FALSE; + } + } + + if ( work->visible && GM_AlertMode != ALERT_ACTIVE && + !( GM_PlayerStatus & ( PLAYER_WATCH | PLAYER_INTRUDE | PLAYER_MOVE | PLAYER_GAME_OVER ) ) ) + { + body = GM_PlayerBody; + if ( body != NULL && ( GV_Time % work->time == 0 ) ) + { + AN_Breath( &body->objs->objs[6].world ); + } + } +} + +static void Die( Work *work ) +{ + /* do nothing */ +} + +static int GetResources( Work *work, int name, int where ) +{ + if (GCL_GetOption('t')) + { + work->time = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->time = DEFAULT_TIME; + } + + work->name = name; + work->visible = TRUE; + return 0; +} + +void *NewSnakeBreath( int name, int where, int argc, char **argv ) +{ + Work *work ; + + work = GV_NewActor( EXEC_LEVEL, sizeof( Work ) ) ; + if ( work != NULL ) { + /* ワークにコールバックを登録する */ + GV_SetNamedActor( &( work->actor ), Act, Die, "breath.c" ) ; + if ( GetResources( work, name, where ) >= 0 ) { + return (void *)work ; + } + GV_DestroyActor( &work->actor ) ; + } + return NULL; +} diff --git a/components/mgs/mgs/source/chara/snake/breath.h b/components/mgs/mgs/source/chara/snake/breath.h new file mode 100644 index 00000000..5a6c6196 --- /dev/null +++ b/components/mgs/mgs/source/chara/snake/breath.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_BREATH_H__ +#define __MGS_CHARA_BREATH_H__ + +void *NewSnakeBreath(int name, int where, int argc, char **argv); + +#endif // __MGS_CHARA_BREATH_H__ diff --git a/components/mgs/mgs/source/chara/snake/shadow.c b/components/mgs/mgs/source/chara/snake/shadow.c new file mode 100644 index 00000000..199a0d6e --- /dev/null +++ b/components/mgs/mgs/source/chara/snake/shadow.c @@ -0,0 +1,247 @@ +#include "shadow.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_USER + +#define SHADOW_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE \ + | DG_FLAG_GBOUND | DG_FLAG_ONEPIECE | DG_FLAG_AMBIENT ) + +typedef struct _Work +{ + GV_ACT actor; + CONTROL *control; + OBJECT *parent; + OBJECT_NO_ROTS object; + MATRIX light[2]; + int color; // Unused + int enabled; + SVECTOR indices; +} Work; + +typedef struct _SCRPAD_DATA +{ + SVECTOR iter[4]; + SVECTOR rot_in; + VECTOR rot_out; + MATRIX mtx; +} SCRPAD_DATA; + +/*---------------------------------------------------------------------------*/ + +static void RotateShadow(Work *work) +{ + MATRIX *pWorld; + MATRIX *pMatrix; + + int lVar9; + int lVar5; + int lVar4; + + int sVar6; + int sVar7; + + int iVar12; + + SVECTOR *psVar14; + + int iVar15; + int iVar16; + int iVar17; + + SCRPAD_DATA *pScratch = (SCRPAD_DATA *)getScratchAddr(0); + + SVECTOR *iter1 = pScratch->iter; + short *iter2 = (short *)&work->indices; + + int i; + + for (i = 4; i > 0; i--, iter1++, iter2++) + { + pMatrix = &work->parent->objs->objs[*iter2].world; + iter1->vx = pMatrix->t[0]; + + pMatrix = &work->parent->objs->objs[*iter2].world; + iter1->vy = pMatrix->t[1]; + + pMatrix = &work->parent->objs->objs[*iter2].world; + iter1->vz = pMatrix->t[2]; + } + + pScratch->mtx = work->parent->objs->world; + + pScratch->mtx.t[0] = work->parent->objs[0].objs[0].world.t[0]; + lVar9 = pScratch->mtx.t[0]; + + pScratch->mtx.t[1] = work->parent->objs[0].objs[0].world.t[1]; + lVar5 = pScratch->mtx.t[1]; + + pScratch->mtx.t[2] = work->parent->objs[0].objs[0].world.t[2]; + lVar4 = pScratch->mtx.t[2]; + + DG_TransposeMatrix(&pScratch->mtx, &pScratch->mtx); + SetRotMatrix(&pScratch->mtx); + + psVar14 = pScratch->iter; + + iVar17 = -32000; + iVar15 = -32000; + iVar16 = 32000; + iVar12 = 32000; + + for (i = 4; i > 0; i--) + { + pScratch->rot_in.vx = psVar14->vx - lVar9; + pScratch->rot_in.vy = psVar14->vy - lVar5; + pScratch->rot_in.vz = psVar14->vz - lVar4; + + ApplyRotMatrix(&pScratch->rot_in, &pScratch->rot_out); + + if (iVar15 < pScratch->rot_out.vx) + { + iVar15 = pScratch->rot_out.vx; + } + + if (iVar12 > pScratch->rot_out.vx) + { + iVar12 = pScratch->rot_out.vx; + } + + if (iVar17 < pScratch->rot_out.vz) + { + iVar17 = pScratch->rot_out.vz; + } + + if (iVar16 > pScratch->rot_out.vz) + { + iVar16 = pScratch->rot_out.vz; + } + + psVar14++; + } + + sVar6 = (iVar15 - iVar12) / 2; + sVar7 = (iVar17 - iVar16) / 2; + + pWorld = &work->object.objs[0].world; + *pWorld = DG_ZeroMatrix; + + if (sVar7 >= 0x1f5) + { + sVar6 += 100; + } + else + { + sVar6 += 200; + sVar7 += 200; + } + + pWorld->m[0][0] = sVar6; + pWorld->m[2][2] = sVar7; +} + +static void MoveShadow(Work *work) +{ + DG_OBJS *objs = work->object.objs; + + RotMatrixY(work->control->rot.vy, &objs->world); + objs->world.t[0] = work->parent->objs->world.t[0]; + objs->world.t[2] = work->parent->objs->world.t[2]; + objs->world.t[1] = work->control->levels[0]; + + if ((work->control->mov.vy - work->control->levels[0]) < 450) + { + objs->objs[0].raise = -500; + } + else + { + objs->objs[0].raise = GV_NearExp8(objs->objs[0].raise, 150); + } +} + +static void Act(Work *work) +{ + if ((work->parent->objs->flag & DG_FLAG_INVISIBLE) || !work->enabled) + { + DG_InvisibleObjs(work->object.objs); + } + else + { + DG_VisibleObjs(work->object.objs); + RotateShadow(work); + MoveShadow(work); + DG_GroupObjs(work->object.objs, work->parent->map_name); + } +} + +static void Die(Work *work) +{ + GM_FreeObject((OBJECT *)&work->object); +} + +static int GetResources(Work *work, CONTROL *control, OBJECT *parent, SVECTOR indices) +{ + GM_InitObjectNoRots(&work->object, GV_StrCode("kage"), SHADOW_FLAG, 0); + GM_ConfigObjectLight((OBJECT *)&work->object, work->light); + + DG_GroupObjs(work->object.objs, parent->map_name); + + work->light[0].m[1][1] = -0x1000; + work->light[1].m[0][1] = 0x480; + work->light[1].m[1][1] = 0x480; + work->light[1].m[2][1] = 0x480; + work->light[0].t[0] = 0; + work->light[0].t[1] = 0; + work->light[0].t[2] = 0; + + work->object.objs->objs->model->flags |= DG_MODEL_TRANS; + work->object.objs->objs->model->materials[0] = GV_StrCode("shadow"); + + work->control = control; + work->parent = parent; + work->indices = indices; + work->color = MAKE_RGBA(72,72,72, GPU_CODE_POLY_FT4); + work->enabled = TRUE; + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewShadow(CONTROL *control, OBJECT *parent, SVECTOR indices) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "shadow.c"); + if (GetResources(work, control, parent, indices) >= 0) + { + return (void *)work; + } + GV_DestroyActor(&work->actor); + } + return NULL; +} + +void *NewShadow2(CONTROL *control, OBJECT *parent, SVECTOR indices, int **enabled) +{ + Work *work; + + work = (Work *)NewShadow(control, parent, indices); + if (work != NULL && enabled != NULL) + { + *enabled = &work->enabled; + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/snake/shadow.h b/components/mgs/mgs/source/chara/snake/shadow.h new file mode 100644 index 00000000..22e66117 --- /dev/null +++ b/components/mgs/mgs/source/chara/snake/shadow.h @@ -0,0 +1,9 @@ +#ifndef __MGS_CHARA_SHADOW_H__ +#define __MGS_CHARA_SHADOW_H__ + +#include "game/game.h" + +void *NewShadow(CONTROL *control, OBJECT *parent, SVECTOR indices); +void *NewShadow2(CONTROL *control, OBJECT *parent, SVECTOR indices, int **enabled); + +#endif // __MGS_CHARA_SHADOW_H__ diff --git a/components/mgs/mgs/source/chara/snake/sna_init.c b/components/mgs/mgs/source/chara/snake/sna_init.c new file mode 100644 index 00000000..853e7e85 --- /dev/null +++ b/components/mgs/mgs/source/chara/snake/sna_init.c @@ -0,0 +1,8648 @@ +#include "sna_init.h" + +#include +#include +#include +#include + +#include "common.h" +#include "chara/snake/afterse.h" +#include "chara/snake/shadow.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "linkvar.h" +#include "okajima/blood.h" +#include "bullet/bakudan.h" +#include "game/vibrate.h" +#include "anime/animconv/anime.h" +#include "equip/equip.h" +#include "okajima/d_blood.h" +#include "takabe/goggle.h" +#include "takabe/goggleir.h" +#include "weapon/weapon.h" +#include "sound/g_sound.h" +#include "strcode.h" + +SnaInitWork *SECTION(".sbss") sna_800ABBA0; +int SECTION(".sbss") gSnaMoveDir_800ABBA4; +int SECTION(".sbss") dword_800ABBA8; +SVECTOR *SECTION(".sbss") dword_800ABBAC; +void *SECTION(".sbss") dword_800ABBB0; +char *SECTION(".sbss") dword_800ABBB4; +HZD_FLR *SECTION(".sbss") flr_800ABBB8[2]; +int SECTION(".sbss") dword_800ABBC0; +int SECTION(".sbss") dword_800ABBC4; +SVECTOR *SECTION(".sbss") pVec_800ABBC8; +SVECTOR *SECTION(".sbss") pVec_800ABBCC; +int SECTION(".sbss") dword_800ABBD0; +short SECTION(".sbss") dword_800ABBD4; +void *SECTION(".sbss") GM_BombSeg; +short SECTION(".sbss") dword_800ABBDC; + +extern GM_CameraSystemWork GM_Camera; +extern unsigned short GM_WeaponTypes[]; +extern unsigned short GM_ItemTypes[]; +extern void *dword_8009EEA4[]; +extern int bakudan_count_8009F42C; +extern int counter_8009F448; +extern int tabako_dword_8009F2C0; +extern GM_SnakeCameraWork GM_SnakeCamera; +extern CONTROL *tenage_ctrls_800BDD30[16]; +extern HITTABLE GM_C4Datas[C4_COUNT]; +extern HITTABLE GM_ClayDatas[8]; +extern unsigned char gBulNames_800BDC78[64]; +extern unsigned char gBulNames_800BDC78[64]; +extern int dword_8009F440; +extern int dword_8009F444; +extern int dword_8009F46C[]; +extern int dword_8009F470; +extern int dword_8009F474; +extern TARGET *target_800BDF00; +extern int dword_800BDD28; +extern int tenage_ctrls_count_800BDD70; +extern int dword_8009F434; +extern void *dword_8009EEB0[]; +extern void *dword_8009EEB8[]; +extern short HzdHeights_8009EEC4[]; +extern char dword_8009EF1C[]; +extern char dword_8009EF20[]; +extern TSnakeEquipFuncion gSnakeEquips_8009EF8C[]; +extern char dword_8009EEE0[]; +extern char dword_8009EEE4[]; +extern char dword_8009EEF0[]; +extern char dword_8009EEF4[]; +extern char dword_8009EED4[]; +extern char dword_8009EED8[]; +extern short snake_weapon_idx_800BDCBA; +extern short snake_mag_size_800BDCB8; +extern short snake_weapon_max_ammo_800BDCBC; + +#define TARGET_FLAG ( TARGET_CAPTURE | TARGET_POWER | TARGET_PUSH | TARGET_SEEK | TARGET_TOUCH ) +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE | DG_FLAG_GBOUND | DG_FLAG_AMBIENT | DG_FLAG_IRTEXTURE ) + +#define SEGMENT_ATR ( HZD_SEG_NO_COLLIDE ) + +/* +actions are numbers which are an index to a motion in an oar file +*/ + +//correct names +#define stand2_socom 0 +#define stand2_famas 1 +#define stand1_fig 2 +#define stand_p 3 +#define set_up_famas 5 +#define stand2_fig 6 +#define squat_socom 15 +#define squat_fig 16 +#define crouch_socom 22 +#define caution1 25 //caution is when leaned against a wall +#define caution_fig 26 +#define squat_caution 31 +#define caution_punch_r 40 +#define caution_punch_l 41 +#define cau_punch_r_fig 88 +#define cau_punch_l_fig 89 +#define stand_c4 90 +#define squat_caution_fig 129 + +//guessed names +#define run_socom 10 +#define run_fig 12 +#define crouch_trans 20 +#define squat_trans 21 +#define caution_c4 43 +#define squat_caution_c4 53 +#define stand_nik 76 +#define squat_nik 78 +#define stand_trans 84 +#define crouch_nik 85 +#define squat_caution_nik 87 +#define stand_clay 95 +#define squat_clay 99 +#define crouch_clay 100 +#define squat_caution_clay 102 +//TODO: need to finish this off + +ACTMOVE NoneMove = {12u, 82u, 19u, 23u, 24u, 29u, 30u, 12u, 0u, 0u}; +ACTMOVE SocomMove = {10u, 8u, 17u, 23u, 24u, 27u, 28u, 80u, 106u, 107u}; +ACTMOVE FamasMove = {11u, 9u, 18u, 23u, 24u, 27u, 28u, 73u, 110u, 111u}; +ACTMOVE NikitaMove = {75u, 77u, 79u, 23u, 24u, 27u, 28u, 75u, 0u, 0u}; +ACTMOVE C4Move = {92u, 91u, 93u, 23u, 24u, 29u, 30u, 92u, 0u, 0u}; +ACTMOVE ClaymoreMove = {97u, 96u, 98u, 101u, 24u, 29u, 30u, 97u, 0u, 0u}; +ACTMOVE GrenadeMove = {104u, 103u, 19u, 23u, 24u, 29u, 30u, 104u, 0u, 0u}; + +ACTDAMAGE NoneDamage = {63u, 65u, 67u, 68u, 69u, 70u, 71u, 72u, 66u, 123u, 130u, 131u}; +ACTDAMAGE SocomDamage = {61u, 64u, 67u, 68u, 69u, 70u, 71u, 72u, 66u, 123u, 130u, 131u}; +ACTDAMAGE FamasDamage = {62u, 83u, 67u, 68u, 69u, 70u, 71u, 72u, 66u, 123u, 130u, 131u}; + +ACTATTACK NoneAttack = {136u, 136u, 35u, 136u, 36u, 37u, 38u, 39u, 0u}; +ACTATTACK SocomAttack = {42u, 51u, 35u, 45u, 47u, 0u, 0u, 0u, 0u}; +ACTATTACK FamasAttack = {5u, 52u, 34u, 46u, 48u, 0u, 0u, 0u, 0u}; +ACTATTACK StingerAttack = {74u, 136u, 136u, 136u, 105u, 0u, 0u, 0u, 0u}; +ACTATTACK NikitaAttack = {74u, 136u, 34u, 46u, 81u, 0u, 0u, 0u, 0u}; +ACTATTACK C4Attack = {43u, 136u, 35u, 136u, 53u, 55u, 49u, 54u, 56u}; +ACTATTACK ClaymoreAttack = {57u, 136u, 35u, 136u, 58u, 59u, 0u, 0u, 0u}; +ACTATTACK GrenadeAttack = {60u, 136u, 35u, 136u, 50u, 0u, 0u, 0u, 0u}; +ACTATTACK PSG1Attack = {44u, 136u, 136u, 136u, 136u, 0u, 0u, 0u, 0u}; + +//u_char[] special +Sna_E6 e6_8009ED48[] = { + {115u, 133u, 0u, 0u}, // none + {112u, 133u, 0u, 0u}, // socom + {113u, 133u, 0u, 0u}, // famas + {136u, 136u, 0u, 0u}, // grenade + {114u, 132u, 0u, 0u}, // c4 + {116u, 133u, 0u, 0u}, // nikita + {117u, 133u, 0u, 0u}, // claymore + {118u, 133u, 0u, 0u} // stinger/psg1 +}; + +//u_char[] special2 +// Should be part of the above special array +ACTSPECIAL SECTION(".data") e7_8009ED68 = {7u, 13u, 4u, 14u, 32u, 119u, 124u, 0u}; + +ACTSTILL NoneStill = { stand1_fig, stand2_fig, squat_fig, crouch_socom, caution_fig, squat_caution_fig, cau_punch_r_fig, cau_punch_l_fig }; +ACTSTILL SocomStill = { stand2_socom, stand2_socom, squat_socom, crouch_socom, caution1, squat_caution, caution_punch_r, caution_punch_l }; +ACTSTILL FamasStill = { stand2_famas, set_up_famas, squat_socom, crouch_socom, caution1, squat_caution, caution_punch_r, caution_punch_l }; +ACTSTILL GrenadeStill = { stand_p, stand_p, squat_fig, crouch_socom, caution_fig, squat_caution_fig, cau_punch_r_fig, cau_punch_l_fig }; +ACTSTILL C4Still = { stand_c4, stand_c4, squat_fig, crouch_socom, caution_c4, squat_caution_c4, cau_punch_r_fig, cau_punch_l_fig }; +ACTSTILL NikitaSill = { stand_nik, stand_nik, squat_nik, crouch_nik, caution1, squat_caution_nik, caution_punch_r, caution_punch_l }; +ACTSTILL ClaymoreStill = { stand_clay, stand_clay, squat_clay, crouch_clay, caution_fig, squat_caution_clay, cau_punch_r_fig, cau_punch_l_fig }; + +ACTTRANS AllTrans = { crouch_trans, squat_trans, stand_trans }; + +unsigned short short_8009ED6C = 980; + +ACTPACK actions_no_weapon_8009ED70 = +{ + &NoneStill, &NoneMove, &AllTrans, &NoneDamage, &NoneAttack, &e6_8009ED48[0], &e7_8009ED68 +}; + +ACTPACK weapon_actions_8009ED8C[10] = +{ + {&SocomStill, &SocomMove, &AllTrans, &SocomDamage, &SocomAttack, &e6_8009ED48[1], &e7_8009ED68}, + {&FamasStill, &FamasMove, &AllTrans, &FamasDamage, &FamasAttack, &e6_8009ED48[2], &e7_8009ED68}, + {&GrenadeStill, &GrenadeMove, &AllTrans, &NoneDamage, &GrenadeAttack, &e6_8009ED48[7], &e7_8009ED68}, + {&NikitaSill, &NikitaMove, &AllTrans, &FamasDamage, &NikitaAttack, &e6_8009ED48[4], &e7_8009ED68}, + {&FamasStill, &FamasMove, &AllTrans, &FamasDamage, &StingerAttack, &e6_8009ED48[3], &e7_8009ED68}, + {&ClaymoreStill, &ClaymoreMove, &AllTrans, &NoneDamage, &ClaymoreAttack, &e6_8009ED48[6], &e7_8009ED68}, + {&C4Still, &C4Move, &AllTrans, &NoneDamage, &C4Attack, &e6_8009ED48[5], &e7_8009ED68}, + {&GrenadeStill, &GrenadeMove, &AllTrans, &NoneDamage, &GrenadeAttack, &e6_8009ED48[7], &e7_8009ED68}, + {&GrenadeStill, &GrenadeMove, &AllTrans, &NoneDamage, &GrenadeAttack, &e6_8009ED48[7], &e7_8009ED68}, + {&NoneStill, NULL, &AllTrans, &NoneDamage, &PSG1Attack, &e6_8009ED48[3], &e7_8009ED68} +}; + +void *dword_8009EEA4[] = { + sna_anim_idle_8005275C, + sna_anim_crouch_800527DC, + sna_anim_prone_idle_800528BC +}; + +void *SECTION(".data") dword_8009EEB0[] = { + sna_anim_wall_idle_and_c4_80052A5C, + sna_anim_wall_crouch_80052CCC +}; + +void *dword_8009EEB8[] = { + sna_anim_run_8005292C, + sna_anim_run_8005292C, + sna_anim_prone_move_800529C0 +}; + +short SECTION(".data") HzdHeights_8009EEC4[] = {750, 250, 250, 0}; + +short SECTION(".data") dword_8009EECC[] = {1100, 496, 136, 0}; + +char SECTION(".data") dword_8009EED4[] = {127, 4, 0, 0}; +char SECTION(".data") dword_8009EED8[] = {225, 1, 255, 2, 0, 0, 0, 0}; +char SECTION(".data") dword_8009EEE0[] = {127, 6, 0, 0}; +char SECTION(".data") dword_8009EEE4[] = {225, 1, 255, 4, 225, 2, 75, 8, 0, 0, 0, 0}; +char SECTION(".data") dword_8009EEF0[] = {127, 1, 0, 0}; +char SECTION(".data") dword_8009EEF4[] = {155, 3, 0, 0}; +char SECTION(".data") dword_8009EEF8[] = {0, 2, 127, 2, 0, 0, 0, 0}; +char SECTION(".data") dword_8009EF00[] = {140, 4, 80, 6, 0, 0, 0, 0}; +char SECTION(".data") dword_8009EF08[] = {0, 10, 127, 2, 0, 0, 0, 0}; +char SECTION(".data") dword_8009EF10[] = {80, 2, 50, 8, 140, 4, 80, 6, 0, 0, 0, 0}; +char SECTION(".data") dword_8009EF1C[] = {196, 4, 0, 0}; +char SECTION(".data") dword_8009EF20[] = {148, 4, 0, 0}; +char SECTION(".data") dword_8009EF24[] = {0, 2, 127, 2, 0, 0, 0, 0}; +char SECTION(".data") dword_8009EF2C[] = {60, 2, 200, 4, 40, 2, 0, 0}; + +typedef void *(*WeaponCreateFn)(CONTROL *, OBJECT *, int, u_long *, int); +typedef void (*WeaponStateFn)(SnaInitWork *, int); + +typedef struct WeaponCreateEntry +{ + WeaponCreateFn mCreateActorFn; + WeaponStateFn mStateFn; +} WeaponCreateEntry; + +WeaponCreateEntry gSnakeWeapons_8009EF3C[] = { + {NULL, sna_anim_chokethrow_begin1_80054210}, + {NewSOCOM, sna_gun_800540D0}, + {NewFAMAS, sna_gun_800540D0}, + {NewGrenade, sna_anim_grenade_80058470}, + {NewRCM, sna_gun_800540D0}, + {NewAAM, sna_anim_stinger_800570C0}, + {NewMine, sna_anim_claymore_80057474}, + {NewBomb, sna_bomb_800541A8}, + {NewStanGrenade, sna_anim_grenade_80058470}, + {NewChaffGrenade, sna_anim_grenade_80058470}, + {NewRifle, sna_anim_psg1_80056DDC}}; + +TSnakeEquipFuncion gSnakeEquips_8009EF8C[] = { + NewTabako, // 0 + NewScope, // 1 + NewBox, // 2 + NewBox, // 3 + NewBox, // 4 + NewGoggle, // 5 + NewGoggleIr, // 6 + NewGasMask, // 7 + NewBodyArmor, // 8 + NewKetchap, // 9 + NewKogaku2, // 10 + NewBandana, // 11 + NewJpegcam, // 12 +}; + +short word_8009EFC0[] = {0, 500, 0, 320, 400, 320, 400, 32, 32, 0}; + +SVECTOR stru_8009EFD4[2] = {{-100, 200, 200, 0}, {-150, 18, 200, 0}}; + +Target_Data stru_8009EFE4[4] = +{ + {{0, 0, 300, 0}, {400, 600, 400, 0}, 0, 0, 64, 0}, // TODO: Might not be part of this array, might be its own element + {{-400, 0, 600, 0}, {400, 600, 400, 0}, 0, 0, 36, 5}, + {{400, 0, 600, 0}, {400, 600, 400, 0}, 0, 0, 36, 5}, + {{0, 0, 0, 0}, {501, 600, 501, 0}, 0, 0, 7, 0} +}; + +typedef struct _PUNCH +{ + SVECTOR field_0; + SVECTOR size; + SVECTOR field_10; + int life; + int faint; +} PUNCH; + +PUNCH punch_info[] = +{ + {{-200, 200, 600}, {400, 200, 400}, {5, 0, 50}, 0, 1}, + {{200, 200, 600}, {400, 200, 400}, {-5, 0, 50}, 0, 1}, + {{0, 200, 600}, {500, 250, 500}, {0, 0, 100}, 0, 3} +}; + +GV_PAD GV_PadData_8009F0C4 = {0, 0, 0, 0, -1, 0, 0, 0, 0, 0}; + +SVECTOR svector_800AB7CC = {150, 0, 50, 0}; +SVECTOR svector_800AB7D4 = {0, 0, 100, 0}; +SVECTOR svector_800AB7DC = {-1024, 0, 0, 0}; +SVECTOR svector_800AB7E4 = {5, 500, 80, 0}; +SVECTOR svector_800AB7EC = {0, 0, 800, 0}; +SVECTOR svector_800AB7F4 = {0, 100, -300, 0}; +SVECTOR svector_800AB7FC = {0, 0, -300, 0}; + +#define RIFLE_TEBURE_TIME 90 // delay before camera shake +#define TEBURE 122 +#define GetAction( work ) (work->body.action) +#define EndMotion( work ) (work->body.is_end != 0) +#define SET work->actpack->attack->aim +#define DispEmpty( work ) (work->field_9A0 = 4) + +void sub_8004EB74(SnaInitWork *work) +{ + if (GM_Camera.first_person == 0) + { + if (work->field_9B0_pad_ptr->dir == (short)-1) + { + work->field_A20 = -6; + } + else + { + work->field_A20 = -3; + } + } + + GM_Camera.first_person = 1; + work->field_A56 = 0; + GM_SetPlayerStatusFlag(PLAYER_WATCH); + + if (GM_UnkFlagBE == 0) + { + GM_SetPlayerStatusFlag(PLAYER_MENU_DISABLE); + } + + sd_set_cli(0x01ffff20, SD_ASYNC); +} + +void sna_8004EC00(SnaInitWork *work) +{ + if (GM_Camera.first_person == 1) + { + work->field_A20 = 6; + } + GM_Camera.first_person = 0; + work->field_A56 = 0; + + GM_ClearPlayerStatusFlag(PLAYER_NORMAL_WATCH | PLAYER_WATCH); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK16); + + if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + sd_set_cli(0x01ffff21, SD_ASYNC); + } + + sna_clear_flags2_8004E344(work, (SNA_FLAG2_UNK5 | SNA_FLAG2_UNK6)); + GM_ClearPlayerStatusFlag(PLAYER_STILL_WATCH); +} + +void sna_8004EC8C(SnaInitWork *work) +{ + u_short v2; // $v1 + + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK12); + work->field_A28 = 460; + GM_Camera.first_person = 1; + v2 = short_8009ED6C; + work->field_A20 = -6; + work->field_9D0[2] = 320; + work->field_9D0[4] = v2; + work->field_9D0[6] = v2; + sd_set_cli(0x01ffff20, SD_ASYNC); + sna_set_flags2_8004E330(work, SNA_FLAG2_UNK5); + GM_ClearPlayerStatusFlag(PLAYER_WATCH); +} + +void sub_8004ED08(SnaInitWork *work) +{ + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK12); + work->field_A28 = 0x1c2; + GM_Camera.first_person = 0; // weapon related? + work->field_A20 = 6; + sna_8004EB14(work); + sd_set_cli(0x01ffff21, SD_ASYNC); + sna_clear_flags2_8004E344(work, (SNA_FLAG2_UNK5 | SNA_FLAG2_UNK6)); +} + +void sna_act_helper2_helper3_8004ED6C(SnaInitWork *work) +{ + if (work->field_A20 != 0) + { + if (work->field_A20 >= 1) + { + + if ((GM_Camera.first_person == 0) && (--work->field_A20 == 0)) + { + DG_VisibleObjs(work->body.objs); + + if (!GM_CheckPlayerStatusFlag(PLAYER_ACT_ONLY)) + { + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE); + } + } + } + else if ((GM_Camera.first_person != 0) && (++work->field_A20 == 0)) + { + DG_InvisibleObjs(work->body.objs); + } + } +} + +void sna_8004EE28(SnaInitWork *snake) +{ + MATRIX mat1; + MATRIX mat2; + SVECTOR vec; + int vx; + MATRIX *world = &snake->body.objs->objs[6].world; + + ReadRotMatrix(&mat1); + DG_TransposeMatrix(&mat1, &mat2); + MulMatrix0(&mat2, world, &mat2); + DG_MatrixRotZYX(&mat2, &vec); + SetRotMatrix(&mat1); + + vx = (vec.vx / 16) * 16; + GM_SnakeCamera.rotate2.vx = vx; +} + +void sub_8004EEB0(SnaInitWork *work) +{ + short sVar1; + int iVar2; + int arg0; + + if (gSnaMoveDir_800ABBA4 >= 0) + { + arg0 = work->control.turn.vy; + if (GV_DiffDirS(arg0, gSnaMoveDir_800ABBA4)) + { + iVar2 = GV_NearPhase(arg0, gSnaMoveDir_800ABBA4); + sVar1 = GV_NearSpeed(iVar2, gSnaMoveDir_800ABBA4, 0x40); + work->control.turn.vy = sVar1; + } + } +} + +void sna_8004EF14(SnaInitWork *work) +{ + int actionFlag; + int diff; + int y; + + y = work->control.turn.vy; + + diff = GV_DiffDirS(y, gSnaMoveDir_800ABBA4); + if (diff == 0) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + y = work->actpack->still->setup; + SetAction_8004E22C(work, y, 4); + } + else + { + short sVar2; + int iVar4; + + if (diff < 0) + { + actionFlag = work->actpack->move->aim_stance_l; + } + else + { + actionFlag = work->actpack->move->aim_stance_r; + } + + SetAction_8004E22C(work, actionFlag, 4); + iVar4 = GV_NearPhase(y, gSnaMoveDir_800ABBA4); + sVar2 = GV_NearSpeed(iVar4, gSnaMoveDir_800ABBA4, 0x40); + work->control.turn.vy = sVar2; + } +} + +int sub_8004EFE4(SnaInitWork *work, int set) +{ + MOTION_INFO *info; + int action; + + if (set == 1) + { + info = &work->m_ctrl.info1; + } + else + { + info = &work->m_ctrl.info2; + } + + if (set == 1) + { + action = GetAction(work); + } + else + { + action = work->body.action2; + } + + if (info->frames_left + info->frame == 1) + { + return action; + } + + return -1; +} + +void sna_8004F034(SnaInitWork *work, unsigned int bits) +{ + if ( bits != work->m_ctrl.info1.mask ) + { + work->m_ctrl.info2.mask = ~bits; + work->m_ctrl.info1.mask = bits; + if ( bits == 0xFFFF && sub_8004EFE4(work, 2) >= 0 ) + { + work->m_ctrl.interp = 4; + work->m_ctrl.info2.field_14 = 2; + } + } +} + +void sna_act_helper2_helper4_8004F090(SnaInitWork *work, int param_2) +{ + int iVar1; + MATRIX mtx; + + DG_SetPos(&work->body.objs->objs[6].world); + DG_MovePos(&svector_800AB7D4); + DG_RotatePos(&svector_800AB7DC); + ReadRotMatrix(&mtx); + + iVar1 = -1; + switch (param_2) + { + case 3: + iVar1 = 0; + break; + case 1: + case 4: + iVar1 = 1; + break; + case 2: + iVar1 = 2; + break; + } + + if (iVar1 >= 0) + { + NewBlood(&mtx, iVar1); + } +} + +void sub_8004F14C(SnaInitWork *work) +{ + work->field_91C_weapon_idx = WP_None; + GM_CurrentWeaponId = WP_None; + + if (work->field_908_weapon_actor != 0) + { + GV_DestroyActorQuick(work->field_908_weapon_actor); + } + + work->field_908_weapon_actor = 0; + work->field_920_tbl = GM_WeaponTypes[0]; + work->field_918_n_bullets = 0; + work->actpack = &actions_no_weapon_8009ED70; + work->field_90C_pWeaponFn = &sna_anim_chokethrow_begin1_80054210; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) != 0) + { + work->field_A56 = 0; + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_STILL_WATCH | + PLAYER_NORMAL_WATCH | PLAYER_WATCH); + sna_clear_flags2_8004E344(work, SNA_FLAG2_UNK6); + } + else + { + sna_8004EC00(work); + } +} + +void sub_8004F204(SnaInitWork *work) +{ + if (work->field_9A4_item_actor != 0) + { + GV_DestroyActorQuick(work->field_9A4_item_actor); + } + + work->field_9A4_item_actor = 0; + work->field_9A8_current_item = IT_None; + GM_CurrentItemId = IT_None; + work->field_9AC = GM_ItemTypes[0]; + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) != 0) + { + work->field_A56 = 0; + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_STILL_WATCH | + PLAYER_NORMAL_WATCH | PLAYER_WATCH); + sna_clear_flags2_8004E344(work, SNA_FLAG2_UNK6); + } + else + { + sna_8004EC00(work); + } + + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_CB_BOX); +} + +void sna_set_invuln_8004F2A0(SnaInitWork *work, char invuln_frames) +{ + GM_SetPlayerStatusFlag(PLAYER_INVINCIBLE); + work->field_A24_invuln_frames = invuln_frames; + work->field_89C_pTarget->class &= ~0x96; +} + +void sna_clear_invuln_8004F2EC(SnaInitWork *work) +{ + GM_ClearPlayerStatusFlag(PLAYER_INVINCIBLE); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK1); + work->field_89C_pTarget->class |= TARGET_FLAG; +} + +void sub_8004F338(SnaInitWork *work) +{ + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_STILL_WATCH | + PLAYER_NORMAL_WATCH | PLAYER_WATCH); + sna_clear_flags2_8004E344(work, SNA_FLAG2_UNK6); + work->field_A56 = 0; + } + else + { + sna_8004EC00(work); + } + + sna_clear_invuln_8004F2EC(work); + work->field_A24_invuln_frames = 0; + sna_sub_8004E41C(work, 2); + GM_ConfigMotionAdjust(&work->body, 0); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK9); + work->field_9C0 = 0; + sna_8004E260(work, 0, 4, 0); + DG_InvisiblePrim(work->field_92C); + + GM_ExitBehindCamera(); + + GM_PlayerStatus &= PLAYER_SECOND_AVAILABLE | PLAYER_NOT_SIGHT | + PLAYER_GAME_OVER | PLAYER_CB_BOX | PLAYER_INTRUDE | + PLAYER_ACT_ONLY | PLAYER_SQUAT | PLAYER_GROUND; + + if ((work->field_920_tbl & 0x200) != 0) + { + GM_SetPlayerStatusFlag(PLAYER_WATCH); + } + + work->field_A28 = 0x1c2; + work->field_894_flags1 &= (SNA_FLAG1_UNK20 | SNA_FLAG1_UNK28 | SNA_FLAG1_UNK29); + work->control.skip_flag &= ~CTRL_BOTH_CHECK; + sna_clear_flags2_8004E344(work, SNA_FLAG2_UNK9); +} + +void sub_8004F454(SnaInitWork *work) +{ + int i; + + GM_ClearPlayerStatusFlag(PLAYER_MOVE | PLAYER_ATTACK | + PLAYER_CHECK_WALL); + GM_ConfigMotionAdjust(&work->body, NULL); + + for (i = 0; i < SNAKEJOINT_NUM; i++) + { + work->adjust[i] = DG_ZeroVector; + } + + sna_8004E260(work, 0, 4, 0); + sna_sub_8004E41C(work, 2); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK2 | SNA_FLAG1_UNK3 | SNA_FLAG1_UNK26); + DG_InvisiblePrim(work->field_92C); + + if (GM_CheckPlayerStatusFlag(PLAYER_CAUTION) == 0) + { + work->field_A28 = 450; + GM_ExitBehindCamera(); + } + + work->control.skip_flag &= ~CTRL_BOTH_CHECK; + sna_clear_flags2_8004E344(work, SNA_FLAG2_UNK9); +} + +int sna_8004F544(SnaInitWork *work, SVECTOR param_2, int a3, int a4, int a5) +{ + MATRIX mtx; + SVECTOR vec1; + SVECTOR vec2; + SVECTOR vec3; + + int bVar1; + int iVar2; + + vec1 = param_2; + + vec3.vy = 0; + vec3.vx = 0; + vec3.vz = a3; + + ReadRotMatrix(&mtx); + DG_SetPos2(¶m_2, &work->control.turn); + DG_PutVector(&vec3, &vec2, 1); + DG_SetPos(&mtx); + + iVar2 = sna_line_check(&vec1, work->control.map->hzd, a4, a5); + + bVar1 = a3 < iVar2; + + if (a3 < 0) + { + a3 = -a3; + bVar1 = a3 < iVar2; + } + + if (bVar1) + { + iVar2 = -1; + } + + return iVar2; +} + +int sna_8004F628(SnaInitWork *work, SVECTOR *param_2, int param_3, int param_4, int param_5, int param_6) +{ + SVECTOR vec; + + DG_SetPos2(&work->control.mov, &work->control.rot); + DG_PutVector(param_2, &vec, 1); + + if (param_6 >= 1) + { + vec.vy = work->control.levels[0] + param_6; + } + + return sna_8004F544(work, vec, param_3, param_4, param_5) >= 0; +} + +static inline int sna_update_life_helper_8004F6E8(int health, int item) +{ + int temp; + + if ((health == 0) && + (GM_GameOverTimer == 0) && + (item == IT_Ration) && + (GM_Items[IT_Ration] > 0) && + !GM_FrozenItemsState) + { + temp = (GM_DifficultyFlag > DIFFICULTY_EASY) ? 256 : 384; + + if (GM_DifficultyFlag < DIFFICULTY_EASY) + { + temp = GM_SnakeMaxHealth; + } + + health = temp; + + if (health > GM_SnakeMaxHealth) + { + health = GM_SnakeMaxHealth; + } + + GM_Items[IT_Ration] -= 1; + + if (GM_Items[IT_Ration] == 0) + { + GM_CurrentItemId = IT_None; + } + + GM_TotalRationsUsed += 1; + GM_SeSet2(0, 63, SE_RECOVER_LIFE); + } + + return health; +} + +void UpdateLife_8004F6E8(SnaInitWork *work) +{ + int health; + + if (sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK23)) + { + return; + } + + if (sna_check_flags1_8004E31C(work,SNA_FLAG1_UNK25) && + (GM_SnakeCurrentHealth == work->field_89C_pTarget->vital)) + { + return; + } + + if ((work->field_9A8_current_item == IT_BodyArmor) && (work->field_89C_pTarget->a_mode == 1)) + { + work->field_89C_pTarget->damage /= 2; + work->field_89C_pTarget->vital += work->field_89C_pTarget->damage; + work->field_89C_pTarget->damage = 0; + } + + health = work->field_89C_pTarget->vital + GM_SnakeCurrentHealth - work->field_A22_snake_current_health; + + if (health > GM_SnakeMaxHealth) + { + health = GM_SnakeMaxHealth; + } + + if (health < 0) + { + health = 0; + } + + if (work->field_89C_pTarget->weapon != WP_Max) + { + health = sna_update_life_helper_8004F6E8(health, work->field_9A8_current_item); + } + else + { + health = 0; + } + + work->field_89C_pTarget->vital = health; + work->field_A22_snake_current_health = health; + GM_SnakeCurrentHealth = health; + + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK25); +} + +void sna_8004F8E4(SnaInitWork *work, int a2) +{ + GV_MSG msg; + + if ((GM_GameOverTimer == -1) || (GM_GameOverTimer == 0)) + { + msg.address = GV_StrCode("スネーク"); + msg.message_len = 6; + msg.message[0] = HASH_GO_MOTION; + msg.message[1] = a2; + msg.message[2] = -1; + msg.message[3] = -1; + msg.message[4] = 2; + msg.message[5] = 2042; + + work->field_7A4_msgs[work->field_7A0_msg_count] = msg; + work->field_7A0_msg_count++; + + GM_GameOverTimer = 0; + GM_GameOver(); + + GM_GameStatus |= STATE_PADRELEASE; + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK20); + + if (work->field_A70 >= 0) + { + GCL_ForceExecProc(work->field_A70, NULL); + } + + if (GM_CheckPlayerStatusFlag(PLAYER_CB_BOX)) + { + sub_8004F204(work); + } + } +} + +int sna_act_unk_helper4_8004FA3C(void) +{ + if (GM_SnakeCurrentHealth == 0 || GM_GameOverTimer != 0) + { + return 1; + } + return 0; +} + +void sna_8004FA74(SnaInitWork *param_1) +{ + sna_clear_flags2_8004E344(param_1, SNA_FLAG2_UNK6); + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_STILL_WATCH | + PLAYER_NORMAL_WATCH); +} + +void sub_8004FA9C(SnaInitWork *snake) +{ + u_short action = snake->field_9B0_pad_ptr->status; + sna_80051FD0(snake); + if (action & PAD_DOWN) + { + sna_set_flags2_8004E330(snake, SNA_FLAG2_UNK1); + } +} + +void sub_8004FAE8(SnaInitWork *snake) +{ + SVECTOR vec; + + DG_MatrixRotYXZ(&snake->body.objs->objs[6].world, &vec); + GM_SnakeCamera.rotate2 = vec; +} + +int sna_current_item_8004FB38(void) +{ + return sna_800ABBA0->field_9A8_current_item; +} + +int sna_ration_available_8004FB4C(void) +{ + if ((sna_800ABBA0->field_9A8_current_item == IT_Ration) && + (GM_Items[IT_Ration] > 0) && + (GM_FrozenItemsState == 0)) + { + return 1; + } + + return 0; +} + +HZD_FLR ** sub_8004FB90(void) +{ + return flr_800ABBB8; +} + +int GM_Next_BulName_8004FBA0() +{ + int i; // $a0 + for (i = 1; i < 64; i++) // for some reason skip the first one ?? + { + if (!gBulNames_800BDC78[i]) + { + gBulNames_800BDC78[i] = 1; + return i; + } + } + return 0; +} + +void GM_ClearBulName(int idx) +{ + gBulNames_800BDC78[idx] = 0; +} + +void GM_CheckShukanReverse(unsigned short *pInput) +{ + unsigned short old; + unsigned int v2; // $v1 + unsigned int v3; // $v0 + + if ((GM_OptionFlag & OPTION_SHUKAN_REVERSE) != 0 + && (GM_GameStatus & STATE_PADDEMO) == 0) + { + old = *pInput; + + *pInput = old & ~0x5000; + + v2 = old & 0x5000; + v3 = v2 & 0x1000; + + if (v3) + { + *pInput |= 0x4000; + } + + if (v2 & 0x4000) + { + *pInput |= 0x1000; + } + } +} + +void GM_CheckShukanReverseAnalog(unsigned char *pInput) +{ + if ((GM_OptionFlag & OPTION_SHUKAN_REVERSE) != 0 + && (GM_GameStatus & STATE_PADDEMO) == 0) + { + *pInput = ~*pInput; + } +} + +int sub_8004FCB8(SnaInitWork *work, PUNCH *punch, int a_mode) +{ + TARGET *target; + SVECTOR vec; + SVECTOR line[2]; + + target = &work->field_8A0_target; + GM_SetTarget(target, 4, PLAYER_SIDE, &punch->size); + DG_RotVector(&punch->field_10, &vec, 1); + GM_SetPowerTarget(target, POWER_CONST, a_mode, punch->life, punch->faint, &vec); + DG_PutVector(&punch->field_0, &vec, 1); + + line[0].vx = work->body.objs->objs[5].world.t[0]; + line[0].vy = work->body.objs->objs[5].world.t[1]; + line[0].vz = work->body.objs->objs[5].world.t[2]; + line[1] = vec; + + if ( sna_line_check(line, work->control.map->hzd, HZD_CHK_ALL, SEGMENT_ATR) < 0 ) + { + GM_MoveTarget(target, &vec); + return GM_PowerTarget(target); + } + + return 0; +} + +int sna_8004FDE8(SnaInitWork *work, Target_Data *pTargetData) +{ + int flags = 0x2; + TARGET *pTarget; + SVECTOR vec; + SVECTOR vecs[2]; + + if (pTargetData->field_18 == 0x40) + { + flags = 0x40; + } + + GM_SetTarget(&work->field_8A0_target, flags, PLAYER_SIDE, &pTargetData->field_8_size); + GM_SetCaptureTarget(&work->field_8A0_target, pTargetData->field_18, pTargetData->field_1C, &work->field_8F4, &work->field_8FC); + DG_PutVector(&pTargetData->field_0, &vec, 1); + GM_MoveTarget(&work->field_8A0_target, &vec); + + work->field_8E8_pTarget = NULL; + + if (flags == 2) + { + pTarget = GM_CaptureTarget(&work->field_8A0_target); + } + else + { + pTarget = GM_C4Target(&work->field_8A0_target); + } + + if (pTarget) + { + if (pTarget->class & 0x20) + { + pTarget->damaged &= ~flags; + pTarget->faint += pTargetData->field_1C; + return 0; + } + + vecs[0] = work->control.mov; + vecs[1] = pTarget->center; + + if (sna_line_check(vecs, work->control.map->hzd, HZD_CHK_ALL, SEGMENT_ATR) < 0) + { + work->field_8E8_pTarget = pTarget; + return 1; + } + + pTarget->damaged &= ~flags; + pTarget->faint += pTargetData->field_1C; + } + + return 0; +} + +int sna_act_helper2_helper5_8004FF88(SnaInitWork *work) +{ + void *pAnim; + + if (!(GM_GameStatus & (STATE_PADRELEASE | STATE_PADDEMO | STATE_DEMO)) && (GM_AlertMode != ALERT_ACTIVE)) + { + if (!GM_CheckPlayerStatusFlag(0x20001304) && + !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK9) && + (GM_SnakeCurrentHealth != 0) && + (GM_GameOverTimer == 0) && + (GM_StreamStatus() == -1) && + (work->body.action2 == 0)) + { + pAnim = work->field_9B8_fn_anim; + + if ((pAnim == sna_anim_idle_8005275C) || + (pAnim == sna_anim_crouch_800527DC) || + (pAnim == sna_anim_prone_idle_800528BC) || + (pAnim == sna_anim_wall_idle_and_c4_80052A5C) || + (pAnim == sna_anim_wall_crouch_80052CCC) || + (pAnim == sna_anim_duct_move_80054424)) + { + return 1; + } + } + } + + return 0; +} + +int sna_8005009C(SnaInitWork *work) +{ + GV_PAD *pPad = work->field_9B0_pad_ptr; + int tbl = work->field_920_tbl; + unsigned short press = pPad->press; + unsigned short input = press; + void *pAnimFn; + + if (!(tbl & 0x100)) + { + input = pPad->status; + } + + if (input & PAD_SQUARE) + { + work->field_910 = 0; + + if (tbl & 0x10) + { + sna_start_anim_8004E1F4(work, work->field_90C_pWeaponFn); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK3); + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + return 1; + } + + if (tbl & 8) + { + if (GM_CheckPlayerStatusFlag(PLAYER_MOVE)) + { + pAnimFn = sna_anim_rungun_80056C3C; + } + else + { + pAnimFn = sna_anim_shoot_weapon_80056B88; + } + + sna_start_anim_8004E1F4(work, pAnimFn); + GM_SetPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_ITEM_DISABLE); + } + else + { + if ((input & PAD_CROSS) && (work->field_9B0_pad_ptr->dir >= 0)) + { + pAnimFn = sna_anim_rungun_80056C3C; + } + else + { + pAnimFn = sna_anim_shoot_weapon_80056B88; + } + + sna_start_anim_8004E1F4(work, pAnimFn); + } + + GM_SetPlayerStatusFlag(PLAYER_ATTACK); + return 1; + } + + if ((press & PAD_CIRCLE) && ((work->actpack->attack->punch ^ 0x88) != 0)) + { + sna_start_anim_8004E1F4(work, sna_anim_punch_80058C10); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK3); + return 1; + } + + return 0; +} + +void sna_check_knock_800501F8(SnaInitWork *work, int time) +{ + void *func; + + if (time >= 8) + { + if (work->field_91C_weapon_idx != WP_C4 || GM_CheckPlayerStatusFlag(PLAYER_MOVE) != 0) + { + if ((work->field_9B0_pad_ptr->press & PAD_CIRCLE) != 0) + { + func = sna_knock_80054D68; + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK9); + work->field_9C0 = func; + work->field_9C4 = 0; + } + } + } +} + +void sna_8005027C(SnaInitWork *work, int time) +{ + SVECTOR *pVec; + + if (work->field_91C_weapon_idx != WP_C4) + { + return; + } + + if (!GM_CheckPlayerStatusFlag(PLAYER_MOVE)) + { + work->control.turn.vy = dword_800ABBD0; + } + + if ((time < 8) || !(work->field_9B0_pad_ptr->press & PAD_SQUARE) || (work->field_9C0 == sna_80057378)) + { + return; + } + + if ((*work->field_918_n_bullets != 0) && (bakudan_count_8009F42C < 16)) + { + pVec = &stru_8009EFD4[0]; + + if (work->field_A26_stance != SNA_STANCE_STANDING) + { + pVec = &stru_8009EFD4[1]; + } + + if (sna_8004F628(work, pVec, 300, HZD_CHK_D_SEGMENT | HZD_CHK_F_SEGMENT, SEGMENT_ATR | HZD_SEG_NO_BEHIND | HZD_SEG_NO_HARITSUKI, -1) > 0) + { + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK9); + work->field_9C0 = sna_80057378; + work->field_9C4 = 0; + return; + } + } + + GM_SeSet2(0, 63, SE_BUZZER); +} + +int sna_prone_check_standup_80050398(SnaInitWork *work) +{ + if ((work->field_9B0_pad_ptr->press & PAD_CROSS) != 0 && !sna_8004E808(work, 1, 0, -622, 1500)) + { + work->field_A28 = 450; + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_prone_standup_80053D74); + SetAction_8004E22C(work, work->actpack->trans->stand, 4); + work->control.turn.vy = work->control.rot.vy; + return 1; + } + + return 0; +} + +void sna_80050440(SnaInitWork *work) +{ + CONTROL *pCtrl; + unsigned short *pArr; + int inDuct; + int i; + + // TODO: There is probably a better way to match this... + SnaInitWork *work2; + int a; + + if ((GM_GameOverTimer == 0) && (GM_SnakeCurrentHealth != 0)) + { + pCtrl = &work->control; + pArr = pCtrl->evt.inside; + + inDuct = GM_CheckPlayerStatusFlag(PLAYER_INTRUDE); + GM_ClearPlayerStatusFlag(PLAYER_INTRUDE); + + for (i = pCtrl->evt.n_inside; i > 0; pArr++, i--) + { + if (*pArr == 0x73D2) + { + GM_SetPlayerStatusFlag(PLAYER_INTRUDE); + break; + } + } + + if (!inDuct) + { + if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + return; + } + + sna_8004EC8C(work); + // TODO: There is probably a better way to match this... + work2 = work; + a = 4; + dword_800ABA1C = 0x100; + } + else + { + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + return; + } + + sub_8004ED08(work); + // TODO: There is probably a better way to match this... + work2 = work; + a = 4; + dword_800ABA1C = 0; + } + + sna_set_flags1_8004E2F4(work2, a); + sna_set_flags2_8004E330(work, 0x100); + + work->control.turn.vy = work->control.rot.vy; + } +} + +void sna_80050568(SnaInitWork *work) +{ + SVECTOR diff; + DG_OBJS *objs; + short dir; + + sna_80050440(work); + + if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + return; + } + + objs = work->body.objs; + work->control.turn.vz = 0; + work->control.turn.vx = 0; + work->control.step.vz = 0; + work->control.step.vx = 0; + + DG_InvisibleObjs(objs); + + sna_start_anim_8004E1F4(work, sna_anim_duct_move_80054424); + work->field_A54.prone_bool_thing = 0; + sna_set_invuln_8004F2A0(work, 24); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK1); + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_DOWNED | PLAYER_DAMAGED); + sna_clear_flags1_8004E308(work, (SNA_FLAG1_UNK2 | SNA_FLAG1_UNK3 | SNA_FLAG1_UNK5)); + SetAction_8004E22C(work, work->actpack->still->crouch, 0); + + objs = work->body.objs; + diff.vx = objs->objs[6].world.t[0] - objs->objs[0].world.t[0]; + + objs = work->body.objs; + diff.vz = objs->objs[6].world.t[2] - objs->objs[0].world.t[2]; + + dir = GV_VecDir2(&diff); + work->control.turn.vy = dir; + work->control.rot.vy = dir; + + work->control.skip_flag &= ~CTRL_BOTH_CHECK; +} + +void sna_knockdown_getup_80050668(SnaInitWork *work) +{ + SVECTOR vec; + int var_a2; + int var_a3; + int dir; + DG_OBJS *pObjs; + + var_a2 = 500; + + if (work->field_A54.knockdown_thing == 1) + { + var_a2 = 2; + var_a3 = -630; + } + else + { + var_a3 = -18; + } + + if (!sna_8004E808(work, 1, var_a2, var_a3, 1500)) + { + sna_set_invuln_8004F2A0(work, 0); + sna_start_anim_8004E1F4(work, &sna_anim_knockdown_getup_80054A10); + } + else + { + work->field_A54.knockdown_thing = 0; + + sna_set_invuln_8004F2A0(work, 24); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK1); + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_DOWNED | PLAYER_DAMAGED); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK5 | SNA_FLAG1_UNK3 | SNA_FLAG1_UNK2); + + DG_VisibleObjs(work->body.objs); + + if (GetAction(work) == 0x45) + { + SetAction_8004E22C(work, work->actpack->still->crouch, 4); + + // TODO: Duplication is probably from an inline + pObjs = work->body.objs; + vec.vx = pObjs->objs[6].world.t[0] - pObjs->objs[0].world.t[0]; + + pObjs = work->body.objs; + vec.vz = pObjs->objs[6].world.t[2] - pObjs->objs[0].world.t[2]; + + dir = GV_VecDir2(&vec); + work->control.turn.vy = dir; + work->control.rot.vy = dir; + + sna_start_anim_8004E1F4(work, &sna_anim_prone_idle_800528BC); + } + else + { + GM_SetPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_DOWNED); + sna_set_invuln_8004F2A0(work, 0); + sna_set_flags1_8004E2F4(work, 4); + sna_start_anim_8004E1F4(work, &sna_8005425C); + } + } +} + +int sub_800507D8(SnaInitWork *work) +{ + if (dword_800ABBC4 == 0 && work->field_A38_local_data == 0) + { + work->field_A28 = 450; + GM_ClearPlayerStatusFlag(PLAYER_CHECK_WALL | PLAYER_CAUTION | PLAYER_MOVE); + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK9); + work->field_9C0 = NULL; + sna_8004E260(work, 0, 4, 0); + GM_ExitBehindCamera(); + return 1; + } + + return 0; +} + +void CheckMessage0_80050878(SnaInitWork *work) +{ + int ret; + int length; + GV_MSG *pEntry; + GV_MSG *pMsg; + unsigned short id; + + if (GM_CheckPlayerStatusFlag(PLAYER_TIMERBOMB_THROWN) && + (GM_SnakeCurrentHealth != 0) && + (GM_GameOverTimer == 0) && + !GM_CheckPlayerStatusFlag(PLAYER_ACT_ONLY)) + { + sna_start_anim_8004E1F4(work, &sna_act_helper3_helper_80056650); + sna_set_invuln_8004F2A0(work, 0); + sna_act_unk2_80051170(work->field_89C_pTarget); + GM_ClearPlayerStatusFlag(PLAYER_TIMERBOMB_THROWN); + GM_SetPlayerStatusFlag(PLAYER_ACT_ONLY); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK28); + } + + if (sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK20)) + { + return; + } + + ret = GV_ReceiveMessage(work->control.name, &pMsg); + + if (ret == 0) + { + return; + } + + length = ret; + + pEntry = &work->field_7A4_msgs[work->field_7A0_msg_count]; + pMsg = (pMsg + length) - 1; + + for (; length > 0; work->field_7A0_msg_count++, pMsg--, pEntry++, length--) + { + if (work->field_7A0_msg_count == 8) + { + break; + } + + id = pMsg->message[0]; + + if ((id == HASH_MOTION) || (id == HASH_RUN_MOVE) || (id == HASH_STANCE) || (id == HASH_GO_MOTION)) + { + if (!GM_CheckPlayerStatusFlag(PLAYER_ACT_ONLY)) + { + GM_SetPlayerStatusFlag(PLAYER_ACT_ONLY); + } + } + + *pEntry = *pMsg; + } +} + +static inline void sna_act_unk_helper_80050A64(SnaInitWork *work, GV_MSG *pMsg, int var_s4) +{ + int flags; + int len; + UnkSnakeStruct2 *pStr; + + if (sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK28)) + { + return; + } + + flags = 0; + len = pMsg->message_len; + pStr = &work->field_9E4; + + pMsg->message_len = 0; + pStr->field_9E4 = pMsg->message[1]; + pStr->field_9E6 = -1; + pStr->field_9E8 = -1; + pStr->field_9EA = -1; + pStr->field_9EC_flags3 = 0; + + if (len > 2) + { + pStr->field_9E6 = pMsg->message[2]; + } + + if (len > 3) + { + pStr->field_9E8 = pMsg->message[3]; + } + + if (len > 4) + { + pStr->field_9EA = pMsg->message[4]; + } + + if (len > 5) + { + flags = pStr->field_9EC_flags3 = pMsg->message[5]; + } + + if (sna_act_unk_helper4_8004FA3C() && (var_s4 == 0)) + { + GM_ClearPlayerStatusFlag(PLAYER_ACT_ONLY); + pMsg->message_len = 0; + printf("force act cancel\n"); + } + else + { + pStr->field_9F2 = 0; + + if (var_s4 != 0) + { + pStr->field_9F2 = 1; + } + + if (!(flags & 0x80)) + { + if (!work->field_A5A) + { + return; + } + + GM_ConfigMotionControl(&work->body, &work->m_ctrl, work->field_A5A, + work->m_segs1, work->m_segs2, &work->control, + work->rots); + } + + if (flags & 4) + { + if (len <= 6) + { + return; + } + + pStr->field_9EE = work->control.mov.vy - pMsg->message[6]; + } + else + { + pStr->field_9EE = work->control.levels[0]; + } + + if (flags & 1) + { + GM_ClearPlayerStatusFlag(PLAYER_INTRUDE); + sub_8004ED08(work); + } + + if (flags & 0x10) + { + work->control.skip_flag |= CTRL_SKIP_TRAP; + } + + pStr->field_9F0 = 1; + + sna_start_anim_8004E1F4(work, &sna_anim_dying_80055524); + GM_SetPlayerStatusFlag(4); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK28); + } +} + +static inline void sna_act_unk_helper2_80050A64(SnaInitWork *work, GV_MSG *pMsg) +{ + UnkSnakeStruct2 *pStr; + + if (sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK28)) + { + return; + } + + pStr = &work->field_9E4; + + pStr->field_9E8 = -1; + pStr->field_9F4.vx = pMsg->message[1]; + pStr->field_9F4.vy = pMsg->message[2]; + pStr->field_9F4.vz = pMsg->message[3]; + pStr->field_9F4.pad = pMsg->message[4]; + pStr->field_9EC_flags3 = 0; + + if (pMsg->message_len > 5) + { + pStr->field_9EC_flags3 = pMsg->message[5]; + } + + if (sna_act_unk_helper4_8004FA3C() && !(pStr->field_9EC_flags3 & 0x400)) + { + GM_ClearPlayerStatusFlag(PLAYER_ACT_ONLY); + pMsg->message_len = 0; + printf("run move cancel\n"); + } + else + { + if (pMsg->message_len > 6) + { + pStr->field_9E8 = pMsg->message[6]; + } + + if (pStr->field_9EC_flags3 & 4) + { + work->control.skip_flag |= CTRL_SKIP_TRAP; + } + + pStr->field_9F0 = 0; + sna_start_anim_8004E1F4(work, &sna_anim_mini_cutscene_800559D8); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK28); + pMsg->message_len = 0; + } +} + +static inline void sna_act_unk_helper3_80050A64(SnaInitWork *work, GV_MSG *pMsg) +{ + UnkSnakeStruct2 *pStr; + + if (sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK28)) + { + return; + } + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + GM_ClearPlayerStatusFlag(PLAYER_ACT_ONLY); + pMsg->message_len = 0; + } + else + { + pStr = &work->field_9E4; + + work->field_9FC = work->field_9B8_fn_anim; + pStr->field_9EE = 0; + pStr->field_9E8 = -1; + pStr->field_9EA = pMsg->message[1]; + pStr->field_9EC_flags3 = 0; + + if (pMsg->message_len > 2) + { + pStr->field_9E8 = pMsg->message[2]; + } + + if (pMsg->message_len > 3) + { + pStr->field_9EC_flags3 = pMsg->message[3]; + } + + if (sna_act_unk_helper4_8004FA3C() && !(pStr->field_9EC_flags3 & 0x400)) + { + GM_ClearPlayerStatusFlag(PLAYER_ACT_ONLY); + pMsg->message_len = 0; + printf("force stance cancel\n"); + } + else + { + if (pStr->field_9EC_flags3 & 4) + { + work->control.skip_flag |= CTRL_SKIP_TRAP; + } + + sna_start_anim_8004E1F4(work, &sna_act_unk_helper3_80055DD8); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK28); + pMsg->message_len = 0; + pStr->field_9F0 = 0; + } + } +} + +void sna_act_unk_80050A64(SnaInitWork *work) +{ + int levels[2]; + GV_MSG *pMsg; + GV_MSG *pMsgIter; + int msgCount; + int count; + int state; + + if ((work->field_7A0_msg_count == 0) || sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK18)) + { + return; + } + + msgCount = 0; + pMsgIter = work->field_7A4_msgs; + pMsg = work->field_7A4_msgs; + + for (count = work->field_7A0_msg_count; count > 0; count--, pMsg++) + { + switch (pMsg->message[0]) + { + case 0xc000: + pMsg->message_len = 0; + break; + + case HASH_MOTION: + state = 0; + goto helper3; + + case HASH_GO_MOTION: + state = 1; +helper3: + sna_act_unk_helper_80050A64(work, pMsg, state); + break; + + case HASH_RUN_MOVE: + sna_act_unk_helper2_80050A64(work, pMsg); + break; + + case HASH_STANCE: + sna_act_unk_helper3_80050A64(work, pMsg); + break; + + case HASH_STOP: + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK19); + pMsg->message_len = 0; + break; + + case HASH_START: + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK19); + pMsg->message_len = 0; + break; + + case HASH_KILL: + GV_DestroyActor(&work->actor); + pMsg->message_len = 0; + break; + + case HASH_PADON: + GM_ClearPlayerStatusFlag(PLAYER_PAD_OFF); + pMsg->message_len = 0; + break; + + case HASH_PADOFF: + GM_SetPlayerStatusFlag(PLAYER_PAD_OFF); + pMsg->message_len = 0; + break; + + case HASH_TABAKO: + tabako_dword_8009F2C0 = pMsg->message[1]; + pMsg->message_len = 0; + break; + + case HASH_POSITION: + work->control.mov.vx = pMsg->message[1]; + work->control.mov.vy = pMsg->message[2]; + work->control.mov.vz = pMsg->message[3]; + work->control.step = DG_ZeroVector; + sub_8004E588(work->control.map->hzd, &work->control.mov, levels); + work->control.levels[0] = levels[0]; + work->control.levels[1] = levels[1]; + pMsg->message_len = 0; + break; + + case HASH_SLOW: + if (pMsg->message[1] == HASH_ON) + { + sna_set_flags1_8004E2F4(work, SNA_FLAG2_UNK27); + pMsg->message_len = 0; + } + else + { + sna_clear_flags1_8004E308(work, SNA_FLAG2_UNK27); + pMsg->message_len = 0; + } + + break; + + default: + pMsg->message_len = 0; + break; + } + + if (pMsg->message_len != 0) + { + *pMsgIter = *pMsg; + pMsgIter++; + msgCount++; + } + } + + work->field_7A0_msg_count = msgCount; +} + +void sna_act_unk2_80051170(TARGET *param_1) +{ + param_1->damage = 0; + param_1->faint = 0; + param_1->damaged &= ~(0x80 | 0x04); + param_1->force = DG_ZeroVector; + param_1->weapon = WP_None; + param_1->a_mode = 0; +} + +static inline int sna_weapon_switching_helper_800511BC(SnaInitWork *work) +{ + int flag; + + flag = (GM_UnkFlagBE != 0) ? 1024 : 1; + + if (sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK5)) + { + return 0; + } + + if (GM_CheckPlayerStatusFlag(PLAYER_CB_BOX | PLAYER_DOWNED | PLAYER_DAMAGED | PLAYER_ACT_ONLY)) + { + return 0; + } + + if (!(work->field_920_tbl & 0x200) && GM_CheckPlayerStatusFlag(flag)) + { + return 0; + } + + if (GM_CheckPlayerStatusFlag(PLAYER_PAD_OFF) || + (GM_GameStatus & STATE_PADRELEASE)) + { + if (GM_WeaponTypes[GM_CurrentWeaponId + 1] & 0x200) + { + return 0; + } + } + + return 1; +} + +static inline int sna_weapon_switching_helper2_800511BC(SnaInitWork *work, int callback) +{ + WeaponCreateEntry *pWeaponEntry; + WeaponCreateFn pWeaponCreateFn; + GV_ACT *pWeaponActor; + + pWeaponActor = work->field_908_weapon_actor; + + if (pWeaponActor) + { + GV_DestroyActorQuick(pWeaponActor); + } + + pWeaponEntry = &gSnakeWeapons_8009EF3C[GM_CurrentWeaponId + 1]; + pWeaponCreateFn = pWeaponEntry->mCreateActorFn; + pWeaponActor = NULL; + + if (pWeaponCreateFn) + { + pWeaponActor = pWeaponCreateFn(&work->control, &work->body, 4, &work->field_914_trigger, PLAYER_SIDE); + + if (!pWeaponActor) + { + work->field_908_weapon_actor = 0; + return -1; + } + } + + if (work->field_91C_weapon_idx == WP_Nikita) + { + GM_ClearPlayerStatusFlag(PLAYER_ITEM_DISABLE); + } + + if (work->field_90C_pWeaponFn == &sna_anim_grenade_80058470) + { + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_ITEM_DISABLE); + } + + work->field_91C_weapon_idx = GM_CurrentWeaponId; + + if (callback) + { + GM_CallSystemCallbackProc(3, GM_CurrentWeaponId); + } + + work->field_920_tbl = GM_WeaponTypes[GM_CurrentWeaponId + 1]; + + if (GM_CurrentWeaponId >= 0) + { + work->field_918_n_bullets = &GM_Weapons[GM_CurrentWeaponId]; + } + + work->field_90C_pWeaponFn = pWeaponEntry->mStateFn; + + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + work->field_908_weapon_actor = pWeaponActor; +#ifdef __psyz + /* Unarmed is weapon id -1, and on the console indexing the array with it + * landed on actions_no_weapon_8009ED70 BY MEMORY LAYOUT: the ROM placed + * that struct exactly 28 bytes (one ACTPACK) before the array. A native + * linker gives no such guarantee -- here [-1] hit a neighboring function + * pointer and ->still->stand read from code space. Make the layout trick + * explicit. */ + work->actpack = (GM_CurrentWeaponId < 0) + ? &actions_no_weapon_8009ED70 + : &weapon_actions_8009ED8C[GM_CurrentWeaponId]; +#else + work->actpack = &weapon_actions_8009ED8C[GM_CurrentWeaponId]; +#endif + + return 0; +} + +void sna_weapon_switching_800511BC(SnaInitWork *work, int callback) +{ + int temp_s1_2; + + if (GM_CurrentWeaponId != work->field_91C_weapon_idx) + { + GM_WeaponChanged = 1; + } + + if (!GM_WeaponChanged) + { + return; + } + + if (!sna_weapon_switching_helper_800511BC(work)) + { + return; + } + + if (!(work->field_920_tbl & 0x1000) && (GM_CurrentWeaponId == work->field_91C_weapon_idx)) + { + return; + } + + if (sna_weapon_switching_helper2_800511BC(work, callback) < 0) + { + return; + } + + temp_s1_2 = work->field_920_tbl; + GM_WeaponChanged = 0; + + if (temp_s1_2 & 0x200) + { + sub_8004EB74(work); + + if (GM_UnkFlagBE != 0) + { + GM_SetPlayerStatusFlag(PLAYER_STILL_WATCH); + sna_clear_flags2_8004E344(work, SNA_FLAG2_UNK6 | SNA_FLAG2_UNK5); + } + } + else if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) && ((GM_UnkFlagBE == 0) || !(work->field_898_flags2 & 0x10))) + { + sna_8004EC00(work); + } + + if (temp_s1_2 & 0x20) + { + sna_start_anim_8004E1F4(work, work->field_90C_pWeaponFn); + GM_ClearPlayerStatusFlag(PLAYER_CAUTION); + } + else if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + sna_start_anim_8004E1F4(work, &sna_anim_duct_move_80054424); + } + else if (GM_CheckPlayerStatusFlag(PLAYER_CAUTION)) + { + if (GM_CheckPlayerStatusFlag(PLAYER_MOVE)) + { + sna_start_anim_8004E1F4(work, &sna_anim_wall_move_80052BA8); + } + else + { + sna_start_anim_8004E1F4(work, dword_8009EEB0[work->field_A26_stance]); + } + } + else if (GM_CheckPlayerStatusFlag(PLAYER_MOVE)) + { + sna_start_anim_8004E1F4(work, dword_8009EEB8[work->field_A26_stance]); + } + else + { + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + } + + sub_8004F454(work); +} + +static inline int sna_helper_800515BC(SnaInitWork *work) +{ + if ( GM_CheckPlayerStatusFlag(PLAYER_ACT_ONLY) ) + { + return 0; + } + + if ( sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK6) ) + { + return 0; + } + + if ( (GM_CurrentItemId == IT_Bandana) && (work->field_90C_pWeaponFn == &sna_80057A90) ) + { + return 0; + } + + if ( (GM_CheckPlayerStatusFlag(PLAYER_PAD_OFF) || + ((GM_GameStatus & (STATE_PADRELEASE | STATE_PADDEMO)) == STATE_PADRELEASE)) && + ((GM_ItemTypes[GM_CurrentItemId + 1] & 2) != 0) ) + { + return 0; + } + + return 1; +} + +void sna_800515BC(SnaInitWork *work, int a2) +{ + int itemType; + int temp_s4; + GV_ACT *pItemActor; + TSnakeEquipFuncion *ppfnEquip; + TSnakeEquipFuncion pfnEquip; + int temp_s0_2; + int var_s2; + + if ( !sna_helper_800515BC(work) ) + { + return; + } + + if ( GM_CurrentItemId == work->field_9A8_current_item ) + { + return; + } + + itemType = GM_ItemTypes[GM_CurrentItemId + 1]; + + if ( GM_CheckPlayerStatusFlag(0x300) && ((itemType & 0x8000) != 0) ) + { + return; + } + + temp_s4 = work->field_9AC; + pItemActor = work->field_9A4_item_actor; + + if ( pItemActor ) + { + GV_DestroyActorQuick(pItemActor); + } + + ppfnEquip = &gSnakeEquips_8009EF8C[GM_CurrentItemId]; + pItemActor = NULL; + + if ( ((itemType & 0x8000) != 0) && (*ppfnEquip != 0) ) + { + pfnEquip = *ppfnEquip; + + if ( (GM_CurrentItemId == 2) || (GM_CurrentItemId == 3) || (GM_CurrentItemId == 4) ) + { + pItemActor = pfnEquip(&work->control, &work->body, 0); + } + else + { + pItemActor = pfnEquip(&work->control, &work->body, 6); + } + + if ( !pItemActor ) + { + work->field_9A4_item_actor = NULL; + return; + } + } + + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_CB_BOX); + + work->field_9A8_current_item = GM_CurrentItemId; + + if ( a2 != 0 ) + { + GM_CallSystemCallbackProc(4, GM_CurrentItemId); + } + + work->field_9AC = itemType; + work->field_9A4_item_actor = pItemActor; + + if ( (itemType & 1) != 0 ) + { + if ( GM_CurrentItemId == IT_Scope ) + { + sna_start_anim_8004E1F4(work, &sna_anim_scope_80055334); + } + else if ( GM_CurrentItemId == IT_Camera ) + { + sna_start_anim_8004E1F4(work, &sna_anim_jpegcam_800553CC); + } + else + { + sna_start_anim_8004E1F4(work, &sna_anim_box_idle_800553EC); + GM_SetPlayerStatusFlag(0x401000); + } + + GM_ClearPlayerStatusFlag(PLAYER_CAUTION | PLAYER_MOVE); + } + else + { + if ( ((work->field_920_tbl & 0x200) == 0) && ((temp_s4 & 1) != 0) ) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + + if ( GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) ) + { + sna_start_anim_8004E1F4(work, &sna_anim_duct_move_80054424); + } + else if ( GM_CheckPlayerStatusFlag(PLAYER_CAUTION) ) + { + sna_start_anim_8004E1F4(work, dword_8009EEB0[work->field_A26_stance]); + } + else + { + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + } + } + + if ( work->field_8E8_pTarget && (work->field_9B8_fn_anim != &sna_anim_throw_800589C8) ) + { + sna_start_anim_8004E1F4(work, &sna_anim_choke_hold_80059154); + } + } + + if ((itemType & 2) != 0) + { + sub_8004EB74(work); + + if ( GM_UnkFlagBE != 0 ) + { + GM_SetPlayerStatusFlag(PLAYER_STILL_WATCH); + sna_clear_flags2_8004E344(work, 0x30); + } + + GM_SetPlayerStatusFlag(PLAYER_WEAPON_DISABLE); + } else if ( !(work->field_920_tbl & 0x200) && + !GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) && + ((GM_UnkFlagBE == 0) || ((work->field_898_flags2 & 0x10) == 0)) ) + { + sna_8004EC00(work); + } + + temp_s0_2 = GM_CheckPlayerStatusFlag(PLAYER_MOVE); + + var_s2 = 0; + if ( (work->control.skip_flag & CTRL_BOTH_CHECK) && ((itemType & 1) == 0) ) + { + var_s2 = 1; + } + + sub_8004F454(work); + + if ( temp_s0_2 != 0 ) + { + GM_SetPlayerStatusFlag(PLAYER_MOVE); + } + + if ( var_s2 != 0 ) + { + work->control.skip_flag |= CTRL_BOTH_CHECK; + } + + if ( GM_CheckPlayerStatusFlag(0x300) ) + { + GM_SetPlayerStatusFlag(PLAYER_WEAPON_DISABLE); + + if ( (work->field_9B8_fn_anim == &sna_anim_knockdown_80054710) || GM_CheckPlayerStatusFlag(PLAYER_DOWNED) ) + { + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK3); + } + } +} + +void sna_80051A10(SnaInitWork *work, SVECTOR *pPos, SVECTOR *pOut, SVECTOR *pVec) +{ + MATRIX rot; + SVECTOR vec; + SVECTOR vec2; + SVECTOR vec3; + SVECTOR vec4; + + int len; + + ReadRotMatrix(&rot); + DG_SetPos2(pPos, &work->control.rot); + DG_PutVector(pVec, pVec, 1); + SetRotMatrix(&rot); + + vec = *pPos; + vec2 = *pVec; + + if (sna_line_check(&vec, work->control.map->hzd, HZD_CHK_ALL, SEGMENT_ATR) < 0) + { + vec2 = *pVec; + } + + GV_SubVec3(&vec2, pPos, &vec3); + len = GV_VecLen3(&vec3); + + if ((len - work->field_A28) <= 0) + { + vec3 = *pPos; + } + else + { + GV_LenVec3(&vec3, &vec4, len, len - work->field_A28); + GV_AddVec3(pPos, &vec4, &vec3); + } + + *pOut = vec3; +} + +int sna_80051BA4(SnaInitWork *work) +{ + SVECTOR vec; + SVECTOR vec2; + SVECTOR vec3; + + GV_PAD *pPad = work->field_9B0_pad_ptr; + unsigned short press = pPad->press; + unsigned short release = pPad->release; + + if (pPad->status & (PAD_R1 | PAD_L1)) + { + if (press & PAD_R1) + { + work->field_A56 = 8; + } + + if (press & PAD_L1) + { + work->field_A56 = 4; + } + + if (release & PAD_R1) + { + work->field_A56 = 4; + } + + if (release & PAD_L1) + { + work->field_A56 = 8; + } + + vec2 = vec = DG_ZeroVector; + vec2.vx = 1280; + vec.vx = -1280; + vec3 = work->field_A60; + + sna_80051A10(work, &vec3, &work->field_A3C, &vec2); + sna_80051A10(work, &vec3, &work->field_A44, &vec); + + if (!sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK16)) + { + work->field_A58 = 8; + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK16); + } + else if (work->field_A56 & 4) + { + work->field_A60 = work->field_A3C; + + } + else if (work->field_A56 & 8) + { + work->field_A60 = work->field_A44; + } + + return 1; + } + + if (work->field_A58 > 0) + { + if (--work->field_A58 == 0) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK16); + return 0; + } + + return 1; + } + + return 0; +} + +void sna_80051DA0(SnaInitWork *work) +{ + unsigned int status = work->field_9B0_pad_ptr->status; + unsigned char ldy = work->field_9B0_pad_ptr->left_dy; + short *temp_s4; + int var_s1; + int var_s2; + int var_s0; + int temp_v0; + int temp_a1; + short var_a0; + short var_a2; + + // status is passed using the wrong type here + GM_CheckShukanReverse((unsigned short *)&status); + GM_CheckShukanReverseAnalog(&ldy); + + temp_s4 = work->field_9D0; + var_s2 = temp_s4[4]; + var_s1 = -temp_s4[3]; + + if (work->field_9D0[0] == 0) + { + if (work->field_A26_stance == SNA_STANCE_CROUCH) + { + var_s0 = work->field_A2A; + } + else + { + var_s0 = 0; + } + } + else + { + var_s0 = work->control.turn.vx; + } + + if (work->field_A26_stance == SNA_STANCE_CROUCH) + { + if (work->field_91C_weapon_idx == WP_Rifle) + { + temp_v0 = work->field_A2A; + var_s1 += temp_v0; + var_s2 += temp_v0; + } + else + { + var_s1 += var_s0; + var_s2 += var_s0; + } + } + + if (var_s1 < dword_800ABBDC) + { + var_s1 = dword_800ABBDC; + } + + if (var_s2 > dword_800ABBD4) + { + var_s2 = dword_800ABBD4; + } + + temp_a1 = sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK15); + var_a2 = 1; + var_a0 = 1; + + if (status & PAD_UP) + { + if (temp_a1 & 0xff) + { + var_a0 = 64 - ldy; + var_a2 = 64; + } + + var_s0 -= (temp_s4[5] * (var_a0 & 0xff)) / var_a2; + } + else if (status & PAD_DOWN) + { + if (temp_a1 & 0xff) + { + var_a0 = ldy + 64; + var_a2 = 63; + } + + var_s0 += (temp_s4[6] * (var_a0 & 0xff)) / var_a2; + } + + if (var_s0 < var_s1) + { + var_s0 = var_s1; + } + + if (var_s2 < var_s0) + { + var_s0 = var_s2; + } + + work->control.turn.vx = var_s0; +} + +void sna_80051FD0(SnaInitWork *work) +{ + int iVar1; + u_short uVar2; + unsigned int uVar3; + short sVar4; + short sVar5; + int iVar6; + short *short_arr; + + short_arr = work->field_9D0; + uVar2 = work->field_9B0_pad_ptr->status; + if ((uVar2 & (PAD_LEFT | PAD_RIGHT)) != 0) + { + iVar1 = work->control.turn.vy; + uVar3 = sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK15); + sVar4 = 1; + sVar5 = 1; + if (uVar2 & PAD_LEFT) + { + if ((uVar3 & 0xff) != 0) + { + sVar4 = 0x40; + sVar5 = 0x40 - (*((unsigned char *)((short *)work->field_9B0_pad_ptr + 7))); + } + iVar6 = ((int)short_arr[8]) * (sVar5 & 0xff); + iVar1 += iVar6 / sVar4; + } + else + { + if ((uVar3 & 0xff) != 0) + { + sVar4 = 0x3f; + sVar5 = (*((unsigned char *)((short *)work->field_9B0_pad_ptr + 7))) + 0x40; + } + iVar6 = ((int)short_arr[7]) * (sVar5 & 0xff); + iVar1 -= iVar6 / sVar4; + } + work->control.turn.vy = iVar1; + } +} + +void sna_fn_80052120(SnaInitWork *work, int time) +{ + unsigned short press; + int sound; + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) != 0) + { + if (GM_UnkFlagBE == 0) + { + sna_8004EC00(work); + } + else + { + sna_8004FA74(work); + } + + sna_set_invuln_8004F2A0(work, 0); + sna_start_anim_8004E1F4(work, sna_anim_enter_duct_80053E9C); + } + else + { + press = work->field_9B0_pad_ptr->press; + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(work, sna_8004EAA8(work, (int)work->field_A26_stance), 4); + + if ((work->field_9B0_pad_ptr->status & PAD_TRIANGLE) == 0) + { + if (GM_UnkFlagBE == 0) + { + sna_8004EC00(work); + } + else + { + sna_8004FA74(work); + } + + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + } + else + { + if (sna_80051BA4(work)) + { + return; + } + + sna_80051DA0(work); + sna_80051FD0(work); + + if ((press & PAD_CROSS) != 0) + { + switch (work->field_A26_stance) + { + case 0: + sna_start_anim_8004E1F4(work, sna_anim_crouch_800527DC); + break; + case 2: + if (sna_8004E808(work, 1, 0, -0x26e, 0x5dc) != 0) + { + return; + } + default: + if (sna_8004E808(work, 0, 0, 0, 0x5dc) != 0) + { + return; + } + + work->control.turn.vy = work->control.rot.vy; + GM_ClearPlayerStatusFlag(PLAYER_SQUAT | PLAYER_GROUND); + + sound = SE_CHANGE_STANCE; + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) != 0) + { + sound = 183; + } + + GM_SeSet(&work->control.mov, sound); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + break; + } + } + } + } +} + +void sub_8005230C(SnaInitWork *work, int time) +{ + int action; + unsigned short status; + unsigned short press; + + status = work->field_9B0_pad_ptr->status; + press = work->field_9B0_pad_ptr->press; + + if ((status & PAD_TRIANGLE) == 0) + { + sna_8004EC00(work); + work->field_A38_local_data = 2; + } + else + { + if (GM_CheckPlayerStatusFlag(PLAYER_MOVE) != 0) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + + if (work->field_A26_stance == SNA_STANCE_STANDING) + { + action = work->actpack->still->against_wall; + } + else + { + action = work->actpack->still->squat_against_wall; + } + + SetAction_8004E22C(work, action, 4); + } + if (((press & (PAD_LEFT | PAD_DOWN | PAD_RIGHT | PAD_UP)) != 0) || + ((status & (PAD_LEFT | PAD_DOWN | PAD_RIGHT | PAD_UP)) == 0)) + { + + GM_ClearPlayerStatusFlag(PLAYER_CAUTION); + work->field_A28 = 0x1c2; + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + GM_ExitBehindCamera(); + } + + if (!sna_80051BA4(work) && (press & PAD_CROSS) != 0) + { + if (work->field_A26_stance == SNA_STANCE_STANDING) + { + sna_start_anim_8004E1F4(work, sna_anim_wall_crouch_80052CCC); + } + else + { + GM_ClearPlayerStatusFlag(PLAYER_SQUAT); + sna_start_anim_8004E1F4(work, sna_anim_wall_idle_and_c4_80052A5C); + } + } + } +} + +void sub_80052468(SnaInitWork *work, int time) +{ + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) == 0) + { + work->field_A56 = 0; + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_STILL_WATCH | + PLAYER_NORMAL_WATCH | PLAYER_WATCH); + sna_set_invuln_8004F2A0(work, 0); + sna_start_anim_8004E1F4(work, sna_anim_enter_duct_80053E9C); + } + else + { + SetAction_8004E22C(work, work->actpack->still->crouch, 4); + + if ((work->field_9B0_pad_ptr->status & PAD_TRIANGLE) == 0) + { + work->field_A56 = 0; + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_STILL_WATCH | + PLAYER_NORMAL_WATCH | PLAYER_WATCH); + sna_start_anim_8004E1F4(work, sna_anim_duct_move_80054424); + sna_clear_flags2_8004E344(work, SNA_FLAG2_UNK6); + } + + sna_80051DA0(work); + sna_80051FD0(work); + } +} + +void sna_fn_80052540(SnaInitWork *work, int time) +{ + if ((work->field_9B0_pad_ptr->status & PAD_TRIANGLE) == 0) + { + if (GM_UnkFlagBE == 0) + { + sna_8004EC00(work); + } + else + { + sna_8004FA74(work); + } + } + + if (work->field_9B8_fn_anim == sna_anim_knockdown_idle_80054930 && + ((work->field_9B0_pad_ptr->press & ~PAD_TRIANGLE) != 0)) + { + sna_knockdown_getup_80050668(work); + } + + GM_SnakeCamera.position.vy += 320; + sna_8004EE28(work); +} + +void sna_fn_800525F8(SnaInitWork *work, int time) +{ + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + if (work->field_9B8_fn_anim != sna_anim_box_idle_800553EC) + { + if (work->body.is_end) + { + SetAction_8004E22C(work, work->actpack->special2->field_2, 4); + } + } + + if (!(work->field_9B0_pad_ptr->status & PAD_TRIANGLE)) + { + if (!GM_UnkFlagBE) + { + sna_8004EC00(work); + } + else + { + sna_8004FA74(work); + } + work->control.turn.vx = 0; + } + else + { + sna_80051DA0(work); + sna_80051FD0(work); + } +} + +void sna_act_helper2_helper_helper_800526BC(SnaInitWork *work, int time) +{ + if (!(work->field_9B0_pad_ptr->status & PAD_TRIANGLE)) + { + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + work->field_A56 = 0; + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE + | PLAYER_NORMAL_WATCH + | PLAYER_WATCH); + } + else if (!GM_UnkFlagBE) + { + sna_8004EC00(work); + } + else + { + sna_8004FA74(work); + } + } + + if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + sna_8004EE28(work); + } +} + +void sna_anim_idle_8005275C(SnaInitWork *work, int time) +{ + int action_flag; // $a1 + + if (time == 0) + { + work->field_A26_stance = SNA_STANCE_STANDING; + + work->field_9C8_anim_update_fn_3p = sna_fn_80052E58; + work->field_9CC_anim_update_fn_1p = sna_fn_80052120; + + if (GM_AlertMode >= ALERT_ACTIVE) + { + action_flag = work->actpack->still->setup; + } + else + { + action_flag = work->actpack->still->stand; + } + + SetAction_8004E22C(work, action_flag, 4); + } +} + +void sna_anim_crouch_800527DC(SnaInitWork *work, int time) +{ + int sound; + + if (time == 0) + { + if (work->field_A26_stance != SNA_STANCE_SQUAT) + { + sound = SE_CHANGE_STANCE; + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) != 0) + { + sound = 183; + } + GM_SeSet(&work->control.mov, sound); + } + + work->field_9C8_anim_update_fn_3p = sna_anim_crouch_helper_80053014; + work->field_9CC_anim_update_fn_1p = sna_fn_80052120; + SetAction_8004E22C(work, work->actpack->still->squat, 4); + work->field_A2C.vy = -128; + } + + if ((time == 0 && work->field_A26_stance != SNA_STANCE_STANDING) || + (time == 4 && work->field_A26_stance == SNA_STANCE_STANDING)) + { + GM_SetPlayerStatusFlag(PLAYER_SQUAT); + work->field_A26_stance = SNA_STANCE_SQUAT; + } +} + +void sna_anim_prone_idle_800528BC(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_A26_stance = SNA_STANCE_CROUCH; + work->field_A28 = 450; + work->field_9C8_anim_update_fn_3p = sna_fn_800531F4; + work->field_9CC_anim_update_fn_1p = sna_fn_80052120; + GM_SetPlayerStatusFlag(PLAYER_GROUND); + SetAction_8004E22C(work, work->actpack->still->crouch, 4); + } +} + +void sna_anim_run_8005292C(SnaInitWork *work, int time) +{ + int action_flag; // $a1 + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_80053360; + work->field_9CC_anim_update_fn_1p = sna_fn_80052120; + GM_SetPlayerStatusFlag(PLAYER_MOVE); + if (GM_AlertMode >= ALERT_ACTIVE) + { + action_flag = work->actpack->move->aim; + } + else + { + action_flag = work->actpack->move->stand; + } + SetAction_8004E22C(work, action_flag, 4); + } +} + +void sna_anim_prone_move_800529C0(SnaInitWork *work, int time) +{ + int action_flag; // $a1 + + if (time == 0) + { + work->field_A28 = 450; + GM_SetPlayerStatusFlag(PLAYER_MOVE); + work->field_9C8_anim_update_fn_3p = sna_fn_800535B8; + work->field_9CC_anim_update_fn_1p = sna_fn_80052120; + if (work->field_A54.prone_bool_thing) + { + action_flag = work->actpack->move->crouch_backwards; + } + else + { + action_flag = work->actpack->move->crouch; + } + SetAction_8004E22C(work, action_flag, 4); + work->field_A54.prone_bool_thing = 0; + } + sna_80050440(work); +} + +void sna_anim_wall_idle_and_c4_80052A5C(SnaInitWork *work, int time) +{ + int sVar3; + int sound; + + if (time == 0) + { + if (GM_CheckPlayerStatusFlag(PLAYER_CAUTION) == 0) + { + sound = SE_WALLPRESS; + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) != 0) + { + sound = 183; + } + GM_SeSet(&work->control.mov, sound); + work->control.turn.vy = dword_800ABBD0 - 2048; + GM_SetPlayerStatusFlag(PLAYER_CAUTION); + } + + sVar3 = work->field_91C_weapon_idx != WP_C4 ? 300 : 472; + + work->field_9C8_anim_update_fn_3p = sna_anim_wall_idle_and_c4_helper_800537D4; + work->field_A28 = sVar3; + work->field_A26_stance = 0; + work->field_9CC_anim_update_fn_1p = sub_8005230C; + SetAction_8004E22C(work, work->actpack->still->against_wall, 4); + } + + GM_CheckBehindCamera(work->control.map->hzd, &work->control); + + if (work->field_A3A < 8 && pVec_800ABBCC != NULL) + { + (work->control).step.vx += pVec_800ABBCC->vx / 8; + (work->control).step.vz += pVec_800ABBCC->vz / 8; + work->field_A3A++; + } + + return; +} + +void sna_anim_wall_move_80052BA8(SnaInitWork *work, int time) +{ + int action; + + if (time == 0) + { + GM_SetPlayerStatusFlag(PLAYER_MOVE | PLAYER_CAUTION); + work->field_9C8_anim_update_fn_3p = sna_anim_wall_move_helper_800538CC; + work->field_9CC_anim_update_fn_1p = sub_8005230C; + + if (work->field_A54.wall_thing == 2) + { + action = work->actpack->move->against_wall_r; + } + else + { + action = work->actpack->move->against_wall_l; + } + + work->field_A28 = 300; + work->field_A54.wall_thing = 0; + SetAction_8004E22C(work, action, 4); + work->control.turn.vy = dword_800ABBD0 - 2048; + } + + if (work->field_A3A < 8 && pVec_800ABBCC != NULL) + { + work->control.step.vx += pVec_800ABBCC->vx / 8; + work->control.step.vz += pVec_800ABBCC->vz / 8; + work->field_A3A++; + } +} + +void sna_anim_wall_crouch_80052CCC(SnaInitWork *work, int time) +{ + int sound; + int sound2; + + if (time == 0) + { + if (work->field_A26_stance != SNA_STANCE_SQUAT) + { + sound = SE_CHANGE_STANCE; + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) != 0) + { + sound = 183; + } + GM_SeSet(&work->control.mov, sound); + } + + work->field_A26_stance = SNA_STANCE_SQUAT; + work->field_9C8_anim_update_fn_3p = sna_anim_wall_crouch_helper_80053A54; + work->field_9CC_anim_update_fn_1p = sub_8005230C; + + if (GM_CheckPlayerStatusFlag(PLAYER_CAUTION) == 0) + { + sound2 = SE_WALLPRESS; + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) != 0) + { + sound2 = 183; + } + GM_SeSet(&work->control.mov, sound2); + work->control.turn.vy = dword_800ABBD0 - 2048; + } + + GM_SetPlayerStatusFlag(PLAYER_SQUAT | PLAYER_CAUTION); + SetAction_8004E22C(work, work->actpack->still->squat_against_wall, 4); + + work->field_A28 = work->field_91C_weapon_idx != WP_C4 ? 300 : 472; + } + + GM_CheckBehindCamera(((work->control).map)->hzd, &work->control); + + if (work->field_A3A < 8 && pVec_800ABBCC != NULL) + { + work->control.step.vx += pVec_800ABBCC->vx / 8; + work->control.step.vz += pVec_800ABBCC->vz / 8; + work->field_A3A++; + } +} + +void sna_fn_80052E58(SnaInitWork *work, int time) +{ + int action; + int diff; + + if (sna_8005009C(work)) + { + return; + } + + if ((work->field_9B0_pad_ptr->press & PAD_CROSS) != 0) + { + sna_start_anim_8004E1F4(work, sna_anim_crouch_800527DC); + return; + } + + if (GM_AlertMode >= ALERT_ACTIVE) + { + action = work->actpack->still->setup; + } + else + { + action = work->actpack->still->stand; + } + + SetAction_8004E22C(work, action, 4); + + if (work->field_A38_local_data > 0) + { + work->field_A38_local_data--; + } + + if (work->field_A38_local_data == 0 && gSnaMoveDir_800ABBA4 >= 0) + { + if (dword_800ABBC4 == 1 || (dword_800ABBC4 != 0 && work->field_79C == 2)) + { + sna_start_anim_8004E1F4(work, sna_anim_wall_idle_and_c4_80052A5C); + } + else + { + if (sna_sub_8004E358(work, SNA_FLAG2_UNK5) == 0) + { + diff = GV_DiffDirAbs(work->control.turn.vy, gSnaMoveDir_800ABBA4); + if (diff > 256) + { + work->field_A38_local_data = diff / 1024 + 1; + } + else + { + sna_start_anim_8004E1F4(work, sna_anim_run_begin_80053B88); + } + + work->control.turn.vy = gSnaMoveDir_800ABBA4; + } + else if ((work->field_9B0_pad_ptr->status & (PAD_DOWN | PAD_UP)) != 0) + { + sna_start_anim_8004E1F4(work, sna_anim_run_begin_80053B88); + } + else + { + sna_80051FD0(work); + } + } + } +} + +void sna_anim_crouch_helper_80053014(SnaInitWork *work, int time) +{ + int diff; + int sound; + + if ((work->field_9B0_pad_ptr->press & PAD_CROSS) != 0) + { + if (sna_8004E808(work, 0, 0, 0, 1100) == 0) + { + work->control.turn.vy = work->control.rot.vy; + + sound = SE_CHANGE_STANCE; + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) != 0) + { + sound = 183; + } + + GM_SeSet(&work->control.mov, sound); + GM_ClearPlayerStatusFlag(PLAYER_SQUAT); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } + } + else + { + if (work->field_A38_local_data > 0) + { + work->field_A38_local_data--; + } + + if (work->field_A38_local_data == 0 && gSnaMoveDir_800ABBA4 >= 0) + { + if (dword_800ABBC4 == 1 || (dword_800ABBC4 != 0 && work->field_79C == 2)) + { + sna_start_anim_8004E1F4(work, sna_anim_wall_crouch_80052CCC); + } + else + { + if (sna_sub_8004E358(work, SNA_FLAG2_UNK5) == 0) + { + diff = GV_DiffDirAbs(work->control.turn.vy, gSnaMoveDir_800ABBA4); + if (diff > 256) + { + work->field_A38_local_data = diff / 1024 + 1; + } + else + { + sna_start_anim_8004E1F4(work, sna_anim_prone_begin_80053BE8); + } + + work->control.turn.vy = gSnaMoveDir_800ABBA4; + } + else if ((work->field_9B0_pad_ptr->status & (PAD_DOWN | PAD_UP)) != 0) + { + sna_start_anim_8004E1F4(work, sna_anim_prone_begin_80053BE8); + + if ((work->field_9B0_pad_ptr->status & PAD_DOWN) != 0) + { + work->control.turn.vy += 2048; + } + } + else + { + sna_80051FD0(work); + } + } + } + } +} + +void sna_fn_800531F4(SnaInitWork *work, int time) +{ + if (work->field_A38_local_data > 0) + { + work->field_A38_local_data -= 1; + } + + if (!sna_prone_check_standup_80050398(work) && work->field_A38_local_data == 0 && gSnaMoveDir_800ABBA4 >= 0) + { + if (dword_800ABBC4 == 1 || (dword_800ABBC4 != 0 && work->field_79C == 2)) + { + if (sna_8004E808(work, 0, 0, 0, 1500) == 0) + { + work->control.turn.vy = work->control.rot.vy; + GM_ClearPlayerStatusFlag(PLAYER_GROUND); + GM_SetPlayerStatusFlag(PLAYER_SQUAT | PLAYER_CAUTION); + sna_start_anim_8004E1F4(work, sna_anim_wall_crouch_80052CCC); + } + } + else + { + if (sna_sub_8004E358(work, SNA_FLAG2_UNK5) == 0) + { + if (sub_8004E458(work->control.turn.vy, gSnaMoveDir_800ABBA4) == 3) + { + work->field_A54.prone_bool_thing = 1; + } + else + { + work->field_A54.prone_bool_thing = 0; + } + + sna_start_anim_8004E1F4(work, sna_anim_prone_move_800529C0); + } + else if ((work->field_9B0_pad_ptr->status & (PAD_DOWN | PAD_UP)) != 0) + { + sna_start_anim_8004E1F4(work, sna_anim_prone_move_800529C0); + } + else + { + sna_80051FD0(work); + work->control.turn.vx = work->field_A2A; + } + } + } +} + +void sna_80053360(SnaInitWork *work, int time) +{ + int angle; + + if (sna_8005009C(work)) + { + return; + } + + if (work->field_9B0_pad_ptr->press & PAD_CROSS) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, &sna_anim_crouch_800527DC); + return; + } + + if (gSnaMoveDir_800ABBA4 < 0) + { + if (++work->field_A3A >= 5) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, &sna_anim_idle_8005275C); + return; + } + } + else + { + work->field_A3A = 0; + } + + if ((dword_800ABBC4 == 1) || ((dword_800ABBC4 != 0) && (work->field_79C == 2))) + { + if (++work->field_A38_local_data >= 3) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, &sna_anim_wall_idle_and_c4_80052A5C); + return; + } + } + else + { + work->field_A38_local_data = 0; + } + + if (sna_sub_8004E358(work, 16) == 0) + { + if (work->field_A38_local_data == 0) + { + if (gSnaMoveDir_800ABBA4 >= 0) + { + angle = sub_8004E4C0(work, gSnaMoveDir_800ABBA4); + } + else + { + angle = work->control.turn.vy; + } + } + else + { + angle = gSnaMoveDir_800ABBA4; + + if (angle < 0) + { + angle = work->control.turn.vy; + } + } + + if (GV_DiffDirAbs(work->control.turn.vy, angle) > 1048) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, &sna_anim_idle_8005275C); + work->control.turn.vy = angle; + return; + } + + work->control.turn.vy = angle; + sub_8004EA50(work, angle); + } + else + { + if (!(work->field_9B0_pad_ptr->status & (PAD_DOWN | PAD_UP))) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, &sna_anim_idle_8005275C); + return; + } + + sub_8004FA9C(work); + } + + if (GM_AlertMode >= ALERT_ACTIVE) + { + SetAction_8004E22C(work, work->actpack->move->aim, 4); + } + else + { + SetAction_8004E22C(work, work->actpack->move->stand, 4); + } +} + +void sna_fn_800535B8(SnaInitWork *work, int time) +{ + int var_s0; + int temp_s2; + int temp_s3; + unsigned char var_a1; + unsigned char var_a1_2; + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + sna_start_anim_8004E1F4(work, &sna_anim_enter_duct_80053E9C); + sna_set_invuln_8004F2A0(work, 0); + return; + } + + if (!sna_prone_check_standup_80050398(work)) + { + if (gSnaMoveDir_800ABBA4 < 0) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(work, work->actpack->still->crouch, 4); + sna_start_anim_8004E1F4(work, &sna_anim_prone_idle_800528BC); + return; + } + + if ((dword_800ABBC4 == 1) || ((dword_800ABBC4 != 0) && (work->field_79C == 2))) + { + if (!sna_8004E808(work, 0, 0, 0, 1500)) + { + work->control.turn.vy = work->control.rot.vy; + GM_ClearPlayerStatusFlag(PLAYER_GROUND | PLAYER_MOVE); + GM_SetPlayerStatusFlag(PLAYER_CAUTION | PLAYER_SQUAT); + sna_start_anim_8004E1F4(work, &sna_anim_wall_crouch_80052CCC); + } + } + else + { + temp_s3 = work->actpack->move->crouch; + temp_s2 = work->actpack->move->crouch_backwards; + + if (!sna_sub_8004E358(work, 16)) + { + if (GetAction(work) == temp_s2) + { + var_s0 = gSnaMoveDir_800ABBA4 + 2048; + } + else + { + var_s0 = gSnaMoveDir_800ABBA4; + } + + if (sub_8004E458(work->control.turn.vy, var_s0) == 3) + { + var_s0 += 2048; + var_a1 = temp_s3; + + if (GetAction(work) == var_a1) + { + var_a1 = temp_s2; + } + + SetAction_8004E22C(work, (var_a1), 4); + } + + work->control.turn.vy = sub_8004E4C0(work, var_s0); + return; + } + + if (!(work->field_9B0_pad_ptr->status & (PAD_DOWN | PAD_UP))) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(work, work->actpack->still->crouch, 4); + sna_start_anim_8004E1F4(work, &sna_anim_prone_idle_800528BC); + return; + } + + var_a1_2 = temp_s2; + + if (work->field_9B0_pad_ptr->status & PAD_UP) + { + var_a1_2 = temp_s3; + } + + SetAction_8004E22C(work, var_a1_2, 4); + sna_80051FD0(work); + + work->control.turn.vx = work->field_A2A; + } + } +} + +void sna_anim_wall_idle_and_c4_helper_800537D4(SnaInitWork *work, int time) +{ + if (work->field_A38_local_data != 0) + { + work->field_A38_local_data--; + } + + if (sub_800507D8(work)) + { + return; + } + + if ((work->field_9B0_pad_ptr->press & PAD_CROSS) && !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK9)) + { + sna_start_anim_8004E1F4(work, &sna_anim_wall_crouch_80052CCC); + return; + } + + if ((dword_800ABBC4 == 2) || (dword_800ABBC4 == 4)) + { + work->field_A54.wall_thing = dword_800ABBC4; + sna_start_anim_8004E1F4(work, &sna_anim_wall_move_80052BA8); + } + + if ((dword_800ABBD0 - 2048) != work->control.rot.vy) + { + work->field_A3A = 0; + work->control.turn.vy = dword_800ABBD0 - 2048; + } + + sna_check_knock_800501F8(work, time); + sna_8005027C(work, time); +} + +void sna_anim_wall_move_helper_800538CC(SnaInitWork *work, int time) +{ + int var_s1; + + if (work->field_A38_local_data != 0) + { + work->field_A38_local_data--; + } + + if (sub_800507D8(work)) + { + return; + } + + GM_CheckBehindCamera(work->control.map->hzd, &work->control); + + if ((work->field_9B0_pad_ptr->press & PAD_CROSS) && !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK9)) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, &sna_anim_wall_crouch_80052CCC); + return; + } + + if (dword_800ABBC4 == 1) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, &sna_anim_wall_idle_and_c4_80052A5C); + return; + } + + if (((dword_800ABBD0 - 2048) != work->control.rot.vy) || (pVec_800ABBC8 != pVec_800ABBCC)) + { + work->field_A3A = 0; + work->control.turn.vy = dword_800ABBD0 - 2048; + } + + sna_check_knock_800501F8(work, time); + sna_8005027C(work, time); + + if (dword_800ABBC4 == 2) + { + var_s1 = work->actpack->move->against_wall_r; + } + else + { + var_s1 = work->actpack->move->against_wall_l; + } + + GM_SetPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(work, var_s1, 4); +} + +void sna_anim_wall_crouch_helper_80053A54(SnaInitWork *work, int time) +{ + int sound; + + if (work->field_A38_local_data != 0) + { + work->field_A38_local_data--; + } + + if (sub_800507D8(work)) + { + return; + } + + if ((work->field_9B0_pad_ptr->press & PAD_CROSS) && !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK9)) + { + if (!sna_8004E808(work, 0, 0, 0, 1100)) + { + GM_ClearPlayerStatusFlag(PLAYER_SQUAT); + sound = SE_CHANGE_STANCE; + + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER)) + { + sound = 183; + } + + GM_SeSet(&work->control.mov, sound); + sna_start_anim_8004E1F4(work, &sna_anim_wall_idle_and_c4_80052A5C); + } + } + else + { + if (!(GM_GameStatus & STATE_BEHIND_CAMERA) && ((dword_800ABBD0 - 2048) != work->control.rot.vy)) + { + work->field_A3A = 0; + work->control.turn.vy = dword_800ABBD0 - 2048; + } + + sna_check_knock_800501F8(work, time); + sna_8005027C(work, time); + } +} + +void sna_fn_nothing_80053B80(SnaInitWork *work, int time) +{ +} + +void sna_anim_run_begin_80053B88(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_80054318; + work->field_9CC_anim_update_fn_1p = sna_fn_80052120; + GM_SetPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(work, work->actpack->move->setup, 4); + } +} + +void sna_anim_prone_begin_80053BE8(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + sna_set_flags1_8004E2F4(work, (SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6)); + SetAction_8004E22C(work, work->actpack->trans->crouch, 4); + work->field_A26_stance = SNA_STANCE_CROUCH; + work->control.skip_flag |= CTRL_BOTH_CHECK | CTRL_SKIP_TRAP; + GM_ClearPlayerStatusFlag(PLAYER_SQUAT); + GM_SetPlayerStatusFlag(PLAYER_GROUND); + } + + if (time == 7) + { + work->control.skip_flag &= ~CTRL_SKIP_TRAP; + } + + switch (work->field_A38_local_data) + { + case 0: + if (work->field_798_p_height < 250) + { + work->control.skip_flag &= ~CTRL_BOTH_CHECK; + } + + if (EndMotion(work)) + { + SetAction_8004E22C(work, work->actpack->still->crouch, 4); + work->field_A38_local_data = 1; + } + break; + case 1: + sna_80050440(work); + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) != 0) + { + sna_start_anim_8004E1F4(work, sna_anim_enter_duct_80053E9C); + sna_set_invuln_8004F2A0(work, 0); + SetAction_8004E22C(work, work->actpack->move->crouch, 4); + } + else + { + sna_clear_flags1_8004E308(work, 0x30); + sna_start_anim_8004E1F4(work, sna_anim_prone_idle_800528BC); + } + } +} + +void sna_anim_prone_standup_80053D74(SnaInitWork *work, int time) +{ + int sound; + + if (time == 0) + { + GM_ClearPlayerStatusFlag(PLAYER_GROUND); + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, work->actpack->trans->stand, 4); + + sound = SE_CHANGE_STANCE; + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) != 0) + { + sound = 183; + } + GM_SeSet(&work->control.mov, sound); + sna_set_flags1_8004E2F4(work, (SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6)); + } + + if (work->control.height >= 250) + { + work->control.skip_flag |= CTRL_BOTH_CHECK; + } + + if (work->control.height > 500) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + + if (time == 2) + { + work->control.turn.vx = 0; + } + + if (time == 7) + { + work->control.skip_flag |= CTRL_SKIP_TRAP; + } + + if (EndMotion(work)) + { + work->control.skip_flag &= ~(CTRL_BOTH_CHECK | CTRL_SKIP_TRAP); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + sna_clear_flags1_8004E308(work, (SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6)); + } +} + +void sna_anim_enter_duct_80053E9C(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_800543A8; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + GM_SetPlayerStatusFlag(PLAYER_PAD_OFF | PLAYER_MENU_DISABLE); + sna_set_flags1_8004E2F4(work, (SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6)); + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) == 0 && + GetAction(work) == work->actpack->move->crouch_backwards) + { + work->field_A38_local_data = 48; + } + else + { + work->field_A38_local_data = 24; + } + } + + if (time == work->field_A38_local_data) + { + sna_clear_invuln_8004F2EC(work); + sna_80050440(work); + sna_clear_flags1_8004E308(work, (SNA_FLAG1_UNK3 | SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6)); + sna_clear_flags2_8004E344(work, SNA_FLAG2_UNK9); + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + GM_ClearPlayerStatusFlag(PLAYER_PAD_OFF | PLAYER_MENU_DISABLE); + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) != 0) + { + sna_start_anim_8004E1F4(work, sna_anim_duct_move_80054424); + } + else + { + sna_start_anim_8004E1F4(work, sna_anim_prone_idle_800528BC); + } + } +} + +void sub_80053FAC(SnaInitWork *work, int time) +{ + short vec_y; // $v1 + unsigned short pad_status; // $s1 + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, work->actpack->attack->setup, 2); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK3); + GM_ClearPlayerStatusFlag(PLAYER_ATTACK); + vec_y = work->field_A54.prone_bool_thing; + work->field_A54.prone_bool_thing = 0; + work->field_90C_pWeaponFn = sna_gun_800540D0; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + work->control.turn.vy = vec_y; + sna_8004E260(work, 0, 4, 0); + } + + pad_status = work->field_9B0_pad_ptr->status; + if (work->body.is_end || (pad_status & PAD_SQUARE) == 0) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + work->field_910 = 0; + if ((pad_status & PAD_SQUARE) != 0) + { + GM_SeSet(&work->control.mov, SE_READY_WEAPON); + GM_SetPlayerStatusFlag(PLAYER_ATTACK); + sna_start_anim_8004E1F4(work, sna_anim_shoot_weapon_80056B88); + work->field_90C_pWeaponFn = sub_80057BF0; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + } + else + { + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } + } +} + +void sna_gun_800540D0(SnaInitWork *work, int time) +{ + int iVar1; + int a4; + + if (time == 0) + { + iVar1 = GM_CheckPlayerStatusFlag(PLAYER_MOVE); + a4 = 0xffff; + if (iVar1 != 0) + { + a4 = 0x3fe; + } + sna_8004E260(work, SET, 4, a4); + work->field_A38_local_data = 0; + } + + if ((work->field_9B0_pad_ptr->release & PAD_SQUARE) != 0) + { + work->field_A38_local_data = 1; + } + + if ((work->field_9B0_pad_ptr->status & PAD_SQUARE) != 0) + { + work->field_926++; + } + else + { + work->field_926 = 0; + } + + if (time == 8) + { + work->field_90C_pWeaponFn = sub_80057BF0; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + } +} + +void sna_bomb_800541A8(SnaInitWork *work, int time) +{ + void *pFn; // $a1 + + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + if (sna_8004FDE8(work, &stru_8009EFE4[0])) + { + // c4 is attached to a moving target + pFn = sna_800571B8; + } + else + { + // c4 is placed on a static surface + pFn = sna_80057118; + } + sna_start_anim_8004E1F4(work, pFn); +} + +// or sna_no_weapon_80054210 ? +void sna_anim_chokethrow_begin1_80054210(SnaInitWork *work, int time) +{ + void *pFn; + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + if (gSnaMoveDir_800ABBA4 >= 0) + { + pFn = sna_anim_throw_800589C8; + } + else + { + pFn = sna_anim_chokethrow_begin2_80058C80; + } + sna_start_anim_8004E1F4(work, pFn); +} + +void sna_8005425C(SnaInitWork *work, int time) +{ + short new_y; // $v0 + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, 134, 4); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK26); + } + + if (work->body.is_end) + { + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_DOWNED); + sna_start_anim_8004E1F4(work, sna_anim_prone_idle_800528BC); + SetAction_8004E22C(work, work->actpack->still->crouch, 0); + new_y = work->control.turn.vy + 2048; + work->control.turn.vy = new_y; + work->control.rot.vy = new_y; + sna_clear_flags1_8004E308(work, (SNA_FLAG1_UNK3 | SNA_FLAG1_UNK26)); + sna_clear_invuln_8004F2EC(work); + } +} + +void sna_80054318(SnaInitWork *work, int time) +{ + if (time == 2 && gSnaMoveDir_800ABBA4 >= 0) + { + sna_start_anim_8004E1F4(work, sna_anim_run_8005292C); + return; + } + + if ((work->field_9B0_pad_ptr->status & PAD_DOWN) != 0) + { + sna_set_flags2_8004E330(work, SNA_FLAG2_UNK1); + } + + if (work->body.is_end) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + return; + } +} + +void sna_fn_800543A8(SnaInitWork *work, int time) +{ + if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) && time >= 16) + { + GM_ClearPlayerStatusFlag(PLAYER_PAD_OFF | PLAYER_MENU_DISABLE); + if (sna_prone_check_standup_80050398(work)) + { + sna_clear_invuln_8004F2EC(work); + sna_clear_flags1_8004E308( + work, (SNA_FLAG1_UNK2 | SNA_FLAG1_UNK3 | SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6 | SNA_FLAG1_UNK10)); + sna_clear_flags2_8004E344(work, SNA_FLAG2_UNK9); + } + } +} + +void sna_anim_duct_move_80054424(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sub_8005688C; + work->field_9CC_anim_update_fn_1p = sub_80052468; + SetAction_8004E22C(work, work->actpack->still->crouch, 4); + } + work->field_A60.vy = work->control.levels[0] + 150; +} + +void sna_anim_duct_idle_80054488(SnaInitWork *work, int time) +{ + if (time == 0) + { + GM_SetPlayerStatusFlag(PLAYER_MOVE); + work->field_9C8_anim_update_fn_3p = sub_80056928; + work->field_9CC_anim_update_fn_1p = sub_80052468; + } + work->field_A60.vy = work->control.levels[0] + 250; + sna_80050440(work); +} + +void sna_anim_shot_flinch_800544E0(SnaInitWork *work, int time) +{ + int action_flag; + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_80052540; + sna_set_invuln_8004F2A0(work, 0); + + if (work->field_89C_pTarget->vital < 1) + { + GM_SeSet2(0, 0x3f, SE_PLAYEROUT); + sna_8004F8E4(work, work->field_A26_stance == SNA_STANCE_CROUCH ? 125 : 128); + } + + if (work->field_A26_stance == SNA_STANCE_CROUCH) + { + action_flag = work->actpack->damage->field_8; + } + else if ((GV_Time & 1) != 0) + { + action_flag = work->actpack->damage->field_0; + work->field_A26_stance = SNA_STANCE_STANDING; + } + else + { + action_flag = work->actpack->damage->field_1; + work->field_A26_stance = SNA_STANCE_STANDING; + } + + SetAction_8004E22C(work, action_flag, 4); + } + + if (EndMotion(work)) + { + sna_set_invuln_8004F2A0(work, 32); + GM_ClearPlayerStatusFlag(PLAYER_DAMAGED | PLAYER_WEAPON_DISABLE); + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + } + + if (work->field_A26_stance == SNA_STANCE_CROUCH) + { + sna_80050568(work); + } +} + +void sna_act_helper2_helper7_80054648(SnaInitWork *work, int time) +{ + if (time == 0) + { + sna_set_invuln_8004F2A0(work, 0); + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_800525F8; + + SetAction_8004E22C(work, work->actpack->special2->field_4, 4); + + if (work->field_89C_pTarget->vital <= 0) + { + GM_SeSet2(0, 63, SE_PLAYEROUT); + sna_8004F8E4(work, 128); + } + } + + if (work->body.is_end) + { + sna_set_invuln_8004F2A0(work, 32); + GM_ClearPlayerStatusFlag(PLAYER_DAMAGED); + sna_start_anim_8004E1F4(work, sna_anim_box_idle_800553EC); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK6); + } +} + +// knocked down by grenade etc +void sna_anim_knockdown_80054710(SnaInitWork *work, int time) +{ + int bVar1; + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_A26_stance = SNA_STANCE_STANDING; + work->field_9CC_anim_update_fn_1p = sna_fn_80052540; + sna_set_invuln_8004F2A0(work, 0); + + if ((GV_Time & 1) != 0) + { + bVar1 = work->actpack->damage->field_2; + work->field_A54.prone_bool_thing = 1; + } + else + { + bVar1 = work->actpack->damage->field_3; + work->field_A54.prone_bool_thing = 2; + } + + SetAction_8004E22C(work, bVar1, 4); + + if (work->field_89C_pTarget->vital < 1 && GM_GameOverTimer == 0) + { + GM_SeSet2(0, 0x3f, SE_PLAYEROUT); + GM_GameOverTimer = -1; + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK20); + } + + work->control.skip_flag |= CTRL_BOTH_CHECK; + } + + if (work->control.height < 500) + { + work->field_A26_stance = SNA_STANCE_CROUCH; + GM_SetPlayerStatusFlag(PLAYER_GROUND); + } + + if (work->field_798_p_height < 250) + { + work->control.skip_flag &= ~CTRL_BOTH_CHECK; + } + + if (EndMotion(work)) + { + sna_set_invuln_8004F2A0(work, 32); + sna_start_anim_8004E1F4(work, sna_anim_knockdown_idle_80054930); + } + else + { + if (time < 12 && work->field_A54.prone_bool_thing == 1) + { + work->control.turn.vy += 170; + } + + if (time > 16) + { + GV_NearExp8V(&work->field_A2C.vx, &DG_ZeroVector.vx, 3); + } + + if ((work->field_A54.prone_bool_thing == 1 && time == 12) || + (work->field_A54.prone_bool_thing == 2 && time == 0x14)) + { + GM_SeSetMode(&work->control.mov, SE_HIT_FLOOR, GM_SEMODE_BOMB); + } + + work->control.step.vx = work->field_A2C.vx; + work->control.step.vz = work->field_A2C.vz; + sna_80050568(work); + } +} + +void sna_anim_knockdown_idle_80054930(SnaInitWork *work, int time) +{ + int local_field_A54; // $v1 + int action_flag; // $a1 + short v5; // $a1 + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_knockdown_check_getup_80056A1C; + work->field_9CC_anim_update_fn_1p = sna_fn_80052540; + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK5); + GM_SetPlayerStatusFlag(PLAYER_DOWNED | PLAYER_GROUND); + GM_ClearPlayerStatusFlag(PLAYER_DAMAGED); + local_field_A54 = work->field_A54.prone_bool_thing; + work->field_A26_stance = SNA_STANCE_CROUCH; + if (local_field_A54 == 1) + { + action_flag = work->actpack->damage->field_4; + } + else + { + action_flag = work->actpack->damage->field_5; + } + SetAction_8004E22C(work, action_flag, 4); + if (work->field_89C_pTarget->vital <= 0) + { + v5 = 127; + if (work->field_A54.prone_bool_thing == 1) + { + v5 = 126; + } + sna_8004F8E4(work, v5); + } + sna_80050568(work); + } +} + +void sna_anim_knockdown_getup_80054A10(SnaInitWork *work, int time) +{ + int action_flag; // $a1 + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_80052540; + if (work->field_A54.prone_bool_thing == 1) + { + action_flag = work->actpack->damage->field_6; + } + else + { + action_flag = work->actpack->damage->field_7; + } + SetAction_8004E22C(work, action_flag, 4); + work->field_A54.prone_bool_thing = 0; + } + + if (work->control.height >= 250) + { + work->control.skip_flag |= CTRL_BOTH_CHECK; + } + + if (work->control.height >= 501) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + + if (time == 2) + { + GM_ClearPlayerStatusFlag(PLAYER_GROUND); + work->control.turn.vx = 0; + } + + if (work->body.is_end) + { + sna_clear_flags1_8004E308(work, (SNA_FLAG1_UNK3 | SNA_FLAG1_UNK5)); + sna_set_invuln_8004F2A0(work, 24); + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_DOWNED); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK1); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + work->control.skip_flag &= ~CTRL_BOTH_CHECK; + } +} + +void sna_anim_knockdown_shot_80054B50(SnaInitWork *work) +{ + work->field_9C8_anim_update_fn_3p = sna_knockdown_check_getup_80056A1C; + work->field_9CC_anim_update_fn_1p = sna_fn_80052540; + + GM_ClearPlayerStatusFlag(PLAYER_DAMAGED); + + if (work->field_89C_pTarget->vital <= 0 && !GM_GameOverTimer) + { + GM_SeSet2(0, 63, SE_PLAYEROUT); + sna_8004F8E4(work, work->field_A54.prone_bool_thing == 1 ? 126 : 127); + } + else + { + sna_set_invuln_8004F2A0(work, 32); + } + + sna_start_anim_8004E1F4(work, sna_anim_knockdown_idle_80054930); +} + +// shot by tank while proned +void sna_anim_knockdown_shot_tank_80054C08(SnaInitWork *work, int time) +{ + char bVar1; + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_80052540; + sna_set_invuln_8004F2A0(work, 0); + + if (work->field_A54.prone_bool_thing == 1) + { + bVar1 = work->actpack->damage->field_A; + } + else + { + bVar1 = work->actpack->damage->field_B; + } + + SetAction_8004E22C(work, bVar1, 4); + + if (work->field_89C_pTarget->vital < 1 && GM_GameOverTimer == 0) + { + GM_SeSet2(0, 0x3f, SE_PLAYEROUT); + GM_GameOverTimer = -1; + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK20); + } + } + + if (EndMotion(work)) + { + sna_set_invuln_8004F2A0(work, 32); + sna_start_anim_8004E1F4(work, sna_anim_knockdown_idle_80054930); + } + else + { + if (time == 12) + { + GM_SetPlayerStatusFlag(PLAYER_GROUND); + } + + if (time > 16) + { + GV_NearExp8V(&work->field_A2C.vx, &DG_ZeroVector.vx, 3); + } + + work->control.step.vx = work->field_A2C.vx; + work->control.step.vz = work->field_A2C.vz; + sna_80050568(work); + } +} + +void sna_knock_80054D68(SnaInitWork *work, int time) +{ + SVECTOR vec; + + int var_a1; + int var_t0; + int temp_v0; + int noise; + int code; + + if (time == 0) + { + GM_SetPlayerStatusFlag(PLAYER_CHECK_WALL); + + if (dword_800ABBC4 == 4) + { + var_a1 = work->actpack->still->tap_wall_l; + } + else + { + var_a1 = work->actpack->still->tap_wall_r; + } + + GM_ConfigObjectOverride(&work->body, var_a1, 0, 4, 1022); + + if (((work->field_91C_weapon_idx >= 0) && (work->field_91C_weapon_idx < 2)) || (work->field_91C_weapon_idx == 3)) + { + var_t0 = 0x578; + + if (work->field_A26_stance != 0) + { + var_t0 = 0x302; + } + } + else + { + var_t0 = 0x325; + + if (work->field_A26_stance != 0) + { + var_t0 = 0x15E; + } + } + + vec.vz = -150; + vec.vy = 0; + vec.vx = (dword_800ABBC4 == 4) ? 300 : -300; + + if (sna_8004F628(work, &vec, -250, HZD_CHK_D_SEGMENT | HZD_CHK_F_SEGMENT, SEGMENT_ATR, var_t0)) + { + temp_v0 = HZD_GetOnlineHazardAtr(); + code = (temp_v0 >> 8) & 7; + + if (code < 4) + { + temp_v0 = GM_GetNoiseSound(temp_v0, 0); + noise = temp_v0; + NewAfterSe(noise, 6); + } + } + } + + if ((work->body.time2 != 0) || (work->body.action2 == 0)) + { + sna_8004E260(work, 0, 4, 0); + GM_ClearPlayerStatusFlag(PLAYER_CHECK_WALL); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK9); + work->field_9C0 = NULL; + } +} + +void sna_act_helper2_helper_80054EFC(SnaInitWork *work, int pTime) +{ + int time2; + int status; + unsigned char actionFlag; + SnaInitWork *actor; + int time; + + actor = work; + time = pTime; + + if (time == 0) + { + actor->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + actor->field_9CC_anim_update_fn_1p = sna_act_helper2_helper_helper_800526BC; + if (GM_CheckPlayerStatusFlag(PLAYER_GROUND)) + { + actionFlag = actor->actpack->special1->field_1; + } + else + { + actionFlag = actor->actpack->special1->field_0; + } + if (GM_CheckPlayerStatusFlag(PLAYER_SQUAT) != 0) + { + sna_8004E260(actor, actionFlag, 4, 0x3FE); + actor->field_A38_local_data = 1; + } + else + { + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) != 0) + { + actor->field_A38_local_data = 2; + } + SetAction_8004E22C(actor, actionFlag, 4); + } + sna_set_flags1_8004E2F4(actor, SNA_FLAG1_UNK26); + } + if (time == 12 && actor->field_A26_stance == 2) + { + NewPadVibration(dword_8009EF08, 1); + NewPadVibration(dword_8009EF10, 2); + } + + if (time == 8 && actor->field_A26_stance != 2 && actor->field_91C_weapon_idx == -1) + { + NewPadVibration(dword_8009EEF8, 1); + NewPadVibration(dword_8009EF00, 2); + } + else if (time == 18 && actor->field_A26_stance != 2 && actor->field_91C_weapon_idx != -1) + { + NewPadVibration(dword_8009EEF8, 1); + NewPadVibration(dword_8009EF00, 2); + } + + if ((time == 6 && actor->field_A26_stance == 2) || + (time == 0 && actor->field_A26_stance != 2 && actor->field_91C_weapon_idx == -1) || + (time == 13 && actor->field_A26_stance != 2 && actor->field_91C_weapon_idx != -1)) + { + GM_SeSetMode(&actor->control.mov, SE_SNEEZE, GM_SEMODE_BOMB); + GM_SetNoise(0x64, 0x10, &actor->control.mov); + } + status = 0; + if (time >= 25) + { + status = actor->field_9B0_pad_ptr->status & (PAD_UP | PAD_DOWN | PAD_LEFT | PAD_RIGHT); + } + switch (actor->field_A38_local_data) + { + case 0: + if (actor->body.is_end != 0 || status != 0) + { + sna_clear_flags1_8004E308(actor, SNA_FLAG1_UNK26); + if (GM_CheckPlayerStatusFlag(PLAYER_CAUTION) != 0) + { + sna_start_anim_8004E1F4(actor, sna_anim_wall_idle_and_c4_80052A5C); + return; + } + sna_start_anim_8004E1F4(actor, dword_8009EEA4[actor->field_A26_stance]); + return; + } + return; + case 1: + if (actor->body.time2 != 0 || status != 0) + { + sna_clear_flags1_8004E308(actor, SNA_FLAG1_UNK26); + sna_8004E260(actor, 0, 4, 0); + if (GM_CheckPlayerStatusFlag(PLAYER_CAUTION) != 0) + { + sna_start_anim_8004E1F4(actor, sna_anim_wall_crouch_80052CCC); + return; + } + sna_start_anim_8004E1F4(actor, sna_anim_crouch_800527DC); + return; + } + break; + case 2: + actor->field_A60.vy = actor->control.levels[0] + 150; + if (time >= 19) + { + time2 = 40 - time; + if (time2 == 0 || status != 0) + { + sna_clear_flags1_8004E308(actor, SNA_FLAG1_UNK26); + sna_start_anim_8004E1F4(actor, sna_anim_duct_move_80054424); + return; + } + actor->field_A60.vy += GV_RandS(16) * time2; + } + break; + } +} + +void sna_anim_scope_80055334(SnaInitWork *param_1, int time) +{ + int action_flag; + int sVar2; + + if (time == 0) + { + param_1->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + param_1->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + + if (param_1->field_A26_stance == SNA_STANCE_STANDING) + { + action_flag = 0x87; + } + else + { + action_flag = sna_8004EAA8(param_1, param_1->field_A26_stance); + } + + SetAction_8004E22C(param_1, action_flag, 4); + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE); + } + + sVar2 = param_1->field_A2A; + if (sVar2 < dword_800ABBDC) + { + sVar2 = dword_800ABBDC; + } + else if (dword_800ABBD4 < sVar2) + { + sVar2 = dword_800ABBD4; + } + + param_1->control.turn.vx = sVar2; +} + +void sna_anim_jpegcam_800553CC(SnaInitWork *work, int time) +{ + sna_anim_scope_80055334(work, time); +} + +void sna_anim_box_idle_800553EC(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_80056A54; + work->field_9CC_anim_update_fn_1p = sna_fn_800525F8; + GM_ClearPlayerStatusFlag(PLAYER_GROUND | PLAYER_SQUAT); + work->field_A26_stance = SNA_STANCE_STANDING; + SetAction_8004E22C(work, work->actpack->special2->field_2, 4); + } +} + +void sna_anim_box_move_8005544C(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_80056AD8; + work->field_9CC_anim_update_fn_1p = sna_fn_800525F8; + SetAction_8004E22C(work, work->actpack->special2->field_3, 4); + GM_SetPlayerStatusFlag(PLAYER_MOVE); + GM_ConfigControlInterp(&work->control, 4); + } +} + +void sna_anim_box_stop_800554B4(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_800525F8; + SetAction_8004E22C(work, work->actpack->special2->field_4, 4); + } + + if (work->body.is_end) + { + sna_start_anim_8004E1F4(work, sna_anim_box_idle_800553EC); + } +} + +void sna_anim_dying_80055524(SnaInitWork *work, int time) +{ + UnkSnakeStruct2 *unk2 = &work->field_9E4; + + if (time == 0) + { + int f9e6; + + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + + sub_8004F338(work); + GM_SetPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_PAD_OFF); + + if ((unk2->field_9EC_flags3 & 0x400) == 0) // TODO: make flags enum for flags3 + { + if ((unk2->field_9EC_flags3 & 0x4000) != 0) + { + work->control.s_sphere = -2; + work->control.r_sphere = -2; + work->field_A28 = -2; + } + else + { + work->control.s_sphere = -1; + work->control.r_sphere = -1; + work->field_A28 = -1; + } + } + + sna_set_invuln_8004F2A0(work, 0); + GetAction(work) = -1; + SetAction_8004E22C(work, unk2->field_9E4, 4); + + f9e6 = unk2->field_9E6; + if (f9e6 >= 0) + { + work->control.turn.vy = f9e6; + work->control.rot.vy = f9e6; + } + + work->field_A38_local_data = -1; + work->field_A3A = -1; + + if ((unk2->field_9EC_flags3 & 0x1000) == 0) + { + if (GM_CheckPlayerStatusFlag(PLAYER_CB_BOX) == 0) + { + if ((work->field_9AC & 2) != 0) + { + if ((GM_GameOverTimer == 0 && unk2->field_9F2 == 0) || + sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK29) != 0) + { + work->field_A38_local_data = work->field_9A8_current_item; + sub_8004F204(work); + } + else + { + GM_SetPlayerStatusFlag(PLAYER_NOT_SIGHT); + } + } + } + else + { + work->field_A38_local_data = work->field_9A8_current_item; + sub_8004F204(work); + } + + if ((work->field_920_tbl & 0x200) != 0) + { + if (GM_GameOverTimer != 0 || unk2->field_9F2 != 0) + { + GM_SetPlayerStatusFlag(PLAYER_NOT_SIGHT); + } + else + { + work->field_A3A = work->field_91C_weapon_idx; + sub_8004F14C(work); + } + } + else if (work->field_91C_weapon_idx != -1 && GM_CurrentWeaponId == WP_None) + { + sub_8004F14C(work); + } + } + + GM_ClearPlayerStatusFlag(PLAYER_SQUAT); + } + + if ((unk2->field_9EC_flags3 & 0x200) != 0 && work->control.height < 500) + { + GM_SetPlayerStatusFlag(PLAYER_GROUND); + } + else + { + GM_ClearPlayerStatusFlag(PLAYER_GROUND); + work->control.turn.vx = 0; + } + + if ((unk2->field_9EC_flags3 & 2) == 0) + { + work->field_A2C = DG_ZeroVector; + } + + if ((unk2->field_9EC_flags3 & 0x4000) == 0) + { + if ((unk2->field_9EC_flags3 & 2) == 0) + { + int level; + int new_height; + short y_pos; + + new_height = (unsigned short)work->body.height - (-unk2->field_9EE + work->control.levels[0]); + level = work->control.levels[0]; + work->control.height = new_height; + y_pos = new_height + level; + + work->control.mov.vy = y_pos; + work->body.objs->world.t[1] = y_pos; + + DG_GetLightMatrix2(&work->control.mov, work->light); + } + } + else + { + int levels[2]; + sub_8004E588(work->control.map->hzd, &work->control.mov, levels); + work->control.levels[0] = levels[0]; + work->control.levels[1] = levels[1]; + } + + if (work->body.is_end) + { + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK22); + + if ((unk2->field_9EC_flags3 & 0x20) == 0) + { + work->control.skip_flag &= ~CTRL_SKIP_TRAP; + } + + if ((unk2->field_9EC_flags3 & 0x100) != 0) + { + work->m_ctrl.interp = 15; + work->m_ctrl.info1.field_14 = 0; + work->field_9B8_fn_anim = sna_fn_nothing_80053B80; + work->field_9BC_anim_frame = 0; + work->field_A3A = 0; + work->field_A38_local_data = 0; + } + else + { + int stance; + int fa38; + int fa3a; + + GM_ClearPlayerStatusFlag(PLAYER_GROUND | PLAYER_MENU_DISABLE | + PLAYER_PAD_OFF); + sna_clear_invuln_8004F2EC(work); + + stance = unk2->field_9EA; + fa38 = work->field_A38_local_data; + fa3a = work->field_A3A; + + if (stance < SNA_STANCE_STANDING) + { + if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + stance = work->field_A26_stance; + } + else + { + stance = SNA_STANCE_UNK; + } + } + + if ((unk2->field_9EC_flags3 & 0x1000) != 0) + { + sna_start_anim_8004E1F4(work, sna_anim_box_idle_800553EC); + work->field_A26_stance = SNA_STANCE_STANDING; + } + else + { + if (stance < SNA_STANCE_UNK) + { + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + GM_ClearPlayerStatusFlag(PLAYER_INTRUDE); + sub_8004ED08(work); + } + + work->field_A26_stance = stance; + sna_start_anim_8004E1F4(work, dword_8009EEA4[stance]); + } + else + { + if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + work->field_A26_stance = SNA_STANCE_CROUCH; + GM_SetPlayerStatusFlag(PLAYER_INTRUDE | PLAYER_GROUND); + sna_8004EC8C(work); + } + + sna_start_anim_8004E1F4(work, sna_anim_duct_move_80054424); + } + + if ((unk2->field_9EC_flags3 & 8) == 0) + { + if (fa38 >= 0) + { + GM_CurrentItemId = fa38; + + if ((unsigned int)(fa38 - 2) < 3) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + } + if (fa3a >= 0) + { + GM_CurrentWeaponId = fa3a; + GM_WeaponChanged = 1; + + if (fa3a == WP_Rifle) + { + work->field_A26_stance = SNA_STANCE_CROUCH; + } + } + } + } + } + } +} + +// things like "a surveillance camera??" where player control is halted +void sna_anim_mini_cutscene_800559D8(SnaInitWork *work, int time) +{ + SVECTOR vec; + + UnkSnakeStruct2 *pStr = &work->field_9E4; + int weapon; + int item; + int stance; + unsigned char action; + + short temp_v0; + short temp_v1_2; + short temp_v0_2; + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = &sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = &sna_fn_nothing_80053B80; + + sub_8004F338(work); + + GM_SetPlayerStatusFlag(PLAYER_PAD_OFF | PLAYER_MENU_DISABLE); + sna_set_invuln_8004F2A0(work, 0); + GM_ClearPlayerStatusFlag(PLAYER_GROUND | PLAYER_SQUAT); + + work->field_A00.target_next = -1; + work->field_A00.addr = GM_PlayerAddress; + work->field_A00.next = GM_PlayerAddress; + + NavigateSetTarget(&work->field_A00, work->control.map->hzd, &work->field_9E4.field_9F4); + temp_v0 = HZD_GetAddress(work->control.map->hzd, &work->field_9E4.field_9F4, work->field_A00.target); + + temp_v1_2 = (temp_v0 >> 8) & 0xff; + temp_v0_2 = temp_v0 & 0xff; + + if ((temp_v1_2 == 0xff) || (temp_v0_2 == 0xff) || (temp_v1_2 != temp_v0_2)) + { + pStr->field_9EC_flags3 |= 0x200; + } + + if (pStr->field_9EC_flags3 & 0x200) + { + GV_SubVec3(&pStr->field_9F4, &work->control.mov, &vec); + work->control.turn.vy = GV_VecDir2(&vec); + work->control.s_sphere = -1; + work->control.r_sphere = -1; + work->field_A28 = -1; + } + + work->field_A38_local_data = -1; + + if (!(pStr->field_9EC_flags3 & 0x100) && ((GM_CheckPlayerStatusFlag(PLAYER_CB_BOX)) || (work->field_9AC & 2))) + { + work->field_A38_local_data = work->field_9A8_current_item; + sub_8004F204(work); + } + + work->field_A3A = -1; + + if (work->field_920_tbl & 0x200) + { + work->field_A3A = work->field_91C_weapon_idx; + sub_8004F14C(work); + } + + if (pStr->field_9EC_flags3 & 0x100) + { + stance = SNA_STANCE_STANDING; + action = work->actpack->special2->field_3; + } + else if (pStr->field_9EC_flags3 & 1) + { + stance = SNA_STANCE_CROUCH; + action = work->actpack->move->crouch; + GM_SetPlayerStatusFlag(PLAYER_GROUND); + } + else + { + stance = SNA_STANCE_STANDING; + action = work->actpack->move->stand; + } + + work->field_A26_stance = stance; + + SetAction_8004E22C(work, action, 4); + GM_SetPlayerStatusFlag(PLAYER_MOVE); + } + + if (!(pStr->field_9EC_flags3 & 0x200)) + { + work->field_A00.addr = GM_PlayerAddress; + GM_ZoneNavi(&work->field_A00, &work->control); + } + + GV_NearExp4PV(&work->control.rot.vx, &work->control.turn.vx, 3); + GV_NearExp4PV(&work->control.rot.vx, &work->control.turn.vx, 3); + + if (NavigateGetTargetDist(&work->field_A00, &work->control.mov) < pStr->field_9F4.pad) + { + sna_clear_invuln_8004F2EC(work); + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK22); + GM_ClearPlayerStatusFlag(PLAYER_PAD_OFF | PLAYER_MENU_DISABLE); + + if (pStr->field_9EC_flags3 & 0x200) + { + work->control.s_sphere = 450; + work->control.r_sphere = 450; + work->field_A28 = 450; + } + + if (!(pStr->field_9EC_flags3 & 2)) + { + item = work->field_A38_local_data; + weapon = work->field_A3A; + + if (item >= 0) + { + GM_CurrentItemId = item; + + if ((item == IT_Box1) || (item == IT_Box2) || (item == IT_Box3)) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + } + + if (weapon >= 0) + { + GM_CurrentWeaponId = weapon; + GM_WeaponChanged = 1; + + if (weapon == WP_Rifle) + { + work->field_A26_stance = SNA_STANCE_CROUCH; + } + else if (weapon == WP_Stinger) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + } + } + + if (!(pStr->field_9EC_flags3 & 8)) + { + work->control.skip_flag &= ~CTRL_SKIP_TRAP; + } + + if (pStr->field_9EC_flags3 & 1) + { + sna_80050440(work); + sna_clear_flags1_8004E308(work, 4); + } + + if (pStr->field_9EC_flags3 & 0x100) + { + sna_start_anim_8004E1F4(work, &sna_anim_box_stop_800554B4); + } + else + { + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + } + + if (pStr->field_9E8 >= 0) + { + work->control.turn.vy = pStr->field_9E8; + } + } +} + +// similar to above, seems freeze snake and make him look at a certain direction or track something +// triggers on first elevator ride at dock and right before mantis fight to look at meryl +// PERMUTER +static inline void sna_act_unk_helper3_helper_80055DD8(SnaInitWork *work, UnkSnakeStruct2 *pStr) +{ + SnaInitWork *work2; + char new_var; + int action; + int field_9E4; + work2 = work; + action = work->body.action; + if (!(((action != work->actpack->trans->crouch) || work->body.is_end) && ((action != work->actpack->trans->stand) || work2->body.is_end))) + { + pStr->field_9E4 = 1; + } else { + work2 = work; + if (((((action == work->actpack->damage->field_6) && (work->body.is_end == 0)) || ((action == work->actpack->damage->field_7) && (work2->body.is_end == 0))) || ((action == work->actpack->damage->field_2) && work->body.is_end)) || ((action == work->actpack->damage->field_3) && work2->body.is_end)) + { + if ((action == work->actpack->damage->field_2) && work->body.is_end) + { + SetAction_8004E22C(work, 71, 4); + } + if ((action == work->actpack->damage->field_3) && work->body.is_end) + { + SetAction_8004E22C(work, 72, 4); + } + field_9E4 = 2; + + } + else + if ((((action == work->actpack->damage->field_4) || (action == work->actpack->damage->field_5)) || (action == work->actpack->damage->field_A)) || (action == work->actpack->damage->field_B)) + { + field_9E4 = 3; + } + else + if (((action == work->actpack->damage->field_2) && (work->body.is_end == 0)) || ((action == (new_var = work->actpack->damage->field_3)) && (work->body.is_end == 0))) + { + field_9E4 = 4; + } + else + { + field_9E4 = 0; + } + pStr->field_9E4 = field_9E4; + } +} + +static inline void sna_act_unk_helper3_helper2_80055DD8(SnaInitWork *work) +{ + switch (work->field_9E4.field_9EA) + { + case 0: + switch (work->field_A26_stance) + { + case SNA_STANCE_STANDING: + if (++work->field_9E4.field_9EE >= 4) + { + work->field_9E4.field_9EE = 0; + work->field_9E4.field_9E4 = 6; + } + break; + + case SNA_STANCE_SQUAT: + SetAction_8004E22C(work, work->actpack->still->stand, 4); + + if (++work->field_9E4.field_9EE == 4) + { + work->field_9E4.field_9EE = 0; + work->field_9E4.field_9E4 = 6; + } + + break; + + case SNA_STANCE_CROUCH: + SetAction_8004E22C(work, work->actpack->trans->stand, 4); + + if (work->body.is_end != 0) + { + SetAction_8004E22C(work, work->actpack->still->stand, 4); + work->field_9E4.field_9E4 = 6; + } + break; + } + break; + + case 1: + switch (work->field_A26_stance) + { + case SNA_STANCE_STANDING: + SetAction_8004E22C(work, work->actpack->still->squat, 4); + + if (++work->field_9E4.field_9EE == 4) + { + work->field_9E4.field_9EE = 0; + work->field_9E4.field_9E4 = 6; + } + break; + + case SNA_STANCE_SQUAT: + if (++work->field_9E4.field_9EE == 4) + { + work->field_9E4.field_9EE = 0; + work->field_9E4.field_9E4 = 6; + } + break; + + case SNA_STANCE_CROUCH: + SetAction_8004E22C(work, work->actpack->trans->stand, 4); + + if (work->body.is_end != 0) + { + SetAction_8004E22C(work, work->actpack->still->squat, 4); + work->field_A26_stance = SNA_STANCE_STANDING; + } + break; + } + break; + + case 2: + switch (work->field_A26_stance) + { + case SNA_STANCE_STANDING: + SetAction_8004E22C(work, work->actpack->still->squat, 4); + + if (++work->field_9E4.field_9EE == 4) + { + work->field_9E4.field_9EE = 0; + work->field_A26_stance = SNA_STANCE_SQUAT; + } + break; + + case SNA_STANCE_SQUAT: + SetAction_8004E22C(work, work->actpack->trans->crouch, 4); + + if (work->body.is_end != 0) + { + SetAction_8004E22C(work, work->actpack->still->crouch, 4); + work->field_9E4.field_9E4 = 6; + } + break; + + case SNA_STANCE_CROUCH: + if (++work->field_9E4.field_9EE == 4) + { + work->field_9E4.field_9EE = 0; + work->field_9E4.field_9E4 = 6; + } + } + break; + } +} + +void sna_act_unk_helper3_80055DD8(SnaInitWork *work, int time) +{ + UnkSnakeStruct2 *pStr; + int height; + int stance; + int action_flag; + int item; + int weapon; + + pStr = &work->field_9E4; + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = &sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = &sna_fn_nothing_80053B80; + + sub_8004F338(work); + + GM_SetPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_PAD_OFF); + sna_set_invuln_8004F2A0(work, 0); + GM_ClearPlayerStatusFlag(PLAYER_SQUAT | PLAYER_GROUND); + + work->field_A38_local_data = IT_None; + + if (GM_CheckPlayerStatusFlag(PLAYER_CB_BOX) || (work->field_9AC & 2)) + { + work->field_A38_local_data = work->field_9A8_current_item; + sub_8004F204(work); + } + + work->field_A3A = WP_None; + + if (work->field_920_tbl & 0x200) + { + work->field_A3A = work->field_91C_weapon_idx; + sub_8004F14C(work); + } + + if (pStr->field_9EA != 2) + { + work->control.turn.vx = 0; + } + + sna_act_unk_helper3_helper_80055DD8(work, pStr); + } + + height = work->control.height; + stance = pStr->field_9EA; + + switch (stance) + { + case 0: + if (height > 500) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + break; + + case 1: + if (height < 500) + { + work->field_A26_stance = stance; + } + break; + + case 2: + if (height < 500) + { + work->field_A26_stance = stance; + } + break; + } + + switch(pStr->field_9E4) + { + case 0: + SetAction_8004E22C(work, sna_8004EAA8(work, work->field_A26_stance), 4); + pStr->field_9E4 = 5; + break; + + case 1: + if (work->body.is_end != 0) + { + SetAction_8004E22C(work, sna_8004EAA8(work, work->field_A26_stance), 4); + pStr->field_9E4 = 5; + } + break; + + case 2: + if (work->body.is_end != 0) + { + SetAction_8004E22C(work, sna_8004EAA8(work, 0), 4); + work->field_A26_stance = 0; + pStr->field_9E4 = 5; + } + break; + + case 3: + if (work->field_A54.prone_bool_thing == 1) + { + action_flag = work->actpack->damage->field_6; + } + else + { + action_flag = work->actpack->damage->field_7; + } + + work->field_A54.prone_bool_thing = 0; + SetAction_8004E22C(work, action_flag, 4); + pStr->field_9E4 = 2; + break; + + case 4: + if (work->body.is_end != 0) + { + if (work->field_A54.prone_bool_thing == 1) + { + SetAction_8004E22C(work, work->actpack->damage->field_6, 4); + } + else + { + SetAction_8004E22C(work, work->actpack->damage->field_7, 4); + } + + pStr->field_9E4 = 2; + } + break; + + case 5: + sna_act_unk_helper3_helper2_80055DD8(work); + break; + + case 6: + work->field_A26_stance = pStr->field_9EA; + + if (work->field_A26_stance == SNA_STANCE_SQUAT) + { + GM_SetPlayerStatusFlag(PLAYER_SQUAT); + } + else if (work->field_A26_stance == SNA_STANCE_CROUCH) + { + GM_SetPlayerStatusFlag(PLAYER_GROUND); + } + + sna_clear_invuln_8004F2EC(work); + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_set_flags1_8004E2F4(work, 0x200000); + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_PAD_OFF); + + if (!(pStr->field_9EC_flags3 & 0x2)) + { + item = work->field_A38_local_data; + weapon = work->field_A3A; + + if (item >= 0) + { + GM_CurrentItemId = item; + + if (item == IT_Box1 || item == IT_Box2 || item == IT_Box3) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + } + + if (weapon >= 0) + { + GM_CurrentWeaponId = weapon; + GM_WeaponChanged = 1; + + if (weapon == WP_Rifle) + { + work->field_A26_stance = SNA_STANCE_CROUCH; + } + else if (weapon == WP_Stinger) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + } + } + + if (!(pStr->field_9EC_flags3 & 0x8)) + { + work->control.skip_flag &= ~CTRL_SKIP_TRAP; + } + + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + + if (pStr->field_9E8 >= 0) + { + work->control.turn.vy = pStr->field_9E8; + } + break; + } +} + +void sna_act_helper3_helper_80056650(SnaInitWork *work, int time) +{ + int result; + int footstepsFrame; // guessed + + if (time == 0) + { + int *status = &GM_GameStatus; + + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + + *status |= STATE_PADRELEASE; + + sub_8004F338(work); + work->control.skip_flag &= ~CTRL_SKIP_TRAP; + sna_set_invuln_8004F2A0(work, 0); + GM_ClearPlayerStatusFlag(PLAYER_SQUAT | PLAYER_GROUND); + sub_8004F14C(work); + sub_8004F204(work); + work->field_A26_stance = 0; + SetAction_8004E22C(work, 3, 1); + } + + if (!work->field_908_weapon_actor) + { + GV_ACT *timerBomb; + timerBomb = NewTimerBomb(&work->control, &work->body, 4, + &work->field_914_trigger, 1); + work->field_908_weapon_actor = timerBomb; + work->field_924 = 0; + SetAction_8004E22C(work, 0x3c, 4); + + if (!work->field_908_weapon_actor) + { + return; + } + } + + result = 1; + footstepsFrame = work->m_ctrl.info1.frame; + switch (work->field_924) + { + case 0: + if (EndMotion(work)) + { + SetAction_8004E22C(work, 3, 1); + work->field_924 = 1; + } + break; + case 1: + SetAction_8004E22C(work, 0x32, 1); + work->field_924 = 2; + break; + case 2: + if (footstepsFrame == 6) + { + result = 3; + work->field_924 = 3; + } + break; + case 3: + result = 0; + + if (EndMotion(work)) + { + sna_clear_invuln_8004F2EC(work); + GM_GameStatus &= ~STATE_PADRELEASE; + GV_DestroyActorQuick(work->field_908_weapon_actor); + work->field_908_weapon_actor = NULL; + work->field_924 = 0; + work->field_9E4.field_9F0 = 0; + sna_set_flags1_8004E2F4(work, 0x200000); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } + break; + } + + work->field_914_trigger = result; +} + +void sub_8005684C(SnaInitWork *work) +{ + int y; + + if ((work->field_9B0_pad_ptr->status & (PAD_LEFT | PAD_RIGHT)) == 0) + { + return; + } + + y = work->control.turn.vy; + if ((work->field_9B0_pad_ptr->status & PAD_LEFT) != 0) + { + y += 32; + } + else + { + y -= 32; + } + work->control.turn.vy = y; +} + +void sub_8005688C(SnaInitWork *work, int time) +{ + int action_flag; // $a1 + + if ((work->field_9B0_pad_ptr->status & (PAD_DOWN | PAD_UP)) != 0) + { + if ((work->field_9B0_pad_ptr->status & PAD_UP) != 0) + { + action_flag = work->actpack->move->crouch; + } + else + { + action_flag = work->actpack->move->crouch_backwards; + } + SetAction_8004E22C(work, action_flag, 4); + sna_start_anim_8004E1F4(work, sna_anim_duct_idle_80054488); + } + else + { + sub_8005684C(work); + } +} + +void sub_80056928(SnaInitWork *param_1, int time) +{ + char bVar1; + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) == 0) + { + sna_start_anim_8004E1F4(param_1, sna_anim_enter_duct_80053E9C); + sna_set_invuln_8004F2A0(param_1, 0); + } + else + { + if ((param_1->field_9B0_pad_ptr->status & (PAD_DOWN | PAD_UP)) == 0) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(param_1, sna_anim_duct_move_80054424); + SetAction_8004E22C(param_1, param_1->actpack->still->crouch, 4); + } + else + { + if ((param_1->field_9B0_pad_ptr->status & PAD_UP) != 0) + { + bVar1 = param_1->actpack->move->crouch; + } + else + { + bVar1 = param_1->actpack->move->crouch_backwards; + } + + SetAction_8004E22C(param_1, bVar1, 4); + sub_8005684C(param_1); + } + } +} + +void sna_knockdown_check_getup_80056A1C(SnaInitWork *work, int time) +{ + if (work->field_9B0_pad_ptr->press) + { + sna_knockdown_getup_80050668(work); + } +} + +void sna_80056A54(SnaInitWork *work, int time) +{ + if (sna_sub_8004E358(work, SNA_FLAG2_UNK5) == 0) + { + if (gSnaMoveDir_800ABBA4 >= 0) + { + sna_start_anim_8004E1F4(work, sna_anim_box_move_8005544C); + } + } + else + { + if ((work->field_9B0_pad_ptr->status & (PAD_DOWN | PAD_UP)) != 0) + { + sna_start_anim_8004E1F4(work, sna_anim_box_move_8005544C); + } + + sub_8004FA9C(work); + } +} + +void sna_fn_80056AD8(SnaInitWork *work, int time) +{ + if (gSnaMoveDir_800ABBA4 < 0) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_box_stop_800554B4); + } + else + { + SetAction_8004E22C(work, work->actpack->special2->field_3, 4); + GM_SetPlayerStatusFlag(PLAYER_MOVE); + + if (sna_sub_8004E358(work, SNA_FLAG2_UNK5) == 0) + { + work->control.turn.vy = (short)gSnaMoveDir_800ABBA4; + } + else if ((work->field_9B0_pad_ptr->status & (PAD_DOWN | PAD_UP)) == 0) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_box_stop_800554B4); + } + else + { + sub_8004FA9C(work); + } + } +} + +void sna_anim_shoot_weapon_80056B88(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_anim_shoot_weapon_helper_80057590; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, work->actpack->still->setup, 4); + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + } +} + +void sna_anim_rungun_begin_80056BDC(SnaInitWork *param_1, int time) +{ + if (time == 0) + { + param_1->field_9C8_anim_update_fn_3p = sna_anim_rungun_begin_helper_800577B4; + param_1->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + GM_SetPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(param_1, param_1->actpack->move->setup, 4); + } +} + +void sna_anim_rungun_80056C3C(SnaInitWork *param_1, int time) +{ + if (time == 0) + { + param_1->field_9C8_anim_update_fn_3p = sna_anim_rungun_helper_80057844; + param_1->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + GM_SetPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(param_1, param_1->actpack->move->stand, 4); + } +} + +void sna_anim_nikita_80056C9C(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + + SetAction_8004E22C(work, work->actpack->attack->shoot, 4); + sna_8004E260(work, 0, 4, 0); + + if (*work->field_918_n_bullets == 0) + { + work->field_A38_local_data = 1; + } + else + { + if (sna_sub_8004E358(work, SNA_FLAG2_UNK5)) + { + sna_8004EC00(work); + } + } + + sna_set_flags2_8004E330(work, SNA_FLAG2_UNK9); + } + + if (EndMotion(work)) + { + SetAction_8004E22C(work, SET, 4); + } + + if (work->field_A38_local_data != 0 || time >= 24) + { + if (work->field_A38_local_data != 0 || time == 24) + { + GM_ClearPlayerStatusFlag(PLAYER_ITEM_DISABLE); + } + + if (dword_8009F46C[0] == 0) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + sna_clear_flags2_8004E344(work, SNA_FLAG2_UNK9); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } + } +} + +static inline int sna_anim_psg1_helper_80056DDC(void) +{ + return 1; +} + +void sna_anim_psg1_80056DDC(SnaInitWork *work, int time) +{ + MATRIX rotation; + + if (time == 0) + { + work->field_A44.pad = 0; + work->field_A52 = 0; + + work->field_9C8_anim_update_fn_3p = &sna_anim_psg1_helper_80057FD4; + work->field_9CC_anim_update_fn_1p = &sna_anim_psg1_helper_80057FD4; + + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_set_invuln_8004F2A0(work, 0); + + GM_Camera.first_person = 0; + work->field_A20 = sna_anim_psg1_helper_80056DDC(); + + switch (work->field_A26_stance) + { + case 0: + SetAction_8004E22C(work, work->actpack->still->squat, 4); + work->field_A2C.vy = -128; + work->field_A3A = 0; + break; + + case 1: + work->field_A3A = 1; + break; + + case 2: + work->field_A3A = 3; + break; + } + } + + if (work->field_A3A < 3) + { + GM_SetPlayerStatusFlag(PLAYER_MENU_DISABLE); + } + + switch (work->field_A3A) + { + case 0: + if (time == 4) + { + work->field_A3A = 1; + } + + break; + + case 1: + GM_SetPlayerStatusFlag(PLAYER_SQUAT); + work->field_A26_stance = 1; + SetAction_8004E22C(work, work->actpack->trans->crouch, 4); + work->field_A3A = 2; + work->control.skip_flag |= (CTRL_BOTH_CHECK | CTRL_SKIP_TRAP); + break; + + case 2: + if (work->field_798_p_height < 250) + { + work->control.skip_flag &= ~CTRL_BOTH_CHECK; + } + + if (work->m_ctrl.info1.frame == 7) + { + work->control.skip_flag &= ~CTRL_SKIP_TRAP; + } + + if (EndMotion(work)) + { + work->field_A3A = 3; + GM_SetPlayerStatusFlag(PLAYER_GROUND); + work->field_A26_stance = 2; + } + + break; + + case 3: + sub_8004EB74(work); + GM_SetPlayerStatusFlag(PLAYER_NORMAL_WATCH); + + if (GM_UnkFlagBE != 0) + { + GM_SetPlayerStatusFlag(PLAYER_STILL_WATCH); + } + + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_SQUAT); + sna_clear_invuln_8004F2EC(work); + work->control.turn.vx = work->field_A2A; + SetAction_8004E22C(work, SET, 4); + work->field_A3A = 4; + break; + + default: + DG_SetPos(&work->body.objs->objs[4].world); + DG_MovePos(&svector_800AB7E4); + ReadRotMatrix(&rotation); + + work->field_A60.vx = rotation.t[0]; + work->field_A60.vy = rotation.t[1]; + work->field_A60.vz = rotation.t[2]; + + if (GM_CheckPlayerStatusFlag(PLAYER_INVINCIBLE) && (GM_Camera.first_person != 0)) + { + GM_SnakeCamera.position.vy += GV_RandU(16) * work->field_A24_invuln_frames; + } + + break; + } +} + +void sna_anim_stinger_800570C0(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = OP_ShootStinger; + work->field_9CC_anim_update_fn_1p = OP_ShootStinger; + SetAction_8004E22C(work, SET, 4); + work->field_A26_stance = SNA_STANCE_STANDING; + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_GROUND | + PLAYER_SQUAT); + } +} + +void sna_80057118(SnaInitWork *work, int time) +{ + if (time == 0) + { + if (bakudan_count_8009F42C >= 16) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + GM_SeSet2(0, 0x3f, SE_BUZZER); + return; + } + + work->field_9C8_anim_update_fn_3p = sub_80058644; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, work->actpack->attack->field_5, 4); + } + + sub_8004E9D0(work); +} + +void sna_800571B8(SnaInitWork *work, int time) +{ + SVECTOR *vec1; + TARGET *pGVar3; + SVECTOR *vec2; + + vec2 = &work->field_8EC_vec; + + if (time == 0) + { + if (bakudan_count_8009F42C >= 16) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + work->field_8E8_pTarget->damaged &= ~(0x40); + work->field_8E8_pTarget = NULL; + return; + } + + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, work->actpack->attack->field_6, 4); + pGVar3 = work->field_8E8_pTarget; + DG_PutVector(&svector_800AB7EC, vec2, 1); + GV_SubVec3(&pGVar3->center, vec2, vec2); + + vec2->vx /= 4; + vec2->vy /= 4; + vec2->vz /= 4; + } + + vec1 = &work->control.step; + + if (time < 4) + { + GV_AddVec3(vec1, vec2, vec1); + } + + if (time == 6) + { + GM_SeSet(&work->control.mov, SE_C4_PUT); + NewBakudan(work->field_8E8_pTarget->body, &svector_800AB7F4, 1, 1, work->field_8E8_pTarget); + work->field_914_trigger = 5; + work->field_8E8_pTarget->damaged &= ~(0x40); + work->field_8E8_pTarget = 0; + } + + if (EndMotion(work)) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } +} + +void sna_80057378(SnaInitWork *work, int time) +{ + int action; + int stance; + + stance = work->field_A26_stance; + work->control.turn.vy = dword_800ABBA8; + + if (time == 0) + { + if (stance == 0) + { + action = work->actpack->attack->field_6; + } + else + { + action = work->actpack->attack->field_7; + } + + sna_8004E260(work, action, 4, 0xffff); + } + + if ((stance == SNA_STANCE_STANDING && time == 6) || (stance == SNA_STANCE_SQUAT && time == 14)) + { + work->field_914_trigger = 3; + GM_BombSeg = dword_800ABBB0; + } + + if (work->body.time2 != 0 || work->body.action2 == 0) + { + sna_8004E260(work, 0, 4, 0); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK9); + work->field_9C0 = NULL; + } +} + +void sna_anim_claymore_80057474(SnaInitWork *work, int time) +{ + int i; + int down_count; + TARGET *targets; + + if (time == 0) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + GM_GetTargets(&down_count, &targets); + + if (counter_8009F448 == 8 || down_count == 64) + { + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + GM_SeSet2(0, 63, SE_BUZZER); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + return; + } + + work->field_9C8_anim_update_fn_3p = sna_anim_claymore_helper_80058780; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, SET, 4); + + for (i = 0; i < SNAKEJOINT_NUM; i++) + { + work->adjust[i] = DG_ZeroVector; + } + + GM_ConfigMotionAdjust(&work->body, work->adjust); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK26); + } + + sub_8004E9D0(work); +} + +static inline int sna_anim_shoot_weapon_helper_helper_80057590(SnaInitWork *work) +{ + if ((work->actpack->attack->setup == 136) || + (work->field_90C_pWeaponFn == &sna_80057A90) || + sna_sub_8004E358(work, SNA_FLAG2_UNK5)) + { + return 0; + } + + return 1; +} + +void sna_anim_shoot_weapon_helper_80057590(SnaInitWork *work, int time) +{ + unsigned short status; + int diff; + + if (!GM_CheckPlayerStatusFlag(PLAYER_ATTACK)) + { + sna_8004E260(work, 0, 4, 0); + sna_start_anim_8004E1F4(work, &sna_anim_idle_8005275C); + return; + } + + if (gSnaMoveDir_800ABBA4 < 0) + { + SetAction_8004E22C(work, work->actpack->still->setup, 4); + return; + } + + status = work->field_9B0_pad_ptr->status; + + if ((sub_8004E458(work->control.turn.vy, gSnaMoveDir_800ABBA4) == 3) && + sna_anim_shoot_weapon_helper_helper_80057590(work)) + { + work->field_A54.prone_bool_thing = gSnaMoveDir_800ABBA4; + sna_start_anim_8004E1F4(work, &sub_80053FAC); + } + else if ((status & PAD_CROSS) || (work->field_920_tbl & 8)) + { + if (work->field_A38_local_data > 0) + { + work->field_A38_local_data--; + } + + if (work->field_A38_local_data == 0) + { + if (sna_sub_8004E358(work, SNA_FLAG2_UNK5) == 0) + { + diff = GV_DiffDirAbs(work->control.turn.vy, gSnaMoveDir_800ABBA4); + if (diff > 256) + { + work->field_A38_local_data = (diff / 1024) + 1; + } + else + { + sna_start_anim_8004E1F4(work, &sna_anim_rungun_begin_80056BDC); + } + + work->control.turn.vy = gSnaMoveDir_800ABBA4; + } + else if (work->field_9B0_pad_ptr->status & (PAD_DOWN | PAD_UP)) + { + sna_start_anim_8004E1F4(work, &sna_anim_rungun_begin_80056BDC); + } + else + { + sna_80051FD0(work); + } + } + } + else if (!sna_sub_8004E358(work, SNA_FLAG2_UNK5)) + { + sna_8004EF14(work); + } + else + { + sna_80051FD0(work); + } +} + +void sna_anim_rungun_begin_helper_800577B4(SnaInitWork *work, int time) +{ + if (time == 2 && gSnaMoveDir_800ABBA4 >= 0) + { + sna_start_anim_8004E1F4(work, sna_anim_rungun_80056C3C); + } + else + { + if ((work->field_9B0_pad_ptr->status & PAD_DOWN) != 0) + { + sna_set_flags2_8004E330(work, SNA_FLAG2_UNK1); + } + + if (work->body.is_end) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_shoot_weapon_80056B88); + } + } +} + +void sna_anim_rungun_helper_80057844(SnaInitWork *work, int time) +{ + int angle; + + if (!GM_CheckPlayerStatusFlag(PLAYER_ATTACK)) + { + sna_8004E260(work, 0, 4, 0); + sna_start_anim_8004E1F4(work, sna_anim_run_8005292C); + return; + } + + if (gSnaMoveDir_800ABBA4 < 0 || (!(work->field_920_tbl & 8) && !(work->field_9B0_pad_ptr->status & PAD_CROSS))) + { + if (++work->field_A3A >= 5) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_shoot_weapon_80056B88); + return; + } + } + else + { + work->field_A3A = 0; + } + + if (!sna_sub_8004E358(work, SNA_FLAG2_UNK5)) + { + if (gSnaMoveDir_800ABBA4 < 0) + { + angle = work->control.turn.vy; + } + else + { + angle = sub_8004E4C0(work, gSnaMoveDir_800ABBA4); + } + + work->control.turn.vy = angle; + sub_8004EA50(work, angle); + } + else if (!(work->field_9B0_pad_ptr->status & (PAD_DOWN | PAD_UP))) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_shoot_weapon_80056B88); + } + else + { + sub_8004FA9C(work); + } +} + +void sna_auto_aim_800579A0(SnaInitWork *work) +{ + int unk; + int out_x_copy; + int snake_not_moving; + int out_x; + int out_y; + int diff; + + // loops enemies and finds candidate to aim at, returns angle to auto turn/aim to + // melee also uses this in a different func + GM_GetHomingTarget2(&work->body.objs->objs[6].world, + work->control.rot.vy, // input snake horizontal facing angle + &out_y, &out_x, work->control.map->index, + work->field_890_autoaim_max_dist, + work->field_892_autoaim_min_angle); // min angle to activate auto aim + + // ? + unk = work->adjust[2].vx; + diff = unk - out_x; + if (diff >= 65) + { + out_x = unk - 64; + } + else if (diff < -64) + { + out_x = unk + 64; + } + + // ? + out_x_copy = out_x; + work->adjust[2].vx = out_x; + work->adjust[6].vx = out_x; + snake_not_moving = gSnaMoveDir_800ABBA4 < 0; + work->adjust[7].vx = 3 * out_x_copy / 2; // maybe aim gun/head up/down?? + + if (snake_not_moving && out_y >= 0) // if not moving, set snake turn angle + { + work->control.turn.vy = out_y; + } + + if (sna_sub_8004E358(work, SNA_FLAG2_UNK5)) + { + GM_SnakeCamera.rotate2.vx = out_x; + } +} + +void sna_80057A90(SnaInitWork *work, int time) +{ + int bits; + int ammo, magSize; + + if (GM_CheckPlayerStatusFlag(PLAYER_MOVE)) + { + bits = 0x3fe; + } + else + { + bits = 0xffff; + } + + if (time == 0) + { + GM_ConfigMotionAdjust(&work->body, NULL); + NewAnime_8005DDE0(&work->body.objs->objs[4].world); + sna_8004E260(work, work->actpack->attack->reload, 4, bits); + + ammo = GM_MagazineMax; + magSize = GM_Weapons[work->field_91C_weapon_idx]; + + if (ammo > 0 && ammo < magSize) + { + magSize = ammo; + } + + GM_MagazineMax = ammo; + GM_Magazine = magSize; + } + + sna_8004F034(work, bits); + + if (time == 16) + { + GM_SeSet(&work->control.mov, SE_RELOAD); + } + + if (work->body.time2 != 0) + { + if (!(work->field_9B0_pad_ptr->status & PAD_SQUARE)) + { + GM_ClearPlayerStatusFlag(PLAYER_ATTACK); + sna_8004E260(work, 0, 4, 0); + sna_start_anim_8004E1F4(work, &sna_anim_idle_8005275C); + work->field_90C_pWeaponFn = sna_gun_800540D0; + } + else + { + work->field_90C_pWeaponFn = sub_80057BF0; + } + + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + } +} + +void sub_80057BF0(SnaInitWork *work, int time) +{ + int var_s4; + int temp_s3; + int var_s2; + int status; + int release; + int trg; + + var_s4 = GM_CheckPlayerStatusFlag(PLAYER_MOVE) ? 0x3FE : 0xFFFF; + temp_s3 = work->field_920_tbl; + var_s2 = 0; + + if (time == 0) + { + sna_8004E260(work, SET, 4, var_s4); + GM_SeSet(&work->control.mov, SE_READY_WEAPON); + + var_s2 = work->field_A38_local_data; + work->field_924 = 0; + + if (temp_s3 & 2) + { + GM_ConfigMotionAdjust(&work->body, work->adjust); + } + } + + sna_8004F034(work, var_s4); + + if ( (GM_Magazine == 0) && (GM_MagazineMax != 0) && (GM_Weapons[work->field_91C_weapon_idx] > 0) ) + { + work->field_90C_pWeaponFn = &sna_80057A90; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + return; + } + + if ( (temp_s3 & 2) != 0 ) + { + sna_auto_aim_800579A0(work); + } + + status = work->field_9B0_pad_ptr->status; + release = work->field_9B0_pad_ptr->release; + + trg = 1; + + if ( (temp_s3 & 1) ? (status & PAD_SQUARE) : (release & PAD_SQUARE) ) + { + var_s2 = 1; + } + + if ( (GM_GameStatus & STATE_PADRELEASE) || + GM_CheckPlayerStatusFlag(PLAYER_PAD_OFF) || + (DG_UnDrawFrameCount > 0) ) + { + var_s2 = 0; + } + + if ( var_s2 != 0 ) + { + trg |= 2; + } + + work->field_914_trigger = trg; + + switch ( work->field_924 ) + { + case 0: + if ( trg & 2 ) + { + if ( *work->field_918_n_bullets == 0 ) + { + DispEmpty(work); + + if ( temp_s3 & 1 ) + { + work->field_924 = 2; + break; + } + } + + work->field_924 = 1; + sna_8004E260(work, work->actpack->attack->shoot, 2, var_s4); + + if ( temp_s3 & 0x80 ) + { + GM_ClearPlayerStatusFlag(PLAYER_ATTACK | PLAYER_MOVE); + + if ( dword_8009F470 != 0 ) + { + GM_SeSet2(0, 63, SE_BUZZER); + work->field_914_trigger &= ~2; + sna_8004E260(work, 0, 4, 0); + sna_start_anim_8004E1F4(work, &sna_anim_idle_8005275C); + } + else + { + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK3); + GM_SetPlayerStatusFlag(PLAYER_ITEM_DISABLE); + sna_start_anim_8004E1F4(work, &sna_anim_nikita_80056C9C); + } + + return; + } + } + break; + + case 1: + if ( work->body.time2 != 0 ) + { + if ( (temp_s3 & 1) == 0 ) + { + work->field_924 = 0; + sna_8004E260(work, SET, 2, var_s4); + } + else if ( *work->field_918_n_bullets == 0 ) + { + DispEmpty(work); + work->field_924 = 2; + sna_8004E260(work, SET, 2, var_s4); + } + } + break; + + case 2: + work->field_914_trigger = trg & ~2; + break; + } + + if ( (status & 0x80) == 0 ) + { + if ( (++work->field_926 > 4) || (temp_s3 & 1) ) + { + work->field_90C_pWeaponFn = &sna_gun_800540D0; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + + GM_ConfigMotionAdjust(&work->body, NULL); + GM_ClearPlayerStatusFlag(PLAYER_ATTACK); + } + } + else + { + work->field_926 = 0; + } +} + +void sna_anim_psg1_helper_80057FD4(SnaInitWork* work, int time) +{ + int trg; + int ammo; + int mag_size; + short dpad_status; + int val; + short *pShortArr; + int rx, ry; + + if ( work->field_A3A < 3 ) + { + return; + } + + trg = 1; + + if ( work->field_A34 != 0 ) + { + if ( (--work->field_A34 & 0xFF) != 4 ) + { + return; + } + + GM_SeSet(&work->control.mov, SE_RELOAD); + + ammo = GM_Weapons[WP_Rifle]; + mag_size = 5; + + if ( mag_size < ammo ) + { + ammo = mag_size; + } + + GM_MagazineMax = mag_size; + GM_Magazine = ammo; + } + + if ( (GM_Magazine == 0) && (GM_Weapons[WP_Rifle] > 0) ) + { + work->field_A34 = 24; + return; + } + + if ( (work->field_9B0_pad_ptr->release & PAD_SQUARE) && + !(GM_GameStatus & STATE_PADRELEASE) && + !GM_CheckPlayerStatusFlag(PLAYER_PAD_OFF) && + (DG_UnDrawFrameCount == 0) ) + { + trg |= 2; + + if ( *work->field_918_n_bullets == 0 ) + { + DispEmpty(work); + } + } + + work->field_914_trigger = trg; + + dpad_status = work->field_9B0_pad_ptr->status & (PAD_UP | PAD_DOWN | PAD_LEFT | PAD_RIGHT); + if ( dpad_status != 0 ) + { + val = 1; + + if ( work->field_A52 == dpad_status ) + { + if ( !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK15) ) + { + if ( work->field_A44.pad < 10000 ) + { + work->field_A44.pad++; + } + + if ( work->field_A44.pad > 16 ) + { + val = 3; + } + } + else + { + val = 3; + } + } + else + { + work->field_A44.pad = 0; + work->field_A52 = dpad_status; + } + + pShortArr = work->field_9D0; + pShortArr[0] = 1; + pShortArr[3] = 0x78; + pShortArr[4] = 0x78; + pShortArr[5] = val; + pShortArr[6] = val; + pShortArr[7] = val; + pShortArr[8] = val; + + sna_80051DA0(work); + sna_80051FD0(work); + sna_8004EB14(work); + } + else + { + work->field_A52 = 0; + work->field_A44.pad = 0; + } + + rx = work->body.objs->rots[0].vx; + ry = work->body.objs->rots[0].vy; + + if ( GetAction(work) == 0x7A ) + { + work->control.turn.vy += ry - work->field_924; + work->control.turn.vx += rx - work->field_926; + } + + work->field_924 = ry; + work->field_926 = rx; + + if ( GM_TranquilizerTimer > 0 ) + { + if ( (time % 48) == 0 ) + { + NewPadVibration(dword_8009EF20, 2); + work->field_A35_vibtime2 = 2; + } + else if ( (work->field_A35_vibtime2 != 0) && + (--work->field_A35_vibtime2 == 0) ) + { + GM_SeSet2(0, 63, SE_HEARTBEAT); + } + } + else if ( (time & 0x1F) == 0 ) + { + NewPadVibration(dword_8009EF1C, 2); + work->field_A35_vibtime2 = 2; + } + else if ( (work->field_A35_vibtime2 > 0) && + (--work->field_A35_vibtime2 == 0) ) + { + GM_SeSet2(0, 63, SE_HEARTBEAT); + } + + if ( GetAction(work) == SET ) + { + if ( ++work->field_A38_local_data == RIFLE_TEBURE_TIME ) + { + work->field_A38_local_data = 0; + + if ( GM_TranquilizerTimer <= 0 ) + { + GM_StatusEvent &= ~EV_Tranquilizer; + SetAction_8004E22C(work, TEBURE, 4); + } + } + } + else if ( EndMotion(work) || + (GM_StatusEvent & EV_Tranquilizer) ) + { + SetAction_8004E22C(work, SET, 4); + } +} + +STATIC void OP_ShootStinger(SnaInitWork *work, int time) +{ + short *ws; // WATCH_SET *ws + int trg; + + trg = 1; // 1 = WEAPON_TAKE + + if ((work->field_9B0_pad_ptr->release & PAD_SQUARE) && + !(GM_GameStatus & STATE_PADRELEASE) && + !GM_CheckPlayerStatusFlag(PLAYER_PAD_OFF)) + { + if ( *work->field_918_n_bullets > 0 ) + { + trg |= 2; // |= WEAPON_TRIG + } + else + { + GM_SeSet(&work->control.mov, SE_KARASHT); + DispEmpty(work); + } + } + + work->field_914_trigger = trg; + + if ( work->field_9B0_pad_ptr->status & (PAD_LEFT | PAD_DOWN | PAD_RIGHT | PAD_UP) ) + { + ws = work->field_9D0; + ws[0] = 1; // ws->type = WATCH_TYPE_STILL + ws[3] = 0x200; + ws[4] = 0x200; + ws[5] = 0x20; + ws[6] = 0x20; + ws[7] = 0x20; + ws[8] = 0x20; + + sna_80051DA0(work); + sna_80051FD0(work); + sna_8004EB14(work); + } +} + +void sna_anim_grenade_80058470(SnaInitWork *work, int time) +{ + int bits; + int uVar2; + int res; + + if ( GM_CheckPlayerStatusFlag(PLAYER_MOVE) ) + { + bits = 0x3fe; + } + else + { + bits = 0xffff; + } + + if (time == 0) + { + sna_8004E260(work, SET, 4, bits); + work->field_924 = 0; + GM_SeSet(&(work->control).mov, SE_PINNUKI); + } + + sna_8004F034(work, bits); + res = 1; + + uVar2 = work->m_ctrl.info2.frame; + + switch (work->field_924) + { + case 0: + if (work->body.time2 != 0) + { + sna_8004E260(work, work->actpack->still->stand, 1, bits); + work->field_924 = 1; + } + + break; + case 1: + if (!(work->field_9B0_pad_ptr->status & PAD_SQUARE) && (DG_UnDrawFrameCount == 0)) + { + sna_8004E260(work, work->actpack->attack->shoot, 1, bits); + work->field_924 = 2; + } + + break; + case 2: + if (uVar2 == 7) + { + res = 3; + work->field_924 = 3; + } + + break; + case 3: + res = 0; + + if ((uVar2 > 11) && (work->field_9B0_pad_ptr->status & PAD_SQUARE)) + { + work->field_910 = 0; + return; + } + + if (work->body.time2 != 0) + { + sna_8004E260(work, 0, 4, 0); + GM_ClearPlayerStatusFlag(PLAYER_ATTACK | PLAYER_ITEM_DISABLE | PLAYER_WEAPON_DISABLE); + } + + break; + } + + work->field_914_trigger = res; +} + +void sub_80058644(SnaInitWork *work, int time) +{ + int iVar1; + + work->field_914_trigger = 1; + + if (time >= 10) + { + iVar1 = (int)work->field_A38_local_data; + if (iVar1 == 0) + { + sub_8004EEB0(work); + } + + if ((work->field_9B0_pad_ptr->status & PAD_SQUARE) == 0 && iVar1 == 0 && DG_UnDrawFrameCount == 0) + { + iVar1 = sub_8004E5E8(work, 0x80); + if (iVar1 == 1) + { + SetAction_8004E22C(work, work->actpack->attack->field_8, 1); + work->field_A38_local_data = 1; + work->field_914_trigger |= 2; + } + else if (iVar1 == 2) + { + GM_SeSet2(0, 0x3f, SE_BUZZER); + } + } + + if ((iVar1 == 1 && EndMotion(work)) || iVar1 == 2) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + sna_start_anim_8004E1F4(work, &sna_anim_idle_8005275C); + } + + work->field_A2C.vy = -100; + } +} + +void sna_anim_claymore_helper_80058780(SnaInitWork *work, int time) +{ + int var_s1; + int x, y; + int w; + POLY_GT4 *pPoly; + int i, j; + + work->field_914_trigger = 1; // 1 = WEAPON_TAKE + + if ( time < 10 ) + { + return; + } + + var_s1 = work->field_A38_local_data; + + if ( var_s1 == 0 ) + { + sub_8004EEB0(work); + + DG_GroupPrim(work->field_92C, DG_CurrentGroupID); + DG_VisiblePrim(work->field_92C); + DG_PutPrim(work->field_92C); + + if ( !(work->field_9B0_pad_ptr->status & PAD_SQUARE) && (DG_UnDrawFrameCount == 0) ) + { + var_s1 = sub_8004E5E8(work, 0x40); + + if ( var_s1 == 1 ) + { + SetAction_8004E22C(work, work->actpack->attack->shoot, 1); + work->field_A38_local_data = 1; + work->field_914_trigger |= 2; // 2 = WEAPON_TRIG + } + else if ( var_s1 == 2 ) + { + GM_SeSet2(0, 63, SE_BUZZER); + } + } + } + + y = GV_Time & 63; + + if ( y > 32 ) + { + y = 64 - y; + } + + x = work->field_928->off_x; + w = x + 63; + y += work->field_928->off_y; + + pPoly = work->field_92C->packs[GV_Clock]; + + for ( i = 0; i < 2; i++ ) + { + setUV4(pPoly, x, y, w, y, x, y, w, y); + pPoly++; + } + + if ( ((var_s1 == 1) && (EndMotion(work))) || (var_s1 == 2) ) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK26 | SNA_FLAG1_UNK3); + DG_InvisiblePrim(work->field_92C); + sna_start_anim_8004E1F4(work, &sna_anim_idle_8005275C); + GM_ConfigMotionAdjust(&work->body, NULL); + + for ( j = 0; j < 16; j++ ) + { + work->adjust[j] = DG_ZeroVector; + } + } + + work->field_A2C.vy = -100; +} + +void sna_anim_throw_800589C8(SnaInitWork *work, int time) +{ + Target_Data *pVec; // $s3 + int bClear; // $s4 + ACTPACK *actpack; // $v1 + int action_flag; // $s0 + TARGET *field_8E8_pTarget; // $s0 + SVECTOR* pTmp; + + if ( time == 0) + { + pVec = &stru_8009EFE4[1]; + bClear = 0; + actpack = work->actpack; + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + action_flag = actpack->attack->shoot; + if ( !sna_8004FDE8(work, pVec) ) + { + action_flag = work->actpack->attack->field_5; + pVec = &stru_8009EFE4[2]; + + if ( !sna_8004FDE8(work, pVec) ) + { + bClear = 1; + work->field_A38_local_data = 1; + action_flag = work->actpack->still->setup; + } + } + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(work, action_flag, 4); + + work->field_8FC = work->control.rot; + work->field_8FC.vy += 2048; + + pTmp = &work->field_8EC_vec; + if ( !bClear ) + { + field_8E8_pTarget = work->field_8E8_pTarget; + DG_PutVector(&pVec->field_0, pTmp, 1); + GV_SubVec3(&field_8E8_pTarget->center, pTmp, pTmp); + pTmp->vx /= 4; + pTmp->vy /= 4; + pTmp->vz /= 4; + } + else + { + + *pTmp = DG_ZeroVector; + } + } + + if ( time < 4 ) + { + GV_AddVec3( + &work->control.step, + &work->field_8EC_vec, + &work->control.step); + } + + if ( !work->field_A38_local_data ) + { + if ( sna_sub_8004E358(work, 16) ) + { + sub_8004FAE8(work); + } + } + + if ( work->body.is_end || (work->field_A38_local_data == 1 && time == 4) ) + { + sna_sub_8004E41C(work, 2); + sna_clear_flags1_8004E308(work, 4); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } + +} + +void sna_anim_punch_80058C10(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_anim_punch_helper_800591F4; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(work, work->actpack->attack->punch, 1); + work->field_A38_local_data = 0; + sna_set_flags1_8004E2F4(work, (SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6)); + } +} + +void sna_anim_chokethrow_begin2_80058C80(SnaInitWork *work, int time) +{ + int bClear; // $s0 + TARGET *field_8E8_pTarget; // $v1 + int action_flag; // $a1 + SVECTOR *p; + + if (time == 0) + { + work->field_904_frames_last_choke = 0; + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + bClear = 0; + work->field_A54.choke_count = 0; + + if (!sna_8004FDE8(work, &stru_8009EFE4[3])) + { + bClear = 1; + work->field_A38_local_data = 1; + } + + p = &work->field_8EC_vec; + if (!bClear) + { + + field_8E8_pTarget = work->field_8E8_pTarget; + + field_8E8_pTarget->faint--; + + DG_SetPos2(&field_8E8_pTarget->center, &work->control.rot); + + DG_PutVector(&svector_800AB7FC, p, 1); + DG_SetPos2(&work->control.mov, &work->control.rot); + GV_SubVec3(p, &work->control.mov, p); + p->vx /= 4; + p->vy /= 4; + p->vz /= 4; + action_flag = work->actpack->special2->field_0; + } + else + { + work->field_8EC_vec = DG_ZeroVector; + action_flag = work->actpack->still->setup; + } + SetAction_8004E22C(work, action_flag, 4); + } + + if (time < 4) + { + ++work->field_904_frames_last_choke; + GV_AddVec3(&work->control.step, &work->field_8EC_vec, + &work->control.step); + } + else + { + if (work->field_A38_local_data) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } + else + { + work->field_89C_pTarget->size.vx = 150; + sna_start_anim_8004E1F4(work, sna_anim_choke_80058E88); + } + } +} + +void sna_anim_choke_80058E88(SnaInitWork *work, int time) +{ + short action_flag; // $s0 + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_anim_choke_helper_8005951C; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + + action_flag = work->actpack->special2->field_0; + SetAction_8004E22C(work, action_flag, 4); + work->field_8E8_pTarget->a_mode = action_flag; + } +} + +void sna_anim_choke_rechoke_80058EF4(SnaInitWork *work, int time) +{ + short bVar1; + + if (time == 0 || (work->field_9B0_pad_ptr->press & PAD_SQUARE) != 0) + { + GetAction(work) = -1; + work->field_9C8_anim_update_fn_3p = sna_anim_choke_rechoke_helper_8005961C; + work->field_904_frames_last_choke = 0; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + bVar1 = work->actpack->attack->field_6; + SetAction_8004E22C(work, bVar1, 4); + work->field_8E8_pTarget->a_mode = bVar1; + } +} + +/* +void sna_anim_choke_rechoke_80058EF4(SnaInitWork *work, int time) +{ + ACTPACK *actpack; // $v1 + short action_flag; // $s0 + + if (time == 0) + { + if (!(work->field_9B0_pad_bits[1] & 0x80)) + { + actpack = work->actpack; + + GetAction(work) = -1; + work->field_9C8_anim_update_fn_3p = sub_8005961C; + work->field_904_frames_last_choke = 0; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + action_flag = actpack->attack->field_6; + sna_8004E22C(work, actpack->attack->field_6, 4); + work->field_8E8_pTarget->a_mode = action_flag; + } + } +} +*/ + +void sna_anim_choke_kill_80058F88(SnaInitWork *work, int time) +{ + TARGET *field_8E8_pTarget; // $s0 + int action_flag; // $s1 + + if (time == 0) + { + field_8E8_pTarget = work->field_8E8_pTarget; + action_flag = work->actpack->attack->field_7; + + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + + SetAction_8004E22C(work, action_flag, 4); + + field_8E8_pTarget->a_mode = action_flag; + field_8E8_pTarget->damage = 5; + field_8E8_pTarget->faint--; + work->field_A54.choke_count = 0; + } + + if (work->body.is_end) + { + sna_sub_8004E41C(work, 2); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + } + + if (sna_sub_8004E358(work, SNA_FLAG2_UNK5)) + { + sub_8004FAE8(work); + } +} + +void sna_anim_choke_drag_80059054(SnaInitWork *work, int time) +{ + int bVar2; + int iVar3; + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + bVar2 = work->actpack->special2->field_1; + GM_SetPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(work, bVar2, 4); + work->field_8E8_pTarget->a_mode = bVar2; + } + + iVar3 = work->field_904_frames_last_choke + 1; + work->field_904_frames_last_choke = iVar3; + + if ((gSnaMoveDir_800ABBA4 < 0 || ((work->field_9B0_pad_ptr->status & PAD_SQUARE) == 0))) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_choke_80058E88); + } + else + { + if (!((work->field_8E8_pTarget->vital > 0) && + ((iVar3 < 0x5a && work->field_8E8_pTarget->captured != 0)))) + { + + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_choke_80058E88); + return; + } + else + { + work->control.turn.vy = gSnaMoveDir_800ABBA4 + 0x800; + } + } +} + +void sna_anim_choke_hold_80059154(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, work->actpack->special2->field_6, 4); + } + + if (EndMotion(work)) + { + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } +} + +void sub_800591BC(SnaInitWork *work) +{ + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + sna_clear_flags1_8004E308(work, (SNA_FLAG1_UNK3 | SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6)); +} + +void sna_anim_punch_helper_800591F4(SnaInitWork *work, int time) +{ + int x, y; + int sound; + + if ( time == 0 ) + { + GM_GetHomingTarget2(&work->body.objs->objs[6].world, + work->control.rot.vy, + &x, &y, work->control.map->index, + 2000, 1024); + + if ( x >= 0 ) + { + work->control.turn.vy = x; + } + } + + if ( time == 4 ) + { + sub_8004FCB8(work, &punch_info[0], 3); + + sound = SE_PUNCH_SWING; + if ( GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) ) + { + sound = 182; + } + + GM_SeSet(&work->control.mov, sound); + GM_SetNoise(time, 2, &work->control.mov); + } + + if ( time == 6 ) + { + if ( !(work->field_A38_local_data & 0x20) ) + { + sub_800591BC(work); + return; + } + + work->field_A38_local_data &= ~0x20; + } + + if ( time == 11 ) + { + sub_8004FCB8(work, &punch_info[1], 3); + + sound = SE_PUNCH_SWING; + if ( GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) ) + { + sound = 182; + } + + GM_SeSet(&work->control.mov, sound); + GM_SetNoise(4, 2, &work->control.mov); + } + + if ( time == 13 ) + { + if ( !(work->field_A38_local_data & 0x20) ) + { + sub_800591BC(work); + return; + } + + work->field_A38_local_data = 0; + } + + if ( time == 22 ) + { + sub_8004FCB8(work, &punch_info[2], 4); + + sound = SE_KICK_SWING; + if ( GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) ) + { + sound = 182; + } + + GM_SeSet(&work->control.mov, sound); + GM_SetNoise(4, 2, &work->control.mov); + } + + if ( EndMotion(work) ) + { + sub_800591BC(work); + return; + } + + work->field_A38_local_data |= work->field_9B0_pad_ptr->press; + + if ( (work->field_9B0_pad_ptr->press & PAD_CROSS) != 0 ) + { + sub_800591BC(work); + } +} + +void sna_anim_choke_helper_8005951C(SnaInitWork *work, int time) +{ + int sinceLastChoke; + short press; + short status; + + sinceLastChoke = ++work->field_904_frames_last_choke; + + if (((work->field_8E8_pTarget->vital < 1 || sinceLastChoke >= 90) || + work->field_8E8_pTarget->captured == 0)) + { + sna_sub_8004E41C(work, 2); + sna_start_anim_8004E1F4(work, sna_anim_choke_hold_80059154); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + return; + } + + press = work->field_9B0_pad_ptr->press; + status = work->field_9B0_pad_ptr->status; + + if ((press & PAD_SQUARE) != 0) + { + sna_start_anim_8004E1F4(work, sna_anim_choke_rechoke_80058EF4); + } + else if ((status & PAD_SQUARE) == 0) + { + // A38 is snake stop movement frames, counts down + if (++work->field_A38_local_data >= 9) + { + sna_sub_8004E41C(work, 2); + sna_start_anim_8004E1F4(work, sna_anim_choke_hold_80059154); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + } + } + else + { + work->field_A38_local_data = 0; + if (gSnaMoveDir_800ABBA4 >= 0) + { + sna_start_anim_8004E1F4(work, sna_anim_choke_drag_80059054); + } + } +} + +void sna_anim_choke_rechoke_helper_8005961C(SnaInitWork *work, int time) +{ + if (time == 0 || (work->field_9B0_pad_ptr->press & PAD_SQUARE) != 0) + { + work->field_8E8_pTarget->faint--; + + if (++work->field_A54.choke_count >= 10) // feels good + { + // TODO: fix data when vibrate is figured out + NewPadVibration(dword_8009EF24, 1); + NewPadVibration(dword_8009EF2C, 2); + sna_start_anim_8004E1F4(work, sna_anim_choke_kill_80058F88); + return; + } + } + + if (work->body.is_end == 0 && work->field_8E8_pTarget->captured != 0) + { + return; + } + + sna_start_anim_8004E1F4(work, sna_anim_choke_80058E88); +} + +static inline int sna_init_main_logic_helper_helper_helper_800596FC(RECT *a, RECT *b) +{ + int test1; + int test2; + + test1 = a->x * b->x + a->y * b->y; + test2 = test1; + + if (test1 < 0) + { + return 1; + } + + if (test2 > a->x * a->x + a->y * a->y) + { + return 1; + } + + return 0; +} + +static inline int sna_init_main_logic_helper_helper_800596FC(SnaInitWork *work) +{ + HZD_SEG *local_40[2]; + RECT rect[2]; // Maybe vecs + CONTROL *pCtrl; + SVECTOR *pPosition; + HZD_SEG *seg; + int iVar9; + int iVar10; + int iVar17; + int iVar19; + int iVar11; + int i; + int result; + char *pChk; + + result = -1; + pCtrl = &work->control; + pPosition = &pCtrl->mov; + + if (pCtrl->n_touches == 1) + { + if (!(pCtrl->is_edge[0] & 0x40)) + { + seg = pCtrl->segs[0]; + + rect[1].x = pPosition->vx - seg->p1.x; + rect[1].y = pPosition->vz - seg->p1.z; + rect[0].x = seg->p2.x - seg->p1.x; + rect[0].y = seg->p2.z - seg->p1.z; + + if (!sna_init_main_logic_helper_helper_helper_800596FC(&rect[0], &rect[1])) + { + result = 0; + work->field_79C = 1; + } + } + } + else + { + pChk = work->control.is_edge; + iVar9 = 0; + + for (i = 0; i < 2; i++, pChk++) + { + seg = pCtrl->segs[i]; + local_40[i] = seg; + + if (*pChk & 0x40) + { + continue; + } + + rect[1].x = pPosition->vx - seg->p1.x; + rect[1].y = pPosition->vz - seg->p1.z; + rect[0].x = seg->p2.x - seg->p1.x; + rect[0].y = seg->p2.z - seg->p1.z; + + if (!sna_init_main_logic_helper_helper_helper_800596FC(&rect[0], &rect[1])) + { + result = i; + iVar9++; + work->field_79C = 1; + } + } + + if (iVar9 >= 2) + { + iVar11 = GV_VecLen3(&pCtrl->vecs[0]); + iVar10 = GV_VecLen3(&pCtrl->vecs[1]); + iVar17 = GV_VecDir2(&pCtrl->vecs[0]); + iVar19 = GV_VecDir2(&pCtrl->vecs[1]); + + if (GV_DiffDirAbs(iVar17, iVar19) < 512) + { + work->field_79C = 1; + result = iVar11; + return result >= iVar10; + } + + work->field_79C = 2; + result = 2; + } + } + + return result; +} + +static inline void sna_init_main_logic_helper_800596FC(SnaInitWork *work) +{ + int dir; + signed char cVar4; + int iVar9; + int iVar10; + int result; + int diff; + + gSnaMoveDir_800ABBA4 = work->field_9B0_pad_ptr->dir; + pVec_800ABBC8 = pVec_800ABBCC; + dword_800ABBAC = 0; + dword_800ABBC4 = 0; + dword_800ABBB0 = 0; + pVec_800ABBCC = 0; + dword_800ABBA8 = -1; + dword_800ABBD0 = -1; + + dir = gSnaMoveDir_800ABBA4; + + work->field_79C = 0; + cVar4 = work->control.n_touches; + + if ((cVar4 <= 0) || sna_sub_8004E358(work, SNA_FLAG2_UNK5)) + { + return; + } + + dword_800ABBAC = &work->control.vecs[0]; + dword_800ABBB4 = &work->control.is_edge[0]; + dword_800ABBA8 = GV_VecDir2(&work->control.vecs[0]); + + if ((*dword_800ABBB4 & 0x40) == 0) + { + pVec_800ABBCC = dword_800ABBAC; + dword_800ABBB0 = work->control.segs[0]; + dword_800ABBD0 = dword_800ABBA8; + } + + if (dir < 0) + { + return; + } + + if (cVar4 != 1) + { + iVar9 = GV_VecDir2(&work->control.vecs[1]); + iVar10 = GV_DiffDirAbs(dir, dword_800ABBA8); + + if (GV_DiffDirAbs(dir, iVar9) < iVar10) + { + dword_800ABBAC = &work->control.vecs[1]; + dword_800ABBB4 = &work->control.is_edge[1]; + dword_800ABBA8 = GV_VecDir2(&work->control.vecs[1]); + + if ((*dword_800ABBB4 & 0x40) == 0) + { + pVec_800ABBCC = dword_800ABBAC; + dword_800ABBB0 = work->control.segs[1]; + dword_800ABBD0 = dword_800ABBA8; + } + } + } + + result = sna_init_main_logic_helper_helper_800596FC(work); + + if (result == -1) + { + pVec_800ABBCC = 0; + dword_800ABBD0 = -1; + return; + } + + if (result != 2) + { + pVec_800ABBCC = &work->control.vecs[result]; + dword_800ABBD0 = GV_VecDir2(pVec_800ABBCC); + dword_800ABBB0 = work->control.segs[result]; + } + + diff = GV_DiffDirS(dir, dword_800ABBD0); + + if ((diff >= -896) && (diff <= 896)) + { + dword_800ABBC4 = 1; + + if (diff >= 0) + { + if (diff > 256) + { + dword_800ABBC4 = 4; + } + } + else if (diff < -256) + { + dword_800ABBC4 = 2; + } + } +} + +static inline void sna_init_main_logic_helper2_800596FC(SnaInitWork *work) +{ + int iVar9; + int iVar8; + + if (!GM_CheckPlayerStatusFlag(PLAYER_GROUND)) + { + work->field_A2A = 0; + } + else if (!flr_800ABBB8[0] || (flr_800ABBB8[0]->b1.h == 2)) + { + work->field_A2A = 0; + + if (!GM_CheckPlayerStatusFlag(PLAYER_WATCH)) + { + work->control.turn.vx = 0; + } + } + else + { + iVar8 = sub_8004E930(work, 750); + iVar9 = iVar8 - work->field_A2A; + + if (iVar9 >= 0) + { + if (iVar9 < 16) + { + goto inside; + } + } + else + { + iVar9 = work->field_A2A - iVar8; + + if (iVar9 < 16) + { +inside: + iVar8 = work->field_A2A; + } + } + + work->field_A2A = iVar8; + + if (!GM_CheckPlayerStatusFlag(PLAYER_WATCH)) + { + (work->control).turn.vx = iVar8; + } + } +} + +static inline void sna_init_main_logic_helper3_800596FC(SnaInitWork *work) +{ + int iVar9; + int sVar5; + int iVar8; + int uVar13; + int new_var; + + int msgs; + GV_MSG *pMsg; + unsigned short hash; + + if (!sna_check_flags1_8004E31C(work, 0x200000)) + { + return; + } + + uVar13 = work->field_9E4.field_9EC_flags3; + + if (work->field_9E4.field_9F0 != 0) + { + work->control.s_sphere = 450; + work->control.r_sphere = 450; + work->field_A28 = 450; + + if ((uVar13 & 0x4002) == 0) + { + new_var = work->field_9E4.field_9EE; + iVar9 = work->control.levels[0]; + iVar8 = (unsigned short)work->body.height - (iVar9 - new_var); + work->control.height = iVar8; + iVar8 = iVar8 + iVar9; + work->control.mov.vy = iVar8; + work->body.objs->world.t[1] = work->control.mov.vy; + DG_GetLightMatrix2(&work->control.mov, work->light); + } + else if ((uVar13 & 0x100) == 0) + { + work->control.height = dword_8009EECC[work->field_A26_stance]; + } + + work->body.height = work->control.height; + sVar5 = work->field_9E4.field_9E8; + + if (sVar5 >= 0) + { + if (GV_DiffDirAbs(sVar5, work->control.rot.vy) > 1536) + { + work->control.rot.vy = sVar5; + } + + work->control.turn.vy = sVar5; + } + + if ((uVar13 & 0x80) == 0) + { + GM_ConfigMotionControl( + &work->body, + &work->m_ctrl, + OAR_SNAKE, + work->m_segs1, + work->m_segs2, + &work->control, + work->rots + ); + + work->body.action = -1; + } + + if ((uVar13 & 0x800) != 0) + { + SetAction_8004E22C(work, sna_8004EAA8(work, work->field_A26_stance), 0); + } + + work->field_9E4.field_9F0 = 0; + } + + msgs = work->field_7A0_msg_count; + pMsg = work->field_7A4_msgs; + + while (msgs > 0) + { + hash = pMsg->message[0]; + + if ((hash == HASH_MOTION) || (hash == HASH_RUN_MOVE) || (hash == HASH_STANCE) || (hash == HASH_GO_MOTION)) + { + sna_clear_flags1_8004E308(work, 0x8200000); + return; + } + + msgs--; + pMsg++; + } + + GM_ClearPlayerStatusFlag(PLAYER_ACT_ONLY); + sna_clear_flags1_8004E308(work, 0x8200000); +} + +static inline int sna_init_main_logic_helper4_helper_800596FC(SnaInitWork *work) +{ + if (sna_check_flags1_8004E31C(work, 1) || + GM_CheckPlayerStatusFlag(PLAYER_WATCH | PLAYER_INTRUDE)) + { + return 0; + } + + return 1; +} + +static inline int sna_init_main_logic_helper4_helper2_800596FC(SnaInitWork *work) +{ + if (sna_check_flags1_8004E31C(work, 2) || + GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) || + (work->field_920_tbl & 0x200)) + { + return 0; + } + + return 1; +} + +static inline int sna_init_main_logic_helper4_helper3_800596FC(int f3e) +{ + if ((f3e == 1) || (f3e == 3) || (f3e == 8)) + { + return 0; + } + + return 1; +} + +static inline int sna_init_main_logic_helper4_helper4_800596FC(SnaInitWork *work) +{ + if (sna_check_flags1_8004E31C(work, 2) || + GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) || + (work->field_920_tbl & 0x200)) + { + return 0; + } + + return 1; +} + +static inline void sna_init_main_logic_helper4_800596FC(SnaInitWork *work) +{ + int iframes; + TARGET *pTarget; + int iVar8; + int bVar6; + int iVar9; + + if (GM_CheckPlayerStatusFlag(PLAYER_INVINCIBLE)) + { + iframes = work->field_A24_invuln_frames; + + if (iframes != 0) + { + if (--iframes > 0) + { + if (sna_init_main_logic_helper4_helper_800596FC(work)) + { + if ((iframes & 3) != 0) + { + DG_VisibleObjs(work->body.objs); + } + else + { + DG_InvisibleObjs(work->body.objs); + } + } + + if ((GM_Camera.first_person != 0) && + (!sna_init_main_logic_helper4_helper2_800596FC(work) || (work->field_9AC & 0x2)) && + !sna_check_flags1_8004E31C(work, 1)) + { + GM_SnakeCamera.position.vy += GV_RandS(16) * iframes; + } + } + else + { + sna_clear_invuln_8004F2EC(work); + } + + work->field_A24_invuln_frames = iframes; + } + else + { + if (GM_CheckPlayerStatusFlag(PLAYER_NOT_SIGHT)) + { + DG_VisibleObjs(work->body.objs); + } + + if (GM_Camera.first_person != 0) + { + DG_InvisibleObjs(work->body.objs); + } + else + { + DG_VisibleObjs(work->body.objs); + } + } + } + else + { + pTarget = work->field_89C_pTarget; + + if (pTarget->damaged & 0x6) + { + iVar8 = pTarget->a_mode; + bVar6 = sna_init_main_logic_helper4_helper3_800596FC(iVar8); + + if (sna_init_main_logic_helper4_helper4_800596FC(work) && (bVar6 || !(work->field_9AC & 0x2))) + { + work->control.skip_flag &= ~CTRL_SKIP_TRAP; + GM_ClearPlayerStatusFlag(PLAYER_SQUAT | PLAYER_CAUTION | PLAYER_ITEM_DISABLE); + sna_clear_flags1_8004E308(work, 0x30); + sna_clear_flags1_8004E308(work, 0x100); + work->field_9C0 = 0; + work->field_A28 = 0x1c2; + + if ((work->field_920_tbl & 0x4) && + GM_CheckPlayerStatusFlag(PLAYER_ATTACK) && + (work->field_924 != 3)) + { + work->field_914_trigger |= 0x9; + } + + sub_8004F454(work); + + if (work->field_90C_pWeaponFn == &sna_80057A90) + { + work->field_90C_pWeaponFn = &sna_gun_800540D0; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + } + + GM_SetPlayerStatusFlag(PLAYER_DAMAGED | PLAYER_WEAPON_DISABLE); + + if (!bVar6) + { + if (GM_CheckPlayerStatusFlag(PLAYER_DOWNED)) + { + sna_set_flags1_8004E2F4(work, 0x4); + sna_start_anim_8004E1F4(work, &sna_anim_knockdown_shot_80054B50); + } + else if (GM_CheckPlayerStatusFlag(PLAYER_CB_BOX)) + { + sna_set_flags1_8004E2F4(work, 0x20); + sna_start_anim_8004E1F4(work, &sna_act_helper2_helper7_80054648); + } + else + { + sna_start_anim_8004E1F4(work, &sna_anim_shot_flinch_800544E0); + } + + GM_SeSet2(0, 63, SE_PLAYER_DAMAGE1); + + NewPadVibration(dword_8009EED4, 1); + NewPadVibration(dword_8009EED8, 2); + } + else + { + work->field_A2C = pTarget->force; + work->field_A2C.vx /= 2; + work->field_A2C.vy /= 4; + work->field_A2C.vz /= 2; + + if (GM_CheckPlayerStatusFlag(PLAYER_GROUND | PLAYER_DOWNED)) + { + if (!GM_CheckPlayerStatusFlag(PLAYER_DOWNED)) + { + work->field_A54.knockdown_thing = 1; + } + + sna_start_anim_8004E1F4(work, &sna_anim_knockdown_shot_tank_80054C08); + } + else + { + sna_start_anim_8004E1F4(work, &sna_anim_knockdown_80054710); + } + + if (work->field_9AC & 0x1) + { + sub_8004F204(work); + } + + GM_SeSet2(0, 63, SE_PLAYER_DAMAGE2); + + NewPadVibration(dword_8009EEE0, 1); + NewPadVibration(dword_8009EEE4, 2); + + sna_8004EC00(work); + sna_set_flags1_8004E2F4(work, 0x4); + } + + sna_act_helper2_helper4_8004F090(work, iVar8); + } + else + { + sna_set_invuln_8004F2A0(work, 0x20); + + if (pTarget->damage > 0) + { + sna_act_helper2_helper4_8004F090(work, pTarget->a_mode); + } + + switch (iVar8) + { + case 1: + case 3: + GM_SeSet2(0, 63, SE_PLAYER_DAMAGE1); + NewPadVibration(dword_8009EED4, 1); + NewPadVibration(dword_8009EED8, 2); + break; + + default: + GM_SeSet2(0, 63, SE_PLAYER_DAMAGE2); + NewPadVibration(dword_8009EEE0, 1); + NewPadVibration(dword_8009EEE4, 2); + break; + } + + if ((pTarget->vital < 1) && ((GM_GameOverTimer > -2) && (GM_GameOverTimer < 1))) + { + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE | PLAYER_GROUND)) + { + iVar9 = 0x7d; + } + else + { + iVar9 = 0x80; + work->control.rot.vx = 0; + work->control.turn.vx = 0; + } + + if (GM_CheckPlayerStatusFlag(PLAYER_DOWNED)) + { + if (work->field_A54.choke_count == 1) + { + iVar9 = 0x7e; + } + else + { + iVar9 = 0x7f; + } + } + + GM_SeSet2(0, 63, SE_PLAYEROUT); + sna_8004F8E4(work, iVar9); + } + } + + sna_act_unk2_80051170(pTarget); + + if (GM_Camera.first_person != 0) + { + work->control.turn.vy += GV_RandS(512); + } + } + else if (pTarget->damaged & 0x80) + { + sna_act_unk2_80051170(pTarget); + } + } +} + +static inline void sna_init_main_logic_helper5_800596FC(SnaInitWork *work) +{ + if (GM_CheckPlayerStatusFlag(PLAYER_GAME_OVER)) + { + if (((work->body.action == 128) && (work->m_ctrl.info1.frame == 49)) || + ((work->body.action == 127) && (work->m_ctrl.info1.frame == 72))) + { + NewPadVibration(dword_8009EEF0, 1); + NewPadVibration(dword_8009EEF4, 2); + + if (work->body.action == 128) + { + GM_SeSetMode(&work->control.mov, SE_HIT_FLOOR, GM_SEMODE_BOMB); + } + } + + if ((work->field_7A0_msg_count == 0) && + !GM_CheckPlayerStatusFlag(PLAYER_ACT_ONLY) && + (GM_StreamStatus() != 2)) + { + GM_StreamPlayStop(); + sna_set_flags1_8004E2F4(work, 0x40000); + GM_GameOverTimer = 1; + } + } + else if (GM_GameOverTimer > 0) + { + GM_GameStatus |= STATE_PADRELEASE; + + if (GM_GameOverTimer == 2) + { + sna_set_flags1_8004E2F4(work, 0x80000); + GM_SetPlayerStatusFlag(PLAYER_GAME_OVER); + sna_set_invuln_8004F2A0(work, 0); + GM_GameOverTimer = -1; + } + } +} + +static inline int sna_init_main_logic_helper6_helper_800596FC(SnaInitWork *work, int flags) +{ + if (dword_8009F46C[0] != 0) + { + return 0; + } + + if (sna_check_flags1_8004E31C(work, 0x4)) + { + return 0; + } + + if (GM_CheckPlayerStatusFlag(flags)) + { + return 0; + } + + if (work->field_9CC_anim_update_fn_1p == &sna_fn_nothing_80053B80) + { + return 0; + } + + if ((GM_UnkFlagBE != 0) && !(work->field_898_flags2 & 0x10)) + { + return 0; + } + + return 1; +} + +static inline int sna_init_main_logic_helper6_helper2_800596FC(SnaInitWork *work) +{ + if ((work->field_920_tbl & 0x200) || + (work->field_9AC & 0x2) || + sna_check_flags1_8004E31C(work, 0x4) || + sna_sub_8004E358(work, 0x100)) + { + return 0; + } + + return 1; +} + +static inline int sna_init_main_logic_helper6_helper3_800596FC(SnaInitWork *work) +{ + if ((work->field_920_tbl & 0x200) || + (work->field_9AC & 0x2) || + sna_check_flags1_8004E31C(work, 0x4) || + sna_sub_8004E358(work, 0x100)) + { + return 0; + } + + return 1; +} + +static inline void sna_init_main_logic_helper6_800596FC(SnaInitWork *work) +{ + int flags; + + if (GM_UnkFlagBE == 0) + { + if ((work->field_9B0_pad_ptr->press & PAD_TRIANGLE) && + sna_init_main_logic_helper6_helper_800596FC(work, PLAYER_WATCH | PLAYER_ATTACK)) + { + sub_8004EB74(work); + GM_SetPlayerStatusFlag(PLAYER_NORMAL_WATCH); + } + } + else + { + if (!sna_sub_8004E358(work, 0x10) && sna_init_main_logic_helper6_helper2_800596FC(work)) + { + if (work->field_9B0_pad_ptr->quick & PAD_TRIANGLE) + { + sub_8004EB74(work); + sna_set_flags2_8004E330(work, 0x10); + } + + return; + } + + if ((work->field_9B0_pad_ptr->quick & PAD_TRIANGLE) || + (GM_GameStatus & STATE_PADRELEASE) || + GM_CheckPlayerStatusFlag(PLAYER_PAD_OFF)) + { + if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) && + sna_init_main_logic_helper6_helper3_800596FC(work)) + { + sna_8004EC00(work); + } + + return; + } + + if (!(work->field_9B0_pad_ptr->press & PAD_TRIANGLE)) + { + return; + } + + flags = PLAYER_ATTACK; + + if (GM_UnkFlagBE == 0) + { + flags |= PLAYER_WATCH; + } + + if (sna_init_main_logic_helper6_helper_800596FC(work, flags)) + { + sna_set_flags2_8004E330(work, 0x20); + + flags = PLAYER_WATCH | + PLAYER_NORMAL_WATCH | + PLAYER_STILL_WATCH | + PLAYER_MENU_DISABLE; + GM_SetPlayerStatusFlag(flags); + } + } +} + +void sna_init_main_logic_800596FC(SnaInitWork *work) +{ + int vy; + GV_PAD *pPad; + unsigned char *pAxis; + int i; + int frame; + int var_v0_8; + int hzd_count; + unsigned short *pHzdVal; + + work->field_798_p_height = work->control.height; + + vy = work->body.height; + work->control.height = vy; + + if ( vy < 500 ) + { + vy = 500; + } + + if ( vy > 700 ) + { + vy = 700; + } + + work->field_89C_pTarget->size.vy = vy; + + if ( (work->field_A2C.vy < 0) && (work->control.grounded != 0) ) + { + work->field_A2C.vy = 0; + } + + work->field_A2C.vy -= 32; + + if ( GM_CheckPlayerStatusFlag(PLAYER_NOT_PLAYABLE) ) + { + return; + } + + if ( GM_CheckPlayerStatusFlag(PLAYER_PAD_OFF) ) + { + work->field_9B0_pad_ptr = &GV_PadData_8009F0C4; + } + + work->field_914_trigger = 0; + work->control.turn.vz = 0; + + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK15); + + pPad = work->field_9B0_pad_ptr; + + if ( pPad->analog != 0 && (pPad->status & (PAD_LEFT | PAD_DOWN | PAD_RIGHT | PAD_UP)) ) + { + pAxis = &pPad->left_dx; + + for ( i = 0; i < 2; i++, pAxis++ ) + { + if ( (*pAxis < 64) || (*pAxis > 192) ) + { + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK15); + break; + } + } + } + + sna_init_main_logic_helper_800596FC(work); + sna_init_main_logic_helper2_800596FC(work); + sna_init_main_logic_helper3_800596FC(work); + sna_init_main_logic_helper4_800596FC(work); + + if ( GM_StatusEvent & EV_CommonCold ) + { + if ( !(GM_GameStatus & (STATE_PADRELEASE | STATE_PADDEMO | STATE_DEMO)) && + !GM_CheckPlayerStatusFlag(PLAYER_PAD_OFF) && + (work->field_A5C < 900) ) + { + work->field_A5C++; + } + + if ( sna_act_helper2_helper5_8004FF88(work) && (work->field_A5C == 900) ) + { + work->field_A5C = 0; + sna_start_anim_8004E1F4(work, &sna_act_helper2_helper_80054EFC); + } + } + + sna_weapon_switching_800511BC(work, 1); + sna_800515BC(work, 1); + + if ( !GM_CheckPlayerStatusFlag(0x1304) && + ((work->field_9AC & 2) == 0) && + ((work->field_920_tbl & 0x4000) != 0) && + (*work->field_918_n_bullets == 0)) + { + *work->field_918_n_bullets = -1; + GM_CurrentWeaponId = WP_None; + work->field_90C_pWeaponFn = &sna_anim_chokethrow_begin1_80054210; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + GM_WeaponChanged = 1; + work->field_920_tbl = GM_WeaponTypes[0]; + work->field_918_n_bullets = 0; + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_ITEM_DISABLE); + } + + sna_act_unk_80050A64(work); + sna_init_main_logic_helper5_800596FC(work); + sna_act_helper2_helper3_8004ED6C(work); + + frame = work->field_9BC_anim_frame++; + work->field_9B8_fn_anim(work, frame); + + if ( GM_UnkFlagBE != 0 ) + { + var_v0_8 = sna_sub_8004E358(work, SNA_FLAG2_UNK6); + } + else + { + var_v0_8 = GM_CheckPlayerStatusFlag(PLAYER_WATCH); + } + + if ( var_v0_8 != 0 ) + { + work->field_9CC_anim_update_fn_1p(work, frame); + } + else + { + work->field_9C8_anim_update_fn_3p(work, frame); + } + + if ((work->field_920_tbl & 0x40) && + GM_CheckPlayerStatusFlag(PLAYER_ATTACK)) + { + work->field_910++; + work->field_90C_pWeaponFn(work, work->field_910 - 1); + } + + if ( sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK9) ) + { + work->field_9C4++; + work->field_9C0(work, work->field_9C4 - 1); + } + + work->control.r_sphere = GV_NearExp4(work->control.r_sphere, work->field_A28); + work->control.step.vy = work->field_A2C.vy; + + sna_init_main_logic_helper6_800596FC(work); + + if ( GM_StreamStatus() == 2 ) + { + if ( sub_8004EFE4(work, 1) >= 0 ) + { + work->m_ctrl.info1.field_14 = 1; + } + + sna_act_helper2_helper2_80033054(SNAKE_NAME, &work->adjust[6]); + + if ( !work->body.objs->adjust && (work->m_ctrl.info1.field_14 == 1) ) + { + GV_AddVec3(&work->rots[6], &work->adjust[6], &work->rots[6]); + } + } + + GM_PlayerAction = work->body.action; + dword_800AB9D4 = work->m_ctrl.info1.frame; + GM_PlayerStance = work->field_A26_stance; + + if ( GM_TranquilizerTimer > 0 ) + { + GM_TranquilizerTimer--; + } + + GM_ClearPlayerStatusFlag(PLAYER_NOHIDDEN | PLAYER_HIDDEN); + + hzd_count = work->control.evt.n_inside; + pHzdVal = work->control.evt.inside; + + while ( hzd_count > 0 ) + { + if ( *pHzdVal == 0x375E ) + { + GM_SetPlayerStatusFlag(PLAYER_NOHIDDEN); + } + else if ( *pHzdVal == 0xF715 ) + { + GM_SetPlayerStatusFlag(PLAYER_HIDDEN); + } + + hzd_count -= 1; + pHzdVal += 1; + } + + if ( work->field_9A0 != 0 ) + { + addPrim(DG_Chanl(1)->ot[GV_Clock], &work->field_950[GV_Clock]); + work->field_9A0--; + } + + snake_weapon_idx_800BDCBA = work->field_91C_weapon_idx; +#ifdef __psyz + /* unarmed: the ammo pointer is NULL, and the console read address 0 + * without complaint (garbage in, ignored). A host cannot. */ + snake_weapon_max_ammo_800BDCBC = + work->field_918_n_bullets ? *work->field_918_n_bullets : 0; +#else + snake_weapon_max_ammo_800BDCBC = *work->field_918_n_bullets; +#endif + snake_mag_size_800BDCB8 = GM_Magazine; +} + +static inline int sna_act_helper_8005AD10(SnaInitWork *work) +{ + if ( sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK19) ) + { + CheckMessage0_80050878(work); + sna_act_unk_80050A64(work); + + if ( sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK19) ) + { + dword_800AB9D4 = 0; + DG_SetPos2(&work->control.mov, &work->control.rot); + DG_PutObjs(work->body.objs); + return 1; + } + } + + return 0; +} + +static inline void sna_act_helper2_8005AD10(SnaInitWork *work) +{ + SVECTOR vec; + + if ( sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK24) && !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK28) ) + { + vec = work->field_A68; + GV_NearExp8V(&vec.vx, &DG_ZeroVector.vx, 3); + + if ( GV_VecLen3(&vec) > 8 ) + { + work->field_A68 = vec; + } + + work->control.step.vx = work->field_A68.vx; + work->control.step.vz = work->field_A68.vz; + + if ( work->control.grounded & 1 ) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK24); + } + } + + if ( sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK26) ) + { + work->control.step.vz = 0; + work->control.step.vx = 0; + } +} + +static void Act(SnaInitWork *work) +{ + SVECTOR vec; + SVECTOR vec2; + TARGET *pTarget; + int height; + int level; + TARGET *pTarget2; + + GM_ClearPlayerStatusFlag(PLAYER_MOVE_WATCH); + + if ( sna_act_helper_8005AD10(work) ) + { + return; + } + + if ( (GM_UnkFlagBE != 0) && (GM_GameStatus & STATE_PADDEMO) ) + { + if ( !GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) && !(work->field_9B0_pad_ptr->status & PAD_TRIANGLE) ) + { + sna_8004EC00(work); + work->control.turn.vx = work->field_A2A; + } + + GM_UnkFlagBE = 0; + sna_set_flags2_8004E330(work, SNA_FLAG2_UNK13); + } + + if ( (work->field_898_flags2 & 0x1000) && !(GM_GameStatus & STATE_PADDEMO) ) + { + GM_UnkFlagBE = 1; + sna_clear_flags2_8004E344(work, SNA_FLAG2_UNK13); + } + + if ( GM_CheckPlayerStatusFlag(PLAYER_SECOND_AVAILABLE) ) + { + if ( ((GV_PadData + 1)->status | (GV_PadData + 1)->release) != 0 ) + { + GM_SetPlayerStatusFlag(PLAYER_SECOND_CONTROLLER); + work->field_9B0_pad_ptr = &GV_PadData[1]; + } + else if ( (GV_PadData->status | GV_PadData->release) != 0 ) + { + GM_ClearPlayerStatusFlag(PLAYER_SECOND_CONTROLLER); + work->field_9B0_pad_ptr = &GV_PadData[0]; + } + } + else + { + work->field_9B0_pad_ptr = &GV_PadData[0]; + } + + pTarget = work->field_89C_pTarget; + + if ( pTarget->damaged & 8 ) + { + GV_AddVec3(&pTarget->offset, &work->control.step, &work->control.step); + pTarget->damaged &= ~0x8; + pTarget->offset = DG_ZeroVector; + } + + height = (short)work->body.height; + + GM_ActMotion(&work->body); + + if ( sna_sub_8004E358(work, SNA_FLAG2_UNK1) ) + { + work->control.step.vx *= -1; + work->control.step.vz *= -1; + sna_clear_flags2_8004E344(work, SNA_FLAG2_UNK1); + } + + sna_act_helper2_8005AD10(work); + + level = work->control.levels[0]; + work->control.hzd_height = HzdHeights_8009EEC4[work->field_A26_stance] + level; + + if ( (work->control.skip_flag & CTRL_BOTH_CHECK) && (work->field_798_p_height < 750) ) + { + work->control.hzd_height = work->control.mov.vy; + } + + /* しゃがみ天井突き抜け防止 */ + if ( (work->field_A26_stance != SNA_STANCE_STANDING) && (height >= 500) ) + { + work->control.height = 496; + } + + UpdateLife_8004F6E8(work); + CheckSnakeDead_8004E384(work); + CheckMessage0_80050878(work); + + if ( !GM_lpfnPlayerActControl || !GM_lpfnPlayerActControl(&work->actor) ) + { + GM_ActControl(&work->control); + } + + flr_800ABBB8[1] = 0; + do {} while (0); + flr_800ABBB8[0] = NULL; + dword_800ABBC0 = 0; + + if ( work->control.r_sphere != -2 ) + { + HZD_GetLevelHazard(flr_800ABBB8); + + if ( flr_800ABBB8[0] ) + { + dword_800ABBC0 = flr_800ABBB8[0]->p4.h; + } + } + + if ( ((level - work->control.levels[0]) >= 250) && !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK28) ) + { + if ( !flr_800ABBB8[0] || (flr_800ABBB8[0]->b1.h == 2) ) + { + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK24); + work->field_A68 = work->control.step; + work->field_A68.vy = 0; + } + } + + if ( GM_lpfnPlayerActControl ) + { + UpdateLife_8004F6E8(work); + } + + if ( work->control.r_sphere == -2 ) + { + work->control.mov.vy += work->body.height - height; + DG_SetPos2(&work->control.mov, &work->control.rot); + } + + if ( !GM_lpfnPlayerActObject2 || !GM_lpfnPlayerActObject2(&work->actor) ) + { + UpdateLife_8004F6E8(work); + GM_ActObject(&work->body); + } + + if ( GM_lpfnPlayerActObject2 ) + { + UpdateLife_8004F6E8(work); + } + + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK25); + DG_GetLightMatrix2(&work->control.mov, work->light); + *work->enable_shadow = dword_800ABA1C == 0; + GM_MoveTarget(work->field_89C_pTarget, &work->control.mov); + + vec2 = work->control.mov; + + if ( !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK12) ) + { + vec2.vy = work->body.objs->objs[6].world.t[1]; + } + + if (work->field_A26_stance == SNA_STANCE_CROUCH) + { + vec2.vy += 320; + } + + work->field_A60 = vec2; + + GM_SnakeCamera.rotate2 = work->control.rot; + GM_SnakeCamera.rotate2.vy &= 0xFFF; + + sna_init_main_logic_800596FC(work); + + pTarget2 = work->field_89C_pTarget; + pTarget2->force = work->control.step; + GM_PushTarget(pTarget2); + + if ( ((GM_Camera.first_person != 0) && GM_CheckPlayerStatusFlag(PLAYER_NORMAL_WATCH)) || GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) ) + { + GV_NearExp4V(&GM_SnakeCamera.position.vx, &work->field_A60.vx, 3); + } + else + { + GM_SnakeCamera.position = vec2; + } + + GM_PlayerPosition = vec = work->control.mov; + + if ( sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK16) ) + { + GM_PlayerPosition = work->field_A60; + GM_MoveTarget(work->field_89C_pTarget, &work->field_A60); + } + + vec.vy = level = work->control.levels[0]; + GM_PlayerAddress = work->field_844 = HZD_GetAddress(work->control.map->hzd, &vec, work->field_844); + + if ( GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) ) + { + if ( GM_SnakeCamera.position.vy < (level + 150) ) + { + GM_SnakeCamera.position.vy = level + 150; + } + else if ( GM_SnakeCamera.position.vy > (level + 250) ) + { + GM_SnakeCamera.position.vy = level + 250; + } + } + + GM_ClearPlayerStatusFlag(PLAYER_INVISIBLE); + + if ( GM_CheckPlayerStatusFlag(3) ) + { + GM_SetPlayerStatusFlag(PLAYER_INVISIBLE); + } + + if ( sna_sub_8004E358(work, SNA_FLAG2_UNK5) && !sna_sub_8004E358(work, SNA_FLAG2_UNK6) ) + { + GM_SetPlayerStatusFlag(PLAYER_MOVE_WATCH); + } +} + +static void Die(SnaInitWork *work) +{ + CONTROL *pCtrl; + GV_ACT *pShadow; + GV_ACT *pWeapon; + GV_ACT *pItem; + + if ((work->field_898_flags2 & 0x1000) != 0) + { + GM_UnkFlagBE = 1; + } + + pCtrl = &work->control; + GM_FreeControl(&work->control); + GM_FreeObject(&work->body); + GM_FreeTarget(work->field_89C_pTarget); + GM_FreePrim(work->field_92C); + + GM_PlayerStance = work->field_A26_stance; + + pShadow = work->shadow; + if (pShadow) + { + GV_DestroyOtherActor(pShadow); + } + + pWeapon = work->field_908_weapon_actor; + if (pWeapon) + { + GV_DestroyOtherActor(pWeapon); + } + + pItem = work->field_9A4_item_actor; + if (pItem) + { + GV_DestroyOtherActor(pItem); + } + + if (GM_PlayerControl == pCtrl) + { + GM_PlayerControl = 0; + } + + if (GM_PlayerBody == &work->body) + { + GM_PlayerBody = 0; + } +} + +static inline int InitLaserSight(SnaInitWork *work) +{ + SVECTOR *temp_s1; + SVECTOR *temp_s2; + DG_PRIM *pPrim; + DG_TEX *pTex; + POLY_GT4 *pPoly; + int i, j; + + temp_s1 = &work->field_930; + pPrim = GM_MakePrim(DG_PRIM_LINE_GT2, 2, temp_s1, NULL); + + if (!pPrim) + { + return -1; + } + + pTex = DG_GetTexture(PCX_LSIGHT); + work->field_928 = pTex; + + if (!pTex) + { + return -1; + } + + pPrim->raise = 500; + + for (i = 0; i < 2; i++) + { + pPoly = pPrim->packs[i]; + + for (j = 0; j < 2; j++) + { + LSTORE(COLOR_BLACK, &pPoly->r0); + LSTORE(COLOR_GRAY, &pPoly->r1); + LSTORE(COLOR_BLACK, &pPoly->r2); + LSTORE(COLOR_GRAY, &pPoly->r3); + setPolyGT4(pPoly); + setSemiTrans(pPoly, 1); + pPoly->tpage = pTex->tpage; + pPoly->clut = pTex->clut; + pPoly++; + } + } + + // ??? + temp_s2 = temp_s1 + 2; + + temp_s1[2].vx = -100; + temp_s1[0].vx = -100; + temp_s1[3].vy = -300; + temp_s2[0].vy = -300; + temp_s1[1].vy = -300; + temp_s1[0].vy = -300; + temp_s2[0].vz = 300; + temp_s1[0].vz = 300; + temp_s1[1].vx = -800; + temp_s1[1].vz = 2000; + temp_s1[3].vx = 800; + temp_s1[3].vz = 2000; + + DG_InvisiblePrim(pPrim); + work->field_92C = pPrim; + return 0; +} + +static inline void InitEmpty(SnaInitWork *work) +{ + u_short t1, t2; + short stance; + short temp_a1; + int temp_v1_3; + int var_s0_2; + int var_v_2; + + stance = work->field_A26_stance = GM_PlayerStance; + if (stance == 3) + { + t1 = GM_ItemTypes[GM_CurrentItemId + 1]; + t2 = GM_CurrentItemId; + + if ((t1 & 2) || ((t2 - 2) < 3u)) + { + GM_CurrentItemId = IT_None; + } + + if (GM_WeaponTypes[GM_CurrentWeaponId + 1] & 0x200) + { + GM_CurrentWeaponId = WP_None; + } + } + + work->field_844 = -1; + work->field_91C_weapon_idx = -2; + + sna_weapon_switching_800511BC(work, 0); + + work->field_9A8_current_item = IT_None; + + sna_800515BC(work, 0); + +#ifdef __psyz + /* Unarmed is weapon id -1, and on the console indexing the array with it + * landed on actions_no_weapon_8009ED70 BY MEMORY LAYOUT: the ROM placed + * that struct exactly 28 bytes (one ACTPACK) before the array. A native + * linker gives no such guarantee -- here [-1] hit a neighboring function + * pointer and ->still->stand read from code space. Make the layout trick + * explicit. */ + work->actpack = (GM_CurrentWeaponId < 0) + ? &actions_no_weapon_8009ED70 + : &weapon_actions_8009ED8C[GM_CurrentWeaponId]; +#else + work->actpack = &weapon_actions_8009ED8C[GM_CurrentWeaponId]; +#endif + work->field_A28 = 450; + work->field_A44 = work->control.mov; + work->field_A60 = work->control.mov; + + GM_SnakeCamera.position = work->field_A60; + + if (GCL_GetOption('o')) // oar + { + work->field_A5A = GCL_StrToInt(GCL_NextStr()); + } + + work->field_890_autoaim_max_dist = 6000; + + if (GCL_GetOption('l')) // len + { + work->field_890_autoaim_max_dist = GCL_StrToInt(GCL_NextStr()); + } + + work->field_892_autoaim_min_angle = 512; + + if (GCL_GetOption('r')) // rot + { + work->field_892_autoaim_min_angle = GCL_StrToInt(GCL_NextStr()); + } + + dword_800ABBDC = -1023; + dword_800ABBD4 = 1023; + + if (GCL_GetOption('t')) // turn + { + dword_800ABBDC = GCL_StrToInt(GCL_NextStr()); + dword_800ABBD4 = GCL_StrToInt(GCL_NextStr()); + } + + work->field_A70 = -1; + + if (GCL_GetOption('e')) // exec + { + work->field_A70 = GCL_StrToInt(GCL_NextStr()); + } + + temp_a1 = work->field_A26_stance; + + if (temp_a1 == 3) + { + work->field_A26_stance = 2; + GM_SetPlayerStatusFlag(PLAYER_GROUND | PLAYER_INTRUDE); + DG_InvisibleObjs(work->body.objs); + sna_8004EC8C(work); + sna_start_anim_8004E1F4(work, &sna_anim_duct_move_80054424); + GM_Camera.first_person = 2; + var_v_2 = sna_8004EAA8(work, 2); + } + else + { + var_s0_2 = sna_8004EAA8(work, temp_a1); + + if ((work->field_9AC & 1) || (work->field_920_tbl & 0x20)) + { + if ((unsigned int)(work->field_9A8_current_item - 2) < 3) + { + var_s0_2 = 4; + } + } + else + { + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + } + + var_v_2 = var_s0_2; + } + + GM_ConfigObjectAction(&work->body, var_v_2, 0, 0); + GM_ActMotion(&work->body); + + temp_v1_3 = (short)work->body.height; + + work->field_798_p_height = temp_v1_3; + work->control.height = temp_v1_3; + + work->field_9B0_pad_ptr = GV_PadData; +} + +static inline void InitPacks( POLY_FT4 *packs, int n_packs, DG_TEX *tex ) +{ + char u, v; + int i; + + u = tex->off_x; + v = tex->off_y; + + for ( i = 0; i < n_packs; i ++ ) + { + setPolyFT4( packs ); + setSemiTrans( packs, 1 ); + setRGB0( packs, 128, 128, 128 ); + setXY4( packs, 255, 164, 303, 164, 255, 176, 303, 176 ); + setUV4( packs, u, v, u + 48, v, u, v + 12, u + 48, v + 12 ); + packs->tpage = tex->tpage; + packs->clut = tex->clut; + packs ++ ; + } +} + +static inline int GetResources(SnaInitWork *work, int name, int where) +{ + SVECTOR shadow; + SVECTOR vec; + int attr; + CONTROL *control; + OBJECT *body; + int model; + SVECTOR *size; + TARGET *trg; + HITTABLE *hittable; + int i; + + attr = RADAR_VISIBLE; + + control = &work->control; + if (GM_InitControl(control, name, where) < 0) + { + return -1; + } + + GM_ConfigControlString(control, GCL_GetOption('p'), GCL_GetOption('d')); + GM_ConfigControlHazard(control, 0, 450, 450); + control->seg_flag = SEGMENT_ATR; + GM_ConfigControlAttribute(control, attr); + GM_ConfigControlTrapCheck(control); + + body = &work->body; + + model = KMD_SNAKE; + if (GCL_GetOption('m')) // model + { + model = GCL_StrToInt(GCL_NextStr()); + } + + GM_InitObject(body, model, BODY_FLAG, OAR_SNAKE); + GM_ConfigObjectJoint(body); + GM_ConfigMotionControl(body, &work->m_ctrl, OAR_SNAKE, work->m_segs1, work->m_segs2, control, work->rots); + GM_ConfigObjectLight(body, work->light); + + GM_PlayerControl = control; + GM_PlayerPosition = work->control.mov; + GM_PlayerBody = body; + + sna_8004EB14(work); + + if (InitLaserSight(work) < 0) + { + return -1; + } + + InitEmpty(work); + + size = &vec; + setVector(size, 300, 650, 300); + + trg = work->field_89C_pTarget = GM_AllocTarget(); + GM_SetTarget(trg, ( TARGET_AVAIL | TARGET_FLAG ), PLAYER_SIDE, size); + GM_SetPowerTarget(trg, POWER_DECREASE, -1, GM_SnakeCurrentHealth, 0, &DG_ZeroVector); + GM_SetCaptureTarget(trg, 0, 0, &work->field_8F4, &work->field_8FC); + GM_MoveTarget(trg, &work->control.mov); + + work->field_A22_snake_current_health = GM_SnakeCurrentHealth; + + InitPacks(work->field_950, 2, DG_GetTexture(PCX_EMPTY2)); + + shadow.vx = 0; + shadow.vy = 6; + shadow.vz = 12; + shadow.pad = 15; + + work->shadow = NewShadow2(control, body, shadow, &work->enable_shadow); + + dword_800ABA1C = 0; + GM_BombSeg = 0; + + for (i = 0; i < 16; i++) + { + tenage_ctrls_800BDD30[i] = NULL; + } + + dword_800BDD28 = 0; + tenage_ctrls_count_800BDD70 = 0; + + hittable = GM_C4Datas; + for (i = 0; i < C4_COUNT; i++, hittable++) + { + hittable->type = WP_C4; + hittable->actor = NULL; + } + + dword_8009F434 = 0; + bakudan_count_8009F42C = 0; + + hittable = GM_ClayDatas; + for (i = 0; i < 8; i++, hittable++) + { + hittable->type = WP_Claymore; + hittable->actor = NULL; + } + + dword_8009F440 = 0; + dword_8009F444 = 0; + counter_8009F448 = 0; + dword_8009F46C[0] = 0; + dword_8009F470 = 0; + dword_8009F474 = 0; + target_800BDF00 = 0; + + for (i = 0; i < 64; i++) + { + gBulNames_800BDC78[i] = 0; + } + + tabako_dword_8009F2C0 = 0; + pVec_800ABBCC = NULL; + + if (model == KMD_SNE_WET2) // wet suit in the docks + { + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK29); + } + + return 0; +} + +// Unused strings left in binary for some reason +static const char aPosDDD[] = "pos %d %d %d\n"; +static const char aTurnDDD[] = "turn %d %d %d\n"; +static const char aCeilFloorDD[] = "ceil floor %d %d\n"; +static const char aStatus4x[] = "status %4x\n"; +static const char aWeaponD[] = "weapon %d\n"; +static const char aItemD[] = "item %d\n"; +static const char aFlag4x[] = "flag %4x\n"; +static const char aFlag24x[] = "flag2 %4x\n"; +static const char aStanceD[] = "stance %d\n"; +static const char aPadtoD[] = "padto %d\n"; +static const char aTrapCheckD[] = "trap check %d\n"; +static const char aPadX[] = "pad %x\n"; +static const char aKaze[] = "kaze \n"; + +#define EXEC_LEVEL GV_ACTOR_USER + +void *NewSnake(int name, int where, int argc, char **argv) +{ + SnaInitWork *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(SnaInitWork)); + if (!work) + { + return NULL; + } + + GV_SetNamedActor(&work->actor, Act, Die, "sna_init.c"); + + if (GetResources(work, name, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + sna_800ABBA0 = work; + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/snake/sna_init.h b/components/mgs/mgs/source/chara/snake/sna_init.h new file mode 100644 index 00000000..18f1f5f4 --- /dev/null +++ b/components/mgs/mgs/source/chara/snake/sna_init.h @@ -0,0 +1,480 @@ +#ifndef __MGS_CHARA_SNA_INIT_H__ +#define __MGS_CHARA_SNA_INIT_H__ + +#include +#include +#include + +#include "libgv/libgv.h" +#include "game/game.h" +#include "game/navi.h" + +typedef struct ACTSTILL +{ + unsigned char stand; + unsigned char setup; + unsigned char squat; + unsigned char crouch; + unsigned char against_wall; + unsigned char squat_against_wall; + unsigned char tap_wall_r; + unsigned char tap_wall_l; +} ACTSTILL; + +typedef struct ACTTRANS +{ + unsigned char crouch; + unsigned char squat; + unsigned char stand; + unsigned char pad; +} ACTTRANS; + +typedef struct ACTMOVE +{ + unsigned char stand; + unsigned char setup; + unsigned char squat; + unsigned char crouch; + unsigned char crouch_backwards; + unsigned char against_wall_r; + unsigned char against_wall_l; + unsigned char aim; + unsigned char aim_stance_l; + unsigned char aim_stance_r; +} ACTMOVE; + +typedef struct ACTDAMAGE +{ + unsigned char field_0; + unsigned char field_1; + unsigned char field_2; + unsigned char field_3; + unsigned char field_4; + unsigned char field_5; + unsigned char field_6; + unsigned char field_7; + unsigned char field_8; + unsigned char field_9; + unsigned char field_A; + unsigned char field_B; +} ACTDAMAGE; + +typedef struct ACTATTACK +{ + unsigned char aim; + unsigned char reload; + unsigned char punch; + unsigned char setup; + unsigned char shoot; + unsigned char field_5; + unsigned char field_6; + unsigned char field_7; + unsigned char field_8; + unsigned char field_9; +} ACTATTACK; + +typedef struct ACTSPECIAL +{ + unsigned char field_0; + unsigned char field_1; + unsigned char field_2; + unsigned char field_3; + unsigned char field_4; + unsigned char field_5; + unsigned char field_6; + unsigned char field_7; +} ACTSPECIAL; + +typedef struct Sna_E6 +{ + unsigned char field_0; + unsigned char field_1; + unsigned char field_2; + unsigned char field_3; +} Sna_E6; + +typedef struct Target_Data +{ + SVECTOR field_0; + SVECTOR field_8_size; + int field_10; + int field_14; + int field_18; + unsigned int field_1C; +} Target_Data; + +typedef struct ACTPACK +{ + ACTSTILL *still; /* 静止モーション */ + ACTMOVE *move; /* 移動モーション */ + ACTTRANS *trans; /* 遷移モーション */ + ACTDAMAGE *damage; /* ダメージモーション */ + ACTATTACK *attack; /* 攻撃モーション */ + Sna_E6 *special1; //u_char *special1 ; /* 特殊モーション1 */ + ACTSPECIAL *special2; //u_char *special2 ; /* 特殊モーション2 */ +} ACTPACK; + +typedef enum +{ + SNA_FLAG1_UNK1 = 0x1, + SNA_FLAG1_UNK2 = 0x2, + SNA_FLAG1_UNK3 = 0x4, + SNA_FLAG1_UNK4 = 0x8, + SNA_FLAG1_UNK5 = 0x10, + SNA_FLAG1_UNK6 = 0x20, + SNA_FLAG1_UNK7 = 0x40, + SNA_FLAG1_UNK8 = 0x80, + SNA_FLAG1_UNK9 = 0x100, + SNA_FLAG1_UNK10 = 0x200, + SNA_FLAG1_UNK11 = 0x400, + SNA_FLAG1_UNK12 = 0x800, + SNA_FLAG1_UNK13 = 0x1000, + SNA_FLAG1_UNK14 = 0x2000, + SNA_FLAG1_UNK15 = 0x4000, + SNA_FLAG1_UNK16 = 0x8000, + SNA_FLAG1_UNK17 = 0x10000, + SNA_FLAG1_UNK18 = 0x20000, + SNA_FLAG1_UNK19 = 0x40000, + SNA_FLAG1_UNK20 = 0x80000, + SNA_FLAG1_UNK21 = 0x100000, + SNA_FLAG1_UNK22 = 0x200000, + SNA_FLAG1_UNK23 = 0x400000, + SNA_FLAG1_UNK24 = 0x800000, + SNA_FLAG1_UNK25 = 0x1000000, + SNA_FLAG1_UNK26 = 0x2000000, + SNA_FLAG1_UNK27 = 0x4000000, + SNA_FLAG1_UNK28 = 0x8000000, + SNA_FLAG1_UNK29 = 0x10000000, + SNA_FLAG1_UNK30 = 0x20000000, + SNA_FLAG1_UNK31 = 0x40000000, + SNA_FLAG1_UNK32 = 0x80000000, +} SnaFlag1; + +typedef enum +{ + SNA_FLAG2_UNK1 = 0x1, + SNA_FLAG2_UNK2 = 0x2, + SNA_FLAG2_UNK3 = 0x4, + SNA_FLAG2_UNK4 = 0x8, + SNA_FLAG2_UNK5 = 0x10, + SNA_FLAG2_UNK6 = 0x20, + SNA_FLAG2_UNK7 = 0x40, + SNA_FLAG2_UNK8 = 0x80, + SNA_FLAG2_UNK9 = 0x100, + SNA_FLAG2_UNK10 = 0x200, + SNA_FLAG2_UNK11 = 0x400, + SNA_FLAG2_UNK12 = 0x800, + SNA_FLAG2_UNK13 = 0x1000, + SNA_FLAG2_UNK14 = 0x2000, + SNA_FLAG2_UNK15 = 0x4000, + SNA_FLAG2_UNK16 = 0x8000, + SNA_FLAG2_UNK17 = 0x10000, + SNA_FLAG2_UNK18 = 0x20000, + SNA_FLAG2_UNK19 = 0x40000, + SNA_FLAG2_UNK20 = 0x80000, + SNA_FLAG2_UNK21 = 0x100000, + SNA_FLAG2_UNK22 = 0x200000, + SNA_FLAG2_UNK23 = 0x400000, + SNA_FLAG2_UNK24 = 0x800000, + SNA_FLAG2_UNK25 = 0x1000000, + SNA_FLAG2_UNK26 = 0x2000000, + SNA_FLAG2_UNK27 = 0x4000000, + SNA_FLAG2_UNK28 = 0x8000000, + SNA_FLAG2_UNK29 = 0x10000000, + SNA_FLAG2_UNK30 = 0x20000000, + SNA_FLAG2_UNK31 = 0x40000000, + SNA_FLAG2_UNK32 = 0x80000000, +} SnaFlag2; + +// sub struct of SnaInitWork +typedef struct UnkSnakeStruct2 +{ + unsigned short field_9E4; + short field_9E6; + short field_9E8; + short field_9EA; + short field_9EC_flags3; + short field_9EE; + short field_9F0; + short field_9F2; + SVECTOR field_9F4; +} UnkSnakeStruct2; + +struct SnaInitWork; +typedef void (*TSnakeFunction)(struct SnaInitWork *, int); + +#define SNAKEJOINT_NUM 16 + +// TODO: Many fields and sub structures are not yet recovered/incorrect +typedef struct SnaInitWork +{ + GV_ACT actor; + CONTROL control; + OBJECT body; + MOTION_CONTROL m_ctrl; + MOTION_SEGMENT m_segs1[SNAKEJOINT_NUM + 1]; + MOTION_SEGMENT m_segs2[SNAKEJOINT_NUM + 1]; + SVECTOR rots[SNAKEJOINT_NUM]; + SVECTOR adjust[SNAKEJOINT_NUM]; + int field_798_p_height; + int field_79C; + int field_7A0_msg_count; + GV_MSG field_7A4_msgs[8]; + int field_844; + MATRIX light[2]; + GV_ACT *shadow; + int *enable_shadow; + unsigned short field_890_autoaim_max_dist; + unsigned short field_892_autoaim_min_angle; + SnaFlag1 field_894_flags1; + SnaFlag2 field_898_flags2; + TARGET *field_89C_pTarget; + TARGET field_8A0_target; + TARGET *field_8E8_pTarget; + SVECTOR field_8EC_vec; + int field_8F4; + int field_8F8; + SVECTOR field_8FC; + int field_904_frames_last_choke; + GV_ACT *field_908_weapon_actor; + TSnakeFunction field_90C_pWeaponFn; + int field_910; + u_long field_914_trigger; + short *field_918_n_bullets; + int field_91C_weapon_idx; + int field_920_tbl; + short field_924; + short field_926; + DG_TEX *field_928; + DG_PRIM *field_92C; + SVECTOR field_930; + SVECTOR field_938; + SVECTOR field_940; + SVECTOR field_948; + POLY_FT4 field_950[2]; + int field_9A0; + GV_ACT *field_9A4_item_actor; + int field_9A8_current_item; + int field_9AC; + GV_PAD *field_9B0_pad_ptr; + ACTPACK *actpack; + TSnakeFunction field_9B8_fn_anim; + int field_9BC_anim_frame; + TSnakeFunction field_9C0; + int field_9C4; + TSnakeFunction field_9C8_anim_update_fn_3p; // called for every anim/state while in third person + TSnakeFunction field_9CC_anim_update_fn_1p; // same except first person. caller: 8005a7e0 + short field_9D0[10]; + UnkSnakeStruct2 field_9E4; + void *field_9FC; + NAVIGATE field_A00; // auto movement of snake during mini cutscenes. some above fields may also be this + short field_A20; + short field_A22_snake_current_health; + char field_A24_invuln_frames; + char field_A25; + short field_A26_stance; // SNA_STANCE_... + short field_A28; + short field_A2A; + SVECTOR field_A2C; + char field_A34; + char field_A35_vibtime2; + char field_A36; + char field_A37; + short field_A38_local_data; + short field_A3A; + SVECTOR field_A3C; + SVECTOR field_A44; + int field_A4C; + short field_A50; + short field_A52; + // var reused in differenet contexts, union is better than a generic name for all contexts + union { + short choke_count; + short prone_bool_thing; + short wall_thing; + short knockdown_thing; + } field_A54; + short field_A56; + short field_A58; + unsigned short field_A5A; + unsigned short field_A5C; + short field_A5E; + SVECTOR field_A60; + SVECTOR field_A68; + int field_A70; +} SnaInitWork; + +typedef void * (*TSnakeEquipFuncion)(CONTROL *, OBJECT *, int); + +void sna_start_anim_8004E1F4(SnaInitWork *work, void *pFn); +void SetAction_8004E22C(SnaInitWork *work, int action_flag, int interp); +void sna_8004E260(SnaInitWork *work, int a2, int interp, int a4); +int GM_CheckPlayerStatusFlag(PlayerStatusFlag arg0); +int GM_SetPlayerStatusFlag(PlayerStatusFlag arg0); +void GM_ClearPlayerStatusFlag(PlayerStatusFlag flag); +void sna_set_flags1_8004E2F4(SnaInitWork *snake, SnaFlag1 flags); +void sna_clear_flags1_8004E308(SnaInitWork *snake, SnaFlag1 flags); +int sna_check_flags1_8004E31C(SnaInitWork *snake, SnaFlag1 flags); +void sna_set_flags2_8004E330(SnaInitWork *snake, SnaFlag2 flag); +void sna_clear_flags2_8004E344(SnaInitWork *snake, SnaFlag2 flags); +unsigned int sna_sub_8004E358(SnaInitWork *snake, SnaFlag2 param_2); +void CheckSnakeDead_8004E384(SnaInitWork *snake); +void sna_sub_8004E41C(SnaInitWork *snake, unsigned short flags); +int sub_8004E458(short param_1, int param_2); +int sub_8004E4C0(SnaInitWork *work, int param_2); +int sna_line_check(SVECTOR *line, HZD_HDL *hzd, int flag, int exclude); +void sub_8004E588(HZD_HDL *param_1, SVECTOR *param_2, int *levels); +int sub_8004E930(SnaInitWork *snake, int arg1); +void sub_8004E9D0(SnaInitWork *work); +void sub_8004EA50(SnaInitWork *work, int param_2); +int sna_8004EAA8(SnaInitWork *work, int a2); +void sub_8004EB74(SnaInitWork *work); +void sna_8004EC00(SnaInitWork *work); +void sna_8004EC8C(SnaInitWork *work); +void sub_8004ED08(SnaInitWork *work); +void sub_8004EEB0(SnaInitWork *work); +int sub_8004EFE4(SnaInitWork *work, int param_2); +void sna_8004F034(SnaInitWork *work, unsigned int bits); +void sna_act_helper2_helper4_8004F090(SnaInitWork *work, int param_2); +void sub_8004F14C(SnaInitWork *param_1); +void sub_8004F204(SnaInitWork *param_1); +void sna_set_invuln_8004F2A0(SnaInitWork *work, char invuln_frames); +void sna_clear_invuln_8004F2EC(SnaInitWork *snake); +void sub_8004F338(SnaInitWork *param_1); +int sna_act_unk_helper4_8004FA3C(void); +void sna_8004FA74(SnaInitWork *param_1); +void sub_8004FA9C(SnaInitWork *snake); +int sna_current_item_8004FB38(void); +HZD_FLR **sub_8004FB90(void); +int sna_8004E808(SnaInitWork *work, int a2, int a3, int a4, int a5); + +// TODO: move these to game.h even though theyre defined in sna_init.c? +// .. or move the implementation to a static inline in game.h which the definition in sna_init calls? +int GM_Next_BulName_8004FBA0(); +void GM_ClearBulName(int idx); +void GM_CheckShukanReverse(unsigned short *pInput); +void GM_CheckShukanReverseAnalog(unsigned char *pInput); + +void sna_check_knock_800501F8(SnaInitWork *work, int time); +int sna_prone_check_standup_80050398(SnaInitWork *work); +void sna_80050440(SnaInitWork *work); +void sna_80050568(SnaInitWork *work); +int sub_800507D8(SnaInitWork *param_1); +void sna_80051FD0(SnaInitWork *work); +void sna_fn_80052120(SnaInitWork *work, int time); +void sub_8005230C(SnaInitWork *work, int time); +void sub_80052468(SnaInitWork *work, int time); +void sna_fn_80052540(SnaInitWork *work, int time); +void sna_fn_800525F8(SnaInitWork *work, int time); +void sna_act_helper2_helper_helper_800526BC(SnaInitWork *work, int time); +void sna_anim_idle_8005275C(SnaInitWork *work, int time); +void sna_anim_crouch_800527DC(SnaInitWork *param_1, int time); +void sna_anim_prone_idle_800528BC(SnaInitWork *work, int time); +void sna_anim_run_8005292C(SnaInitWork *work, int time); +void sna_anim_prone_move_800529C0(SnaInitWork *work, int time); +void sna_anim_wall_idle_and_c4_80052A5C(SnaInitWork *work, int time); +void sna_anim_wall_move_80052BA8(SnaInitWork *work, int time); +void sna_anim_wall_crouch_80052CCC(SnaInitWork *work, int time); +void sna_fn_80052E58(SnaInitWork *work, int time); +void sna_anim_crouch_helper_80053014(SnaInitWork *work, int time); +void sna_fn_800531F4(SnaInitWork *work, int time); +void sna_fn_nothing_80053B80(SnaInitWork *param_1, int time); +void sna_anim_run_begin_80053B88(SnaInitWork *param_1, int time); +void sna_anim_prone_begin_80053BE8(SnaInitWork *work, int time); +void sna_anim_prone_standup_80053D74(SnaInitWork *work, int time); +void sna_anim_enter_duct_80053E9C(SnaInitWork *work, int time); +void sub_80053FAC(SnaInitWork *work, int time); +void sna_gun_800540D0(SnaInitWork *work, int time); +void sna_bomb_800541A8(SnaInitWork *work, int time); +void sna_anim_chokethrow_begin1_80054210(SnaInitWork *work, int time); +void sna_8005425C(SnaInitWork *work, int time); +void sna_80054318(SnaInitWork *work, int time); +void sna_fn_800543A8(SnaInitWork *work, int time); +void sna_anim_duct_move_80054424(SnaInitWork *work, int time); +void sna_anim_duct_idle_80054488(SnaInitWork *work, int time); +void sna_anim_shot_flinch_800544E0(SnaInitWork *work, int time); +void sna_act_helper2_helper7_80054648(SnaInitWork *work, int time); +void sna_anim_knockdown_80054710(SnaInitWork *work, int time); +void sna_anim_knockdown_idle_80054930(SnaInitWork *work, int time); +void sna_anim_knockdown_getup_80054A10(SnaInitWork *work, int time); +void sna_anim_knockdown_shot_80054B50(SnaInitWork *work); +void sna_anim_knockdown_shot_tank_80054C08(SnaInitWork *work, int time); +void sna_anim_scope_80055334(SnaInitWork *param_1, int time); +void sna_anim_jpegcam_800553CC(SnaInitWork *work, int time); +void sna_anim_box_idle_800553EC(SnaInitWork *work, int time); +void sna_anim_box_move_8005544C(SnaInitWork *work, int time); +void sna_anim_box_stop_800554B4(SnaInitWork *work, int time); +void sub_8005684C(SnaInitWork *work); +void sub_8005688C(SnaInitWork *work, int time); +void sub_80056928(SnaInitWork *param_1, int time); +void sna_knockdown_check_getup_80056A1C(SnaInitWork *work, int time); +void sna_80056A54(SnaInitWork *work, int time); +void sna_fn_80056AD8(SnaInitWork *work, int time); +void sna_anim_shoot_weapon_80056B88(SnaInitWork *work, int time); +void sna_anim_rungun_begin_80056BDC(SnaInitWork *param_1, int time); +void sna_anim_rungun_80056C3C(SnaInitWork *param_1, int time); +void sna_anim_stinger_800570C0(SnaInitWork *work, int time); +void sna_80057118(SnaInitWork *work, int time); +void sna_800571B8(SnaInitWork *work, int time); +void sna_80057378(SnaInitWork *work, int time); +void sna_anim_shoot_weapon_helper_80057590(SnaInitWork *work, int time); +void sna_anim_rungun_begin_helper_800577B4(SnaInitWork *work, int time); +void sna_auto_aim_800579A0(SnaInitWork *work); +STATIC void OP_ShootStinger(SnaInitWork *work, int time); +void sub_80058644(SnaInitWork *work, int time); +void sna_anim_punch_80058C10(SnaInitWork *work, int time); +void sna_anim_chokethrow_begin2_80058C80(SnaInitWork *work, int time); +void sna_anim_choke_80058E88(SnaInitWork *work, int time); +void sna_anim_choke_rechoke_80058EF4(SnaInitWork *work, int time); +void sna_anim_choke_rechoke_80058EF4(SnaInitWork *work, int time); +void sna_anim_choke_kill_80058F88(SnaInitWork *work, int time); +void sna_anim_choke_drag_80059054(SnaInitWork *work, int time); +void sna_anim_choke_hold_80059154(SnaInitWork *work, int time); +void sub_800591BC(SnaInitWork *work); +void sna_anim_choke_helper_8005951C(SnaInitWork *work, int time); +void sna_anim_choke_rechoke_helper_8005961C(SnaInitWork *work, int time); +void sna_kill_8005B52C(SnaInitWork *work); +void sna_anim_psg1_helper_80057FD4(SnaInitWork *work, int a2); +void sna_80051DA0(SnaInitWork *work); +void sna_80053360(SnaInitWork *work, int time); +void sub_80057BF0(SnaInitWork *work, int a2); +void sna_anim_throw_800589C8(SnaInitWork *work, int a2); +void sna_gun_800540D0(SnaInitWork *work, int a2); +void sna_anim_punch_helper_800591F4(SnaInitWork *work, int a2); +int sna_8004FDE8(SnaInitWork *work, Target_Data *pVec); +void sna_8004F8E4(SnaInitWork *work, int a2); +void sna_fn_800535B8(SnaInitWork *work, int time); +void sna_knockdown_getup_80050668(SnaInitWork *work); +void sna_anim_grenade_80058470(struct SnaInitWork *work, int a2); +void sna_anim_claymore_80057474(struct SnaInitWork *work, int a2); +void sna_anim_psg1_80056DDC(struct SnaInitWork *work, int a2); +void sna_8004EE28(SnaInitWork *work); +void sna_8004EB14(SnaInitWork *work); +void sna_anim_wall_idle_and_c4_helper_800537D4(SnaInitWork *work, int time); +void sna_anim_wall_crouch_helper_80053A54(SnaInitWork *work, int time); +void sub_8004FAE8(SnaInitWork *work); +void sna_anim_wall_move_helper_800538CC(SnaInitWork *work, int time); +void sna_knock_80054D68(SnaInitWork *work, int time); +void sna_anim_rungun_helper_80057844(SnaInitWork *work, int time); +int sna_8005009C(SnaInitWork *work); +int sub_80051BA4(SnaInitWork *work); +int sub_8004EFE4(SnaInitWork *work, int param_2); +int sub_8004E5E8(SnaInitWork *work, int param_2); +int sub_8004E808(SnaInitWork *work, int, int, int, int); // dummy signature +int NavigateGetTargetDist(NAVIGATE *, SVECTOR *); +int NavigateTargetNear(NAVIGATE *param_1, SVECTOR *param_2, int param_3); +void sna_anim_claymore_helper_80058780(SnaInitWork *work, int time); +void sna_act_unk2_80051170(TARGET *pTarget); +void sna_act_helper3_helper_80056650(SnaInitWork *work, int time); +void sna_act_unk_helper3_80055DD8(SnaInitWork *work, int time); +void sna_anim_mini_cutscene_800559D8(SnaInitWork *work, int time); +void sna_anim_dying_80055524(SnaInitWork *work, int time); +void NavigateSetTarget(NAVIGATE *autoMove, HZD_HDL *pHzd, SVECTOR *pVec); +int GM_ZoneNavi(NAVIGATE *pAutoMove, CONTROL *pControl); +void sna_80057A90(SnaInitWork *work, int time); +void sna_8004EF14(SnaInitWork *work); +void *NewSnake(int name, int where, int argc, char **argv); +void sna_act_helper2_helper2_80033054(int id, SVECTOR *vec); +int sna_ration_available_8004FB4C(void); + +#endif // __MGS_CHARA_SNA_INIT_H__ diff --git a/components/mgs/mgs/source/chara/snake/snake.c b/components/mgs/mgs/source/chara/snake/snake.c new file mode 100644 index 00000000..03c28ddd --- /dev/null +++ b/components/mgs/mgs/source/chara/snake/snake.c @@ -0,0 +1,448 @@ +#include "common.h" + +#ifdef VR_EXE +#include "chara/snake_vr/sna_init.h" +#else +#include "chara/snake/sna_init.h" +#endif + +#include "linkvar.h" +#include "game/game.h" + +#define SEGMENT_ATR ( HZD_SEG_NO_COLLIDE ) + +extern short word_8009EFC0[]; + +//TODO: below defined in sna_init.c, need to remove gp hack +extern SVECTOR svector_800AB7CC; +extern SVECTOR svector_800AB7CC; + +extern void *GM_BombSeg; +extern void *GM_BombSeg; + +extern int dword_800ABBA8; +extern int dword_800ABBA8; + +extern char *dword_800ABBB4; +extern char *dword_800ABBB4; + +extern HZD_FLR *flr_800ABBB8[2]; +extern HZD_FLR *flr_800ABBB8[2]; + +#define GetAction( work ) (work->body.action) + +void sna_start_anim_8004E1F4(SnaInitWork *work, void *pFn) +{ + short vec_x = 0; + work->field_9B8_fn_anim = pFn; + work->field_9BC_anim_frame = 0; + work->field_A3A = 0; + work->field_A38_local_data = 0; + + if ((GM_PlayerStatus & PLAYER_GROUND) != 0) + { + vec_x = work->field_A2A; + } + + work->control.turn.vx = vec_x; + work->control.turn.vz = 0; +} + +void SetAction_8004E22C(SnaInitWork *work, int action_flag, int interp) +{ + if (GetAction(work) != action_flag) + { + GM_ConfigObjectAction(&work->body, action_flag, 0, interp); + } +} + +void sna_8004E260(SnaInitWork *work, int a2, int interp, int a4) +{ + if (work->body.action2 != a2) + { + GM_ConfigObjectOverride(&work->body, a2, 0, interp, a4); + } +} + +int GM_CheckPlayerStatusFlag(PlayerStatusFlag arg0) // Status() +{ + return (GM_PlayerStatus & arg0) != 0; +} + +int GM_SetPlayerStatusFlag(PlayerStatusFlag arg0) +{ + int temp_v0; + + temp_v0 = GM_PlayerStatus | arg0; + GM_PlayerStatus = temp_v0; + return temp_v0; +} + +void GM_ClearPlayerStatusFlag(PlayerStatusFlag flag) +{ + GM_PlayerStatus &= ~flag; +} + +void sna_set_flags1_8004E2F4(SnaInitWork *snake, SnaFlag1 flags) +{ + snake->field_894_flags1 |= flags; +} + +void sna_clear_flags1_8004E308(SnaInitWork *snake, SnaFlag1 flags) +{ + snake->field_894_flags1 &= ~flags; +} + +int sna_check_flags1_8004E31C(SnaInitWork *snake, SnaFlag1 flags) +{ + return (snake->field_894_flags1 & flags) != 0; +} + +#ifndef VR_EXE +void sna_set_flags2_8004E330(SnaInitWork *snake, SnaFlag2 flag) +{ + snake->field_898_flags2 |= flag; +} + +void sna_clear_flags2_8004E344(SnaInitWork *snake, SnaFlag2 flags) +{ + snake->field_898_flags2 &= ~flags; +} + +unsigned int sna_sub_8004E358(SnaInitWork *snake, SnaFlag2 param_2) +{ + unsigned int result = 0; + + if (GM_UnkFlagBE != 0) + { + result = (((unsigned int)snake->field_898_flags2 & param_2) != result); + } + + return result; +} +#endif + +void CheckSnakeDead_8004E384(SnaInitWork *snake) +{ + if ((GM_SnakeCurrentHealth == 0) || (GM_GameOverTimer != 0)) + { + snake->control.skip_flag |= CTRL_SKIP_TRAP; + GM_SetPlayerStatusFlag(PLAYER_PAD_OFF | PLAYER_MENU_DISABLE); + sna_set_flags1_8004E2F4(snake, SNA_FLAG1_UNK23); + GM_GameStatus |= (STATE_MENU_OFF | STATE_PADRELEASE); + + if (GM_GameOverTimer != -2) + { + sna_set_flags1_8004E2F4(snake, (SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6)); + } + } +} + +void sna_sub_8004E41C(SnaInitWork *snake, unsigned short flags) +{ + TARGET *target = snake->field_8E8_pTarget; + + if (target != NULL) + { + target->damaged &= ~flags; + snake->field_8E8_pTarget = 0; + snake->field_A54.choke_count = 0; + snake->field_89C_pTarget->size.vx = 300; + } +} + +// ... categorize move/turn direction by angle? +// param_1: snake->control.turn.vy +// param_2: gSnaMoveDir_800ABBA4 +int sub_8004E458(short param_1, int param_2) +{ + short uVar2; + + if (param_2 < 0) + { + return 0; + } + + uVar2 = (param_2 - param_1) & 0xFFF; + + if (uVar2 < 0x800) + { + if (uVar2 < 0x100) + { + return 1; + } + else if (uVar2 > 0x500) + { + return 3; + } + + return 4; + } + else + { + if (uVar2 > 0xF00) + { + return 1; + } + else if (uVar2 < 0xB00) + { + return 3; + } + + return 2; + } +} + +int sub_8004E4C0(SnaInitWork *work, int param_2) +{ + int iVar1; + + if (-1 < dword_800ABBA8) + { + if ((*dword_800ABBB4 & 0x40) != 0) + { + return param_2; + } + iVar1 = (param_2 - dword_800ABBA8) & 0xfff; + if (iVar1 < 0x400) + { + param_2 = dword_800ABBA8 + 0x400; + } + if (iVar1 > 0xc00) + { + param_2 = dword_800ABBA8 - 0x400; + } + } + return param_2; +} + +int sna_line_check(SVECTOR *line, HZD_HDL *hzd, int flag, int exclude) +{ + if (HZD_OnlineHazardCheck(hzd, &line[0], &line[1], flag, exclude) == 0) + { + return -1; + } + + HZD_GetOnlinePoint(&line[1]); + GV_SubVec3(&line[1], line, line); + return GV_VecLen3(line); +} + +void sub_8004E588(HZD_HDL *param_1, SVECTOR *param_2, int *levels) +{ + unsigned int uVar1; + + uVar1 = HZD_LevelHazardCheck(param_1, param_2, HZD_CHK_FLOOR); + HZD_GetLevelHeight(levels); + if ((uVar1 & 1) == 0) + { + levels[0] = -32767; + } + if ((uVar1 & 2) == 0) + { + levels[1] = 32767; + } +} + +int sub_8004E5E8(SnaInitWork *work, int flag) +{ + int i; + SVECTOR vec; + int levels[2]; + HZD_FLR *flr[2]; + + vec.vx = work->body.objs->objs[4].world.t[0]; + vec.vy = work->body.objs->objs[4].world.t[1]; + vec.vz = work->body.objs->objs[4].world.t[2]; + + DG_SetPos2(&vec, &work->control.rot); + DG_PutVector(&svector_800AB7CC, &vec, 1); + sub_8004E588(work->control.map->hzd, &vec, levels); + + i = -1; + + if ((HZD_GetFloorLevel() & flag) == 0) + { + HZD_GetLevelHazard(flr); + + if (vec.vy - levels[0] < 350) + { + i = 0; + } + else if (levels[1] - vec.vy < 125) + { + i = 1; + } + + if (i >= 0) + { + if (!flr[i]) + { + GM_BombSeg = 0; + } + else + { + GM_BombSeg = (void *)((int)flr[i] & ~0x80000000); + } + + return 1; + } + } + + return 2; +} + +int sna_8004E71C(int a1, HZD_HDL *pHzd, SVECTOR *pVec, int a4) +{ + int levels[2]; + SVECTOR vec, vec_saved; + MATRIX mtx; + + pVec->vz = a1; + pVec->vy = 0; + pVec->vx = 0; + + DG_PutVector(pVec, pVec, 1); + ReadRotMatrix(&mtx); + + vec.vx = mtx.t[0]; + vec.vy = mtx.t[1]; + vec.vz = mtx.t[2]; + + vec_saved = *pVec; + + if ( sna_line_check(&vec, pHzd, HZD_CHK_D_SEGMENT | HZD_CHK_F_SEGMENT, SEGMENT_ATR) >= 0 ) + { + *pVec = vec_saved; + } + + sub_8004E588(pHzd, pVec, levels); + return (levels[1] - pVec->vy) < a4; +} + +int sna_8004E808(SnaInitWork *work, int a2, int a3, int a4, int a5) +{ + CONTROL *pCtrl = &work->control; + int bVar1 = 0; + SVECTOR SStack48; + SVECTOR auStack40; + + if (sna_8004E71C(a3, pCtrl->map->hzd, &SStack48, a5)) + { + return 1; + } + + if (a2 == 0) + { + return 0; + } + + if (sna_8004E71C(a4, pCtrl->map->hzd, &auStack40, a5)) + { + if (!flr_800ABBB8[0] || (flr_800ABBB8[0]->b1.h == 2)) + { + return 1; + } + + if (sna_line_check(&SStack48, work->control.map->hzd, HZD_CHK_D_FLOOR | HZD_CHK_F_FLOOR, SEGMENT_ATR) < 0) + { + return 1; + } + + bVar1 = 1; + } + + if ((!bVar1) && sna_8004E71C((a3 + a4) / 2, pCtrl->map->hzd, &SStack48, a5)) + { + return 1; + } + + return 0; +} + +int sub_8004E930(SnaInitWork *snake, int arg1) +{ + int int0; + int int1; + SVECTOR vec0; + SVECTOR vec1; + + vec0.vy = 0; + vec0.vx = 0; + vec0.vz = (short)arg1; + DG_PutVector(&vec0, &vec0, 1); + + int1 = HZD_SlopeFloorLevel(&vec0, flr_800ABBB8[0]); + int1 -= snake->control.levels[0]; + + vec1.vx = int1; + vec1.vz = SquareRoot0(arg1 * arg1 - int1 * int1); + int0 = -GV_VecDir2(&vec1); + + if (int0 < -0x800) + { + int0 += 0x1000; + } + + return int0; +} + +void sub_8004E9D0(SnaInitWork *work) +{ + int iVar1; + + if (flr_800ABBB8[0]) + { + iVar1 = sub_8004E930(work, 500); + iVar1 = iVar1 / 2; + } + else + { + iVar1 = 0; + } + + work->adjust[1].vx = GV_NearExp2(work->adjust[1].vx, iVar1); + work->adjust[4].vx = GV_NearExp2(work->adjust[4].vx, -iVar1); + work->adjust[9].vx = GV_NearExp2(work->adjust[9].vx, -iVar1); +} + +void sub_8004EA50(SnaInitWork *work, int param_2) +{ + int iVar1 = GV_DiffDirS(param_2, work->control.rot.vy); + + if (iVar1 > 128) + { + iVar1 = 128; + } + else if (iVar1 < -128) + { + iVar1 = -128; + } + + work->control.turn.vz = iVar1; +} + +int sna_8004EAA8( SnaInitWork *work, int stance ) +{ + if ( stance == SNA_STANCE_STANDING ) + { + return work->actpack->still->stand; + } + + if ( stance == SNA_STANCE_SQUAT ) + { + return work->actpack->still->squat; + } + + if ( stance == SNA_STANCE_CROUCH ) + { + return work->actpack->still->crouch; + } + + return -1; +} + +void sna_8004EB14(SnaInitWork *work) +{ + memcpy(&work->field_9D0, &word_8009EFC0, sizeof(work->field_9D0)); +} diff --git a/components/mgs/mgs/source/chara/snake_vr/sna_init.c b/components/mgs/mgs/source/chara/snake_vr/sna_init.c new file mode 100644 index 00000000..a4e5753c --- /dev/null +++ b/components/mgs/mgs/source/chara/snake_vr/sna_init.c @@ -0,0 +1,8180 @@ +// FIXME: copy-pasta of snake/sna_init.c + +#include "sna_init.h" + +#include +#include +#include +#include + +#include "common.h" +#include "chara/snake/afterse.h" +#include "chara/snake/shadow.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "linkvar.h" +#include "okajima/blood.h" +#include "bullet/bakudan.h" +#include "game/vibrate.h" +#include "anime/animconv/anime.h" +#include "equip/equip.h" +#include "okajima/d_blood.h" +#include "takabe/goggle.h" +#include "takabe/goggleir.h" +#include "weapon/weapon.h" +#include "sound/g_sound.h" +#include "strcode.h" + +int SECTION(".sbss") gSnaMoveDir_800ABBA4; +int SECTION(".sbss") dword_800ABBA8; +SVECTOR *SECTION(".sbss") dword_800ABBAC; +void *SECTION(".sbss") dword_800ABBB0; +char *SECTION(".sbss") dword_800ABBB4; +int SECTION(".sbss") sna_800ABBA0_pad; +HZD_FLR *SECTION(".sbss") flr_800ABBB8[2]; +int SECTION(".sbss") dword_800ABBC0; +int SECTION(".sbss") dword_800ABBC4; +SVECTOR *SECTION(".sbss") pVec_800ABBC8; +SVECTOR *SECTION(".sbss") pVec_800ABBCC; +int SECTION(".sbss") dword_800ABBD0; +short SECTION(".sbss") dword_800ABBD4; +void *SECTION(".sbss") GM_BombSeg; +short SECTION(".sbss") dword_800ABBDC; + +extern GM_CameraSystemWork GM_Camera; +extern unsigned short GM_WeaponTypes[]; +extern unsigned short GM_ItemTypes[]; +extern void *dword_8009EEA4[]; +extern int bakudan_count_8009F42C; +//extern int gSnaMoveDir_800ABBA4; +extern int counter_8009F448; +extern int tabako_dword_8009F2C0; +extern GM_SnakeCameraWork GM_SnakeCamera; +extern CONTROL *tenage_ctrls_800BDD30[16]; +extern HITTABLE GM_C4Datas[C4_COUNT]; +extern HITTABLE GM_ClayDatas[8]; +extern unsigned char gBulNames_800BDC78[64]; +unsigned char gBulNames_800BDC78[64]; +extern int dword_8009F440; +extern int dword_8009F444; +extern int dword_8009F46C[]; +extern int dword_8009F470; +extern int dword_8009F474; +extern TARGET *target_800BDF00; +extern int dword_800BDD28; +extern int tenage_ctrls_count_800BDD70; +extern int dword_8009F434; +extern void *dword_8009EEB0[]; +extern void *dword_8009EEB8[]; +extern short HzdHeights_8009EEC4[]; +extern char dword_8009EF1C[]; +extern char dword_8009EF20[]; +extern TSnakeEquipFuncion gSnakeEquips_8009EF8C[]; +extern char dword_8009EEE0[]; +extern char dword_8009EEE4[]; +extern char dword_8009EEF0[]; +extern char dword_8009EEF4[]; +extern char dword_8009EED4[]; +extern char dword_8009EED8[]; +extern short snake_weapon_idx_800BDCBA; +extern short snake_mag_size_800BDCB8; +extern short snake_weapon_max_ammo_800BDCBC; + +#define TARGET_FLAG ( TARGET_CAPTURE | TARGET_POWER | TARGET_PUSH | TARGET_SEEK | TARGET_TOUCH ) +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE | DG_FLAG_GBOUND | DG_FLAG_AMBIENT | DG_FLAG_IRTEXTURE ) + +#define SEGMENT_ATR ( HZD_SEG_NO_COLLIDE ) + +ACTMOVE NoneMove = {12u, 82u, 19u, 23u, 24u, 29u, 30u, 12u, 0u, 0u}; +ACTMOVE SocomMove = {10u, 8u, 17u, 23u, 24u, 27u, 28u, 80u, 106u, 107u}; +ACTMOVE FamasMove = {11u, 9u, 18u, 23u, 24u, 27u, 28u, 73u, 110u, 111u}; +ACTMOVE NikitaMove = {75u, 77u, 79u, 23u, 24u, 27u, 28u, 75u, 0u, 0u}; +ACTMOVE C4Move = {92u, 91u, 93u, 23u, 24u, 29u, 30u, 92u, 0u, 0u}; +ACTMOVE ClaymoreMove = {97u, 96u, 98u, 101u, 24u, 29u, 30u, 97u, 0u, 0u}; +ACTMOVE GrenadeMove = {104u, 103u, 19u, 23u, 24u, 29u, 30u, 104u, 0u, 0u}; + +ACTDAMAGE NoneDamage = {63u, 65u, 67u, 68u, 69u, 70u, 71u, 72u, 66u, 123u, 130u, 131u}; +ACTDAMAGE SocomDamage = {61u, 64u, 67u, 68u, 69u, 70u, 71u, 72u, 66u, 123u, 130u, 131u}; +ACTDAMAGE FamasDamage = {62u, 83u, 67u, 68u, 69u, 70u, 71u, 72u, 66u, 123u, 130u, 131u}; + +ACTATTACK NoneAttack = {136u, 136u, 35u, 136u, 36u, 37u, 38u, 39u, 0u, 0u}; +ACTATTACK SocomAttack = {42u, 51u, 35u, 45u, 47u, 0u, 0u, 0u, 0u, 0u}; +ACTATTACK FamasAttack = {5u, 52u, 34u, 46u, 48u, 0u, 0u, 0u, 0u, 0u}; +ACTATTACK StingerAttack = {74u, 136u, 136u, 136u, 105u, 0u, 0u, 0u, 0u, 0u}; +ACTATTACK NikitaAttack = {74u, 136u, 34u, 46u, 81u, 0u, 0u, 0u, 0u, 0u}; +ACTATTACK C4Attack = {43u, 136u, 35u, 136u, 53u, 55u, 49u, 54u, 56u, 0u}; +ACTATTACK ClaymoreAttack = {57u, 136u, 35u, 136u, 58u, 59u, 0u, 0u, 0u, 0u}; +ACTATTACK GrenadeAttack = {60u, 136u, 35u, 136u, 50u, 0u, 0u, 0u, 0u, 0u}; +ACTATTACK PSG1Attack = {44u, 136u, 136u, 136u, 136u, 0u, 0u, 0u, 0u, 0u}; + +Sna_E6 e6_8009ED48[] = { + {115u, 133u, 0u, 0u}, + {112u, 133u, 0u, 0u}, + {113u, 133u, 0u, 0u}, + {136u, 136u, 0u, 0u}, + {114u, 132u, 0u, 0u}, + {116u, 133u, 0u, 0u}, + {117u, 133u, 0u, 0u}, + {118u, 133u, 0u, 0u} +}; + +// TODO: Why is this in .data despite fitting into .sdata? +ACTSPECIAL SECTION(".data") e7_8009ED68 = {7u, 13u, 4u, 14u, 32u, 119u, 124u, 0u}; + +ACTSTILL NoneStill = {2u, 6u, 16u, 22u, 26u, 129u, 88u, 89u}; +ACTSTILL SocomStill = {0u, 0u, 15u, 22u, 25u, 31u, 40u, 41u}; +ACTSTILL FamasStill = {1u, 5u, 15u, 22u, 25u, 31u, 40u, 41u}; +ACTSTILL GrenadeStill = {3u, 3u, 16u, 22u, 26u, 129u, 88u, 89u}; +ACTSTILL C4Still = {90u, 90u, 16u, 22u, 43u, 53u, 88u, 89u}; +ACTSTILL NikitaSill = {76u, 76u, 78u, 85u, 25u, 87u, 40u, 41u}; +ACTSTILL ClaymoreStill = {95u, 95u, 99u, 100u, 26u, 102u, 88u, 89u}; + +ACTTRANS AllTrans = {20u, 21u, 84u }; + +unsigned short short_8009ED6C = 980; + +ACTPACK actions_no_weapon_8009ED70 = +{ + &NoneStill, &NoneMove, &AllTrans, &NoneDamage, &NoneAttack, &e6_8009ED48[0], &e7_8009ED68 +}; + +ACTPACK weapon_actions_8009ED8C[10] = +{ + {&SocomStill, &SocomMove, &AllTrans, &SocomDamage, &SocomAttack, &e6_8009ED48[1], &e7_8009ED68}, + {&FamasStill, &FamasMove, &AllTrans, &FamasDamage, &FamasAttack, &e6_8009ED48[2], &e7_8009ED68}, + {&GrenadeStill, &GrenadeMove, &AllTrans, &NoneDamage, &GrenadeAttack, &e6_8009ED48[7], &e7_8009ED68}, + {&NikitaSill, &NikitaMove, &AllTrans, &FamasDamage, &NikitaAttack, &e6_8009ED48[4], &e7_8009ED68}, + {&FamasStill, &FamasMove, &AllTrans, &FamasDamage, &StingerAttack, &e6_8009ED48[3], &e7_8009ED68}, + {&ClaymoreStill, &ClaymoreMove, &AllTrans, &NoneDamage, &ClaymoreAttack, &e6_8009ED48[6], &e7_8009ED68}, + {&C4Still, &C4Move, &AllTrans, &NoneDamage, &C4Attack, &e6_8009ED48[5], &e7_8009ED68}, + {&GrenadeStill, &GrenadeMove, &AllTrans, &NoneDamage, &GrenadeAttack, &e6_8009ED48[7], &e7_8009ED68}, + {&GrenadeStill, &GrenadeMove, &AllTrans, &NoneDamage, &GrenadeAttack, &e6_8009ED48[7], &e7_8009ED68}, + {&NoneStill, NULL, &AllTrans, &NoneDamage, &PSG1Attack, &e6_8009ED48[3], &e7_8009ED68} +}; + +void *dword_8009EEA4[] = { + sna_anim_idle_8005275C, + sna_anim_crouch_800527DC, + sna_anim_prone_idle_800528BC +}; + +void *SECTION(".data") dword_8009EEB0[] = { + sna_anim_wall_idle_and_c4_80052A5C, + sna_anim_wall_crouch_80052CCC +}; + +void *dword_8009EEB8[] = { + sna_anim_run_8005292C, + sna_anim_run_8005292C, + sna_anim_prone_move_800529C0 +}; + +short SECTION(".data") HzdHeights_8009EEC4[] = {750, 250, 250, 0}; + +short SECTION(".data") dword_8009EECC[] = {1100, 496, 136, 0}; + +char SECTION(".data") dword_8009EED4[] = {127, 4, 0, 0}; +char SECTION(".data") dword_8009EED8[] = {225, 1, 255, 2, 0, 0, 0, 0}; +char SECTION(".data") dword_8009EEE0[] = {127, 6, 0, 0}; +char SECTION(".data") dword_8009EEE4[] = {225, 1, 255, 4, 225, 2, 75, 8, 0, 0, 0, 0}; +char SECTION(".data") dword_8009EEF0[] = {127, 1, 0, 0}; +char SECTION(".data") dword_8009EEF4[] = {155, 3, 0, 0}; +char SECTION(".data") dword_8009EEF8[] = {0, 2, 127, 2, 0, 0, 0, 0}; +char SECTION(".data") dword_8009EF00[] = {140, 4, 80, 6, 0, 0, 0, 0}; +char SECTION(".data") dword_8009EF08[] = {0, 10, 127, 2, 0, 0, 0, 0}; +char SECTION(".data") dword_8009EF10[] = {80, 2, 50, 8, 140, 4, 80, 6, 0, 0, 0, 0}; +char SECTION(".data") dword_8009EF1C[] = {196, 4, 0, 0}; +char SECTION(".data") dword_8009EF20[] = {148, 4, 0, 0}; +char SECTION(".data") dword_8009EF24[] = {0, 2, 127, 2, 0, 0, 0, 0}; +char SECTION(".data") dword_8009EF2C[] = {60, 2, 200, 4, 40, 2, 0, 0}; + +typedef struct WeaponCreateEntry +{ + void *mCreateActorFn; + void *mStateFn; +} WeaponCreateEntry; + +WeaponCreateEntry gSnakeWeapons_8009EF3C[] = { + {NULL, sna_anim_chokethrow_begin1_80054210}, + {NewSOCOM, sna_gun_800540D0}, + {NewFAMAS, sna_gun_800540D0}, + {NewGrenade, sna_anim_grenade_80058470}, + {NewRCM, sna_gun_800540D0}, + {NewAAM, sna_anim_stinger_800570C0}, + {NewMine, sna_anim_claymore_80057474}, + {NewBomb, sna_bomb_800541A8}, + {NewStanGrenade, sna_anim_grenade_80058470}, + {NewChaffGrenade, sna_anim_grenade_80058470}, + {NewRifle, sna_anim_psg1_80056DDC}}; + +TSnakeEquipFuncion gSnakeEquips_8009EF8C[] = { + NewTabako, // 0 + NewScope, // 1 + NewBox, // 2 + NewBox, // 3 + NewBox, // 4 + NewGoggle, // 5 + NewGoggleIr, // 6 + NewGasMask, // 7 + NewBodyArmor, // 8 + NewKetchap, // 9 + NewKogaku2, // 10 + NewBandana, // 11 + NULL, // 12 +}; + +short word_8009EFC0[] = {0, 500, 0, 320, 400, 320, 400, 32, 32, 0}; + +SVECTOR stru_8009EFD4[2] = {{-100, 200, 200, 0}, {-150, 18, 200, 0}}; + +Target_Data stru_8009EFE4[4] = +{ + {{0, 0, 300, 0}, {400, 600, 400, 0}, 0, 0, 64, 0}, // TODO: Might not be part of this array, might be its own element + {{-400, 0, 600, 0}, {400, 600, 400, 0}, 0, 0, 36, 5}, + {{400, 0, 600, 0}, {400, 600, 400, 0}, 0, 0, 36, 5}, + {{0, 0, 0, 0}, {501, 600, 501, 0}, 0, 0, 7, 0} +}; + +typedef struct _PUNCH +{ + SVECTOR field_0; + SVECTOR size; + SVECTOR field_10; + int life; + int faint; +} PUNCH; + +PUNCH punch_info[] = +{ + {{-200, 200, 600}, {400, 200, 400}, {5, 0, 50}, 0, 1}, + { {200, 200, 600}, {400, 200, 400}, {-5, 0, 50}, 0, 1}, + {{0, 200, 600}, {500, 250, 500}, {0, 0, 100}, 0, 3} +}; + +GV_PAD GV_PadData_8009F0C4 = {0, 0, 0, 0, -1, 0, 0, 0, 0, 0}; + +int snainit_item_800A9420 = -1; +SnaInitWork *snainit_actor_800A9424 = NULL; + +SVECTOR svector_800AB7CC = {150, 0, 50, 0}; +SVECTOR svector_800AB7D4 = {0, 0, 100, 0}; +SVECTOR svector_800AB7DC = {-1024, 0, 0, 0}; +SVECTOR svector_800AB7E4 = {5, 500, 80, 0}; +SVECTOR svector_800AB7EC = {0, 0, 800, 0}; +SVECTOR svector_800AB7F4 = {0, 100, -300, 0}; +SVECTOR svector_800AB7FC = {0, 0, -300, 0}; + +#define RIFLE_TEBURE_TIME 90 // delay before camera shake +#define TEBURE 122 +#define GetAction( work ) (work->body.action) +#define EndMotion( work ) (work->body.is_end != 0) +#define SET work->actpack->attack->aim +#define DispEmpty( work ) (work->field_9A0 = 4) + +void sub_8004EB74(SnaInitWork *work) +{ + if (GM_Camera.first_person == 0) + { + if (work->field_9B0_pad_ptr->dir == (short)-1) + { + work->field_A20 = -6; + } + else + { + work->field_A20 = -3; + } + } + + GM_Camera.first_person = 1; + work->field_A56 = 0; + GM_SetPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_WATCH); + + if (!(work->field_920_tbl & 0x200)) + { + sd_set_cli(0x01ffff20, SD_ASYNC); + } +} + +void sna_8004EC00(SnaInitWork *work) +{ + if (GM_Camera.first_person == 1) + { + work->field_A20 = 6; + } + + GM_Camera.first_person = 0; + work->field_A56 = 0; + + GM_ClearPlayerStatusFlag(PLAYER_NORMAL_WATCH | PLAYER_WATCH); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK16); + + if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + sd_set_cli(0x01ffff21, SD_ASYNC); + } +} + +void sna_8004EC8C(SnaInitWork *work) +{ + u_short v2; // $v1 + + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK12); + work->field_A28 = 460; + GM_Camera.first_person = 1; + v2 = short_8009ED6C; + work->field_A20 = -6; + work->field_9D0[2] = 320; + work->field_9D0[4] = v2; + work->field_9D0[6] = v2; + sd_set_cli(0x01ffff20, SD_ASYNC); +} + +void sub_8004ED08(SnaInitWork *work) +{ + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK12); + work->field_A28 = 0x1c2; + GM_Camera.first_person = 0; // weapon related? + work->field_A20 = 6; + sna_8004EB14(work); + sd_set_cli(0x01ffff21, SD_ASYNC); +} + +void sna_act_helper2_helper3_8004ED6C(SnaInitWork *work) +{ + if (work->field_A20 != 0) + { + if (work->field_A20 >= 1) + { + + if ((GM_Camera.first_person == 0) && (--work->field_A20 == 0)) + { + DG_VisibleObjs(work->body.objs); + + if (!GM_CheckPlayerStatusFlag(PLAYER_ACT_ONLY)) + { + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE); + } + } + } + else if ((GM_Camera.first_person != 0) && (++work->field_A20 == 0)) + { + DG_InvisibleObjs(work->body.objs); + } + } +} + +void sna_8004EE28(SnaInitWork *snake) +{ + MATRIX mat1; + MATRIX mat2; + SVECTOR vec; + int vx; + MATRIX *world = &snake->body.objs->objs[6].world; + + ReadRotMatrix(&mat1); + DG_TransposeMatrix(&mat1, &mat2); + MulMatrix0(&mat2, world, &mat2); + DG_MatrixRotZYX(&mat2, &vec); + SetRotMatrix(&mat1); + + vx = (vec.vx / 16) * 16; + GM_SnakeCamera.rotate2.vx = vx; +} + +void sub_8004EEB0(SnaInitWork *work) +{ + short sVar1; + int iVar2; + int arg0; + + if (gSnaMoveDir_800ABBA4 >= 0) + { + arg0 = work->control.turn.vy; + if (GV_DiffDirS(arg0, gSnaMoveDir_800ABBA4)) + { + iVar2 = GV_NearPhase(arg0, gSnaMoveDir_800ABBA4); + sVar1 = GV_NearSpeed(iVar2, gSnaMoveDir_800ABBA4, 0x40); + work->control.turn.vy = sVar1; + } + } +} + +void sna_8004EF14(SnaInitWork *work) +{ + int actionFlag; + int diff; + int y; + + y = work->control.turn.vy; + + diff = GV_DiffDirS(y, gSnaMoveDir_800ABBA4); + if (diff == 0) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + y = work->actpack->still->setup; + SetAction_8004E22C(work, y, 4); + } + else + { + short sVar2; + int iVar4; + + if (diff < 0) + { + actionFlag = work->actpack->move->aim_stance_l; + } + else + { + actionFlag = work->actpack->move->aim_stance_r; + } + + SetAction_8004E22C(work, actionFlag, 4); + iVar4 = GV_NearPhase(y, gSnaMoveDir_800ABBA4); + sVar2 = GV_NearSpeed(iVar4, gSnaMoveDir_800ABBA4, 0x40); + work->control.turn.vy = sVar2; + } +} + +int sub_8004EFE4(SnaInitWork *work, int set) +{ + MOTION_INFO *info; + int action; + + if (set == 1) + { + info = &work->m_ctrl.info1; + } + else + { + info = &work->m_ctrl.info2; + } + + if (set == 1) + { + action = GetAction(work); + } + else + { + action = work->body.action2; + } + + if (info->frames_left + info->frame == 1) + { + return action; + } + + return -1; +} + +void sna_8004F034(SnaInitWork *work, unsigned int bits) +{ + if ( bits != work->m_ctrl.info1.mask ) + { + work->m_ctrl.info2.mask = ~bits; + work->m_ctrl.info1.mask = bits; + if ( bits == 0xFFFF && sub_8004EFE4(work, 2) >= 0 ) + { + work->m_ctrl.interp = 4; + work->m_ctrl.info2.field_14 = 2; + } + } +} + +void sna_act_helper2_helper4_8004F090(SnaInitWork *work, int param_2) +{ + int iVar1; + MATRIX mtx; + + DG_SetPos(&work->body.objs->objs[6].world); + DG_MovePos(&svector_800AB7D4); + DG_RotatePos(&svector_800AB7DC); + ReadRotMatrix(&mtx); + + iVar1 = -1; + switch (param_2) + { + case 3: + iVar1 = 0; + break; + case 1: + case 4: + iVar1 = 1; + break; + case 2: + iVar1 = 2; + break; + } + + if (iVar1 >= 0) + { + NewBlood(&mtx, iVar1); + } +} + +void sub_8004F14C(SnaInitWork *work) +{ + work->field_91C_weapon_idx = WP_None; + GM_CurrentWeaponId = WP_None; + + if (work->field_908_weapon_actor != 0) + { + GV_DestroyActorQuick(work->field_908_weapon_actor); + } + + work->field_908_weapon_actor = 0; + work->field_920_tbl = GM_WeaponTypes[0]; + work->field_918_n_bullets = 0; + work->actpack = &actions_no_weapon_8009ED70; + work->field_90C_pWeaponFn = &sna_anim_chokethrow_begin1_80054210; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) != 0) + { + work->field_A56 = 0; + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | + PLAYER_NORMAL_WATCH | PLAYER_WATCH); + } + else + { + sna_8004EC00(work); + } +} + +void sub_8004F204(SnaInitWork *work) +{ + if (work->field_9A4_item_actor != 0) + { + GV_DestroyActorQuick(work->field_9A4_item_actor); + } + + work->field_9A4_item_actor = 0; + work->field_9A8_current_item = IT_None; + GM_CurrentItemId = IT_None; + work->field_9AC = GM_ItemTypes[0]; + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) != 0) + { + work->field_A56 = 0; + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | + PLAYER_NORMAL_WATCH | PLAYER_WATCH); + } + else + { + sna_8004EC00(work); + } + + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_CB_BOX); +} + +void sna_set_invuln_8004F2A0(SnaInitWork *work, char invuln_frames) +{ + GM_SetPlayerStatusFlag(PLAYER_INVINCIBLE); + work->field_A24_invuln_frames = invuln_frames; + work->field_89C_pTarget->class &= ~0x96; +} + +void sna_clear_invuln_8004F2EC(SnaInitWork *work) +{ + GM_ClearPlayerStatusFlag(PLAYER_INVINCIBLE); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK1); + work->field_89C_pTarget->class |= TARGET_FLAG; +} + +void sub_8004F338(SnaInitWork *work) +{ + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + work->field_A56 = 0; + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE + | PLAYER_NORMAL_WATCH + | PLAYER_WATCH); + } + else + { + sna_8004EC00(work); + } + + sna_clear_invuln_8004F2EC(work); + work->field_A24_invuln_frames = 0; + sna_sub_8004E41C(work, 2); + GM_ConfigMotionAdjust(&work->body, 0); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK9); + work->field_9C0 = 0; + sna_8004E260(work, 0, 4, 0); + DG_InvisiblePrim(work->field_92C); + + GM_ExitBehindCamera(); + + GM_PlayerStatus &= PLAYER_SECOND_AVAILABLE | PLAYER_NOT_SIGHT | + PLAYER_GAME_OVER | PLAYER_CB_BOX | PLAYER_INTRUDE | + PLAYER_ACT_ONLY | PLAYER_SQUAT | PLAYER_GROUND; + + if ((work->field_920_tbl & 0x200) != 0) + { + GM_SetPlayerStatusFlag(PLAYER_WATCH); + } + + work->field_A28 = 0x1c2; + work->field_894_flags1 &= (SNA_FLAG1_UNK20 | SNA_FLAG1_UNK28 | SNA_FLAG1_UNK29); + work->control.skip_flag &= ~CTRL_BOTH_CHECK; +} + +void sub_8004F454(SnaInitWork *work) +{ + int i; + + GM_ClearPlayerStatusFlag(PLAYER_MOVE | PLAYER_ATTACK | PLAYER_CHECK_WALL); + GM_ConfigMotionAdjust(&work->body, NULL); + + for (i = 0; i < SNAKEJOINT_NUM; i++) + { + work->adjust[i] = DG_ZeroVector; + } + + sna_8004E260(work, 0, 4, 0); + sna_sub_8004E41C(work, 2); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK2 | SNA_FLAG1_UNK3 | SNA_FLAG1_UNK26); + DG_InvisiblePrim(work->field_92C); + + if (GM_CheckPlayerStatusFlag(PLAYER_CAUTION) == 0) + { + work->field_A28 = 450; + GM_ExitBehindCamera(); + } + + work->control.skip_flag &= ~CTRL_BOTH_CHECK; +} + +int sna_8004F544(SnaInitWork *work, SVECTOR param_2, int a3, int a4, int a5) +{ + MATRIX mtx; + SVECTOR vec1; + SVECTOR vec2; + SVECTOR vec3; + + int bVar1; + int iVar2; + + vec1 = param_2; + + vec3.vy = 0; + vec3.vx = 0; + vec3.vz = a3; + + ReadRotMatrix(&mtx); + DG_SetPos2(¶m_2, &work->control.turn); + DG_PutVector(&vec3, &vec2, 1); + DG_SetPos(&mtx); + + iVar2 = sna_line_check(&vec1, work->control.map->hzd, a4, a5); + + bVar1 = a3 < iVar2; + + if (a3 < 0) + { + a3 = -a3; + bVar1 = a3 < iVar2; + } + + if (bVar1) + { + iVar2 = -1; + } + + return iVar2; +} + +int sna_8004F628(SnaInitWork *work, SVECTOR *param_2, int param_3, int param_4, int param_5, int param_6) +{ + SVECTOR vec; + + DG_SetPos2(&work->control.mov, &work->control.rot); + DG_PutVector(param_2, &vec, 1); + + if (param_6 >= 1) + { + vec.vy = work->control.levels[0] + param_6; + } + + return sna_8004F544(work, vec, param_3, param_4, param_5) >= 0; +} + +static inline int sna_update_life_helper_8004F6E8(int health, int item) +{ + int temp; + + if ((health == 0) && + (GM_GameOverTimer == 0) && + (item == IT_Ration) && + (GM_Items[IT_Ration] > 0) && + !GM_FrozenItemsState) + { + temp = (GM_DifficultyFlag > DIFFICULTY_EASY) ? 256 : 384; + + if (GM_DifficultyFlag < DIFFICULTY_EASY) + { + temp = GM_SnakeMaxHealth; + } + + health = temp; + + if (health > GM_SnakeMaxHealth) + { + health = GM_SnakeMaxHealth; + } + + GM_Items[IT_Ration] -= 1; + + if (GM_Items[IT_Ration] == 0) + { + GM_CurrentItemId = IT_None; + } + + GM_TotalRationsUsed += 1; + GM_SeSet2(0, 63, SE_RECOVER_LIFE); + } + + return health; +} + +void UpdateLife_8004F6E8(SnaInitWork *work) +{ + TARGET *target; + int health; + + if (GM_CheckPlayerStatusFlag(PLAYER_ACT_ONLY)) + { + target = work->field_89C_pTarget; + + if (target->damage > 0) + { + target->vital += target->damage; + + sna_act_unk2_80051170(work->field_89C_pTarget); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK25); + } + + return; + } + + if (sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK23)) + { + return; + } + + if (sna_check_flags1_8004E31C(work,SNA_FLAG1_UNK25) && + (GM_SnakeCurrentHealth == work->field_89C_pTarget->vital)) + { + return; + } + + if ((work->field_9A8_current_item == IT_BodyArmor) && (work->field_89C_pTarget->a_mode == 1)) + { + work->field_89C_pTarget->damage /= 2; + work->field_89C_pTarget->vital += work->field_89C_pTarget->damage; + work->field_89C_pTarget->damage = 0; + } + + health = work->field_89C_pTarget->vital + GM_SnakeCurrentHealth - work->field_A22_snake_current_health; + + if (health > GM_SnakeMaxHealth) + { + health = GM_SnakeMaxHealth; + } + + if (health < 0) + { + health = 0; + } + + if (work->field_89C_pTarget->weapon != WP_Max) + { + health = sna_update_life_helper_8004F6E8(health, work->field_9A8_current_item); + } + else + { + health = 0; + } + + work->field_89C_pTarget->vital = health; + work->field_A22_snake_current_health = health; + GM_SnakeCurrentHealth = health; + + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK25); +} + +void sna_8004F8E4(SnaInitWork *work, int a2) +{ + GV_MSG msg; + + if ((GM_GameOverTimer == -1) || (GM_GameOverTimer == 0)) + { + // スネーク + msg.address = GV_StrCode("スネーク"); + msg.message_len = 6; + msg.message[0] = HASH_GO_MOTION; + msg.message[1] = a2; + msg.message[2] = -1; + msg.message[3] = -1; + msg.message[4] = 2; + msg.message[5] = 2042; + + work->field_7A4_msgs[work->field_7A0_msg_count] = msg; + work->field_7A0_msg_count++; + + GM_GameOverTimer = 0; + GM_GameOver(); + + GM_GameStatus |= STATE_PADRELEASE; + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK20); + + if (work->field_A70 >= 0) + { + GCL_ForceExecProc(work->field_A70, NULL); + } + + if (GM_CheckPlayerStatusFlag(PLAYER_CB_BOX)) + { + sub_8004F204(work); + } + } +} + +int sna_act_unk_helper4_8004FA3C(void) +{ + if (GM_SnakeCurrentHealth == 0 || GM_GameOverTimer != 0) + { + return 1; + } + + return 0; +} + +int sna_current_item_8004FB38(void) +{ + if (snainit_actor_800A9424) + { + return snainit_actor_800A9424->field_9A8_current_item; + } + + return snainit_item_800A9420; +} + +int sna_ration_available_8004FB4C(void) +{ + if ((snainit_actor_800A9424->field_9A8_current_item == IT_Ration) && + (GM_Items[IT_Ration] > 0) && + (GM_FrozenItemsState == 0)) + { + return 1; + } + + return 0; +} + +HZD_FLR ** sub_8004FB90(void) +{ + return flr_800ABBB8; +} + +int GM_Next_BulName_8004FBA0() +{ + int i; // $a0 + for (i = 1; i < 64; i++) // for some reason skip the first one ?? + { + if (!gBulNames_800BDC78[i]) + { + gBulNames_800BDC78[i] = 1; + return i; + } + } + return 0; +} + +void GM_ClearBulName(int idx) +{ + gBulNames_800BDC78[idx] = 0; +} + +void GM_CheckShukanReverse(unsigned short *pInput) +{ + unsigned short old; + unsigned int v2; // $v1 + unsigned int v3; // $v0 + + if ((GM_OptionFlag & OPTION_SHUKAN_REVERSE) != 0) + { + old = *pInput; + + *pInput = old & ~0x5000; + + v2 = old & 0x5000; + v3 = v2 & 0x1000; + + if (v3) + { + *pInput |= 0x4000; + } + + if (v2 & 0x4000) + { + *pInput |= 0x1000; + } + } +} + +void GM_CheckShukanReverseAnalog(unsigned char *pInput) +{ + if ((GM_OptionFlag & OPTION_SHUKAN_REVERSE) != 0) + { + *pInput = ~*pInput; + } +} + +int sub_8004FCB8(SnaInitWork *work, PUNCH *punch, int a_mode) +{ + TARGET *target; + SVECTOR vec; + SVECTOR line[2]; + + target = &work->field_8A0_target; + GM_SetTarget(target, 4, PLAYER_SIDE, &punch->size); + DG_RotVector(&punch->field_10, &vec, 1); + GM_SetPowerTarget(target, POWER_CONST, a_mode, punch->life, punch->faint, &vec); + DG_PutVector(&punch->field_0, &vec, 1); + + line[0].vx = work->body.objs->objs[5].world.t[0]; + line[0].vy = work->body.objs->objs[5].world.t[1]; + line[0].vz = work->body.objs->objs[5].world.t[2]; + line[1] = vec; + + if ( sna_line_check(line, work->control.map->hzd, HZD_CHK_ALL, SEGMENT_ATR) < 0 ) + { + GM_MoveTarget(target, &vec); + return GM_PowerTarget(target); + } + + return 0; +} + +int sna_8004FDE8(SnaInitWork *work, Target_Data *pTargetData) +{ + int flags = 0x2; + TARGET *pTarget; + SVECTOR vec; + SVECTOR vecs[2]; + + if (pTargetData->field_18 == 0x40) + { + flags = 0x40; + } + + GM_SetTarget(&work->field_8A0_target, flags, PLAYER_SIDE, &pTargetData->field_8_size); + GM_SetCaptureTarget(&work->field_8A0_target, pTargetData->field_18, pTargetData->field_1C, &work->field_8F4, &work->field_8FC); + DG_PutVector(&pTargetData->field_0, &vec, 1); + GM_MoveTarget(&work->field_8A0_target, &vec); + + work->field_8E8_pTarget = NULL; + + if (flags == 2) + { + pTarget = GM_CaptureTarget(&work->field_8A0_target); + } + else + { + pTarget = GM_C4Target(&work->field_8A0_target); + } + + if (pTarget) + { + if (pTarget->class & 0x20) + { + pTarget->damaged &= ~flags; + pTarget->faint += pTargetData->field_1C; + return 0; + } + + vecs[0] = work->control.mov; + vecs[1] = pTarget->center; + + if (sna_line_check(vecs, work->control.map->hzd, HZD_CHK_ALL, SEGMENT_ATR) < 0) + { + work->field_8E8_pTarget = pTarget; + return 1; + } + + pTarget->damaged &= ~flags; + pTarget->faint += pTargetData->field_1C; + } + + return 0; +} + +int sna_act_helper2_helper5_8004FF88(SnaInitWork *work) +{ + void *pAnim; + + if (!(GM_GameStatus & (STATE_PADRELEASE | STATE_PADDEMO | STATE_DEMO)) && (GM_AlertMode != ALERT_ACTIVE)) + { + if (!GM_CheckPlayerStatusFlag(0x20001304) && + !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK9) && + (GM_SnakeCurrentHealth != 0) && + (GM_GameOverTimer == 0) && + (GM_StreamStatus() == -1) && + (work->body.action2 == 0)) + { + pAnim = work->field_9B8_fn_anim; + + if ((pAnim == sna_anim_idle_8005275C) || + (pAnim == sna_anim_crouch_800527DC) || + (pAnim == sna_anim_prone_idle_800528BC) || + (pAnim == sna_anim_wall_idle_and_c4_80052A5C) || + (pAnim == sna_anim_wall_crouch_80052CCC) || + (pAnim == sna_anim_duct_move_80054424)) + { + return 1; + } + } + } + + return 0; +} + +int sna_8005009C(SnaInitWork *work) +{ + GV_PAD *pPad = work->field_9B0_pad_ptr; + int tbl = work->field_920_tbl; + unsigned short press = pPad->press; + unsigned short input = press; + void *pAnimFn; + + if (!(tbl & 0x100)) + { + input = pPad->status; + } + + if (input & PAD_SQUARE) + { + work->field_910 = 0; + + if (tbl & 0x10) + { + sna_start_anim_8004E1F4(work, work->field_90C_pWeaponFn); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK3); + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + return 1; + } + + if (tbl & 8) + { + if (GM_CheckPlayerStatusFlag(PLAYER_MOVE)) + { + pAnimFn = sna_anim_rungun_80056C3C; + } + else + { + pAnimFn = sna_anim_shoot_weapon_80056B88; + } + + sna_start_anim_8004E1F4(work, pAnimFn); + GM_SetPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_ITEM_DISABLE); + } + else + { + if ((input & PAD_CROSS) && (work->field_9B0_pad_ptr->dir >= 0)) + { + pAnimFn = sna_anim_rungun_80056C3C; + } + else + { + pAnimFn = sna_anim_shoot_weapon_80056B88; + } + + sna_start_anim_8004E1F4(work, pAnimFn); + } + + GM_SetPlayerStatusFlag(PLAYER_ATTACK); + return 1; + } + + if ((press & PAD_CIRCLE) && ((work->actpack->attack->punch ^ 0x88) != 0)) + { + sna_start_anim_8004E1F4(work, sna_anim_punch_80058C10); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK3); + return 1; + } + + return 0; +} + +void sna_check_knock_800501F8(SnaInitWork *work, int time) +{ + void *func; + + if (time >= 8) + { + if (work->field_91C_weapon_idx != WP_C4 || GM_CheckPlayerStatusFlag(PLAYER_MOVE) != 0) + { + if ((work->field_9B0_pad_ptr->press & PAD_CIRCLE) != 0) + { + func = sna_knock_80054D68; + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK9); + work->field_9C0 = func; + work->field_9C4 = 0; + } + } + } +} + +void sna_8005027C(SnaInitWork *work, int time) +{ + SVECTOR *pVec; + + if (work->field_91C_weapon_idx != WP_C4) + { + return; + } + + if (!GM_CheckPlayerStatusFlag(PLAYER_MOVE)) + { + work->control.turn.vy = dword_800ABBD0; + } + + if ((time < 8) || !(work->field_9B0_pad_ptr->press & PAD_SQUARE) || (work->field_9C0 == sna_80057378)) + { + return; + } + + if ((*work->field_918_n_bullets != 0) && (bakudan_count_8009F42C < 16)) + { + pVec = &stru_8009EFD4[0]; + + if (work->field_A26_stance != SNA_STANCE_STANDING) + { + pVec = &stru_8009EFD4[1]; + } + + if (sna_8004F628(work, pVec, 300, HZD_CHK_D_SEGMENT | HZD_CHK_F_SEGMENT, SEGMENT_ATR | HZD_SEG_NO_BEHIND | HZD_SEG_NO_HARITSUKI, -1) > 0) + { + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK9); + work->field_9C0 = sna_80057378; + work->field_9C4 = 0; + return; + } + } + + GM_SeSet2(0, 63, SE_BUZZER); +} + +int sna_prone_check_standup_80050398(SnaInitWork *work) +{ + if ((work->field_9B0_pad_ptr->press & PAD_CROSS) != 0 && !sna_8004E808(work, 1, 0, -622, 1500)) + { + work->field_A28 = 450; + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_prone_standup_80053D74); + SetAction_8004E22C(work, work->actpack->trans->stand, 4); + work->control.turn.vy = work->control.rot.vy; + return 1; + } + + return 0; +} + +void sna_80050440(SnaInitWork *work) +{ + CONTROL *pCtrl; + unsigned short *pArr; + int inDuct; + int i; + + // TODO: There is probably a better way to match this... + SnaInitWork *work2; + int a; + + if ((GM_GameOverTimer == 0) && (GM_SnakeCurrentHealth != 0)) + { + pCtrl = &work->control; + pArr = pCtrl->evt.inside; + + inDuct = GM_CheckPlayerStatusFlag(PLAYER_INTRUDE); + GM_ClearPlayerStatusFlag(PLAYER_INTRUDE); + + for (i = pCtrl->evt.n_inside; i > 0; pArr++, i--) + { + if (*pArr == 0x73D2) + { + GM_SetPlayerStatusFlag(PLAYER_INTRUDE); + break; + } + } + + if (!inDuct) + { + if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + return; + } + + sna_8004EC8C(work); + // TODO: There is probably a better way to match this... + work2 = work; + a = 4; + dword_800ABA1C = 0x100; + } + else + { + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + return; + } + + sub_8004ED08(work); + // TODO: There is probably a better way to match this... + work2 = work; + a = 4; + dword_800ABA1C = 0; + } + + sna_set_flags1_8004E2F4(work2, a); + + work->control.turn.vy = work->control.rot.vy; + } +} + +void sna_80050568(SnaInitWork *work) +{ + SVECTOR diff; + DG_OBJS *objs; + short dir; + + sna_80050440(work); + + if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + return; + } + + objs = work->body.objs; + work->control.turn.vz = 0; + work->control.turn.vx = 0; + work->control.step.vz = 0; + work->control.step.vx = 0; + + DG_InvisibleObjs(objs); + + sna_start_anim_8004E1F4(work, sna_anim_duct_move_80054424); + work->field_A54.prone_bool_thing = 0; + sna_set_invuln_8004F2A0(work, 24); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK1); + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_DOWNED | PLAYER_DAMAGED); + sna_clear_flags1_8004E308(work, (SNA_FLAG1_UNK2 | SNA_FLAG1_UNK3 | SNA_FLAG1_UNK5)); + SetAction_8004E22C(work, work->actpack->still->crouch, 0); + + objs = work->body.objs; + diff.vx = objs->objs[6].world.t[0] - objs->objs[0].world.t[0]; + + objs = work->body.objs; + diff.vz = objs->objs[6].world.t[2] - objs->objs[0].world.t[2]; + + dir = GV_VecDir2(&diff); + work->control.turn.vy = dir; + work->control.rot.vy = dir; + work->control.skip_flag &= ~CTRL_BOTH_CHECK; +} + +void snake_vr_sna_init_80053198(SnaInitWork *work, int time); + +void sna_knockdown_getup_80050668(SnaInitWork *work) +{ + SVECTOR vec; + int var_a2; + int var_a3; + int dir; + DG_OBJS *pObjs; + + var_a2 = 500; + + if (work->field_A54.knockdown_thing == 1) + { + var_a2 = 2; + var_a3 = -630; + } + else + { + var_a3 = -18; + } + + if (!sna_8004E808(work, 1, var_a2, var_a3, 1500)) + { + sna_set_invuln_8004F2A0(work, 0); + sna_start_anim_8004E1F4(work, &sna_anim_knockdown_getup_80054A10); + } + else + { + work->field_A54.knockdown_thing = 0; + + sna_set_invuln_8004F2A0(work, 24); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK1); + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_DOWNED | PLAYER_DAMAGED); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK5 | SNA_FLAG1_UNK3 | SNA_FLAG1_UNK2); + + DG_VisibleObjs(work->body.objs); + + if (GetAction(work) == 0x45) + { + SetAction_8004E22C(work, work->actpack->still->crouch, 4); + + // TODO: Duplication is probably from an inline + pObjs = work->body.objs; + vec.vx = pObjs->objs[6].world.t[0] - pObjs->objs[0].world.t[0]; + + pObjs = work->body.objs; + vec.vz = pObjs->objs[6].world.t[2] - pObjs->objs[0].world.t[2]; + + dir = GV_VecDir2(&vec); + work->control.turn.vy = dir; + work->control.rot.vy = dir; + + sna_start_anim_8004E1F4(work, &sna_anim_prone_idle_800528BC); + } + else + { + GM_SetPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_DOWNED); + sna_set_invuln_8004F2A0(work, 0); + sna_set_flags1_8004E2F4(work, 4); + sna_start_anim_8004E1F4(work, &snake_vr_sna_init_80053198); + } + } +} + +int sub_800507D8(SnaInitWork *work) +{ + if (dword_800ABBC4 == 0 && work->field_A38_local_data == 0) + { + work->field_A28 = 450; + GM_ClearPlayerStatusFlag(PLAYER_CHECK_WALL | PLAYER_CAUTION | PLAYER_MOVE); + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK9); + work->field_9C0 = NULL; + sna_8004E260(work, 0, 4, 0); + GM_ExitBehindCamera(); + return 1; + } + + return 0; +} + +void CheckMessage0_80050878(SnaInitWork *work) +{ + int ret; + int length; + GV_MSG *pEntry; + GV_MSG *pMsg; + unsigned short id; + + if (GM_CheckPlayerStatusFlag(PLAYER_TIMERBOMB_THROWN) && + (GM_SnakeCurrentHealth != 0) && + (GM_GameOverTimer == 0) && + !GM_CheckPlayerStatusFlag(PLAYER_ACT_ONLY)) + { + sna_start_anim_8004E1F4(work, &sna_act_helper3_helper_80056650); + sna_set_invuln_8004F2A0(work, 0); + sna_act_unk2_80051170(work->field_89C_pTarget); + GM_ClearPlayerStatusFlag(PLAYER_TIMERBOMB_THROWN); + GM_SetPlayerStatusFlag(PLAYER_ACT_ONLY); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK28); + } + + if (sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK20)) + { + return; + } + + ret = GV_ReceiveMessage(work->control.name, &pMsg); + + if (ret == 0) + { + return; + } + + length = ret; + + pEntry = &work->field_7A4_msgs[work->field_7A0_msg_count]; + pMsg = (pMsg + length) - 1; + + for (; length > 0; work->field_7A0_msg_count++, pMsg--, pEntry++, length--) + { + if (work->field_7A0_msg_count == 8) + { + break; + } + + id = pMsg->message[0]; + + if ((id == HASH_MOTION) || (id == HASH_RUN_MOVE) || (id == HASH_STANCE) || (id == HASH_GO_MOTION)) + { + if (!GM_CheckPlayerStatusFlag(PLAYER_ACT_ONLY)) + { + GM_SetPlayerStatusFlag(PLAYER_ACT_ONLY); + } + } + + *pEntry = *pMsg; + } +} + +static inline void sna_act_unk_helper_80050A64(SnaInitWork *work, GV_MSG *pMsg, int var_s4) +{ + int flags; + int len; + UnkSnakeStruct2 *pStr; + + if (sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK28)) + { + return; + } + + flags = 0; + len = pMsg->message_len; + pStr = &work->field_9E4; + + pMsg->message_len = 0; + pStr->field_9E4 = pMsg->message[1]; + pStr->field_9E6 = -1; + pStr->field_9E8 = -1; + pStr->field_9EA = -1; + pStr->field_9EC_flags3 = 0; + + if (len > 2) + { + pStr->field_9E6 = pMsg->message[2]; + } + + if (len > 3) + { + pStr->field_9E8 = pMsg->message[3]; + } + + if (len > 4) + { + pStr->field_9EA = pMsg->message[4]; + } + + if (len > 5) + { + flags = pStr->field_9EC_flags3 = pMsg->message[5]; + } + + if (sna_act_unk_helper4_8004FA3C() && (var_s4 == 0)) + { + GM_ClearPlayerStatusFlag(PLAYER_ACT_ONLY); + pMsg->message_len = 0; + printf("force act cancel %d\n", pMsg->message[1]); + } + else + { + pStr->field_9F2 = 0; + + if (var_s4 != 0) + { + pStr->field_9F2 = 1; + } + + if (!(flags & 0x80)) + { + if (!work->field_A5A) + { + return; + } + + GM_ConfigMotionControl(&work->body, &work->m_ctrl, work->field_A5A, + work->m_segs1, work->m_segs2, &work->control, + work->rots); + } + + if (flags & 4) + { + if (len <= 6) + { + return; + } + + pStr->field_9EE = work->control.mov.vy - pMsg->message[6]; + } + else + { + pStr->field_9EE = work->control.levels[0]; + } + + if (flags & 1) + { + GM_ClearPlayerStatusFlag(PLAYER_INTRUDE); + sub_8004ED08(work); + } + + if (flags & 0x10) + { + work->control.skip_flag |= CTRL_SKIP_TRAP; + } + + pStr->field_9F0 = 1; + + sna_start_anim_8004E1F4(work, &sna_anim_dying_80055524); + GM_SetPlayerStatusFlag(4); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK28); + } +} + +static inline void sna_act_unk_helper2_80050A64(SnaInitWork *work, GV_MSG *pMsg) +{ + UnkSnakeStruct2 *pStr; + + if (sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK28)) + { + return; + } + + pStr = &work->field_9E4; + + pStr->field_9E8 = -1; + pStr->field_9F4.vx = pMsg->message[1]; + pStr->field_9F4.vy = pMsg->message[2]; + pStr->field_9F4.vz = pMsg->message[3]; + pStr->field_9F4.pad = pMsg->message[4]; + pStr->field_9EC_flags3 = 0; + + if (pMsg->message_len > 5) + { + pStr->field_9EC_flags3 = pMsg->message[5]; + } + + if (sna_act_unk_helper4_8004FA3C() && !(pStr->field_9EC_flags3 & 0x400)) + { + GM_ClearPlayerStatusFlag(PLAYER_ACT_ONLY); + pMsg->message_len = 0; + printf("run move cancel\n"); + } + else + { + if (pMsg->message_len > 6) + { + pStr->field_9E8 = pMsg->message[6]; + } + + if (pStr->field_9EC_flags3 & 4) + { + work->control.skip_flag |= CTRL_SKIP_TRAP; + } + + pStr->field_9F0 = 0; + sna_start_anim_8004E1F4(work, &sna_anim_mini_cutscene_800559D8); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK28); + pMsg->message_len = 0; + } +} + +static inline void sna_act_unk_helper3_80050A64(SnaInitWork *work, GV_MSG *pMsg) +{ + UnkSnakeStruct2 *pStr; + + if (sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK28)) + { + return; + } + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + GM_ClearPlayerStatusFlag(PLAYER_ACT_ONLY); + pMsg->message_len = 0; + } + else + { + pStr = &work->field_9E4; + + work->field_9FC = work->field_9B8_fn_anim; + pStr->field_9EE = 0; + pStr->field_9E8 = -1; + pStr->field_9EA = pMsg->message[1]; + pStr->field_9EC_flags3 = 0; + + if (pMsg->message_len > 2) + { + pStr->field_9E8 = pMsg->message[2]; + } + + if (pMsg->message_len > 3) + { + pStr->field_9EC_flags3 = pMsg->message[3]; + } + + if (sna_act_unk_helper4_8004FA3C() && !(pStr->field_9EC_flags3 & 0x400)) + { + GM_ClearPlayerStatusFlag(PLAYER_ACT_ONLY); + pMsg->message_len = 0; + printf("force stance cancel\n"); + } + else + { + if (pStr->field_9EC_flags3 & 4) + { + work->control.skip_flag |= CTRL_SKIP_TRAP; + } + + sna_start_anim_8004E1F4(work, &sna_act_unk_helper3_80055DD8); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK28); + pMsg->message_len = 0; + pStr->field_9F0 = 0; + } + } +} + +void sna_act_unk_80050A64(SnaInitWork *work) +{ + int levels[2]; + GV_MSG *pMsg; + GV_MSG *pMsgIter; + int msgCount; + int count; + int state; + + if ((work->field_7A0_msg_count == 0) || sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK18)) + { + return; + } + + msgCount = 0; + pMsgIter = work->field_7A4_msgs; + pMsg = work->field_7A4_msgs; + + for (count = work->field_7A0_msg_count; count > 0; count--, pMsg++) + { + switch (pMsg->message[0]) + { + case 0xc000: + pMsg->message_len = 0; + break; + + case HASH_MOTION: + state = 0; + goto helper3; + + case HASH_GO_MOTION: + state = 1; +helper3: + sna_act_unk_helper_80050A64(work, pMsg, state); + break; + + case HASH_RUN_MOVE: + sna_act_unk_helper2_80050A64(work, pMsg); + break; + + case HASH_STANCE: + sna_act_unk_helper3_80050A64(work, pMsg); + break; + + case HASH_STOP: + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK19); + pMsg->message_len = 0; + break; + + case HASH_START: + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK19); + pMsg->message_len = 0; + break; + + case HASH_KILL: + GV_DestroyActor(&work->actor); + pMsg->message_len = 0; + break; + + case HASH_PADON: + GM_ClearPlayerStatusFlag(PLAYER_PAD_OFF); + pMsg->message_len = 0; + break; + + case HASH_PADOFF: + GM_SetPlayerStatusFlag(PLAYER_PAD_OFF); + pMsg->message_len = 0; + break; + + case HASH_TABAKO: + tabako_dword_8009F2C0 = pMsg->message[1]; + pMsg->message_len = 0; + break; + + case HASH_POSITION: + work->control.mov.vx = pMsg->message[1]; + work->control.mov.vy = pMsg->message[2]; + work->control.mov.vz = pMsg->message[3]; + work->control.step = DG_ZeroVector; + sub_8004E588(work->control.map->hzd, &work->control.mov, levels); + work->control.levels[0] = levels[0]; + work->control.levels[1] = levels[1]; + pMsg->message_len = 0; + break; + + case HASH_SLOW: + if (pMsg->message[1] == HASH_ON) + { + sna_set_flags1_8004E2F4(work, SNA_FLAG2_UNK27); + pMsg->message_len = 0; + } + else + { + sna_clear_flags1_8004E308(work, SNA_FLAG2_UNK27); + pMsg->message_len = 0; + } + + break; + + default: + pMsg->message_len = 0; + break; + } + + if (pMsg->message_len != 0) + { + *pMsgIter = *pMsg; + pMsgIter++; + msgCount++; + } + } + + work->field_7A0_msg_count = msgCount; +} + +void sna_act_unk2_80051170(TARGET *param_1) +{ + param_1->damage = 0; + param_1->faint = 0; + param_1->damaged &= ~(0x80 | 0x04); + param_1->force = DG_ZeroVector; + param_1->weapon = WP_None; + param_1->a_mode = 0; +} + +static inline int sna_weapon_switching_helper_800511BC(SnaInitWork *work) +{ + if (sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK5)) + { + return 0; + } + + if (GM_CheckPlayerStatusFlag(PLAYER_CB_BOX | PLAYER_DOWNED | PLAYER_DAMAGED | PLAYER_ACT_ONLY)) + { + return 0; + } + + if (!(work->field_920_tbl & 0x200) && GM_CheckPlayerStatusFlag(PLAYER_WATCH)) + { + return 0; + } + + if (GM_CheckPlayerStatusFlag(PLAYER_PAD_OFF) || (GM_GameStatus & STATE_PADRELEASE)) + { + if (GM_WeaponTypes[GM_CurrentWeaponId + 1] & 0x200) + { + return 0; + } + } + + return 1; +} + +static inline int sna_weapon_switching_helper2_800511BC(SnaInitWork *work, int callback) +{ + WeaponCreateEntry *pWeaponEntry; + void *(*pWeaponCreateFn)(void *, void *, int, void *, int); + GV_ACT *pWeaponActor; + + pWeaponActor = work->field_908_weapon_actor; + + if (pWeaponActor) + { + GV_DestroyActorQuick(pWeaponActor); + } + + pWeaponEntry = &gSnakeWeapons_8009EF3C[GM_CurrentWeaponId + 1]; + pWeaponCreateFn = pWeaponEntry->mCreateActorFn; + pWeaponActor = NULL; + + if (pWeaponCreateFn) + { + pWeaponActor = pWeaponCreateFn(&work->control, &work->body, 4, &work->field_914_trigger, 1); + + if (!pWeaponActor) + { + work->field_908_weapon_actor = 0; + return -1; + } + } + + if (work->field_91C_weapon_idx == WP_Nikita) + { + GM_ClearPlayerStatusFlag(PLAYER_ITEM_DISABLE); + } + + if (work->field_90C_pWeaponFn == &sna_anim_grenade_80058470) + { + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_ITEM_DISABLE); + } + + work->field_91C_weapon_idx = GM_CurrentWeaponId; + + if (callback) + { + GM_CallSystemCallbackProc(3, GM_CurrentWeaponId); + } + + work->field_920_tbl = GM_WeaponTypes[GM_CurrentWeaponId + 1]; + + if (GM_CurrentWeaponId >= 0) + { + work->field_918_n_bullets = &GM_Weapons[GM_CurrentWeaponId]; + } + + work->field_90C_pWeaponFn = pWeaponEntry->mStateFn; + + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + work->field_908_weapon_actor = pWeaponActor; + work->actpack = &weapon_actions_8009ED8C[GM_CurrentWeaponId]; + + return 0; +} + +void sna_weapon_switching_800511BC(SnaInitWork *work, int callback) +{ + int temp_s1_2; + + if (GM_CurrentWeaponId != work->field_91C_weapon_idx) + { + GM_WeaponChanged = 1; + } + + if (!GM_WeaponChanged) + { + return; + } + + if (!sna_weapon_switching_helper_800511BC(work)) + { + return; + } + + if (!(work->field_920_tbl & 0x1000) && (GM_CurrentWeaponId == work->field_91C_weapon_idx)) + { + return; + } + + if (sna_weapon_switching_helper2_800511BC(work, callback) < 0) + { + return; + } + + temp_s1_2 = work->field_920_tbl; + GM_WeaponChanged = 0; + + if (temp_s1_2 & 0x200) + { + sub_8004EB74(work); + } + else if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + sna_8004EC00(work); + } + + if (temp_s1_2 & 0x20) + { + sna_start_anim_8004E1F4(work, work->field_90C_pWeaponFn); + GM_ClearPlayerStatusFlag(PLAYER_CAUTION); + } + else if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + sna_start_anim_8004E1F4(work, &sna_anim_duct_move_80054424); + } + else if (GM_CheckPlayerStatusFlag(PLAYER_CAUTION)) + { + if (GM_CheckPlayerStatusFlag(PLAYER_MOVE)) + { + sna_start_anim_8004E1F4(work, &sna_anim_wall_move_80052BA8); + } + else + { + sna_start_anim_8004E1F4(work, dword_8009EEB0[work->field_A26_stance]); + } + } + else if (GM_CheckPlayerStatusFlag(PLAYER_MOVE)) + { + sna_start_anim_8004E1F4(work, dword_8009EEB8[work->field_A26_stance]); + } + else + { + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + } + + sub_8004F454(work); +} + +static inline int sna_helper_800515BC(SnaInitWork *work) +{ + if ( GM_CheckPlayerStatusFlag(PLAYER_ACT_ONLY) ) + { + return 0; + } + + if ( sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK6) ) + { + return 0; + } + + if ( (GM_CurrentItemId == IT_Bandana) && (work->field_90C_pWeaponFn == &sna_80057A90) ) + { + return 0; + } + + if ( (GM_CheckPlayerStatusFlag(PLAYER_PAD_OFF) || + ((GM_GameStatus & (STATE_PADRELEASE | STATE_PADDEMO)) == STATE_PADRELEASE)) && + ((GM_ItemTypes[GM_CurrentItemId + 1] & 2) != 0) ) + { + return 0; + } + + return 1; +} + +void sna_800515BC(SnaInitWork *work, int a2) +{ + int itemType; + int temp_s4; + GV_ACT *pItemActor; + TSnakeEquipFuncion *ppfnEquip; + TSnakeEquipFuncion pfnEquip; + int temp_s0_2; + int var_s2; + + if ( !sna_helper_800515BC(work) ) + { + return; + } + + if ( GM_CurrentItemId == work->field_9A8_current_item ) + { + return; + } + + itemType = GM_ItemTypes[GM_CurrentItemId + 1]; + + if ( GM_CheckPlayerStatusFlag(0x300) && ((itemType & 0x8000) != 0) ) + { + return; + } + + temp_s4 = work->field_9AC; + pItemActor = work->field_9A4_item_actor; + + if ( pItemActor ) + { + GV_DestroyActorQuick(pItemActor); + } + + ppfnEquip = &gSnakeEquips_8009EF8C[GM_CurrentItemId]; + pItemActor = NULL; + + if ( ((itemType & 0x8000) != 0) && (*ppfnEquip != 0) ) + { + pfnEquip = *ppfnEquip; + + if ( (GM_CurrentItemId == 2) || (GM_CurrentItemId == 3) || (GM_CurrentItemId == 4) ) + { + pItemActor = pfnEquip(&work->control, &work->body, 0); + } + else + { + pItemActor = pfnEquip(&work->control, &work->body, 6); + } + + if ( !pItemActor ) + { + work->field_9A4_item_actor = NULL; + return; + } + } + + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_CB_BOX); + + work->field_9A8_current_item = GM_CurrentItemId; + + if ( a2 != 0 ) + { + GM_CallSystemCallbackProc(4, GM_CurrentItemId); + } + + work->field_9AC = itemType; + work->field_9A4_item_actor = pItemActor; + + if ( (itemType & 1) != 0 ) + { + if ( GM_CurrentItemId == IT_Scope ) + { + sna_start_anim_8004E1F4(work, &sna_anim_scope_80055334); + } + else if ( GM_CurrentItemId == IT_Camera ) + { + sna_start_anim_8004E1F4(work, &sna_anim_jpegcam_800553CC); + } + else + { + sna_start_anim_8004E1F4(work, &sna_anim_box_idle_800553EC); + GM_SetPlayerStatusFlag(0x401000); + } + + GM_ClearPlayerStatusFlag(PLAYER_CAUTION | PLAYER_MOVE); + } + else + { + if ( ((work->field_920_tbl & 0x200) == 0) && ((temp_s4 & 1) != 0) ) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + + if ( GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) ) + { + sna_start_anim_8004E1F4(work, &sna_anim_duct_move_80054424); + } + else if ( GM_CheckPlayerStatusFlag(PLAYER_CAUTION) ) + { + sna_start_anim_8004E1F4(work, dword_8009EEB0[work->field_A26_stance]); + } + else + { + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + } + } + + if ( work->field_8E8_pTarget && (work->field_9B8_fn_anim != &sna_anim_throw_800589C8) ) + { + sna_start_anim_8004E1F4(work, &sna_anim_choke_hold_80059154); + } + } + + if ((itemType & 2) != 0) + { + sub_8004EB74(work); + GM_SetPlayerStatusFlag(PLAYER_WEAPON_DISABLE); + } else if ( !(work->field_920_tbl & 0x200) && + !GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + sna_8004EC00(work); + } + + temp_s0_2 = GM_CheckPlayerStatusFlag(PLAYER_MOVE); + + var_s2 = 0; + if ( (work->control.skip_flag & CTRL_BOTH_CHECK) && ((itemType & 1) == 0) ) + { + var_s2 = 1; + } + + sub_8004F454(work); + + if ( temp_s0_2 != 0 ) + { + GM_SetPlayerStatusFlag(PLAYER_MOVE); + } + + if ( var_s2 != 0 ) + { + work->control.skip_flag |= CTRL_BOTH_CHECK; + } + + if ( GM_CheckPlayerStatusFlag(0x300) ) + { + GM_SetPlayerStatusFlag(PLAYER_WEAPON_DISABLE); + + if ( (work->field_9B8_fn_anim == &sna_anim_knockdown_80054710) || GM_CheckPlayerStatusFlag(PLAYER_DOWNED) ) + { + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK3); + } + } +} + +void sna_anim_idle_8005275C(SnaInitWork *work, int time) +{ + int action_flag; // $a1 + + if (time == 0) + { + work->field_A26_stance = SNA_STANCE_STANDING; + + work->field_9C8_anim_update_fn_3p = sna_fn_80052E58; + work->field_9CC_anim_update_fn_1p = sna_fn_80052120; + + if (GM_AlertMode >= ALERT_ACTIVE) + { + action_flag = work->actpack->still->setup; + } + else + { + action_flag = work->actpack->still->stand; + } + + SetAction_8004E22C(work, action_flag, 4); + } +} + +void sna_anim_crouch_800527DC(SnaInitWork *work, int time) +{ + int sound; + + if (time == 0) + { + if (work->field_A26_stance != SNA_STANCE_SQUAT) + { + sound = SE_CHANGE_STANCE; + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) != 0) + { + sound = 183; + } + GM_SeSet(&work->control.mov, sound); + } + + work->field_9C8_anim_update_fn_3p = sna_anim_crouch_helper_80053014; + work->field_9CC_anim_update_fn_1p = sna_fn_80052120; + SetAction_8004E22C(work, work->actpack->still->squat, 4); + work->field_A2C.vy = -128; + } + + if ((time == 0 && work->field_A26_stance != SNA_STANCE_STANDING) || + (time == 4 && work->field_A26_stance == SNA_STANCE_STANDING)) + { + GM_SetPlayerStatusFlag(PLAYER_SQUAT); + work->field_A26_stance = SNA_STANCE_SQUAT; + } +} + +void sna_anim_prone_idle_800528BC(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_A26_stance = SNA_STANCE_CROUCH; + work->field_A28 = 450; + work->field_9C8_anim_update_fn_3p = sna_fn_800531F4; + work->field_9CC_anim_update_fn_1p = sna_fn_80052120; + GM_SetPlayerStatusFlag(PLAYER_GROUND); + SetAction_8004E22C(work, work->actpack->still->crouch, 4); + } +} + +void sna_anim_run_8005292C(SnaInitWork *work, int time) +{ + int action_flag; // $a1 + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_80053360; + work->field_9CC_anim_update_fn_1p = sna_fn_80052120; + GM_SetPlayerStatusFlag(PLAYER_MOVE); + if (GM_AlertMode >= ALERT_ACTIVE) + { + action_flag = work->actpack->move->aim; + } + else + { + action_flag = work->actpack->move->stand; + } + SetAction_8004E22C(work, action_flag, 4); + } +} + +void sna_anim_prone_move_800529C0(SnaInitWork *work, int time) +{ + int action_flag; // $a1 + + if (time == 0) + { + work->field_A28 = 450; + GM_SetPlayerStatusFlag(PLAYER_MOVE); + work->field_9C8_anim_update_fn_3p = sna_fn_800535B8; + work->field_9CC_anim_update_fn_1p = sna_fn_80052120; + if (work->field_A54.prone_bool_thing) + { + action_flag = work->actpack->move->crouch_backwards; + } + else + { + action_flag = work->actpack->move->crouch; + } + SetAction_8004E22C(work, action_flag, 4); + work->field_A54.prone_bool_thing = 0; + } + sna_80050440(work); +} + +void sna_anim_wall_idle_and_c4_80052A5C(SnaInitWork *work, int time) +{ + int sVar3; + int sound; + + if (time == 0) + { + if (GM_CheckPlayerStatusFlag(PLAYER_CAUTION) == 0) + { + sound = SE_WALLPRESS; + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) != 0) + { + sound = 183; + } + GM_SeSet(&work->control.mov, sound); + work->control.turn.vy = dword_800ABBD0 - 2048; + GM_SetPlayerStatusFlag(PLAYER_CAUTION); + } + + sVar3 = work->field_91C_weapon_idx != WP_C4 ? 300 : 472; + + work->field_9C8_anim_update_fn_3p = sna_anim_wall_idle_and_c4_helper_800537D4; + work->field_A28 = sVar3; + work->field_A26_stance = 0; + work->field_9CC_anim_update_fn_1p = sub_8005230C; + SetAction_8004E22C(work, work->actpack->still->against_wall, 4); + } + + GM_CheckBehindCamera(work->control.map->hzd, &work->control); + + if (work->field_A3A < 8 && pVec_800ABBCC != NULL) + { + (work->control).step.vx += pVec_800ABBCC->vx / 8; + (work->control).step.vz += pVec_800ABBCC->vz / 8; + work->field_A3A++; + } +} + +void sna_anim_wall_move_80052BA8(SnaInitWork *work, int time) +{ + int action; + + if (time == 0) + { + GM_SetPlayerStatusFlag(PLAYER_MOVE | PLAYER_CAUTION); + work->field_9C8_anim_update_fn_3p = sna_anim_wall_move_helper_800538CC; + work->field_9CC_anim_update_fn_1p = sub_8005230C; + + if (work->field_A54.wall_thing == 2) + { + action = work->actpack->move->against_wall_r; + } + else + { + action = work->actpack->move->against_wall_l; + } + + work->field_A28 = 300; + work->field_A54.wall_thing = 0; + SetAction_8004E22C(work, action, 4); + work->control.turn.vy = dword_800ABBD0 - 2048; + } + + if (work->field_A3A < 8 && pVec_800ABBCC != NULL) + { + work->control.step.vx += pVec_800ABBCC->vx / 8; + work->control.step.vz += pVec_800ABBCC->vz / 8; + work->field_A3A++; + } +} + +void sna_anim_wall_crouch_80052CCC(SnaInitWork *work, int time) +{ + int sound; + int sound2; + + if (time == 0) + { + if (work->field_A26_stance != SNA_STANCE_SQUAT) + { + sound = SE_CHANGE_STANCE; + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) != 0) + { + sound = 183; + } + GM_SeSet(&work->control.mov, sound); + } + + work->field_A26_stance = SNA_STANCE_SQUAT; + work->field_9C8_anim_update_fn_3p = sna_anim_wall_crouch_helper_80053A54; + work->field_9CC_anim_update_fn_1p = sub_8005230C; + + if (GM_CheckPlayerStatusFlag(PLAYER_CAUTION) == 0) + { + sound2 = SE_WALLPRESS; + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) != 0) + { + sound2 = 183; + } + GM_SeSet(&work->control.mov, sound2); + work->control.turn.vy = dword_800ABBD0 - 2048; + } + + GM_SetPlayerStatusFlag(PLAYER_SQUAT | PLAYER_CAUTION); + SetAction_8004E22C(work, work->actpack->still->squat_against_wall, 4); + + work->field_A28 = work->field_91C_weapon_idx != WP_C4 ? 300 : 472; + } + + GM_CheckBehindCamera(((work->control).map)->hzd, &work->control); + + if (work->field_A3A < 8 && pVec_800ABBCC != NULL) + { + work->control.step.vx += pVec_800ABBCC->vx / 8; + work->control.step.vz += pVec_800ABBCC->vz / 8; + work->field_A3A++; + } +} + +void sna_fn_80052E58(SnaInitWork *work, int time) +{ + int action; + int diff; + + if (sna_8005009C(work)) + { + return; + } + + if ((work->field_9B0_pad_ptr->press & PAD_CROSS) != 0) + { + sna_start_anim_8004E1F4(work, sna_anim_crouch_800527DC); + return; + } + + if (GM_AlertMode >= ALERT_ACTIVE) + { + action = work->actpack->still->setup; + } + else + { + action = work->actpack->still->stand; + } + + SetAction_8004E22C(work, action, 4); + + if (work->field_A38_local_data > 0) + { + work->field_A38_local_data--; + } + + if (work->field_A38_local_data == 0 && gSnaMoveDir_800ABBA4 >= 0) + { + if (dword_800ABBC4 == 1 || (dword_800ABBC4 != 0 && work->field_79C == 2)) + { + sna_start_anim_8004E1F4(work, sna_anim_wall_idle_and_c4_80052A5C); + } + else + { + diff = GV_DiffDirAbs(work->control.turn.vy, gSnaMoveDir_800ABBA4); + if (diff > 256) + { + work->field_A38_local_data = diff / 1024 + 1; + } + else + { + sna_start_anim_8004E1F4(work, sna_anim_run_begin_80053B88); + } + + work->control.turn.vy = gSnaMoveDir_800ABBA4; + } + } +} + +void sna_anim_crouch_helper_80053014(SnaInitWork *work, int time) +{ + int diff; + int sound; + + if ((work->field_9B0_pad_ptr->press & PAD_CROSS) != 0) + { + if (sna_8004E808(work, 0, 0, 0, 1100) == 0) + { + work->control.turn.vy = work->control.rot.vy; + + sound = SE_CHANGE_STANCE; + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) != 0) + { + sound = 183; + } + + GM_SeSet(&work->control.mov, sound); + GM_ClearPlayerStatusFlag(PLAYER_SQUAT); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } + } + else + { + if (work->field_A38_local_data > 0) + { + work->field_A38_local_data--; + } + + if (work->field_A38_local_data == 0 && gSnaMoveDir_800ABBA4 >= 0) + { + if (dword_800ABBC4 == 1 || (dword_800ABBC4 != 0 && work->field_79C == 2)) + { + sna_start_anim_8004E1F4(work, sna_anim_wall_crouch_80052CCC); + } + else + { + diff = GV_DiffDirAbs(work->control.turn.vy, gSnaMoveDir_800ABBA4); + if (diff > 256) + { + work->field_A38_local_data = diff / 1024 + 1; + } + else + { + sna_start_anim_8004E1F4(work, sna_anim_prone_begin_80053BE8); + } + + work->control.turn.vy = gSnaMoveDir_800ABBA4; + } + } + } +} + +void sna_fn_800531F4(SnaInitWork *work, int time) +{ + if (work->field_A38_local_data > 0) + { + work->field_A38_local_data -= 1; + } + + if (!sna_prone_check_standup_80050398(work) && work->field_A38_local_data == 0 && gSnaMoveDir_800ABBA4 >= 0) + { + if (dword_800ABBC4 == 1 || (dword_800ABBC4 != 0 && work->field_79C == 2)) + { + if (sna_8004E808(work, 0, 0, 0, 1500) == 0) + { + work->control.turn.vy = work->control.rot.vy; + GM_ClearPlayerStatusFlag(PLAYER_GROUND); + GM_SetPlayerStatusFlag(PLAYER_SQUAT | PLAYER_CAUTION); + sna_start_anim_8004E1F4(work, sna_anim_wall_crouch_80052CCC); + } + } + else + { + if (sub_8004E458(work->control.turn.vy, gSnaMoveDir_800ABBA4) == 3) + { + work->field_A54.prone_bool_thing = 1; + } + else + { + work->field_A54.prone_bool_thing = 0; + } + + sna_start_anim_8004E1F4(work, sna_anim_prone_move_800529C0); + } + } +} + +void sna_80053360(SnaInitWork *work, int time) +{ + int angle; + + if (sna_8005009C(work)) + { + return; + } + + if (work->field_9B0_pad_ptr->press & PAD_CROSS) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, &sna_anim_crouch_800527DC); + return; + } + + if (gSnaMoveDir_800ABBA4 < 0) + { + if (++work->field_A3A >= 5) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, &sna_anim_idle_8005275C); + return; + } + } + else + { + work->field_A3A = 0; + } + + if ((dword_800ABBC4 == 1) || ((dword_800ABBC4 != 0) && (work->field_79C == 2))) + { + if (++work->field_A38_local_data >= 3) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, &sna_anim_wall_idle_and_c4_80052A5C); + return; + } + } + else + { + work->field_A38_local_data = 0; + } + + if (work->field_A38_local_data == 0) + { + if (gSnaMoveDir_800ABBA4 >= 0) + { + angle = sub_8004E4C0(work, gSnaMoveDir_800ABBA4); + } + else + { + angle = work->control.turn.vy; + } + } + else + { + angle = gSnaMoveDir_800ABBA4; + + if (angle < 0) + { + angle = work->control.turn.vy; + } + } + + if (GV_DiffDirAbs(work->control.turn.vy, angle) > 1048) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, &sna_anim_idle_8005275C); + work->control.turn.vy = angle; + return; + } + + work->control.turn.vy = angle; + sub_8004EA50(work, angle); + + if (GM_AlertMode >= ALERT_ACTIVE) + { + SetAction_8004E22C(work, work->actpack->move->aim, 4); + } + else + { + SetAction_8004E22C(work, work->actpack->move->stand, 4); + } +} + +void sna_fn_800535B8(SnaInitWork *work, int time) +{ + int var_s0; + int temp_s2; + int temp_s3; + unsigned char var_a1; + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + sna_start_anim_8004E1F4(work, &sna_anim_enter_duct_80053E9C); + sna_set_invuln_8004F2A0(work, 0); + return; + } + + if (!sna_prone_check_standup_80050398(work)) + { + if (gSnaMoveDir_800ABBA4 < 0) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(work, work->actpack->still->crouch, 4); + sna_start_anim_8004E1F4(work, &sna_anim_prone_idle_800528BC); + return; + } + + if ((dword_800ABBC4 == 1) || ((dword_800ABBC4 != 0) && (work->field_79C == 2))) + { + if (!sna_8004E808(work, 0, 0, 0, 1500)) + { + work->control.turn.vy = work->control.rot.vy; + GM_ClearPlayerStatusFlag(PLAYER_GROUND | PLAYER_MOVE); + GM_SetPlayerStatusFlag(PLAYER_CAUTION | PLAYER_SQUAT); + sna_start_anim_8004E1F4(work, &sna_anim_wall_crouch_80052CCC); + } + } + else + { + temp_s3 = work->actpack->move->crouch; + temp_s2 = work->actpack->move->crouch_backwards; + + if (GetAction(work) == temp_s2) + { + var_s0 = gSnaMoveDir_800ABBA4 + 2048; + } + else + { + var_s0 = gSnaMoveDir_800ABBA4; + } + + if (sub_8004E458(work->control.turn.vy, var_s0) == 3) + { + var_s0 += 2048; + var_a1 = temp_s3; + + if (GetAction(work) == var_a1) + { + var_a1 = temp_s2; + } + + SetAction_8004E22C(work, (var_a1), 4); + } + + work->control.turn.vy = sub_8004E4C0(work, var_s0); + } + } +} + +void sna_anim_wall_idle_and_c4_helper_800537D4(SnaInitWork *work, int time) +{ + if (work->field_A38_local_data != 0) + { + work->field_A38_local_data--; + } + + if (sub_800507D8(work)) + { + return; + } + + if ((work->field_9B0_pad_ptr->press & PAD_CROSS) && !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK9)) + { + sna_start_anim_8004E1F4(work, &sna_anim_wall_crouch_80052CCC); + return; + } + + if ((dword_800ABBC4 == 2) || (dword_800ABBC4 == 4)) + { + work->field_A54.wall_thing = dword_800ABBC4; + sna_start_anim_8004E1F4(work, &sna_anim_wall_move_80052BA8); + } + + if ((dword_800ABBD0 - 2048) != work->control.rot.vy) + { + work->field_A3A = 0; + work->control.turn.vy = dword_800ABBD0 - 2048; + } + + sna_check_knock_800501F8(work, time); + sna_8005027C(work, time); +} + +void sna_anim_wall_move_helper_800538CC(SnaInitWork *work, int time) +{ + int var_s1; + + if (work->field_A38_local_data != 0) + { + work->field_A38_local_data--; + } + + if (sub_800507D8(work)) + { + return; + } + + GM_CheckBehindCamera(work->control.map->hzd, &work->control); + + if ((work->field_9B0_pad_ptr->press & PAD_CROSS) && !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK9)) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, &sna_anim_wall_crouch_80052CCC); + return; + } + + if (dword_800ABBC4 == 1) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, &sna_anim_wall_idle_and_c4_80052A5C); + return; + } + + if (((dword_800ABBD0 - 2048) != work->control.rot.vy) || (pVec_800ABBC8 != pVec_800ABBCC)) + { + work->field_A3A = 0; + work->control.turn.vy = dword_800ABBD0 - 2048; + } + + sna_check_knock_800501F8(work, time); + sna_8005027C(work, time); + + if (dword_800ABBC4 == 2) + { + var_s1 = work->actpack->move->against_wall_r; + } + else + { + var_s1 = work->actpack->move->against_wall_l; + } + + GM_SetPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(work, var_s1, 4); +} + +void sna_anim_wall_crouch_helper_80053A54(SnaInitWork *work, int time) +{ + int sound; + + if (work->field_A38_local_data != 0) + { + work->field_A38_local_data--; + } + + if (sub_800507D8(work)) + { + return; + } + + if ((work->field_9B0_pad_ptr->press & PAD_CROSS) && !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK9)) + { + if (!sna_8004E808(work, 0, 0, 0, 1100)) + { + GM_ClearPlayerStatusFlag(PLAYER_SQUAT); + sound = SE_CHANGE_STANCE; + + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER)) + { + sound = 183; + } + + GM_SeSet(&work->control.mov, sound); + sna_start_anim_8004E1F4(work, &sna_anim_wall_idle_and_c4_80052A5C); + } + } + else + { + if (!(GM_GameStatus & STATE_BEHIND_CAMERA) && ((dword_800ABBD0 - 2048) != work->control.rot.vy)) + { + work->field_A3A = 0; + work->control.turn.vy = dword_800ABBD0 - 2048; + } + + sna_check_knock_800501F8(work, time); + sna_8005027C(work, time); + } +} + +void sna_fn_nothing_80053B80(SnaInitWork *work, int time) +{ +} + +void sna_anim_run_begin_80053B88(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_80054318; + work->field_9CC_anim_update_fn_1p = sna_fn_80052120; + GM_SetPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(work, work->actpack->move->setup, 4); + } +} + +void sna_anim_prone_begin_80053BE8(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + sna_set_flags1_8004E2F4(work, (SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6)); + SetAction_8004E22C(work, work->actpack->trans->crouch, 4); + work->field_A26_stance = SNA_STANCE_CROUCH; + work->control.skip_flag |= CTRL_BOTH_CHECK | CTRL_SKIP_TRAP; + GM_ClearPlayerStatusFlag(PLAYER_SQUAT); + GM_SetPlayerStatusFlag(PLAYER_GROUND); + } + + if (time == 7) + { + work->control.skip_flag &= ~CTRL_SKIP_TRAP; + } + + switch (work->field_A38_local_data) + { + case 0: + if (work->field_798_p_height < 250) + { + work->control.skip_flag &= ~CTRL_BOTH_CHECK; + } + + if (EndMotion(work)) + { + SetAction_8004E22C(work, work->actpack->still->crouch, 4); + work->field_A38_local_data = 1; + } + break; + case 1: + sna_80050440(work); + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) != 0) + { + sna_start_anim_8004E1F4(work, sna_anim_enter_duct_80053E9C); + sna_set_invuln_8004F2A0(work, 0); + SetAction_8004E22C(work, work->actpack->move->crouch, 4); + } + else + { + sna_clear_flags1_8004E308(work, 0x30); + sna_start_anim_8004E1F4(work, sna_anim_prone_idle_800528BC); + } + } +} + +void sna_anim_prone_standup_80053D74(SnaInitWork *work, int time) +{ + int sound; + + if (time == 0) + { + GM_ClearPlayerStatusFlag(PLAYER_GROUND); + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, work->actpack->trans->stand, 4); + + sound = SE_CHANGE_STANCE; + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) != 0) + { + sound = 183; + } + GM_SeSet(&work->control.mov, sound); + sna_set_flags1_8004E2F4(work, (SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6)); + } + + if (work->control.height >= 250) + { + work->control.skip_flag |= CTRL_BOTH_CHECK; + } + + if (work->control.height > 500) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + + if (time == 2) + { + work->control.turn.vx = 0; + } + + if (time == 7) + { + work->control.skip_flag |= CTRL_SKIP_TRAP; + } + + if (EndMotion(work)) + { + work->control.skip_flag &= ~(CTRL_BOTH_CHECK | CTRL_SKIP_TRAP); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + sna_clear_flags1_8004E308(work, (SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6)); + } +} + +void sna_anim_enter_duct_80053E9C(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_800543A8; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + GM_SetPlayerStatusFlag(PLAYER_PAD_OFF | PLAYER_MENU_DISABLE); + sna_set_flags1_8004E2F4(work, (SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6)); + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) == 0 && + GetAction(work) == work->actpack->move->crouch_backwards) + { + work->field_A38_local_data = 48; + } + else + { + work->field_A38_local_data = 24; + } + GM_SetPlayerStatusFlag(PLAYER_MOVE); + } + + if (time == work->field_A38_local_data) + { + sna_clear_invuln_8004F2EC(work); + sna_80050440(work); + sna_clear_flags1_8004E308(work, (SNA_FLAG1_UNK3 | SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6)); + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + GM_ClearPlayerStatusFlag(PLAYER_PAD_OFF | PLAYER_MENU_DISABLE); + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) != 0) + { + sna_start_anim_8004E1F4(work, sna_anim_duct_move_80054424); + } + else + { + sna_start_anim_8004E1F4(work, sna_anim_prone_idle_800528BC); + } + } +} + +void sub_80053FAC(SnaInitWork *work, int time) +{ + short vec_y; // $v1 + unsigned short pad_status; // $s1 + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, work->actpack->attack->setup, 2); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK3); + GM_ClearPlayerStatusFlag(PLAYER_ATTACK); + vec_y = work->field_A54.prone_bool_thing; + work->field_A54.prone_bool_thing = 0; + work->field_90C_pWeaponFn = sna_gun_800540D0; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + work->control.turn.vy = vec_y; + sna_8004E260(work, 0, 4, 0); + } + + pad_status = work->field_9B0_pad_ptr->status; + if (work->body.is_end || (pad_status & PAD_SQUARE) == 0) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + work->field_910 = 0; + if ((pad_status & PAD_SQUARE) != 0) + { + GM_SeSet(&work->control.mov, SE_READY_WEAPON); + GM_SetPlayerStatusFlag(PLAYER_ATTACK); + sna_start_anim_8004E1F4(work, sna_anim_shoot_weapon_80056B88); + work->field_90C_pWeaponFn = sub_80057BF0; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + } + else + { + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } + } +} + +void sna_gun_800540D0(SnaInitWork *work, int time) +{ + int iVar1; + int a4; + + if (time == 0) + { + iVar1 = GM_CheckPlayerStatusFlag(PLAYER_MOVE); + a4 = 0xffff; + if (iVar1 != 0) + { + a4 = 0x3fe; + } + sna_8004E260(work, SET, 4, a4); + work->field_A38_local_data = 0; + } + + if ((work->field_9B0_pad_ptr->release & PAD_SQUARE) != 0) + { + work->field_A38_local_data = 1; + } + + if ((work->field_9B0_pad_ptr->status & PAD_SQUARE) != 0) + { + work->field_926++; + } + else + { + work->field_926 = 0; + } + + if (time == 8) + { + work->field_90C_pWeaponFn = sub_80057BF0; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + } +} + +void sna_bomb_800541A8(SnaInitWork *work) +{ + void *pFn; // $a1 + + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + if (sna_8004FDE8(work, &stru_8009EFE4[0])) + { + pFn = sna_800571B8; + } + else + { + pFn = sna_80057118; + } + sna_start_anim_8004E1F4(work, pFn); +} + +// or sna_no_weapon_80054210 ? +void sna_anim_chokethrow_begin1_80054210(SnaInitWork *work, int time) +{ + void *pFn; + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + if (gSnaMoveDir_800ABBA4 >= 0) + { + pFn = sna_anim_throw_800589C8; + } + else + { + pFn = sna_anim_chokethrow_begin2_80058C80; + } + sna_start_anim_8004E1F4(work, pFn); +} + +void snake_vr_sna_init_80053198(SnaInitWork *work, int time) +{ + short new_y; // $v0 + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, 134, 4); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK26); + } + + if (work->body.is_end) + { + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_DOWNED); + sna_start_anim_8004E1F4(work, sna_anim_prone_idle_800528BC); + SetAction_8004E22C(work, work->actpack->still->crouch, 0); + new_y = work->control.turn.vy + 2048; + work->control.turn.vy = new_y; + work->control.rot.vy = new_y; + sna_clear_flags1_8004E308(work, (SNA_FLAG1_UNK3 | SNA_FLAG1_UNK26)); + sna_clear_invuln_8004F2EC(work); + } +} + +void sna_80054318(SnaInitWork *work, int time) +{ + if (time == 2 && gSnaMoveDir_800ABBA4 >= 0) + { + sna_start_anim_8004E1F4(work, sna_anim_run_8005292C); + return; + } + + if (work->body.is_end) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + return; + } +} + +void sna_fn_800543A8(SnaInitWork *work, int time) +{ + if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) && time >= 16) + { + GM_ClearPlayerStatusFlag(PLAYER_PAD_OFF | PLAYER_MENU_DISABLE); + if (sna_prone_check_standup_80050398(work)) + { + sna_clear_invuln_8004F2EC(work); + sna_clear_flags1_8004E308( + work, (SNA_FLAG1_UNK2 | SNA_FLAG1_UNK3 | SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6 | SNA_FLAG1_UNK10)); + } + } +} + +void sna_anim_duct_move_80054424(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sub_8005688C; + work->field_9CC_anim_update_fn_1p = sub_80052468; + SetAction_8004E22C(work, work->actpack->still->crouch, 4); + } + work->field_A60.vy = work->control.levels[0] + 150; +} + +void sna_anim_duct_idle_80054488(SnaInitWork *work, int time) +{ + if (time == 0) + { + GM_SetPlayerStatusFlag(PLAYER_MOVE); + work->field_9C8_anim_update_fn_3p = sub_80056928; + work->field_9CC_anim_update_fn_1p = sub_80052468; + } + work->field_A60.vy = work->control.levels[0] + 250; + sna_80050440(work); +} + +void sna_anim_shot_flinch_800544E0(SnaInitWork *work, int time) +{ + int action_flag; + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_80052540; + sna_set_invuln_8004F2A0(work, 0); + + if (work->field_89C_pTarget->vital < 1) + { + GM_SeSet2(0, 0x3f, SE_PLAYEROUT); + sna_8004F8E4(work, work->field_A26_stance == SNA_STANCE_CROUCH ? 125 : 128); + } + + if (work->field_A26_stance == SNA_STANCE_CROUCH) + { + action_flag = work->actpack->damage->field_8; + } + else if ((GV_Time & 1) != 0) + { + action_flag = work->actpack->damage->field_0; + work->field_A26_stance = SNA_STANCE_STANDING; + } + else + { + action_flag = work->actpack->damage->field_1; + work->field_A26_stance = SNA_STANCE_STANDING; + } + + SetAction_8004E22C(work, action_flag, 4); + } + + if (EndMotion(work)) + { + sna_set_invuln_8004F2A0(work, 32); + GM_ClearPlayerStatusFlag(PLAYER_DAMAGED | PLAYER_WEAPON_DISABLE); + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + } + + if (work->field_A26_stance == SNA_STANCE_CROUCH) + { + sna_80050568(work); + } +} + +void sna_act_helper2_helper7_80054648(SnaInitWork *work, int time) +{ + if (time == 0) + { + sna_set_invuln_8004F2A0(work, 0); + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_800525F8; + + SetAction_8004E22C(work, work->actpack->special2->field_4, 4); + + if (work->field_89C_pTarget->vital <= 0) + { + GM_SeSet2(0, 63, SE_PLAYEROUT); + sna_8004F8E4(work, 128); + } + } + + if (work->body.is_end) + { + sna_set_invuln_8004F2A0(work, 32); + GM_ClearPlayerStatusFlag(PLAYER_DAMAGED); + sna_start_anim_8004E1F4(work, sna_anim_box_idle_800553EC); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK6); + } +} + +// knocked down by grenade etc +void sna_anim_knockdown_80054710(SnaInitWork *work, int time) +{ + int bVar1; + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_A26_stance = SNA_STANCE_STANDING; + work->field_9CC_anim_update_fn_1p = sna_fn_80052540; + sna_set_invuln_8004F2A0(work, 0); + + bVar1 = work->actpack->damage->field_2; + work->field_A54.prone_bool_thing = 1; + + SetAction_8004E22C(work, bVar1, 4); + + if (work->field_89C_pTarget->vital < 1 && GM_GameOverTimer == 0) + { + GM_SeSet2(0, 0x3f, SE_PLAYEROUT); + GM_GameOverTimer = -1; + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK20); + } + + work->control.skip_flag |= CTRL_BOTH_CHECK; + } + + if (work->control.height < 500) + { + work->field_A26_stance = SNA_STANCE_CROUCH; + GM_SetPlayerStatusFlag(PLAYER_GROUND); + } + + if (work->field_798_p_height < 250) + { + work->control.skip_flag &= ~CTRL_BOTH_CHECK; + } + + if (EndMotion(work)) + { + sna_set_invuln_8004F2A0(work, 32); + sna_start_anim_8004E1F4(work, sna_anim_knockdown_idle_80054930); + } + else + { + if (time < 12 && work->field_A54.prone_bool_thing == 1) + { + work->control.turn.vy += 170; + } + + if (time > 16) + { + GV_NearExp8V(&work->field_A2C.vx, &DG_ZeroVector.vx, 3); + } + + if ((work->field_A54.prone_bool_thing == 1 && time == 12) || + (work->field_A54.prone_bool_thing == 2 && time == 0x14)) + { + GM_SeSetMode(&work->control.mov, SE_HIT_FLOOR, GM_SEMODE_BOMB); + } + + work->control.step.vx = work->field_A2C.vx; + work->control.step.vz = work->field_A2C.vz; + sna_80050568(work); + } +} + +void sna_anim_knockdown_idle_80054930(SnaInitWork *work, int time) +{ + int local_field_A54; // $v1 + int action_flag; // $a1 + short v5; // $a1 + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_knockdown_check_getup_80056A1C; + work->field_9CC_anim_update_fn_1p = sna_fn_80052540; + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK5); + GM_SetPlayerStatusFlag(PLAYER_DOWNED | PLAYER_GROUND); + GM_ClearPlayerStatusFlag(PLAYER_DAMAGED); + local_field_A54 = work->field_A54.prone_bool_thing; + work->field_A26_stance = SNA_STANCE_CROUCH; + if (local_field_A54 == 1) + { + action_flag = work->actpack->damage->field_4; + } + else + { + action_flag = work->actpack->damage->field_5; + } + SetAction_8004E22C(work, action_flag, 4); + if (work->field_89C_pTarget->vital <= 0) + { + v5 = 127; + if (work->field_A54.prone_bool_thing == 1) + { + v5 = 126; + } + sna_8004F8E4(work, v5); + } + sna_80050568(work); + } +} + +void sna_anim_knockdown_getup_80054A10(SnaInitWork *work, int time) +{ + int action_flag; // $a1 + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_80052540; + if (work->field_A54.prone_bool_thing == 1) + { + action_flag = work->actpack->damage->field_6; + } + else + { + action_flag = work->actpack->damage->field_7; + } + SetAction_8004E22C(work, action_flag, 4); + work->field_A54.prone_bool_thing = 0; + } + + if (work->control.height >= 250) + { + work->control.skip_flag |= CTRL_BOTH_CHECK; + } + + if (work->control.height >= 501) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + + if (time == 2) + { + GM_ClearPlayerStatusFlag(PLAYER_GROUND); + work->control.turn.vx = 0; + } + + if (work->body.is_end) + { + sna_clear_flags1_8004E308(work, (SNA_FLAG1_UNK3 | SNA_FLAG1_UNK5)); + sna_set_invuln_8004F2A0(work, 24); + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_DOWNED); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK1); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + work->control.skip_flag &= ~CTRL_BOTH_CHECK; + } +} + +void sna_anim_knockdown_shot_80054B50(SnaInitWork *work) +{ + work->field_9C8_anim_update_fn_3p = sna_knockdown_check_getup_80056A1C; + work->field_9CC_anim_update_fn_1p = sna_fn_80052540; + + GM_ClearPlayerStatusFlag(PLAYER_DAMAGED); + + if (work->field_89C_pTarget->vital <= 0 && !GM_GameOverTimer) + { + GM_SeSet2(0, 63, SE_PLAYEROUT); + sna_8004F8E4(work, work->field_A54.prone_bool_thing == 1 ? 126 : 127); + } + else + { + sna_set_invuln_8004F2A0(work, 32); + } + + sna_start_anim_8004E1F4(work, sna_anim_knockdown_idle_80054930); +} + +// shot by tank while proned +void sna_anim_knockdown_shot_tank_80054C08(SnaInitWork *work, int time) +{ + char bVar1; + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_80052540; + sna_set_invuln_8004F2A0(work, 0); + + if (work->field_A54.prone_bool_thing == 1) + { + bVar1 = work->actpack->damage->field_A; + } + else + { + bVar1 = work->actpack->damage->field_B; + } + + SetAction_8004E22C(work, bVar1, 4); + + if (work->field_89C_pTarget->vital < 1 && GM_GameOverTimer == 0) + { + GM_SeSet2(0, 0x3f, SE_PLAYEROUT); + GM_GameOverTimer = -1; + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK20); + } + } + + if (EndMotion(work)) + { + sna_set_invuln_8004F2A0(work, 32); + sna_start_anim_8004E1F4(work, sna_anim_knockdown_idle_80054930); + } + else + { + if (time == 12) + { + GM_SetPlayerStatusFlag(PLAYER_GROUND); + } + + if (time > 16) + { + GV_NearExp8V(&work->field_A2C.vx, &DG_ZeroVector.vx, 3); + } + + work->control.step.vx = work->field_A2C.vx; + work->control.step.vz = work->field_A2C.vz; + sna_80050568(work); + } +} + +void sna_knock_80054D68(SnaInitWork *work, int time) +{ + SVECTOR vec; + + int var_a1; + int var_t0; + int temp_v0; + int noise; + int code; + HZD_FLR *seg; + + if (time == 0) + { + GM_SetPlayerStatusFlag(PLAYER_CHECK_WALL); + + if (dword_800ABBC4 == 4) + { + var_a1 = work->actpack->still->tap_wall_l; + } + else + { + var_a1 = work->actpack->still->tap_wall_r; + } + + GM_ConfigObjectOverride(&work->body, var_a1, 0, 4, 1022); + + if (((work->field_91C_weapon_idx >= 0) && (work->field_91C_weapon_idx < 2)) || (work->field_91C_weapon_idx == 3)) + { + var_t0 = 0x578; + + if (work->field_A26_stance != 0) + { + var_t0 = 0x302; + } + } + else + { + var_t0 = 0x325; + + if (work->field_A26_stance != 0) + { + var_t0 = 0x15E; + } + } + + vec.vz = -150; + vec.vy = 0; + vec.vx = (dword_800ABBC4 == 4) ? 300 : -300; + + if (sna_8004F628(work, &vec, -250, HZD_CHK_D_SEGMENT | HZD_CHK_F_SEGMENT, SEGMENT_ATR | HZD_SEG_NO_BEHIND, var_t0)) + { + temp_v0 = HZD_GetOnlineHazardAtr(); + code = (temp_v0 >> 8) & 7; + + if (code < 4) + { + seg = HZD_GetOnlineHazard(); + printf("seg %d %d %d %d : ", seg->b1.x, seg->b1.z, seg->b1.y, seg->b1.h); + printf("%d %d %d %d\n", seg->b2.x, seg->b2.z, seg->b2.y, seg->b2.h); + printf("code %d\n", code); + + temp_v0 = GM_GetNoiseSound(temp_v0, 0); + noise = temp_v0; + NewAfterSe(noise, 6); + } + } + } + + if ((work->body.time2 != 0) || (work->body.action2 == 0)) + { + sna_8004E260(work, 0, 4, 0); + GM_ClearPlayerStatusFlag(PLAYER_CHECK_WALL); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK9); + work->field_9C0 = NULL; + } +} + +void snake_vr_sna_init_800565C8(SnaInitWork *work, int time); + +void sna_act_helper2_helper_80054EFC(SnaInitWork *work, int pTime) +{ + int time2; + int status; + unsigned char actionFlag; + SnaInitWork *actor; + int time; + + actor = work; + time = pTime; + + if (time == 0) + { + actor->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + actor->field_9CC_anim_update_fn_1p = snake_vr_sna_init_800565C8; + + if (GM_CheckPlayerStatusFlag(PLAYER_GROUND)) + { + actionFlag = actor->actpack->special1->field_1; + } + else + { + actionFlag = actor->actpack->special1->field_0; + } + + if (GM_CheckPlayerStatusFlag(PLAYER_SQUAT)) + { + sna_8004E260(actor, actionFlag, 4, 0x3FE); + actor->field_A38_local_data = 1; + } + else + { + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + actor->field_A38_local_data = 2; + } + + SetAction_8004E22C(actor, actionFlag, 4); + } + + sna_set_flags1_8004E2F4(actor, SNA_FLAG1_UNK26); + } + + if (time == 8) + { + if (actor->field_A26_stance == 2) + { + NewPadVibration(dword_8009EF08, 1); + NewPadVibration(dword_8009EF10, 2); + } + else + { + NewPadVibration(dword_8009EEF8, 1); + NewPadVibration(dword_8009EF00, 2); + } + } + + if ((time == 6 && actor->field_A26_stance == 2) || + (time == 0 && actor->field_A26_stance != 2)) + { + GM_SeSetMode(&actor->control.mov, SE_SNEEZE, GM_SEMODE_BOMB); + GM_SetNoise(0x64, 0x10, &actor->control.mov); + } + + status = 0; + if (time >= 25) + { + status = actor->field_9B0_pad_ptr->status & (PAD_UP | PAD_DOWN | PAD_LEFT | PAD_RIGHT); + } + + switch (actor->field_A38_local_data) + { + case 0: + if (actor->body.is_end != 0 || status != 0) + { + sna_clear_flags1_8004E308(actor, SNA_FLAG1_UNK26); + if (GM_CheckPlayerStatusFlag(PLAYER_CAUTION) != 0) + { + sna_start_anim_8004E1F4(actor, sna_anim_wall_idle_and_c4_80052A5C); + return; + } + sna_start_anim_8004E1F4(actor, dword_8009EEA4[actor->field_A26_stance]); + return; + } + return; + case 1: + if (actor->body.time2 != 0 || status != 0) + { + sna_clear_flags1_8004E308(actor, SNA_FLAG1_UNK26); + sna_8004E260(actor, 0, 4, 0); + if (GM_CheckPlayerStatusFlag(PLAYER_CAUTION) != 0) + { + sna_start_anim_8004E1F4(actor, sna_anim_wall_crouch_80052CCC); + return; + } + sna_start_anim_8004E1F4(actor, sna_anim_crouch_800527DC); + return; + } + break; + case 2: + actor->field_A60.vy = actor->control.levels[0] + 150; + if (time >= 19) + { + time2 = 40 - time; + if (time2 == 0 || status != 0) + { + sna_clear_flags1_8004E308(actor, SNA_FLAG1_UNK26); + sna_start_anim_8004E1F4(actor, sna_anim_duct_move_80054424); + return; + } + actor->field_A60.vy += GV_RandS(16) * time2; + } + break; + } +} + +void sna_anim_scope_80055334(SnaInitWork *param_1, int time) +{ + int action_flag; + int sVar2; + + if (time == 0) + { + param_1->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + param_1->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + + if (param_1->field_A26_stance == SNA_STANCE_STANDING) + { + action_flag = 0x87; + } + else + { + action_flag = sna_8004EAA8(param_1, param_1->field_A26_stance); + } + + SetAction_8004E22C(param_1, action_flag, 4); + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE); + } + + sVar2 = param_1->field_A2A; + if (sVar2 < dword_800ABBDC) + { + sVar2 = dword_800ABBDC; + } + else if (dword_800ABBD4 < sVar2) + { + sVar2 = dword_800ABBD4; + } + + param_1->control.turn.vx = sVar2; +} + +void sna_anim_jpegcam_800553CC(SnaInitWork *work, int time) +{ + sna_anim_scope_80055334(work, time); +} + +void sna_anim_box_idle_800553EC(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_80056A54; + work->field_9CC_anim_update_fn_1p = sna_fn_800525F8; + GM_ClearPlayerStatusFlag(PLAYER_GROUND | PLAYER_SQUAT); + work->field_A26_stance = SNA_STANCE_STANDING; + SetAction_8004E22C(work, work->actpack->special2->field_2, 4); + } +} + +void sna_anim_box_move_8005544C(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_80056AD8; + work->field_9CC_anim_update_fn_1p = sna_fn_800525F8; + SetAction_8004E22C(work, work->actpack->special2->field_3, 4); + GM_SetPlayerStatusFlag(PLAYER_MOVE); + GM_ConfigControlInterp(&work->control, 4); + } +} + +void sna_anim_box_stop_800554B4(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_800525F8; + SetAction_8004E22C(work, work->actpack->special2->field_4, 4); + } + + if (work->body.is_end) + { + sna_start_anim_8004E1F4(work, sna_anim_box_idle_800553EC); + } +} + +void sna_anim_dying_80055524(SnaInitWork *work, int time) +{ + UnkSnakeStruct2 *unk2 = &work->field_9E4; + + if (time == 0) + { + int f9e6; + + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + + sub_8004F338(work); + GM_SetPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_PAD_OFF); + + if ((unk2->field_9EC_flags3 & 0x400) == 0) // TODO: make flags enum for flags3 + { + if ((unk2->field_9EC_flags3 & 0x4000) != 0) + { + work->control.s_sphere = -2; + work->control.r_sphere = -2; + work->field_A28 = -2; + } + else + { + work->control.s_sphere = -1; + work->control.r_sphere = -1; + work->field_A28 = -1; + } + } + + sna_set_invuln_8004F2A0(work, 0); + GetAction(work) = -1; + SetAction_8004E22C(work, unk2->field_9E4, 4); + + f9e6 = unk2->field_9E6; + if (f9e6 >= 0) + { + work->control.turn.vy = f9e6; + work->control.rot.vy = f9e6; + } + + work->field_A38_local_data = -1; + work->field_A3A = -1; + + if ((unk2->field_9EC_flags3 & 0x1000) == 0) + { + if (GM_CheckPlayerStatusFlag(PLAYER_CB_BOX) == 0) + { + if ((work->field_9AC & 2) != 0) + { + if ((GM_GameOverTimer == 0 && unk2->field_9F2 == 0) || + sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK29) != 0) + { + work->field_A38_local_data = work->field_9A8_current_item; + sub_8004F204(work); + } + else + { + GM_SetPlayerStatusFlag(PLAYER_NOT_SIGHT); + } + } + } + else + { + work->field_A38_local_data = work->field_9A8_current_item; + sub_8004F204(work); + } + + if ((work->field_920_tbl & 0x200) != 0) + { + if (GM_GameOverTimer != 0 || unk2->field_9F2 != 0) + { + GM_SetPlayerStatusFlag(PLAYER_NOT_SIGHT); + } + else + { + work->field_A3A = work->field_91C_weapon_idx; + sub_8004F14C(work); + } + } + else if (work->field_91C_weapon_idx != -1 && GM_CurrentWeaponId == WP_None) + { + sub_8004F14C(work); + } + } + + GM_ClearPlayerStatusFlag(PLAYER_SQUAT); + } + + if ((unk2->field_9EC_flags3 & 0x200) != 0 && work->control.height < 500) + { + GM_SetPlayerStatusFlag(PLAYER_GROUND); + } + else + { + GM_ClearPlayerStatusFlag(PLAYER_GROUND); + work->control.turn.vx = 0; + } + + if ((unk2->field_9EC_flags3 & 2) == 0) + { + work->field_A2C = DG_ZeroVector; + } + + if ((unk2->field_9EC_flags3 & 0x4000) == 0) + { + if ((unk2->field_9EC_flags3 & 2) == 0) + { + int level; + int new_height; + short y_pos; + + new_height = (unsigned short)work->body.height - (-unk2->field_9EE + work->control.levels[0]); + level = work->control.levels[0]; + work->control.height = new_height; + y_pos = new_height + level; + + work->control.mov.vy = y_pos; + work->body.objs->world.t[1] = y_pos; + + DG_GetLightMatrix2(&work->control.mov, work->light); + } + } + else + { + int levels[2]; + sub_8004E588(work->control.map->hzd, &work->control.mov, levels); + work->control.levels[0] = levels[0]; + work->control.levels[1] = levels[1]; + } + + if (work->body.is_end) + { + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK22); + + if ((unk2->field_9EC_flags3 & 0x20) == 0) + { + work->control.skip_flag &= ~CTRL_SKIP_TRAP; + } + + if ((unk2->field_9EC_flags3 & 0x100) != 0) + { + work->m_ctrl.interp = 15; + work->m_ctrl.info1.field_14 = 0; + work->field_9B8_fn_anim = sna_fn_nothing_80053B80; + work->field_9BC_anim_frame = 0; + work->field_A3A = 0; + work->field_A38_local_data = 0; + } + else + { + int stance; + int fa38; + int fa3a; + + GM_ClearPlayerStatusFlag(PLAYER_GROUND | PLAYER_MENU_DISABLE | + PLAYER_PAD_OFF); + sna_clear_invuln_8004F2EC(work); + + stance = unk2->field_9EA; + fa38 = work->field_A38_local_data; + fa3a = work->field_A3A; + + if (stance < SNA_STANCE_STANDING) + { + if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + stance = work->field_A26_stance; + } + else + { + stance = SNA_STANCE_UNK; + } + } + + if ((unk2->field_9EC_flags3 & 0x1000) != 0) + { + sna_start_anim_8004E1F4(work, sna_anim_box_idle_800553EC); + work->field_A26_stance = SNA_STANCE_STANDING; + } + else + { + if (stance < SNA_STANCE_UNK) + { + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + GM_ClearPlayerStatusFlag(PLAYER_INTRUDE); + sub_8004ED08(work); + } + + work->field_A26_stance = stance; + sna_start_anim_8004E1F4(work, dword_8009EEA4[stance]); + } + else + { + if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + work->field_A26_stance = SNA_STANCE_CROUCH; + GM_SetPlayerStatusFlag(PLAYER_INTRUDE | PLAYER_GROUND); + sna_8004EC8C(work); + } + + sna_start_anim_8004E1F4(work, sna_anim_duct_move_80054424); + } + + if ((unk2->field_9EC_flags3 & 8) == 0) + { + if (fa38 >= 0) + { + GM_CurrentItemId = fa38; + + if ((unsigned int)(fa38 - 2) < 3) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + } + if (fa3a >= 0) + { + GM_CurrentWeaponId = fa3a; + GM_WeaponChanged = 1; + + if (fa3a == WP_Rifle) + { + work->field_A26_stance = SNA_STANCE_CROUCH; + } + } + } + } + } + } +} + +// things like "a surveillance camera??" where player control is halted +void sna_anim_mini_cutscene_800559D8(SnaInitWork *work, int time) +{ + SVECTOR vec; + + UnkSnakeStruct2 *pStr = &work->field_9E4; + int weapon; + int item; + int stance; + unsigned char action; + + short temp_v0; + short temp_v1_2; + short temp_v0_2; + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = &sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = &sna_fn_nothing_80053B80; + + sub_8004F338(work); + + GM_SetPlayerStatusFlag(PLAYER_PAD_OFF | PLAYER_MENU_DISABLE); + sna_set_invuln_8004F2A0(work, 0); + GM_ClearPlayerStatusFlag(PLAYER_GROUND | PLAYER_SQUAT); + + work->field_A00.target_next = -1; + work->field_A00.addr = GM_PlayerAddress; + work->field_A00.next = GM_PlayerAddress; + + NavigateSetTarget(&work->field_A00, work->control.map->hzd, &work->field_9E4.field_9F4); + temp_v0 = HZD_GetAddress(work->control.map->hzd, &work->field_9E4.field_9F4, work->field_A00.target); + + temp_v1_2 = (temp_v0 >> 8) & 0xff; + temp_v0_2 = temp_v0 & 0xff; + + if ((temp_v1_2 == 0xff) || (temp_v0_2 == 0xff) || (temp_v1_2 != temp_v0_2)) + { + pStr->field_9EC_flags3 |= 0x200; + } + + if (pStr->field_9EC_flags3 & 0x200) + { + GV_SubVec3(&pStr->field_9F4, &work->control.mov, &vec); + work->control.turn.vy = GV_VecDir2(&vec); + work->control.s_sphere = -1; + work->control.r_sphere = -1; + work->field_A28 = -1; + } + + work->field_A38_local_data = -1; + + if (!(pStr->field_9EC_flags3 & 0x100) && ((GM_CheckPlayerStatusFlag(PLAYER_CB_BOX)) || (work->field_9AC & 2))) + { + work->field_A38_local_data = work->field_9A8_current_item; + sub_8004F204(work); + } + + work->field_A3A = -1; + + if (work->field_920_tbl & 0x200) + { + work->field_A3A = work->field_91C_weapon_idx; + sub_8004F14C(work); + } + + if (pStr->field_9EC_flags3 & 0x100) + { + stance = SNA_STANCE_STANDING; + action = work->actpack->special2->field_3; + } + else if (pStr->field_9EC_flags3 & 1) + { + stance = SNA_STANCE_CROUCH; + action = work->actpack->move->crouch; + GM_SetPlayerStatusFlag(PLAYER_GROUND); + } + else + { + stance = SNA_STANCE_STANDING; + action = work->actpack->move->stand; + } + + work->field_A26_stance = stance; + + SetAction_8004E22C(work, action, 4); + GM_SetPlayerStatusFlag(PLAYER_MOVE); + } + + if (!(pStr->field_9EC_flags3 & 0x200)) + { + work->field_A00.addr = GM_PlayerAddress; + GM_ZoneNavi(&work->field_A00, &work->control); + } + + GV_NearExp4PV(&work->control.rot.vx, &work->control.turn.vx, 3); + GV_NearExp4PV(&work->control.rot.vx, &work->control.turn.vx, 3); + + if (NavigateGetTargetDist(&work->field_A00, &work->control.mov) < pStr->field_9F4.pad) + { + sna_clear_invuln_8004F2EC(work); + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK22); + GM_ClearPlayerStatusFlag(PLAYER_PAD_OFF | PLAYER_MENU_DISABLE); + + if (pStr->field_9EC_flags3 & 0x200) + { + work->control.s_sphere = 450; + work->control.r_sphere = 450; + work->field_A28 = 450; + } + + if (!(pStr->field_9EC_flags3 & 2)) + { + item = work->field_A38_local_data; + weapon = work->field_A3A; + + if (item >= 0) + { + GM_CurrentItemId = item; + + if ((item == IT_Box1) || (item == IT_Box2) || (item == IT_Box3)) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + } + + if (weapon >= 0) + { + GM_CurrentWeaponId = weapon; + GM_WeaponChanged = 1; + + if (weapon == WP_Rifle) + { + work->field_A26_stance = SNA_STANCE_CROUCH; + } + else if (weapon == WP_Stinger) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + } + } + + if (!(pStr->field_9EC_flags3 & 8)) + { + work->control.skip_flag &= ~CTRL_SKIP_TRAP; + } + + if (pStr->field_9EC_flags3 & 1) + { + sna_80050440(work); + sna_clear_flags1_8004E308(work, 4); + } + + if (pStr->field_9EC_flags3 & 0x100) + { + sna_start_anim_8004E1F4(work, &sna_anim_box_stop_800554B4); + } + else + { + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + } + + if (pStr->field_9E8 >= 0) + { + work->control.turn.vy = pStr->field_9E8; + } + } +} + +// similar to above, seems freeze snake and make him look at a certain direction or track something +// triggers on first elevator ride at dock and right before mantis fight to look at meryl +// PERMUTER +static inline void sna_act_unk_helper3_helper_80055DD8(SnaInitWork *work, UnkSnakeStruct2 *pStr) +{ + SnaInitWork *work2; + char new_var; + int action; + int field_9E4; + work2 = work; + action = work->body.action; + if (!(((action != work->actpack->trans->crouch) || work->body.is_end) && ((action != work->actpack->trans->stand) || work2->body.is_end))) + { + pStr->field_9E4 = 1; + } else { + work2 = work; + if (((((action == work->actpack->damage->field_6) && (work->body.is_end == 0)) || ((action == work->actpack->damage->field_7) && (work2->body.is_end == 0))) || ((action == work->actpack->damage->field_2) && work->body.is_end)) || ((action == work->actpack->damage->field_3) && work2->body.is_end)) + { + if ((action == work->actpack->damage->field_2) && work->body.is_end) + { + SetAction_8004E22C(work, 71, 4); + } + if ((action == work->actpack->damage->field_3) && work->body.is_end) + { + SetAction_8004E22C(work, 72, 4); + } + field_9E4 = 2; + + } + else + if ((((action == work->actpack->damage->field_4) || (action == work->actpack->damage->field_5)) || (action == work->actpack->damage->field_A)) || (action == work->actpack->damage->field_B)) + { + field_9E4 = 3; + } + else + if (((action == work->actpack->damage->field_2) && (work->body.is_end == 0)) || ((action == (new_var = work->actpack->damage->field_3)) && (work->body.is_end == 0))) + { + field_9E4 = 4; + } + else + { + field_9E4 = 0; + } + pStr->field_9E4 = field_9E4; + } +} + +static inline void sna_act_unk_helper3_helper2_80055DD8(SnaInitWork *work) +{ + switch (work->field_9E4.field_9EA) + { + case 0: + switch (work->field_A26_stance) + { + case SNA_STANCE_STANDING: + if (++work->field_9E4.field_9EE >= 4) + { + work->field_9E4.field_9EE = 0; + work->field_9E4.field_9E4 = 6; + } + break; + + case SNA_STANCE_SQUAT: + SetAction_8004E22C(work, work->actpack->still->stand, 4); + + if (++work->field_9E4.field_9EE == 4) + { + work->field_9E4.field_9EE = 0; + work->field_9E4.field_9E4 = 6; + } + + break; + + case SNA_STANCE_CROUCH: + SetAction_8004E22C(work, work->actpack->trans->stand, 4); + + if (work->body.is_end != 0) + { + SetAction_8004E22C(work, work->actpack->still->stand, 4); + work->field_9E4.field_9E4 = 6; + } + break; + } + break; + + case 1: + switch (work->field_A26_stance) + { + case SNA_STANCE_STANDING: + SetAction_8004E22C(work, work->actpack->still->squat, 4); + + if (++work->field_9E4.field_9EE == 4) + { + work->field_9E4.field_9EE = 0; + work->field_9E4.field_9E4 = 6; + } + break; + + case SNA_STANCE_SQUAT: + if (++work->field_9E4.field_9EE == 4) + { + work->field_9E4.field_9EE = 0; + work->field_9E4.field_9E4 = 6; + } + break; + + case SNA_STANCE_CROUCH: + SetAction_8004E22C(work, work->actpack->trans->stand, 4); + + if (work->body.is_end != 0) + { + SetAction_8004E22C(work, work->actpack->still->squat, 4); + work->field_A26_stance = SNA_STANCE_STANDING; + } + break; + } + break; + + case 2: + switch (work->field_A26_stance) + { + case SNA_STANCE_STANDING: + SetAction_8004E22C(work, work->actpack->still->squat, 4); + + if (++work->field_9E4.field_9EE == 4) + { + work->field_9E4.field_9EE = 0; + work->field_A26_stance = SNA_STANCE_SQUAT; + } + break; + + case SNA_STANCE_SQUAT: + SetAction_8004E22C(work, work->actpack->trans->crouch, 4); + + if (work->body.is_end != 0) + { + SetAction_8004E22C(work, work->actpack->still->crouch, 4); + work->field_9E4.field_9E4 = 6; + } + break; + + case SNA_STANCE_CROUCH: + if (++work->field_9E4.field_9EE == 4) + { + work->field_9E4.field_9EE = 0; + work->field_9E4.field_9E4 = 6; + } + } + break; + } +} + +void sna_act_unk_helper3_80055DD8(SnaInitWork *work, int time) +{ + UnkSnakeStruct2 *pStr; + int height; + int stance; + int action_flag; + int item; + int weapon; + + pStr = &work->field_9E4; + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = &sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = &sna_fn_nothing_80053B80; + + sub_8004F338(work); + + GM_SetPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_PAD_OFF); + sna_set_invuln_8004F2A0(work, 0); + GM_ClearPlayerStatusFlag(PLAYER_SQUAT | PLAYER_GROUND); + + work->field_A38_local_data = IT_None; + + if (GM_CheckPlayerStatusFlag(PLAYER_CB_BOX) || (work->field_9AC & 2)) + { + work->field_A38_local_data = work->field_9A8_current_item; + sub_8004F204(work); + } + + work->field_A3A = WP_None; + + if (work->field_920_tbl & 0x200) + { + work->field_A3A = work->field_91C_weapon_idx; + sub_8004F14C(work); + } + + if (pStr->field_9EA != 2) + { + work->control.turn.vx = 0; + } + + sna_act_unk_helper3_helper_80055DD8(work, pStr); + } + + height = work->control.height; + stance = pStr->field_9EA; + + switch (stance) + { + case 0: + if (height > 500) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + break; + + case 1: + if (height < 500) + { + work->field_A26_stance = stance; + } + break; + + case 2: + if (height < 500) + { + work->field_A26_stance = stance; + } + break; + } + + switch(pStr->field_9E4) + { + case 0: + SetAction_8004E22C(work, sna_8004EAA8(work, work->field_A26_stance), 4); + pStr->field_9E4 = 5; + break; + + case 1: + if (work->body.is_end != 0) + { + SetAction_8004E22C(work, sna_8004EAA8(work, work->field_A26_stance), 4); + pStr->field_9E4 = 5; + } + break; + + case 2: + if (work->body.is_end != 0) + { + SetAction_8004E22C(work, sna_8004EAA8(work, 0), 4); + work->field_A26_stance = 0; + pStr->field_9E4 = 5; + } + break; + + case 3: + if (work->field_A54.prone_bool_thing == 1) + { + action_flag = work->actpack->damage->field_6; + } + else + { + action_flag = work->actpack->damage->field_7; + } + + work->field_A54.prone_bool_thing = 0; + SetAction_8004E22C(work, action_flag, 4); + pStr->field_9E4 = 2; + break; + + case 4: + if (work->body.is_end != 0) + { + if (work->field_A54.prone_bool_thing == 1) + { + SetAction_8004E22C(work, work->actpack->damage->field_6, 4); + } + else + { + SetAction_8004E22C(work, work->actpack->damage->field_7, 4); + } + + pStr->field_9E4 = 2; + } + break; + + case 5: + sna_act_unk_helper3_helper2_80055DD8(work); + break; + + case 6: + work->field_A26_stance = pStr->field_9EA; + + if (work->field_A26_stance == SNA_STANCE_SQUAT) + { + GM_SetPlayerStatusFlag(PLAYER_SQUAT); + } + else if (work->field_A26_stance == SNA_STANCE_CROUCH) + { + GM_SetPlayerStatusFlag(PLAYER_GROUND); + } + + sna_clear_invuln_8004F2EC(work); + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_set_flags1_8004E2F4(work, 0x200000); + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_PAD_OFF); + + if (!(pStr->field_9EC_flags3 & 0x2)) + { + item = work->field_A38_local_data; + weapon = work->field_A3A; + + if (item >= 0) + { + GM_CurrentItemId = item; + + if (item == IT_Box1 || item == IT_Box2 || item == IT_Box3) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + } + + if (weapon >= 0) + { + GM_CurrentWeaponId = weapon; + GM_WeaponChanged = 1; + + if (weapon == WP_Rifle) + { + work->field_A26_stance = SNA_STANCE_CROUCH; + } + else if (weapon == WP_Stinger) + { + work->field_A26_stance = SNA_STANCE_STANDING; + } + } + } + + if (!(pStr->field_9EC_flags3 & 0x8)) + { + work->control.skip_flag &= ~CTRL_SKIP_TRAP; + } + + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + + if (pStr->field_9E8 >= 0) + { + work->control.turn.vy = pStr->field_9E8; + } + break; + } +} + +void sna_act_helper3_helper_80056650(SnaInitWork *work, int time) +{ + int result; + int footstepsFrame; // guessed + + if (time == 0) + { + int *status = &GM_GameStatus; + + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + + *status |= STATE_PADRELEASE; + + sub_8004F338(work); + work->control.skip_flag &= ~CTRL_SKIP_TRAP; + sna_set_invuln_8004F2A0(work, 0); + GM_ClearPlayerStatusFlag(PLAYER_SQUAT | PLAYER_GROUND); + sub_8004F14C(work); + sub_8004F204(work); + work->field_A26_stance = 0; + SetAction_8004E22C(work, 3, 1); + } + + if (!work->field_908_weapon_actor) + { + GV_ACT *timerBomb; + timerBomb = NewTimerBomb(&work->control, &work->body, 4, + &work->field_914_trigger, 1); + work->field_908_weapon_actor = timerBomb; + work->field_924 = 0; + SetAction_8004E22C(work, 0x3c, 4); + + if (!work->field_908_weapon_actor) + { + return; + } + } + + result = 1; + footstepsFrame = work->m_ctrl.info1.frame; + switch (work->field_924) + { + case 0: + if (EndMotion(work)) + { + SetAction_8004E22C(work, 3, 1); + work->field_924 = 1; + } + break; + case 1: + SetAction_8004E22C(work, 0x32, 1); + work->field_924 = 2; + break; + case 2: + if (footstepsFrame == 6) + { + result = 3; + work->field_924 = 3; + } + break; + case 3: + result = 0; + + if (EndMotion(work)) + { + sna_clear_invuln_8004F2EC(work); + GM_GameStatus &= ~STATE_PADRELEASE; + GV_DestroyActorQuick(work->field_908_weapon_actor); + work->field_908_weapon_actor = NULL; + work->field_924 = 0; + work->field_9E4.field_9F0 = 0; + sna_set_flags1_8004E2F4(work, 0x200000); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } + break; + } + + work->field_914_trigger = result; +} + +void sub_8005684C(SnaInitWork *work) +{ + int y; + + if ((work->field_9B0_pad_ptr->status & (PAD_LEFT | PAD_RIGHT)) == 0) + { + return; + } + + y = work->control.turn.vy; + if ((work->field_9B0_pad_ptr->status & PAD_LEFT) != 0) + { + y += 32; + } + else + { + y -= 32; + } + work->control.turn.vy = y; +} + +void sub_8005688C(SnaInitWork *work, int time) +{ + int action_flag; // $a1 + + if ((work->field_9B0_pad_ptr->status & (PAD_DOWN | PAD_UP)) != 0) + { + if ((work->field_9B0_pad_ptr->status & PAD_UP) != 0) + { + action_flag = work->actpack->move->crouch; + } + else + { + action_flag = work->actpack->move->crouch_backwards; + } + SetAction_8004E22C(work, action_flag, 4); + sna_start_anim_8004E1F4(work, sna_anim_duct_idle_80054488); + } + else + { + sub_8005684C(work); + } +} + +void sub_80056928(SnaInitWork *param_1, int time) +{ + char bVar1; + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) == 0) + { + sna_start_anim_8004E1F4(param_1, sna_anim_enter_duct_80053E9C); + sna_set_invuln_8004F2A0(param_1, 0); + } + else + { + if ((param_1->field_9B0_pad_ptr->status & (PAD_DOWN | PAD_UP)) == 0) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(param_1, sna_anim_duct_move_80054424); + SetAction_8004E22C(param_1, param_1->actpack->still->crouch, 4); + } + else + { + if ((param_1->field_9B0_pad_ptr->status & PAD_UP) != 0) + { + bVar1 = param_1->actpack->move->crouch; + } + else + { + bVar1 = param_1->actpack->move->crouch_backwards; + } + + SetAction_8004E22C(param_1, bVar1, 4); + sub_8005684C(param_1); + } + } +} + +void sna_knockdown_check_getup_80056A1C(SnaInitWork *work, int time) +{ + if (work->field_9B0_pad_ptr->press) + { + sna_knockdown_getup_80050668(work); + } +} + +void sna_80056A54(SnaInitWork *work, int time) +{ + if (gSnaMoveDir_800ABBA4 >= 0) + { + sna_start_anim_8004E1F4(work, sna_anim_box_move_8005544C); + } +} + +void sna_fn_80056AD8(SnaInitWork *work, int time) +{ + if (gSnaMoveDir_800ABBA4 < 0) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_box_stop_800554B4); + } + else + { + SetAction_8004E22C(work, work->actpack->special2->field_3, 4); + GM_SetPlayerStatusFlag(PLAYER_MOVE); + work->control.turn.vy = (short)gSnaMoveDir_800ABBA4; + } +} + +void sna_80051A10(SnaInitWork *work, SVECTOR *pPos, SVECTOR *pOut, SVECTOR *pVec) +{ + MATRIX rot; + SVECTOR vec; + SVECTOR vec2; + SVECTOR vec3; + SVECTOR vec4; + + int len; + + ReadRotMatrix(&rot); + DG_SetPos2(pPos, &work->control.rot); + DG_PutVector(pVec, pVec, 1); + SetRotMatrix(&rot); + + vec = *pPos; + vec2 = *pVec; + + if (sna_line_check(&vec, work->control.map->hzd, HZD_CHK_ALL, SEGMENT_ATR) < 0) + { + vec2 = *pVec; + } + + GV_SubVec3(&vec2, pPos, &vec3); + len = GV_VecLen3(&vec3); + + if ((len - work->field_A28) <= 0) + { + vec3 = *pPos; + } + else + { + GV_LenVec3(&vec3, &vec4, len, len - work->field_A28); + GV_AddVec3(pPos, &vec4, &vec3); + } + + *pOut = vec3; +} + +int sna_80051BA4(SnaInitWork *work) +{ + SVECTOR vec; + SVECTOR vec2; + SVECTOR vec3; + + GV_PAD *pPad = work->field_9B0_pad_ptr; + unsigned short press = pPad->press; + unsigned short release = pPad->release; + + if (pPad->status & (PAD_R1 | PAD_L1)) + { + if (press & PAD_R1) + { + work->field_A56 = 8; + } + + if (press & PAD_L1) + { + work->field_A56 = 4; + } + + if (release & PAD_R1) + { + work->field_A56 = 4; + } + + if (release & PAD_L1) + { + work->field_A56 = 8; + } + + vec2 = vec = DG_ZeroVector; + vec2.vx = 1280; + vec.vx = -1280; + vec3 = work->field_A60; + + sna_80051A10(work, &vec3, &work->field_A3C, &vec2); + sna_80051A10(work, &vec3, &work->field_A44, &vec); + + if (!sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK16)) + { + work->field_A58 = 8; + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK16); + } + else if (work->field_A56 & 4) + { + work->field_A60 = work->field_A3C; + + } + else if (work->field_A56 & 8) + { + work->field_A60 = work->field_A44; + } + + return 1; + } + + if (work->field_A58 > 0) + { + if (--work->field_A58 == 0) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK16); + return 0; + } + + return 1; + } + + return 0; +} + +void sna_80051DA0(SnaInitWork *work) +{ + unsigned int status = work->field_9B0_pad_ptr->status; + unsigned char ldy = work->field_9B0_pad_ptr->left_dy; + short *temp_s4; + int var_s1; + int var_s2; + int var_s0; + int temp_v0; + int temp_a1; + short var_a0; + short var_a2; + + // status is passed using the wrong type here + GM_CheckShukanReverse((unsigned short *)&status); + GM_CheckShukanReverseAnalog(&ldy); + + temp_s4 = work->field_9D0; + var_s2 = temp_s4[4]; + var_s1 = -temp_s4[3]; + + if (work->field_9D0[0] == 0) + { + if (work->field_A26_stance == SNA_STANCE_CROUCH) + { + var_s0 = work->field_A2A; + } + else + { + var_s0 = 0; + } + } + else + { + var_s0 = work->control.turn.vx; + } + + if (work->field_A26_stance == SNA_STANCE_CROUCH) + { + if (work->field_91C_weapon_idx == WP_Rifle) + { + temp_v0 = work->field_A2A; + var_s1 += temp_v0; + var_s2 += temp_v0; + } + else + { + var_s1 += var_s0; + var_s2 += var_s0; + } + } + + if (var_s1 < dword_800ABBDC) + { + var_s1 = dword_800ABBDC; + } + + if (var_s2 > dword_800ABBD4) + { + var_s2 = dword_800ABBD4; + } + + temp_a1 = sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK15); + var_a2 = 1; + var_a0 = 1; + + if (status & PAD_UP) + { + if (temp_a1 & 0xff) + { + var_a0 = 64 - ldy; + var_a2 = 64; + } + + var_s0 -= (temp_s4[5] * (var_a0 & 0xff)) / var_a2; + } + else if (status & PAD_DOWN) + { + if (temp_a1 & 0xff) + { + var_a0 = ldy + 64; + var_a2 = 63; + } + + var_s0 += (temp_s4[6] * (var_a0 & 0xff)) / var_a2; + } + + if (var_s0 < var_s1) + { + var_s0 = var_s1; + } + + if (var_s2 < var_s0) + { + var_s0 = var_s2; + } + + work->control.turn.vx = var_s0; +} + +void sna_80051FD0(SnaInitWork *work) +{ + int iVar1; + u_short uVar2; + unsigned int uVar3; + short sVar4; + short sVar5; + int iVar6; + short *short_arr; + + short_arr = work->field_9D0; + uVar2 = work->field_9B0_pad_ptr->status; + if ((uVar2 & (PAD_LEFT | PAD_RIGHT)) != 0) + { + iVar1 = work->control.turn.vy; + uVar3 = sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK15); + sVar4 = 1; + sVar5 = 1; + if (uVar2 & PAD_LEFT) + { + if ((uVar3 & 0xff) != 0) + { + sVar4 = 0x40; + sVar5 = 0x40 - (*((unsigned char *)((short *)work->field_9B0_pad_ptr + 7))); + } + iVar6 = ((int)short_arr[8]) * (sVar5 & 0xff); + iVar1 += iVar6 / sVar4; + } + else + { + if ((uVar3 & 0xff) != 0) + { + sVar4 = 0x3f; + sVar5 = (*((unsigned char *)((short *)work->field_9B0_pad_ptr + 7))) + 0x40; + } + iVar6 = ((int)short_arr[7]) * (sVar5 & 0xff); + iVar1 -= iVar6 / sVar4; + } + work->control.turn.vy = iVar1; + } +} + +void sna_fn_80052120(SnaInitWork *work, int time) +{ + unsigned short press; + int sound; + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) != 0) + { + sna_8004EC00(work); + sna_set_invuln_8004F2A0(work, 0); + sna_start_anim_8004E1F4(work, sna_anim_enter_duct_80053E9C); + } + else + { + press = work->field_9B0_pad_ptr->press; + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(work, sna_8004EAA8(work, (int)work->field_A26_stance), 4); + + if ((work->field_9B0_pad_ptr->status & PAD_TRIANGLE) == 0) + { + sna_8004EC00(work); + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + } + else + { + if (sna_80051BA4(work)) + { + return; + } + + sna_80051DA0(work); + sna_80051FD0(work); + + if ((press & PAD_CROSS) != 0) + { + switch (work->field_A26_stance) + { + case 0: + sna_start_anim_8004E1F4(work, sna_anim_crouch_800527DC); + break; + case 2: + if (sna_8004E808(work, 1, 0, -0x26e, 0x5dc) != 0) + { + return; + } + default: + if (sna_8004E808(work, 0, 0, 0, 0x5dc) != 0) + { + return; + } + + work->control.turn.vy = work->control.rot.vy; + GM_ClearPlayerStatusFlag(PLAYER_SQUAT | PLAYER_GROUND); + + sound = SE_CHANGE_STANCE; + if (GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) != 0) + { + sound = 183; + } + + GM_SeSet(&work->control.mov, sound); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + break; + } + } + } + } +} + +void sub_8005230C(SnaInitWork *work, int time) +{ + int action; + unsigned short status; + unsigned short press; + + status = work->field_9B0_pad_ptr->status; + press = work->field_9B0_pad_ptr->press; + + if ((status & PAD_TRIANGLE) == 0) + { + sna_8004EC00(work); + work->field_A38_local_data = 2; + } + else + { + if (GM_CheckPlayerStatusFlag(PLAYER_MOVE) != 0) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + + if (work->field_A26_stance == SNA_STANCE_STANDING) + { + action = work->actpack->still->against_wall; + } + else + { + action = work->actpack->still->squat_against_wall; + } + + SetAction_8004E22C(work, action, 4); + } + if (((press & (PAD_LEFT | PAD_DOWN | PAD_RIGHT | PAD_UP)) != 0) || + ((status & (PAD_LEFT | PAD_DOWN | PAD_RIGHT | PAD_UP)) == 0)) + { + + GM_ClearPlayerStatusFlag(PLAYER_CAUTION); + work->field_A28 = 0x1c2; + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + GM_ExitBehindCamera(); + } + + if (!sna_80051BA4(work) && (press & PAD_CROSS) != 0) + { + if (work->field_A26_stance == SNA_STANCE_STANDING) + { + sna_start_anim_8004E1F4(work, sna_anim_wall_crouch_80052CCC); + } + else + { + GM_ClearPlayerStatusFlag(PLAYER_SQUAT); + sna_start_anim_8004E1F4(work, sna_anim_wall_idle_and_c4_80052A5C); + } + } + } +} + +void sub_80052468(SnaInitWork *work, int time) +{ + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) == 0) + { + work->field_A56 = 0; + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | + PLAYER_NORMAL_WATCH | PLAYER_WATCH); + sna_set_invuln_8004F2A0(work, 0); + sna_start_anim_8004E1F4(work, sna_anim_enter_duct_80053E9C); + } + else + { + SetAction_8004E22C(work, work->actpack->still->crouch, 4); + + if ((work->field_9B0_pad_ptr->status & PAD_TRIANGLE) == 0) + { + work->field_A56 = 0; + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | + PLAYER_NORMAL_WATCH | PLAYER_WATCH); + sna_start_anim_8004E1F4(work, sna_anim_duct_move_80054424); + } + + sna_80051DA0(work); + sna_80051FD0(work); + } +} + +void sna_fn_80052540(SnaInitWork *work, int time) +{ + if ((work->field_9B0_pad_ptr->status & PAD_TRIANGLE) == 0) + { + sna_8004EC00(work); + } + + if (work->field_9B8_fn_anim == sna_anim_knockdown_idle_80054930 && + ((work->field_9B0_pad_ptr->press & ~PAD_TRIANGLE) != 0)) + { + sna_knockdown_getup_80050668(work); + } + + GM_SnakeCamera.position.vy += 320; + sna_8004EE28(work); +} + +void sna_fn_800525F8(SnaInitWork *work, int time) +{ + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + if (work->field_9B8_fn_anim != sna_anim_box_idle_800553EC) + { + if (work->body.is_end) + { + SetAction_8004E22C(work, work->actpack->special2->field_2, 4); + } + } + + if (!(work->field_9B0_pad_ptr->status & PAD_TRIANGLE)) + { + sna_8004EC00(work); + work->control.turn.vx = 0; + } + else + { + sna_80051DA0(work); + sna_80051FD0(work); + } +} + +void snake_vr_sna_init_800565C8(SnaInitWork *work, int time) +{ + if (!(work->field_9B0_pad_ptr->status & PAD_TRIANGLE)) + { + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + work->field_A56 = 0; + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE + | PLAYER_NORMAL_WATCH + | PLAYER_WATCH); + } + else + { + sna_8004EC00(work); + } + } + + if (!GM_CheckPlayerStatusFlag(PLAYER_INTRUDE)) + { + sna_8004EE28(work); + } +} + +void sna_anim_shoot_weapon_80056B88(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_anim_shoot_weapon_helper_80057590; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, work->actpack->still->setup, 4); + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + } +} + +void sna_anim_rungun_begin_80056BDC(SnaInitWork *param_1, int time) +{ + if (time == 0) + { + param_1->field_9C8_anim_update_fn_3p = sna_anim_rungun_begin_helper_800577B4; + param_1->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + GM_SetPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(param_1, param_1->actpack->move->setup, 4); + } +} + +void sna_anim_rungun_80056C3C(SnaInitWork *param_1, int time) +{ + if (time == 0) + { + param_1->field_9C8_anim_update_fn_3p = sna_anim_rungun_helper_80057844; + param_1->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + GM_SetPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(param_1, param_1->actpack->move->stand, 4); + } +} + +void sna_anim_nikita_80056C9C(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + + SetAction_8004E22C(work, work->actpack->attack->shoot, 4); + sna_8004E260(work, 0, 4, 0); + + if (*work->field_918_n_bullets == 0) + { + work->field_A38_local_data = 1; + } + } + + if (EndMotion(work)) + { + SetAction_8004E22C(work, SET, 4); + } + + if (work->field_A38_local_data != 0 || time >= 24) + { + if (work->field_A38_local_data != 0 || time == 24) + { + GM_ClearPlayerStatusFlag(PLAYER_ITEM_DISABLE); + } + + if (dword_8009F46C[0] == 0) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } + } +} + +static inline int sna_anim_psg1_helper_80056DDC(void) +{ + return 1; +} + +void sna_anim_psg1_80056DDC(SnaInitWork *work, int time) +{ + MATRIX rotation; + + if (time == 0) + { + work->field_A44.pad = 0; + work->field_A52 = 0; + + work->field_9C8_anim_update_fn_3p = &sna_anim_psg1_helper_80057FD4; + work->field_9CC_anim_update_fn_1p = &sna_anim_psg1_helper_80057FD4; + + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_set_invuln_8004F2A0(work, 0); + + GM_Camera.first_person = 0; + work->field_A20 = sna_anim_psg1_helper_80056DDC(); + + switch (work->field_A26_stance) + { + case 0: + SetAction_8004E22C(work, work->actpack->still->squat, 4); + work->field_A2C.vy = -128; + work->field_A3A = 0; + break; + + case 1: + work->field_A3A = 1; + break; + + case 2: + work->field_A3A = 3; + break; + } + } + + if (work->field_A3A < 3) + { + GM_SetPlayerStatusFlag(PLAYER_MENU_DISABLE); + } + + switch (work->field_A3A) + { + case 0: + if (time == 4) + { + work->field_A3A = 1; + } + + break; + + case 1: + GM_SetPlayerStatusFlag(PLAYER_SQUAT); + work->field_A26_stance = 1; + SetAction_8004E22C(work, work->actpack->trans->crouch, 4); + work->field_A3A = 2; + work->control.skip_flag |= (CTRL_BOTH_CHECK | CTRL_SKIP_TRAP); + break; + + case 2: + if (work->field_798_p_height < 250) + { + work->control.skip_flag &= ~CTRL_BOTH_CHECK; + } + + if (work->m_ctrl.info1.frame == 7) + { + work->control.skip_flag &= ~CTRL_SKIP_TRAP; + } + + if (EndMotion(work)) + { + work->field_A3A = 3; + GM_SetPlayerStatusFlag(PLAYER_GROUND); + work->field_A26_stance = 2; + } + + break; + + case 3: + sub_8004EB74(work); + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_SQUAT); + sna_clear_invuln_8004F2EC(work); + work->control.turn.vx = work->field_A2A; + SetAction_8004E22C(work, SET, 4); + work->field_A3A = 4; + break; + + default: + DG_SetPos(&work->body.objs->objs[4].world); + DG_MovePos(&svector_800AB7E4); + ReadRotMatrix(&rotation); + + work->field_A60.vx = rotation.t[0]; + work->field_A60.vy = rotation.t[1]; + work->field_A60.vz = rotation.t[2]; + + if (GM_CheckPlayerStatusFlag(PLAYER_INVINCIBLE) && (GM_Camera.first_person != 0)) + { + GM_SnakeCamera.position.vy += GV_RandU(16) * work->field_A24_invuln_frames; + } + + break; + } +} + +void sna_anim_stinger_800570C0(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = OP_ShootStinger; + work->field_9CC_anim_update_fn_1p = OP_ShootStinger; + SetAction_8004E22C(work, SET, 4); + work->field_A26_stance = SNA_STANCE_STANDING; + GM_ClearPlayerStatusFlag(PLAYER_MENU_DISABLE | PLAYER_GROUND | + PLAYER_SQUAT); + } +} + +void sna_80057118(SnaInitWork *work, int time) +{ + if (time == 0) + { + if (bakudan_count_8009F42C >= 16) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + GM_SeSet2(0, 0x3f, SE_BUZZER); + return; + } + + work->field_9C8_anim_update_fn_3p = sub_80058644; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, work->actpack->attack->field_5, 4); + } + + sub_8004E9D0(work); +} + +void sna_800571B8(SnaInitWork *work, int time) +{ + SVECTOR *vec1; + TARGET *pGVar3; + SVECTOR *vec2; + + vec2 = &work->field_8EC_vec; + + if (time == 0) + { + if (bakudan_count_8009F42C >= 16) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + work->field_8E8_pTarget->damaged &= ~(0x40); + work->field_8E8_pTarget = NULL; + return; + } + + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, work->actpack->attack->field_6, 4); + pGVar3 = work->field_8E8_pTarget; + DG_PutVector(&svector_800AB7EC, vec2, 1); + GV_SubVec3(&pGVar3->center, vec2, vec2); + + vec2->vx /= 4; + vec2->vy /= 4; + vec2->vz /= 4; + } + + vec1 = &work->control.step; + + if (time < 4) + { + GV_AddVec3(vec1, vec2, vec1); + } + + if (time == 6) + { + GM_SeSet(&work->control.mov, SE_C4_PUT); + NewBakudan(work->field_8E8_pTarget->body, &svector_800AB7F4, 1, 1, work->field_8E8_pTarget); + work->field_914_trigger = 5; + work->field_8E8_pTarget->damaged &= ~(0x40); + work->field_8E8_pTarget = 0; + } + + if (EndMotion(work)) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } +} + +void sna_80057378(SnaInitWork *work, int time) +{ + int action; + int stance; + + stance = work->field_A26_stance; + work->control.turn.vy = dword_800ABBA8; + + if (time == 0) + { + if (stance == 0) + { + action = work->actpack->attack->field_6; + } + else + { + action = work->actpack->attack->field_7; + } + + sna_8004E260(work, action, 4, 0xffff); + } + + if ((stance == SNA_STANCE_STANDING && time == 6) || (stance == SNA_STANCE_SQUAT && time == 14)) + { + work->field_914_trigger = 3; + GM_BombSeg = dword_800ABBB0; + } + + if (work->body.time2 != 0 || work->body.action2 == 0) + { + sna_8004E260(work, 0, 4, 0); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK9); + work->field_9C0 = NULL; + } +} + +void sna_anim_claymore_80057474(SnaInitWork *work, int time) +{ + int i; + int down_count; + TARGET *targets; + + if (time == 0) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + GM_GetTargets(&down_count, &targets); + + if (counter_8009F448 == 8 || down_count == 64) + { + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + GM_SeSet2(0, 63, SE_BUZZER); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + return; + } + + work->field_9C8_anim_update_fn_3p = sna_anim_claymore_helper_80058780; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, SET, 4); + + for (i = 0; i < SNAKEJOINT_NUM; i++) + { + work->adjust[i] = DG_ZeroVector; + } + + GM_ConfigMotionAdjust(&work->body, work->adjust); + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK26); + } + + sub_8004E9D0(work); +} + +static inline int sna_anim_shoot_weapon_helper_helper_80057590(SnaInitWork *work) +{ + if ((work->actpack->attack->setup == 136) || + (work->field_90C_pWeaponFn == &sna_80057A90)) + { + return 0; + } + + return 1; +} + +void sna_anim_shoot_weapon_helper_80057590(SnaInitWork *work, int time) +{ + unsigned short status; + int diff; + + if (!GM_CheckPlayerStatusFlag(PLAYER_ATTACK)) + { + sna_8004E260(work, 0, 4, 0); + sna_start_anim_8004E1F4(work, &sna_anim_idle_8005275C); + return; + } + + if (gSnaMoveDir_800ABBA4 < 0) + { + SetAction_8004E22C(work, work->actpack->still->setup, 4); + return; + } + + status = work->field_9B0_pad_ptr->status; + + if ((sub_8004E458(work->control.turn.vy, gSnaMoveDir_800ABBA4) == 3) && + sna_anim_shoot_weapon_helper_helper_80057590(work)) + { + work->field_A54.prone_bool_thing = gSnaMoveDir_800ABBA4; + sna_start_anim_8004E1F4(work, &sub_80053FAC); + } + else if ((status & PAD_CROSS) || (work->field_920_tbl & 8)) + { + if (work->field_A38_local_data > 0) + { + work->field_A38_local_data--; + } + + if (work->field_A38_local_data == 0) + { + diff = GV_DiffDirAbs(work->control.turn.vy, gSnaMoveDir_800ABBA4); + if (diff > 256) + { + work->field_A38_local_data = (diff / 1024) + 1; + } + else + { + sna_start_anim_8004E1F4(work, &sna_anim_rungun_begin_80056BDC); + } + + work->control.turn.vy = gSnaMoveDir_800ABBA4; + } + } + else + { + sna_8004EF14(work); + } +} + +void sna_anim_rungun_begin_helper_800577B4(SnaInitWork *work, int time) +{ + if (time == 2 && gSnaMoveDir_800ABBA4 >= 0) + { + sna_start_anim_8004E1F4(work, sna_anim_rungun_80056C3C); + } + else + { + if (work->body.is_end) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_shoot_weapon_80056B88); + } + } +} + +void sna_anim_rungun_helper_80057844(SnaInitWork *work, int time) +{ + int angle; + + if (!GM_CheckPlayerStatusFlag(PLAYER_ATTACK)) + { + sna_8004E260(work, 0, 4, 0); + sna_start_anim_8004E1F4(work, sna_anim_run_8005292C); + return; + } + + if (gSnaMoveDir_800ABBA4 < 0 || (!(work->field_920_tbl & 8) && !(work->field_9B0_pad_ptr->status & PAD_CROSS))) + { + if (++work->field_A3A >= 5) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_shoot_weapon_80056B88); + return; + } + } + else + { + work->field_A3A = 0; + } + + if (gSnaMoveDir_800ABBA4 < 0) + { + angle = work->control.turn.vy; + } + else + { + angle = sub_8004E4C0(work, gSnaMoveDir_800ABBA4); + } + + work->control.turn.vy = angle; + sub_8004EA50(work, angle); +} + +void sna_auto_aim_800579A0(SnaInitWork *work) +{ + int unk; + int out_x_copy; + int snake_not_moving; + int out_x; + int out_y; + int diff; + + // loops enemies and finds candidate to aim at, returns angle to auto turn/aim to + // melee also uses this in a different func + GM_GetHomingTarget2(&work->body.objs->objs[6].world, + work->control.rot.vy, // input snake horizontal facing angle + &out_y, &out_x, work->control.map->index, + work->field_890_autoaim_max_dist, + work->field_892_autoaim_min_angle); // min angle to activate auto aim + + // ? + unk = work->adjust[2].vx; + diff = unk - out_x; + if (diff >= 65) + { + out_x = unk - 64; + } + else if (diff < -64) + { + out_x = unk + 64; + } + + // ? + out_x_copy = out_x; + work->adjust[2].vx = out_x; + work->adjust[6].vx = out_x; + snake_not_moving = gSnaMoveDir_800ABBA4 < 0; + work->adjust[7].vx = 3 * out_x_copy / 2; // maybe aim gun/head up/down?? + + if (snake_not_moving && out_y >= 0) // if not moving, set snake turn angle + { + work->control.turn.vy = out_y; + } +} + +void sna_80057A90(SnaInitWork *work, int time) +{ + int bits; + int ammo, magSize; + + if (GM_CheckPlayerStatusFlag(PLAYER_MOVE)) + { + bits = 0x3fe; + } + else + { + bits = 0xffff; + } + + if (time == 0) + { + GM_ConfigMotionAdjust(&work->body, NULL); + NewAnime_8005DDE0(&work->body.objs->objs[4].world); + sna_8004E260(work, work->actpack->attack->reload, 4, bits); + + ammo = GM_MagazineMax; + magSize = GM_Weapons[work->field_91C_weapon_idx]; + + if (ammo > 0 && ammo < magSize) + { + magSize = ammo; + } + + GM_MagazineMax = ammo; + GM_Magazine = magSize; + } + + sna_8004F034(work, bits); + + if (time == 16) + { + GM_SeSet(&work->control.mov, SE_RELOAD); + } + + if (work->body.time2 != 0) + { + if (!(work->field_9B0_pad_ptr->status & PAD_SQUARE)) + { + GM_ClearPlayerStatusFlag(PLAYER_ATTACK); + sna_8004E260(work, 0, 4, 0); + sna_start_anim_8004E1F4(work, &sna_anim_idle_8005275C); + work->field_90C_pWeaponFn = sna_gun_800540D0; + } + else + { + work->field_90C_pWeaponFn = sub_80057BF0; + } + + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + } +} + +void sub_80057BF0(SnaInitWork *work, int time) +{ + int var_s4; + int temp_s3; + int var_s2; + int status; + int release; + int trg; + + var_s4 = GM_CheckPlayerStatusFlag(PLAYER_MOVE) ? 0x3FE : 0xFFFF; + temp_s3 = work->field_920_tbl; + var_s2 = 0; + + if (time == 0) + { + sna_8004E260(work, SET, 4, var_s4); + GM_SeSet(&work->control.mov, SE_READY_WEAPON); + + var_s2 = work->field_A38_local_data; + work->field_924 = 0; + + if (temp_s3 & 2) + { + GM_ConfigMotionAdjust(&work->body, work->adjust); + } + } + + sna_8004F034(work, var_s4); + + if ( (GM_Magazine == 0) && (GM_MagazineMax != 0) && (GM_Weapons[work->field_91C_weapon_idx] > 0) ) + { + work->field_90C_pWeaponFn = &sna_80057A90; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + return; + } + + if ( (temp_s3 & 2) != 0 ) + { + sna_auto_aim_800579A0(work); + } + + status = work->field_9B0_pad_ptr->status; + release = work->field_9B0_pad_ptr->release; + + trg = 1; + + if ( (temp_s3 & 1) ? (status & PAD_SQUARE) : (release & PAD_SQUARE) ) + { + var_s2 = 1; + } + + if ( (GM_GameStatus & STATE_PADRELEASE) || + GM_CheckPlayerStatusFlag(PLAYER_PAD_OFF) || + (DG_UnDrawFrameCount > 0) ) + { + var_s2 = 0; + } + + if ( var_s2 != 0 ) + { + trg |= 2; + } + + work->field_914_trigger = trg; + + switch ( work->field_924 ) + { + case 0: + if ( trg & 2 ) + { + if ( *work->field_918_n_bullets == 0 ) + { + DispEmpty(work); + + if ( temp_s3 & 1 ) + { + work->field_924 = 2; + break; + } + } + + work->field_924 = 1; + sna_8004E260(work, work->actpack->attack->shoot, 2, var_s4); + + if ( temp_s3 & 0x80 ) + { + GM_ClearPlayerStatusFlag(PLAYER_ATTACK | PLAYER_MOVE); + + if ( dword_8009F470 != 0 ) + { + GM_SeSet2(0, 63, SE_BUZZER); + work->field_914_trigger &= ~2; + sna_8004E260(work, 0, 4, 0); + sna_start_anim_8004E1F4(work, &sna_anim_idle_8005275C); + } + else + { + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK3); + GM_SetPlayerStatusFlag(PLAYER_ITEM_DISABLE); + sna_start_anim_8004E1F4(work, &sna_anim_nikita_80056C9C); + } + + return; + } + } + break; + + case 1: + if ( work->body.time2 != 0 ) + { + if ( (temp_s3 & 1) == 0 ) + { + work->field_924 = 0; + sna_8004E260(work, SET, 2, var_s4); + } + else if ( *work->field_918_n_bullets == 0 ) + { + DispEmpty(work); + work->field_924 = 2; + sna_8004E260(work, SET, 2, var_s4); + } + } + break; + + case 2: + work->field_914_trigger = trg & ~2; + break; + } + + if ( (status & 0x80) == 0 ) + { + if ( (++work->field_926 > 4) || (temp_s3 & 1) ) + { + work->field_90C_pWeaponFn = &sna_gun_800540D0; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + + GM_ConfigMotionAdjust(&work->body, NULL); + GM_ClearPlayerStatusFlag(PLAYER_ATTACK); + } + } + else + { + work->field_926 = 0; + } +} + +void sna_anim_psg1_helper_80057FD4(SnaInitWork* work, int time) +{ + int trg; + int ammo; + int mag_size; + short dpad_status; + int val; + short *pShortArr; + int rx, ry; + + if ( work->field_A3A < 3 ) + { + return; + } + + trg = 1; + + if ( work->field_A34 != 0 ) + { + if ( (--work->field_A34 & 0xFF) != 4 ) + { + return; + } + + GM_SeSet(&work->control.mov, SE_RELOAD); + + ammo = GM_Weapons[WP_Rifle]; + mag_size = 5; + + if ( mag_size < ammo ) + { + ammo = mag_size; + } + + GM_MagazineMax = mag_size; + GM_Magazine = ammo; + } + + if ( (GM_Magazine == 0) && (GM_Weapons[WP_Rifle] > 0) ) + { + work->field_A34 = 24; + return; + } + + if ( (work->field_9B0_pad_ptr->release & PAD_SQUARE) && + !(GM_GameStatus & STATE_PADRELEASE) && + !GM_CheckPlayerStatusFlag(PLAYER_PAD_OFF) && + (DG_UnDrawFrameCount == 0) ) + { + trg |= 2; + + if ( *work->field_918_n_bullets == 0 ) + { + DispEmpty(work); + } + } + + work->field_914_trigger = trg; + + dpad_status = work->field_9B0_pad_ptr->status & (PAD_LEFT | PAD_DOWN | PAD_RIGHT | PAD_UP); + if ( dpad_status != 0 ) + { + val = 1; + + if ( work->field_A52 == dpad_status ) + { + if ( !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK15) ) + { + if ( work->field_A44.pad < 10000 ) + { + work->field_A44.pad++; + } + + if ( work->field_A44.pad > 16 ) + { + val = 3; + } + } + else + { + val = 3; + } + } + else + { + work->field_A44.pad = 0; + work->field_A52 = dpad_status; + } + + pShortArr = work->field_9D0; + pShortArr[0] = 1; + pShortArr[3] = 0x78; + pShortArr[4] = 0x78; + pShortArr[5] = val; + pShortArr[6] = val; + pShortArr[7] = val; + pShortArr[8] = val; + + sna_80051DA0(work); + sna_80051FD0(work); + sna_8004EB14(work); + } + else + { + work->field_A52 = 0; + work->field_A44.pad = 0; + } + + rx = work->body.objs->rots[0].vx; + ry = work->body.objs->rots[0].vy; + + if ( GetAction(work) == 0x7A ) + { + work->control.turn.vy += ry - work->field_924; + work->control.turn.vx += rx - work->field_926; + } + + work->field_924 = ry; + work->field_926 = rx; + + if ( GM_TranquilizerTimer > 0 ) + { + if ( (time % 48) == 0 ) + { + NewPadVibration(dword_8009EF20, 2); + work->field_A35_vibtime2 = 2; + } + else if ( (work->field_A35_vibtime2 != 0) && + (--work->field_A35_vibtime2 == 0) ) + { + GM_SeSet2(0, 63, SE_HEARTBEAT); + } + } + else if ( (time & 0x1F) == 0 ) + { + NewPadVibration(dword_8009EF1C, 2); + work->field_A35_vibtime2 = 2; + } + else if ( (work->field_A35_vibtime2 > 0) && + (--work->field_A35_vibtime2 == 0) ) + { + GM_SeSet2(0, 63, SE_HEARTBEAT); + } + + if ( GetAction(work) == SET ) + { + if ( ++work->field_A38_local_data == RIFLE_TEBURE_TIME ) + { + work->field_A38_local_data = 0; + + if ( GM_TranquilizerTimer <= 0 ) + { + GM_StatusEvent &= ~EV_Tranquilizer; + SetAction_8004E22C(work, TEBURE, 4); + } + } + } + else if ( EndMotion(work) || + (GM_StatusEvent & EV_Tranquilizer) ) + { + SetAction_8004E22C(work, SET, 4); + } +} + +STATIC void OP_ShootStinger(SnaInitWork *work, int time) +{ + short *ws; // WATCH_SET *ws + int trg; + + trg = 1; // 1 = WEAPON_TAKE + + if ((work->field_9B0_pad_ptr->release & PAD_SQUARE) && + !(GM_GameStatus & STATE_PADRELEASE) && + !GM_CheckPlayerStatusFlag(PLAYER_PAD_OFF)) + { + if ( *work->field_918_n_bullets > 0 ) + { + trg |= 2; // |= WEAPON_TRIG + } + else + { + GM_SeSet(&work->control.mov, SE_KARASHT); + DispEmpty(work); + } + } + + work->field_914_trigger = trg; + + if ( work->field_9B0_pad_ptr->status & (PAD_LEFT | PAD_DOWN | PAD_RIGHT | PAD_UP) ) + { + ws = work->field_9D0; + ws[0] = 1; // ws->type = WATCH_TYPE_STILL + ws[3] = 0x200; + ws[4] = 0x200; + ws[5] = 0x20; + ws[6] = 0x20; + ws[7] = 0x20; + ws[8] = 0x20; + + sna_80051DA0(work); + sna_80051FD0(work); + sna_8004EB14(work); + } +} + +void sna_anim_grenade_80058470(SnaInitWork *work, int time) +{ + int bits; + int uVar2; + int res; + + if ( GM_CheckPlayerStatusFlag(PLAYER_MOVE) ) + { + bits = 0x3fe; + } + else + { + bits = 0xffff; + } + + if (time == 0) + { + sna_8004E260(work, SET, 4, bits); + work->field_924 = 0; + GM_SeSet(&(work->control).mov, SE_PINNUKI); + } + + sna_8004F034(work, bits); + res = 1; + + uVar2 = work->m_ctrl.info2.frame; + + switch (work->field_924) + { + case 0: + if (work->body.time2 != 0) + { + sna_8004E260(work, work->actpack->still->stand, 1, bits); + work->field_924 = 1; + } + + break; + case 1: + if (!(work->field_9B0_pad_ptr->status & PAD_SQUARE) && (DG_UnDrawFrameCount == 0)) + { + sna_8004E260(work, work->actpack->attack->shoot, 1, bits); + work->field_924 = 2; + } + + break; + case 2: + if (uVar2 == 7) + { + res = 3; + work->field_924 = 3; + } + + break; + case 3: + res = 0; + + if ((uVar2 > 11) && (work->field_9B0_pad_ptr->status & PAD_SQUARE)) + { + work->field_910 = 0; + return; + } + + if (work->body.time2 != 0) + { + sna_8004E260(work, 0, 4, 0); + GM_ClearPlayerStatusFlag(PLAYER_ATTACK | PLAYER_ITEM_DISABLE | PLAYER_WEAPON_DISABLE); + } + + break; + } + + work->field_914_trigger = res; +} + +void sub_80058644(SnaInitWork *work, int time) +{ + int iVar1; + + work->field_914_trigger = 1; + + if (time >= 10) + { + iVar1 = (int)work->field_A38_local_data; + if (iVar1 == 0) + { + sub_8004EEB0(work); + } + + if ((work->field_9B0_pad_ptr->status & PAD_SQUARE) == 0 && iVar1 == 0 && DG_UnDrawFrameCount == 0) + { + iVar1 = sub_8004E5E8(work, 0x80); + if (iVar1 == 1) + { + SetAction_8004E22C(work, work->actpack->attack->field_8, 1); + work->field_A38_local_data = 1; + work->field_914_trigger |= 2; + } + else if (iVar1 == 2) + { + GM_SeSet2(0, 0x3f, SE_BUZZER); + } + } + + if ((iVar1 == 1 && EndMotion(work)) || iVar1 == 2) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + sna_start_anim_8004E1F4(work, &sna_anim_idle_8005275C); + } + + work->field_A2C.vy = -100; + } +} + +void sna_anim_claymore_helper_80058780(SnaInitWork *work, int time) +{ + int var_s1; + int x, y; + int w; + POLY_GT4 *pPoly; + int i, j; + + work->field_914_trigger = 1; // 1 = WEAPON_TAKE + + if ( time < 10 ) + { + return; + } + + var_s1 = work->field_A38_local_data; + + if ( var_s1 == 0 ) + { + sub_8004EEB0(work); + + DG_GroupPrim(work->field_92C, DG_CurrentGroupID); + DG_VisiblePrim(work->field_92C); + DG_PutPrim(work->field_92C); + + if ( !(work->field_9B0_pad_ptr->status & PAD_SQUARE) && (DG_UnDrawFrameCount == 0) ) + { + var_s1 = sub_8004E5E8(work, 0x40); + + if ( var_s1 == 1 ) + { + SetAction_8004E22C(work, work->actpack->attack->shoot, 1); + work->field_A38_local_data = 1; + work->field_914_trigger |= 2; // 2 = WEAPON_TRIG + } + else if ( var_s1 == 2 ) + { + GM_SeSet2(0, 63, SE_BUZZER); + } + } + } + + y = GV_Time & 63; + + if ( y > 32 ) + { + y = 64 - y; + } + + x = work->field_928->off_x; + w = x + 63; + y += work->field_928->off_y; + + pPoly = work->field_92C->packs[GV_Clock]; + + for ( i = 0; i < 2; i++ ) + { + setUV4(pPoly, x, y, w, y, x, y, w, y); + pPoly++; + } + + if ( ((var_s1 == 1) && (EndMotion(work))) || (var_s1 == 2) ) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK26 | SNA_FLAG1_UNK3); + DG_InvisiblePrim(work->field_92C); + sna_start_anim_8004E1F4(work, &sna_anim_idle_8005275C); + GM_ConfigMotionAdjust(&work->body, NULL); + + for ( j = 0; j < 16; j++ ) + { + work->adjust[j] = DG_ZeroVector; + } + } + + work->field_A2C.vy = -100; +} + +void sna_anim_throw_800589C8(SnaInitWork *work, int time) +{ + Target_Data *pVec; // $s3 + int bClear; // $s4 + ACTPACK *actpack; // $v1 + int action_flag; // $s0 + TARGET *field_8E8_pTarget; // $s0 + SVECTOR* pTmp; + + if ( time == 0) + { + pVec = &stru_8009EFE4[1]; + bClear = 0; + actpack = work->actpack; + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + action_flag = actpack->attack->shoot; + if ( !sna_8004FDE8(work, pVec) ) + { + action_flag = work->actpack->attack->field_5; + pVec = &stru_8009EFE4[2]; + + if ( !sna_8004FDE8(work, pVec) ) + { + bClear = 1; + work->field_A38_local_data = 1; + action_flag = work->actpack->still->setup; + } + } + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(work, action_flag, 4); + + work->field_8FC = work->control.rot; + work->field_8FC.vy += 2048; + + pTmp = &work->field_8EC_vec; + if ( !bClear ) + { + field_8E8_pTarget = work->field_8E8_pTarget; + DG_PutVector(&pVec->field_0, pTmp, 1); + GV_SubVec3(&field_8E8_pTarget->center, pTmp, pTmp); + pTmp->vx /= 4; + pTmp->vy /= 4; + pTmp->vz /= 4; + } + else + { + + *pTmp = DG_ZeroVector; + } + } + + if ( time < 4 ) + { + GV_AddVec3( + &work->control.step, + &work->field_8EC_vec, + &work->control.step); + } + + if ( work->body.is_end || (work->field_A38_local_data == 1 && time == 4) ) + { + sna_sub_8004E41C(work, 2); + sna_clear_flags1_8004E308(work, 4); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } + +} + +void sna_anim_punch_80058C10(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_anim_punch_helper_800591F4; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(work, work->actpack->attack->punch, 1); + work->field_A38_local_data = 0; + sna_set_flags1_8004E2F4(work, (SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6)); + } +} + +void sna_anim_chokethrow_begin2_80058C80(SnaInitWork *work, int time) +{ + int bClear; // $s0 + TARGET *field_8E8_pTarget; // $v1 + int action_flag; // $a1 + SVECTOR *p; + + if (time == 0) + { + work->field_904_frames_last_choke = 0; + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + bClear = 0; + work->field_A54.choke_count = 0; + + if (!sna_8004FDE8(work, &stru_8009EFE4[3])) + { + bClear = 1; + work->field_A38_local_data = 1; + } + + p = &work->field_8EC_vec; + if (!bClear) + { + + field_8E8_pTarget = work->field_8E8_pTarget; + + field_8E8_pTarget->faint--; + + DG_SetPos2(&field_8E8_pTarget->center, &work->control.rot); + + DG_PutVector(&svector_800AB7FC, p, 1); + DG_SetPos2(&work->control.mov, &work->control.rot); + GV_SubVec3(p, &work->control.mov, p); + p->vx /= 4; + p->vy /= 4; + p->vz /= 4; + action_flag = work->actpack->special2->field_0; + } + else + { + work->field_8EC_vec = DG_ZeroVector; + action_flag = work->actpack->still->setup; + } + SetAction_8004E22C(work, action_flag, 4); + } + + if (time < 4) + { + ++work->field_904_frames_last_choke; + GV_AddVec3(&work->control.step, &work->field_8EC_vec, + &work->control.step); + } + else + { + if (work->field_A38_local_data) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } + else + { + work->field_89C_pTarget->size.vx = 150; + sna_start_anim_8004E1F4(work, sna_anim_choke_80058E88); + } + } +} + +void sna_anim_choke_80058E88(SnaInitWork *work, int time) +{ + short action_flag; // $s0 + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_anim_choke_helper_8005951C; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + + action_flag = work->actpack->special2->field_0; + SetAction_8004E22C(work, action_flag, 4); + work->field_8E8_pTarget->a_mode = action_flag; + } +} + +void sna_anim_choke_rechoke_80058EF4(SnaInitWork *work, int time) +{ + short bVar1; + + if (time == 0 || (work->field_9B0_pad_ptr->press & PAD_SQUARE) != 0) + { + GetAction(work) = -1; + work->field_9C8_anim_update_fn_3p = sna_anim_choke_rechoke_helper_8005961C; + work->field_904_frames_last_choke = 0; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + bVar1 = work->actpack->attack->field_6; + SetAction_8004E22C(work, bVar1, 4); + work->field_8E8_pTarget->a_mode = bVar1; + } +} + +void sna_anim_choke_kill_80058F88(SnaInitWork *work, int time) +{ + TARGET *field_8E8_pTarget; // $s0 + int action_flag; // $s1 + + if (time == 0) + { + field_8E8_pTarget = work->field_8E8_pTarget; + action_flag = work->actpack->attack->field_7; + + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + + SetAction_8004E22C(work, action_flag, 4); + + field_8E8_pTarget->a_mode = action_flag; + field_8E8_pTarget->damage = 5; + field_8E8_pTarget->faint--; + work->field_A54.choke_count = 0; + } + + if (work->body.is_end) + { + sna_sub_8004E41C(work, 2); + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + } +} + +void sna_anim_choke_drag_80059054(SnaInitWork *work, int time) +{ + int bVar2; + int iVar3; + + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + bVar2 = work->actpack->special2->field_1; + GM_SetPlayerStatusFlag(PLAYER_MOVE); + SetAction_8004E22C(work, bVar2, 4); + work->field_8E8_pTarget->a_mode = bVar2; + } + + iVar3 = work->field_904_frames_last_choke + 1; + work->field_904_frames_last_choke = iVar3; + + if ((gSnaMoveDir_800ABBA4 < 0 || ((work->field_9B0_pad_ptr->status & PAD_SQUARE) == 0))) + { + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_choke_80058E88); + } + else + { + if (!((work->field_8E8_pTarget->vital > 0) && + ((iVar3 < 0x5a && work->field_8E8_pTarget->captured != 0)))) + { + + GM_ClearPlayerStatusFlag(PLAYER_MOVE); + sna_start_anim_8004E1F4(work, sna_anim_choke_80058E88); + return; + } + else + { + work->control.turn.vy = gSnaMoveDir_800ABBA4 + 0x800; + } + } +} + +void sna_anim_choke_hold_80059154(SnaInitWork *work, int time) +{ + if (time == 0) + { + work->field_9C8_anim_update_fn_3p = sna_fn_nothing_80053B80; + work->field_9CC_anim_update_fn_1p = sna_fn_nothing_80053B80; + SetAction_8004E22C(work, work->actpack->special2->field_6, 4); + } + + if (EndMotion(work)) + { + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + } +} + +void sub_800591BC(SnaInitWork *work) +{ + sna_start_anim_8004E1F4(work, sna_anim_idle_8005275C); + sna_clear_flags1_8004E308(work, (SNA_FLAG1_UNK3 | SNA_FLAG1_UNK5 | SNA_FLAG1_UNK6)); +} + +void sna_anim_punch_helper_800591F4(SnaInitWork *work, int time) +{ + int x, y; + int sound; + + if ( time == 0 ) + { + GM_GetHomingTarget2(&work->body.objs->objs[6].world, + work->control.rot.vy, + &x, &y, work->control.map->index, + 2000, 1024); + + if ( x >= 0 ) + { + work->control.turn.vy = x; + } + } + + if ( time == 4 ) + { + sub_8004FCB8(work, &punch_info[0], 3); + + sound = SE_PUNCH_SWING; + if ( GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) ) + { + sound = 182; + } + + GM_SeSet(&work->control.mov, sound); + GM_SetNoise(time, 2, &work->control.mov); + } + + if ( time == 6 ) + { + if ( !(work->field_A38_local_data & 0x20) ) + { + sub_800591BC(work); + return; + } + + work->field_A38_local_data &= ~0x20; + } + + if ( time == 11 ) + { + sub_8004FCB8(work, &punch_info[1], 3); + + sound = SE_PUNCH_SWING; + if ( GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) ) + { + sound = 182; + } + + GM_SeSet(&work->control.mov, sound); + GM_SetNoise(4, 2, &work->control.mov); + } + + if ( time == 13 ) + { + if ( !(work->field_A38_local_data & 0x20) ) + { + sub_800591BC(work); + return; + } + + work->field_A38_local_data = 0; + } + + if ( time == 22 ) + { + sub_8004FCB8(work, &punch_info[2], 4); + + sound = SE_KICK_SWING; + if ( GM_CheckPlayerStatusFlag(PLAYER_IN_THE_WATER) ) + { + sound = 182; + } + + GM_SeSet(&work->control.mov, sound); + GM_SetNoise(4, 2, &work->control.mov); + } + + if ( EndMotion(work) ) + { + sub_800591BC(work); + return; + } + + work->field_A38_local_data |= work->field_9B0_pad_ptr->press; + + if ( (work->field_9B0_pad_ptr->press & PAD_CROSS) != 0 ) + { + sub_800591BC(work); + } +} + +void sna_anim_choke_helper_8005951C(SnaInitWork *work, int time) +{ + int sinceLastChoke; + short press; + short status; + + sinceLastChoke = ++work->field_904_frames_last_choke; + + if (((work->field_8E8_pTarget->vital < 1 || sinceLastChoke >= 90) || + work->field_8E8_pTarget->captured == 0)) + { + sna_sub_8004E41C(work, 2); + sna_start_anim_8004E1F4(work, sna_anim_choke_hold_80059154); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + return; + } + + press = work->field_9B0_pad_ptr->press; + status = work->field_9B0_pad_ptr->status; + + if ((press & PAD_SQUARE) != 0) + { + sna_start_anim_8004E1F4(work, sna_anim_choke_rechoke_80058EF4); + } + else if ((status & PAD_SQUARE) == 0) + { + // A38 is snake stop movement frames, counts down + if (++work->field_A38_local_data >= 9) + { + sna_sub_8004E41C(work, 2); + sna_start_anim_8004E1F4(work, sna_anim_choke_hold_80059154); + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK3); + } + } + else + { + work->field_A38_local_data = 0; + if (gSnaMoveDir_800ABBA4 >= 0) + { + sna_start_anim_8004E1F4(work, sna_anim_choke_drag_80059054); + } + } +} + +void sna_anim_choke_rechoke_helper_8005961C(SnaInitWork *work, int time) +{ + if (time == 0 || (work->field_9B0_pad_ptr->press & PAD_SQUARE) != 0) + { + work->field_8E8_pTarget->faint--; + + if (++work->field_A54.choke_count >= 10) // feels good + { + // TODO: fix data when vibrate is figured out + NewPadVibration(dword_8009EF24, 1); + NewPadVibration(dword_8009EF2C, 2); + sna_start_anim_8004E1F4(work, sna_anim_choke_kill_80058F88); + return; + } + } + + if (work->body.is_end == 0 && work->field_8E8_pTarget->captured != 0) + { + return; + } + + sna_start_anim_8004E1F4(work, sna_anim_choke_80058E88); +} + +static inline int sna_init_main_logic_helper_helper_helper_800596FC(RECT *a, RECT *b) +{ + int test1; + int test2; + + test1 = a->x * b->x + a->y * b->y; + test2 = test1; + + if (test1 < 0) + { + return 1; + } + + if (test2 > a->x * a->x + a->y * a->y) + { + return 1; + } + + return 0; +} + +static inline int sna_init_main_logic_helper_helper_800596FC(SnaInitWork *work) +{ + HZD_SEG *local_40[2]; + RECT rect[2]; // Maybe vecs + CONTROL *pCtrl; + SVECTOR *pPosition; + HZD_SEG *seg; + int iVar9; + int iVar10; + int iVar17; + int iVar19; + int iVar11; + int i; + int result; + char *pChk; + + result = -1; + pCtrl = &work->control; + pPosition = &pCtrl->mov; + + if (pCtrl->n_touches == 1) + { + if (!(pCtrl->is_edge[0] & 0x40)) + { + seg = pCtrl->segs[0]; + + rect[1].x = pPosition->vx - seg->p1.x; + rect[1].y = pPosition->vz - seg->p1.z; + rect[0].x = seg->p2.x - seg->p1.x; + rect[0].y = seg->p2.z - seg->p1.z; + + if (!sna_init_main_logic_helper_helper_helper_800596FC(&rect[0], &rect[1])) + { + result = 0; + work->field_79C = 1; + } + } + } + else + { + pChk = work->control.is_edge; + iVar9 = 0; + + for (i = 0; i < 2; i++, pChk++) + { + seg = pCtrl->segs[i]; + local_40[i] = seg; + + if (*pChk & 0x40) + { + continue; + } + + rect[1].x = pPosition->vx - seg->p1.x; + rect[1].y = pPosition->vz - seg->p1.z; + rect[0].x = seg->p2.x - seg->p1.x; + rect[0].y = seg->p2.z - seg->p1.z; + + if (!sna_init_main_logic_helper_helper_helper_800596FC(&rect[0], &rect[1])) + { + result = i; + iVar9++; + work->field_79C = 1; + } + } + + if (iVar9 >= 2) + { + iVar11 = GV_VecLen3(&pCtrl->vecs[0]); + iVar10 = GV_VecLen3(&pCtrl->vecs[1]); + iVar17 = GV_VecDir2(&pCtrl->vecs[0]); + iVar19 = GV_VecDir2(&pCtrl->vecs[1]); + + if (GV_DiffDirAbs(iVar17, iVar19) < 512) + { + work->field_79C = 1; + result = iVar11; + return result >= iVar10; + } + + work->field_79C = 2; + result = 2; + } + } + + return result; +} + +static inline void sna_init_main_logic_helper_800596FC(SnaInitWork *work) +{ + int dir; + signed char cVar4; + int iVar9; + int iVar10; + int result; + int diff; + + gSnaMoveDir_800ABBA4 = work->field_9B0_pad_ptr->dir; + pVec_800ABBC8 = pVec_800ABBCC; + dword_800ABBAC = 0; + dword_800ABBC4 = 0; + dword_800ABBB0 = 0; + pVec_800ABBCC = 0; + dword_800ABBA8 = -1; + dword_800ABBD0 = -1; + + dir = gSnaMoveDir_800ABBA4; + + work->field_79C = 0; + cVar4 = work->control.n_touches; + + if (cVar4 <= 0) + { + return; + } + + dword_800ABBAC = &work->control.vecs[0]; + dword_800ABBB4 = &work->control.is_edge[0]; + dword_800ABBA8 = GV_VecDir2(&work->control.vecs[0]); + + if ((*dword_800ABBB4 & 0x40) == 0) + { + pVec_800ABBCC = dword_800ABBAC; + dword_800ABBB0 = work->control.segs[0]; + dword_800ABBD0 = dword_800ABBA8; + } + + if (dir < 0) + { + return; + } + + if (cVar4 != 1) + { + iVar9 = GV_VecDir2(&work->control.vecs[1]); + iVar10 = GV_DiffDirAbs(dir, dword_800ABBA8); + + if (GV_DiffDirAbs(dir, iVar9) < iVar10) + { + dword_800ABBAC = &work->control.vecs[1]; + dword_800ABBB4 = &work->control.is_edge[1]; + dword_800ABBA8 = GV_VecDir2(&work->control.vecs[1]); + + if ((*dword_800ABBB4 & 0x40) == 0) + { + pVec_800ABBCC = dword_800ABBAC; + dword_800ABBB0 = work->control.segs[1]; + dword_800ABBD0 = dword_800ABBA8; + } + } + } + + result = sna_init_main_logic_helper_helper_800596FC(work); + + if (result == -1) + { + pVec_800ABBCC = 0; + dword_800ABBD0 = -1; + return; + } + + if (result != 2) + { + pVec_800ABBCC = &work->control.vecs[result]; + dword_800ABBD0 = GV_VecDir2(pVec_800ABBCC); + dword_800ABBB0 = work->control.segs[result]; + } + + diff = GV_DiffDirS(dir, dword_800ABBD0); + + if ((diff >= -896) && (diff <= 896)) + { + dword_800ABBC4 = 1; + + if (diff >= 0) + { + if (diff > 256) + { + dword_800ABBC4 = 4; + } + } + else if (diff < -256) + { + dword_800ABBC4 = 2; + } + } +} + +static inline void sna_init_main_logic_helper2_800596FC(SnaInitWork *work) +{ + int iVar9; + int iVar8; + + if (!GM_CheckPlayerStatusFlag(PLAYER_GROUND)) + { + work->field_A2A = 0; + } + else if (!flr_800ABBB8[0] || (flr_800ABBB8[0]->b1.h == 2)) + { + work->field_A2A = 0; + + if (!GM_CheckPlayerStatusFlag(PLAYER_WATCH)) + { + work->control.turn.vx = 0; + } + } + else + { + iVar8 = sub_8004E930(work, 750); + iVar9 = iVar8 - work->field_A2A; + + if (iVar9 >= 0) + { + if (iVar9 < 16) + { + goto inside; + } + } + else + { + iVar9 = work->field_A2A - iVar8; + + if (iVar9 < 16) + { +inside: + iVar8 = work->field_A2A; + } + } + + work->field_A2A = iVar8; + + if (!GM_CheckPlayerStatusFlag(PLAYER_WATCH)) + { + (work->control).turn.vx = iVar8; + } + } +} + +static inline void sna_init_main_logic_helper3_800596FC(SnaInitWork *work) +{ + int iVar9; + int sVar5; + int iVar8; + int uVar13; + int new_var; + + int msgs; + GV_MSG *pMsg; + int hash; + TARGET *pTarget; + + if (!sna_check_flags1_8004E31C(work, 0x200000)) + { + return; + } + + uVar13 = work->field_9E4.field_9EC_flags3; + + if (work->field_9E4.field_9F0 != 0) + { + work->control.s_sphere = 450; + work->control.r_sphere = 450; + work->field_A28 = 450; + + if ((uVar13 & 0x4002) == 0) + { + new_var = work->field_9E4.field_9EE; + iVar9 = work->control.levels[0]; + iVar8 = (unsigned short)work->body.height - (iVar9 - new_var); + work->control.height = iVar8; + iVar8 = iVar8 + iVar9; + work->control.mov.vy = iVar8; + work->body.objs->world.t[1] = work->control.mov.vy; + DG_GetLightMatrix2(&work->control.mov, work->light); + } + else if ((uVar13 & 0x100) == 0) + { + work->control.height = dword_8009EECC[work->field_A26_stance]; + } + + work->body.height = work->control.height; + sVar5 = work->field_9E4.field_9E8; + + if (sVar5 >= 0) + { + if (GV_DiffDirAbs(sVar5, work->control.rot.vy) > 1536) + { + work->control.rot.vy = sVar5; + } + + work->control.turn.vy = sVar5; + } + + if ((uVar13 & 0x80) == 0) + { + GM_ConfigMotionControl( + &work->body, + &work->m_ctrl, + OAR_SNAKE, + work->m_segs1, + work->m_segs2, + &work->control, + work->rots + ); + + work->body.action = -1; + } + + if ((uVar13 & 0x800) != 0) + { + SetAction_8004E22C(work, sna_8004EAA8(work, work->field_A26_stance), 0); + } + + work->field_9E4.field_9F0 = 0; + } + + msgs = work->field_7A0_msg_count; + pMsg = work->field_7A4_msgs; + + while (msgs > 0) + { + hash = pMsg->message[0]; + + if ((hash == HASH_MOTION) || (hash == HASH_RUN_MOVE) || (hash == HASH_STANCE) || (hash == HASH_GO_MOTION)) + { + pTarget = work->field_89C_pTarget; + + if (pTarget->damage > 0) + { + pTarget->vital += pTarget->damage; + sna_act_unk2_80051170(work->field_89C_pTarget); + } + + sna_set_invuln_8004F2A0(work, 0); + goto exit; + } + + msgs--; + pMsg++; + } + + GM_ClearPlayerStatusFlag(PLAYER_ACT_ONLY); + +exit: + sna_clear_flags1_8004E308(work, 0x8200000); +} + +static inline int sna_init_main_logic_helper4_helper_800596FC(SnaInitWork *work) +{ + if (sna_check_flags1_8004E31C(work, 1) || + GM_CheckPlayerStatusFlag(PLAYER_WATCH | PLAYER_INTRUDE)) + { + return 0; + } + + return 1; +} + +static inline int sna_init_main_logic_helper4_helper2_800596FC(SnaInitWork *work) +{ + if (sna_check_flags1_8004E31C(work, 2) || + GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) || + (work->field_920_tbl & 0x200)) + { + return 0; + } + + return 1; +} + +static inline int sna_init_main_logic_helper4_helper3_800596FC(int f3e) +{ + if ((f3e == 1) || (f3e == 3) || (f3e == 8)) + { + return 0; + } + + return 1; +} + +static inline int sna_init_main_logic_helper4_helper4_800596FC(SnaInitWork *work) +{ + if (sna_check_flags1_8004E31C(work, 2) || + GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) || + (work->field_920_tbl & 0x200)) + { + return 0; + } + + return 1; +} + +static inline void sna_init_main_logic_helper4_800596FC(SnaInitWork *work) +{ + int iframes; + TARGET *pTarget; + int iVar8; + int bVar6; + int iVar9; + + if (GM_CheckPlayerStatusFlag(PLAYER_INVINCIBLE)) + { + iframes = work->field_A24_invuln_frames; + + if (iframes != 0) + { + if (--iframes > 0) + { + if (sna_init_main_logic_helper4_helper_800596FC(work)) + { + if ((iframes & 3) != 0) + { + DG_VisibleObjs(work->body.objs); + } + else + { + DG_InvisibleObjs(work->body.objs); + } + } + + if ((GM_Camera.first_person != 0) && + (!sna_init_main_logic_helper4_helper2_800596FC(work) || (work->field_9AC & 0x2)) && + !sna_check_flags1_8004E31C(work, 1)) + { + GM_SnakeCamera.position.vy += GV_RandS(16) * iframes; + } + } + else + { + sna_clear_invuln_8004F2EC(work); + } + + work->field_A24_invuln_frames = iframes; + } + else + { + if (GM_CheckPlayerStatusFlag(PLAYER_NOT_SIGHT)) + { + DG_VisibleObjs(work->body.objs); + } + + if (GM_Camera.first_person != 0) + { + DG_InvisibleObjs(work->body.objs); + } + else + { + DG_VisibleObjs(work->body.objs); + } + } + } + else + { + pTarget = work->field_89C_pTarget; + + if (pTarget->damaged & 0x6) + { + iVar8 = pTarget->a_mode; + bVar6 = sna_init_main_logic_helper4_helper3_800596FC(iVar8); + + if (sna_init_main_logic_helper4_helper4_800596FC(work) && (bVar6 || !(work->field_9AC & 0x2))) + { + work->control.skip_flag &= ~CTRL_SKIP_TRAP; + GM_ClearPlayerStatusFlag(PLAYER_SQUAT | PLAYER_CAUTION | PLAYER_ITEM_DISABLE); + sna_clear_flags1_8004E308(work, 0x30); + sna_clear_flags1_8004E308(work, 0x100); + work->field_9C0 = 0; + work->field_A28 = 0x1c2; + + if ((work->field_920_tbl & 0x4) && + GM_CheckPlayerStatusFlag(PLAYER_ATTACK) && + (work->field_924 != 3)) + { + work->field_914_trigger |= 0x9; + } + + sub_8004F454(work); + + if (work->field_90C_pWeaponFn == &sna_80057A90) + { + work->field_90C_pWeaponFn = &sna_gun_800540D0; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + } + + GM_SetPlayerStatusFlag(PLAYER_DAMAGED | PLAYER_WEAPON_DISABLE); + + if (!bVar6) + { + if (GM_CheckPlayerStatusFlag(PLAYER_DOWNED)) + { + sna_set_flags1_8004E2F4(work, 0x4); + sna_start_anim_8004E1F4(work, &sna_anim_knockdown_shot_80054B50); + } + else if (GM_CheckPlayerStatusFlag(PLAYER_CB_BOX)) + { + sna_set_flags1_8004E2F4(work, 0x20); + sna_start_anim_8004E1F4(work, &sna_act_helper2_helper7_80054648); + } + else + { + sna_start_anim_8004E1F4(work, &sna_anim_shot_flinch_800544E0); + } + + GM_SeSet2(0, 63, SE_PLAYER_DAMAGE1); + + NewPadVibration(dword_8009EED4, 1); + NewPadVibration(dword_8009EED8, 2); + } + else + { + work->field_A2C = pTarget->force; + work->field_A2C.vx /= 2; + work->field_A2C.vy /= 4; + work->field_A2C.vz /= 2; + + if (GM_CheckPlayerStatusFlag(PLAYER_GROUND | PLAYER_DOWNED)) + { + if (!GM_CheckPlayerStatusFlag(PLAYER_DOWNED)) + { + work->field_A54.knockdown_thing = 1; + } + + sna_start_anim_8004E1F4(work, &sna_anim_knockdown_shot_tank_80054C08); + } + else + { + sna_start_anim_8004E1F4(work, &sna_anim_knockdown_80054710); + } + + if (work->field_9AC & 0x1) + { + sub_8004F204(work); + } + + GM_SeSet2(0, 63, SE_PLAYER_DAMAGE2); + + NewPadVibration(dword_8009EEE0, 1); + NewPadVibration(dword_8009EEE4, 2); + + sna_8004EC00(work); + sna_set_flags1_8004E2F4(work, 0x4); + } + + sna_act_helper2_helper4_8004F090(work, iVar8); + } + else + { + sna_set_invuln_8004F2A0(work, 0x20); + + if (pTarget->damage > 0) + { + sna_act_helper2_helper4_8004F090(work, pTarget->a_mode); + } + + switch (iVar8) + { + case 1: + case 3: + GM_SeSet2(0, 63, SE_PLAYER_DAMAGE1); + NewPadVibration(dword_8009EED4, 1); + NewPadVibration(dword_8009EED8, 2); + break; + + default: + GM_SeSet2(0, 63, SE_PLAYER_DAMAGE2); + NewPadVibration(dword_8009EEE0, 1); + NewPadVibration(dword_8009EEE4, 2); + break; + } + + if ((pTarget->vital < 1) && ((GM_GameOverTimer > -2) && (GM_GameOverTimer < 1))) + { + if (GM_CheckPlayerStatusFlag(PLAYER_INTRUDE | PLAYER_GROUND)) + { + iVar9 = 0x7d; + } + else + { + iVar9 = 0x80; + work->control.rot.vx = 0; + work->control.turn.vx = 0; + } + + if (GM_CheckPlayerStatusFlag(PLAYER_DOWNED)) + { + if (work->field_A54.choke_count == 1) + { + iVar9 = 0x7e; + } + else + { + iVar9 = 0x7f; + } + } + + GM_SeSet2(0, 63, SE_PLAYEROUT); + sna_8004F8E4(work, iVar9); + } + } + + sna_act_unk2_80051170(pTarget); + + if (GM_Camera.first_person != 0) + { + work->control.turn.vy += GV_RandS(512); + } + } + else if (pTarget->damaged & 0x80) + { + sna_act_unk2_80051170(pTarget); + } + } +} + +static inline void sna_init_main_logic_helper5_800596FC(SnaInitWork *work) +{ + if (GM_CheckPlayerStatusFlag(PLAYER_GAME_OVER)) + { + if (((work->body.action == 128) && (work->m_ctrl.info1.frame == 49)) || + ((work->body.action == 127) && (work->m_ctrl.info1.frame == 72))) + { + NewPadVibration(dword_8009EEF0, 1); + NewPadVibration(dword_8009EEF4, 2); + + if (work->body.action == 128) + { + GM_SeSetMode(&work->control.mov, SE_HIT_FLOOR, GM_SEMODE_BOMB); + } + } + + if ((work->field_7A0_msg_count == 0) && + !GM_CheckPlayerStatusFlag(PLAYER_ACT_ONLY) && + (GM_StreamStatus() != 2)) + { + GM_StreamPlayStop(); + sna_set_flags1_8004E2F4(work, 0x40000); + GM_GameOverTimer = 1; + } + } + else if (GM_GameOverTimer > 0) + { + GM_GameStatus |= STATE_PADRELEASE; + + if (GM_GameOverTimer == 2) + { + sna_set_flags1_8004E2F4(work, 0x80000); + GM_SetPlayerStatusFlag(PLAYER_GAME_OVER); + sna_set_invuln_8004F2A0(work, 0); + GM_GameOverTimer = -1; + } + } +} + +static inline int sna_init_main_logic_helper6_helper_800596FC(SnaInitWork *work, int flags) +{ + if (dword_8009F46C[0] != 0 || + sna_check_flags1_8004E31C(work, 0x4) || + GM_CheckPlayerStatusFlag(flags) || + work->field_9CC_anim_update_fn_1p == &sna_fn_nothing_80053B80) + { + return 0; + } + + return 1; +} + +static inline int sna_init_main_logic_helper6_helper2_800596FC(SnaInitWork *work) +{ + if ((work->field_920_tbl & 0x200) || + (work->field_9AC & 0x2) || + sna_check_flags1_8004E31C(work, 0x4) || + sna_sub_8004E358(work, 0x100)) + { + return 0; + } + + return 1; +} + +static inline int sna_init_main_logic_helper6_helper3_800596FC(SnaInitWork *work) +{ + if ((work->field_920_tbl & 0x200) || + (work->field_9AC & 0x2) || + sna_check_flags1_8004E31C(work, 0x4) || + sna_sub_8004E358(work, 0x100)) + { + return 0; + } + + return 1; +} + +static inline void sna_init_main_logic_helper6_800596FC(SnaInitWork *work) +{ + if ((work->field_9B0_pad_ptr->press & PAD_TRIANGLE) && + sna_init_main_logic_helper6_helper_800596FC(work, PLAYER_WATCH | PLAYER_ATTACK)) + { + sub_8004EB74(work); + GM_SetPlayerStatusFlag(PLAYER_NORMAL_WATCH); + } +} + +void sna_init_main_logic_800596FC(SnaInitWork *work) +{ + int vy; + GV_PAD *pPad; + unsigned char *pAxis; + int i; + int frame; + int hzd_count; + unsigned short *pHzdVal; + + work->field_798_p_height = work->control.height; + + vy = work->body.height; + work->control.height = vy; + + if ( vy < 500 ) + { + vy = 500; + } + + if ( vy > 700 ) + { + vy = 700; + } + + work->field_89C_pTarget->size.vy = vy; + + if ( (work->field_A2C.vy < 0) && (work->control.grounded != 0) ) + { + work->field_A2C.vy = 0; + } + + work->field_A2C.vy -= 32; + + if ( GM_CheckPlayerStatusFlag(PLAYER_NOT_PLAYABLE) ) + { + return; + } + + if ( GM_CheckPlayerStatusFlag(PLAYER_PAD_OFF) ) + { + work->field_9B0_pad_ptr = &GV_PadData_8009F0C4; + } + + work->field_914_trigger = 0; + work->control.turn.vz = 0; + + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK15); + + pPad = work->field_9B0_pad_ptr; + + if ( pPad->analog != 0 && (pPad->status & (PAD_LEFT | PAD_DOWN | PAD_RIGHT | PAD_UP)) ) + { + pAxis = &pPad->left_dx; + + for ( i = 0; i < 2; i++, pAxis++ ) + { + if ( (*pAxis < 64) || (*pAxis > 192) ) + { + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK15); + break; + } + } + } + + sna_init_main_logic_helper_800596FC(work); + sna_init_main_logic_helper2_800596FC(work); + sna_init_main_logic_helper3_800596FC(work); + sna_init_main_logic_helper4_800596FC(work); + + if ( GM_StatusEvent & EV_CommonCold ) + { + if ( !(GM_GameStatus & (STATE_PADRELEASE | STATE_PADDEMO | STATE_DEMO)) && + !GM_CheckPlayerStatusFlag(PLAYER_PAD_OFF) && + (work->field_A5C < 900) ) + { + work->field_A5C++; + } + + if ( sna_act_helper2_helper5_8004FF88(work) && (work->field_A5C == 900) ) + { + work->field_A5C = 0; + sna_start_anim_8004E1F4(work, &sna_act_helper2_helper_80054EFC); + } + } + + sna_weapon_switching_800511BC(work, 1); + sna_800515BC(work, 1); + + if ( !GM_CheckPlayerStatusFlag(0x1304) && + ((work->field_9AC & 2) == 0) && + ((work->field_920_tbl & 0x4000) != 0) && + (*work->field_918_n_bullets == 0)) + { + *work->field_918_n_bullets = -1; + GM_CurrentWeaponId = WP_None; + work->field_90C_pWeaponFn = &sna_anim_chokethrow_begin1_80054210; + work->field_910 = 0; + work->field_926 = 0; + work->field_924 = 0; + GM_WeaponChanged = 1; + work->field_920_tbl = GM_WeaponTypes[0]; + work->field_918_n_bullets = 0; + GM_ClearPlayerStatusFlag(PLAYER_WEAPON_DISABLE | PLAYER_ITEM_DISABLE); + } + + sna_act_unk_80050A64(work); + sna_init_main_logic_helper5_800596FC(work); + sna_act_helper2_helper3_8004ED6C(work); + + frame = work->field_9BC_anim_frame++; + work->field_9B8_fn_anim(work, frame); + + if (GM_CheckPlayerStatusFlag(PLAYER_WATCH)) + { + work->field_9CC_anim_update_fn_1p(work, frame); + } + else + { + work->field_9C8_anim_update_fn_3p(work, frame); + } + + if ((work->field_920_tbl & 0x40) && + GM_CheckPlayerStatusFlag(PLAYER_ATTACK)) + { + work->field_910++; + work->field_90C_pWeaponFn(work, work->field_910 - 1); + } + + if ( sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK9) ) + { + work->field_9C4++; + work->field_9C0(work, work->field_9C4 - 1); + } + + work->control.r_sphere = GV_NearExp4(work->control.r_sphere, work->field_A28); + work->control.step.vy = work->field_A2C.vy; + + sna_init_main_logic_helper6_800596FC(work); + + if ( GM_StreamStatus() == 2 ) + { + if ( sub_8004EFE4(work, 1) >= 0 ) + { + work->m_ctrl.info1.field_14 = 1; + } + + sna_act_helper2_helper2_80033054(SNAKE_NAME, &work->adjust[6]); + + if ( !work->body.objs->adjust && (work->m_ctrl.info1.field_14 == 1) ) + { + GV_AddVec3(&work->rots[6], &work->adjust[6], &work->rots[6]); + } + } + + GM_PlayerAction = work->body.action; + dword_800AB9D4 = work->m_ctrl.info1.frame; + GM_PlayerStance = work->field_A26_stance; + + if ( GM_TranquilizerTimer > 0 ) + { + GM_TranquilizerTimer--; + } + + GM_ClearPlayerStatusFlag(PLAYER_NOHIDDEN | PLAYER_HIDDEN); + + hzd_count = work->control.evt.n_inside; + pHzdVal = work->control.evt.inside; + + while ( hzd_count > 0 ) + { + if ( *pHzdVal == 0x375E ) + { + GM_SetPlayerStatusFlag(PLAYER_NOHIDDEN); + } + else if ( *pHzdVal == 0xF715 ) + { + GM_SetPlayerStatusFlag(PLAYER_HIDDEN); + } + + hzd_count -= 1; + pHzdVal += 1; + } + + if ( work->field_9A0 != 0 ) + { + addPrim(DG_Chanl(1)->ot[GV_Clock], &work->field_950[GV_Clock]); + work->field_9A0--; + } + + snake_weapon_idx_800BDCBA = work->field_91C_weapon_idx; + snake_weapon_max_ammo_800BDCBC = *work->field_918_n_bullets; + snake_mag_size_800BDCB8 = GM_Magazine; +} + +static inline int sna_act_helper_8005AD10(SnaInitWork *work) +{ + if ( sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK19) ) + { + CheckMessage0_80050878(work); + sna_act_unk_80050A64(work); + + if ( sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK19) ) + { + sna_act_unk2_80051170(work->field_89C_pTarget); + + dword_800AB9D4 = 0; + DG_SetPos2(&work->control.mov, &work->control.rot); + DG_PutObjs(work->body.objs); + return 1; + } + } + + return 0; +} + +static inline void sna_act_helper2_8005AD10(SnaInitWork *work) +{ + SVECTOR vec; + + if ( sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK24) && !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK28) ) + { + vec = work->field_A68; + GV_NearExp8V(&vec.vx, &DG_ZeroVector.vx, 3); + + if ( GV_VecLen3(&vec) > 8 ) + { + work->field_A68 = vec; + } + + work->control.step.vx = work->field_A68.vx; + work->control.step.vz = work->field_A68.vz; + + if ( work->control.grounded & 1 ) + { + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK24); + } + } + + if ( sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK26) ) + { + work->control.step.vz = 0; + work->control.step.vx = 0; + } +} + +static void Act(SnaInitWork *work) +{ + SVECTOR vec; + SVECTOR vec2; + TARGET *pTarget; + int height; + int level; + TARGET *pTarget2; + + if ( sna_act_helper_8005AD10(work) ) + { + return; + } + + if ( GM_CheckPlayerStatusFlag(PLAYER_SECOND_AVAILABLE) ) + { + if ( ((GV_PadData + 1)->status | (GV_PadData + 1)->release) != 0 ) + { + GM_SetPlayerStatusFlag(PLAYER_SECOND_CONTROLLER); + work->field_9B0_pad_ptr = &GV_PadData[1]; + } + else if ( (GV_PadData->status | GV_PadData->release) != 0 ) + { + GM_ClearPlayerStatusFlag(PLAYER_SECOND_CONTROLLER); + work->field_9B0_pad_ptr = &GV_PadData[0]; + } + } + else + { + work->field_9B0_pad_ptr = &GV_PadData[0]; + } + + pTarget = work->field_89C_pTarget; + + if ( pTarget->damaged & 8 ) + { + GV_AddVec3(&pTarget->offset, &work->control.step, &work->control.step); + pTarget->damaged &= ~0x8; + pTarget->offset = DG_ZeroVector; + } + + height = (short)work->body.height; + + GM_ActMotion(&work->body); + sna_act_helper2_8005AD10(work); + + level = work->control.levels[0]; + work->control.hzd_height = HzdHeights_8009EEC4[work->field_A26_stance] + level; + + if ( (work->control.skip_flag & CTRL_BOTH_CHECK) && (work->field_798_p_height < 750) ) + { + work->control.hzd_height = work->control.mov.vy; + } + + /* しゃがみ天井突き抜け防止 */ + if ( (work->field_A26_stance != SNA_STANCE_STANDING) && (height >= 500) ) + { + work->control.height = 496; + } + + UpdateLife_8004F6E8(work); + CheckSnakeDead_8004E384(work); + CheckMessage0_80050878(work); + + GM_ActControl(&work->control); + + flr_800ABBB8[1] = 0; + do {} while (0); + flr_800ABBB8[0] = NULL; + dword_800ABBC0 = 0; + + if ( work->control.r_sphere != -2 ) + { + HZD_GetLevelHazard(flr_800ABBB8); + + if ( flr_800ABBB8[0] ) + { + dword_800ABBC0 = flr_800ABBB8[0]->p4.h; + } + } + + if ( ((level - work->control.levels[0]) >= 250) && !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK28) ) + { + if ( !flr_800ABBB8[0] || (flr_800ABBB8[0]->b1.h == 2) ) + { + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK24); + work->field_A68 = work->control.step; + work->field_A68.vy = 0; + } + } + + if ( work->control.r_sphere == -2 ) + { + work->control.mov.vy += work->body.height - height; + DG_SetPos2(&work->control.mov, &work->control.rot); + } + + GM_ActObject(&work->body); + + sna_clear_flags1_8004E308(work, SNA_FLAG1_UNK25); + DG_GetLightMatrix2(&work->control.mov, work->light); + *work->enable_shadow = dword_800ABA1C == 0; + GM_MoveTarget(work->field_89C_pTarget, &work->control.mov); + + vec2 = work->control.mov; + + if ( !sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK12) ) + { + vec2.vy = work->body.objs->objs[6].world.t[1]; + } + + if (work->field_A26_stance == SNA_STANCE_CROUCH) + { + vec2.vy += 320; + } + + work->field_A60 = vec2; + + GM_SnakeCamera.rotate2 = work->control.rot; + GM_SnakeCamera.rotate2.vy &= 0xFFF; + + sna_init_main_logic_800596FC(work); + + pTarget2 = work->field_89C_pTarget; + pTarget2->force = work->control.step; + GM_PushTarget(pTarget2); + + if (GM_Camera.first_person != 0) + { + GV_NearExp4V(&GM_SnakeCamera.position.vx, &work->field_A60.vx, 3); + } + else + { + GM_SnakeCamera.position = vec2; + } + + GM_PlayerPosition = vec = work->control.mov; + + if ( sna_check_flags1_8004E31C(work, SNA_FLAG1_UNK16) ) + { + GM_PlayerPosition = work->field_A60; + GM_MoveTarget(work->field_89C_pTarget, &work->field_A60); + } + + vec.vy = level = work->control.levels[0]; + GM_PlayerAddress = work->field_844 = HZD_GetAddress(work->control.map->hzd, &vec, work->field_844); + + if ( GM_CheckPlayerStatusFlag(PLAYER_INTRUDE) ) + { + if ( GM_SnakeCamera.position.vy < (level + 150) ) + { + GM_SnakeCamera.position.vy = level + 150; + } + else if ( GM_SnakeCamera.position.vy > (level + 250) ) + { + GM_SnakeCamera.position.vy = level + 250; + } + } + + GM_ClearPlayerStatusFlag(PLAYER_INVISIBLE); + + if ( GM_CheckPlayerStatusFlag(3) ) + { + GM_SetPlayerStatusFlag(PLAYER_INVISIBLE); + } +} + +static void Die(SnaInitWork *work) +{ + CONTROL *pCtrl; + GV_ACT *pShadow; + GV_ACT *pWeapon; + GV_ACT *pItem; + + pCtrl = &work->control; + GM_FreeControl(&work->control); + GM_FreeObject(&work->body); + GM_FreeTarget(work->field_89C_pTarget); + GM_FreePrim(work->field_92C); + + GM_PlayerStance = work->field_A26_stance; + + pShadow = work->shadow; + if (pShadow) + { + GV_DestroyOtherActor(pShadow); + } + + pWeapon = work->field_908_weapon_actor; + if (pWeapon) + { + GV_DestroyOtherActor(pWeapon); + } + + pItem = work->field_9A4_item_actor; + if (pItem) + { + GV_DestroyOtherActor(pItem); + } + + if (GM_PlayerControl == pCtrl) + { + GM_PlayerControl = 0; + } + + if (GM_PlayerBody == &work->body) + { + GM_PlayerBody = 0; + } + + snainit_actor_800A9424 = NULL; +} + +static inline int InitLaserSight(SnaInitWork *work) +{ + SVECTOR *temp_s1; + SVECTOR *temp_s2; + DG_PRIM *pPrim; + DG_TEX *pTex; + POLY_GT4 *pPoly; + int i, j; + + temp_s1 = &work->field_930; + pPrim = GM_MakePrim(DG_PRIM_LINE_GT2, 2, temp_s1, NULL); + + if (!pPrim) + { + return -1; + } + + pTex = DG_GetTexture(PCX_LSIGHT); + work->field_928 = pTex; + + if (!pTex) + { + return -1; + } + + pPrim->raise = 500; + + for (i = 0; i < 2; i++) + { + pPoly = pPrim->packs[i]; + + for (j = 0; j < 2; j++) + { + LSTORE(COLOR_BLACK, &pPoly->r0); + LSTORE(COLOR_GRAY, &pPoly->r1); + LSTORE(COLOR_BLACK, &pPoly->r2); + LSTORE(COLOR_GRAY, &pPoly->r3); + setPolyGT4(pPoly); + setSemiTrans(pPoly, 1); + pPoly->tpage = pTex->tpage; + pPoly->clut = pTex->clut; + pPoly++; + } + } + + // ??? + temp_s2 = temp_s1 + 2; + + temp_s1[2].vx = -100; + temp_s1[0].vx = -100; + temp_s1[3].vy = -300; + temp_s2[0].vy = -300; + temp_s1[1].vy = -300; + temp_s1[0].vy = -300; + temp_s2[0].vz = 300; + temp_s1[0].vz = 300; + temp_s1[1].vx = -800; + temp_s1[1].vz = 2000; + temp_s1[3].vx = 800; + temp_s1[3].vz = 2000; + + DG_InvisiblePrim(pPrim); + work->field_92C = pPrim; + return 0; +} + +static inline void InitEmpty(SnaInitWork *work) +{ + u_short t1, t2; + short stance; + short temp_a1; + int temp_v1_3; + int var_s0_2; + int var_v_2; + + stance = work->field_A26_stance = GM_PlayerStance; + if (stance == 3) + { + t1 = GM_ItemTypes[GM_CurrentItemId + 1]; + t2 = GM_CurrentItemId; + + if ((t1 & 2) || ((t2 - 2) < 3u)) + { + GM_CurrentItemId = IT_None; + } + + if (GM_WeaponTypes[GM_CurrentWeaponId + 1] & 0x200) + { + GM_CurrentWeaponId = WP_None; + } + } + + work->field_844 = -1; + work->field_91C_weapon_idx = -2; + + sna_weapon_switching_800511BC(work, 0); + + work->field_9A8_current_item = IT_None; + + sna_800515BC(work, 0); + + work->actpack = &weapon_actions_8009ED8C[GM_CurrentWeaponId]; + work->field_A28 = 450; + work->field_A44 = work->control.mov; + work->field_A60 = work->control.mov; + + GM_SnakeCamera.position = work->field_A60; + + if (GCL_GetOption('o')) // oar + { + work->field_A5A = GCL_StrToInt(GCL_NextStr()); + } + + work->field_890_autoaim_max_dist = 6000; + + if (GCL_GetOption('l')) // len + { + work->field_890_autoaim_max_dist = GCL_StrToInt(GCL_NextStr()); + } + + work->field_892_autoaim_min_angle = 512; + + if (GCL_GetOption('r')) // rot + { + work->field_892_autoaim_min_angle = GCL_StrToInt(GCL_NextStr()); + } + + dword_800ABBDC = -1023; + dword_800ABBD4 = 1023; + + if (GCL_GetOption('t')) // turn + { + dword_800ABBDC = GCL_StrToInt(GCL_NextStr()); + dword_800ABBD4 = GCL_StrToInt(GCL_NextStr()); + } + + work->field_A70 = -1; + + if (GCL_GetOption('e')) // exec + { + work->field_A70 = GCL_StrToInt(GCL_NextStr()); + } + + temp_a1 = work->field_A26_stance; + + if (temp_a1 == 3) + { + work->field_A26_stance = 2; + GM_SetPlayerStatusFlag(PLAYER_GROUND | PLAYER_INTRUDE); + DG_InvisibleObjs(work->body.objs); + sna_8004EC8C(work); + sna_start_anim_8004E1F4(work, &sna_anim_duct_move_80054424); + GM_Camera.first_person = 2; + var_v_2 = sna_8004EAA8(work, 2); + } + else + { + var_s0_2 = sna_8004EAA8(work, temp_a1); + + if ((work->field_9AC & 1) || (work->field_920_tbl & 0x20)) + { + if ((unsigned int)(work->field_9A8_current_item - 2) < 3) + { + var_s0_2 = 4; + } + } + else + { + sna_start_anim_8004E1F4(work, dword_8009EEA4[work->field_A26_stance]); + } + + var_v_2 = var_s0_2; + } + + GM_ConfigObjectAction(&work->body, var_v_2, 0, 0); + GM_ActMotion(&work->body); + + temp_v1_3 = (short)work->body.height; + + work->field_798_p_height = temp_v1_3; + work->control.height = temp_v1_3; + + work->field_9B0_pad_ptr = GV_PadData; +} + +static inline void InitPacks( POLY_FT4 *packs, int n_packs, DG_TEX *tex ) +{ + char u, v; + int i; + + u = tex->off_x; + v = tex->off_y; + + for ( i = 0; i < n_packs; i ++ ) + { + setPolyFT4( packs ); + setSemiTrans( packs, 1 ); + setRGB0( packs, 128, 128, 128 ); + setXY4( packs, 255, 164, 303, 164, 255, 176, 303, 176 ); + setUV4( packs, u, v, u + 48, v, u, v + 12, u + 48, v + 12 ); + packs->tpage = tex->tpage; + packs->clut = tex->clut; + packs ++ ; + } +} + +static inline int GetResources(SnaInitWork *work, int scriptData, int scriptBinds) +{ + SVECTOR shadow; + SVECTOR vec; + int attr; + CONTROL *control; + OBJECT *body; + int model; + SVECTOR *size; + TARGET *trg; + HITTABLE *hittable; + int i; + + attr = RADAR_VISIBLE; + + control = &work->control; + if (GM_InitControl(control, scriptData, scriptBinds) < 0) + { + return -1; + } + + GM_ConfigControlString(control, GCL_GetOption('p'), GCL_GetOption('d')); + GM_ConfigControlHazard(control, 0, 450, 450); + control->seg_flag = SEGMENT_ATR; + GM_ConfigControlAttribute(control, attr); + GM_ConfigControlTrapCheck(control); + + body = &work->body; + model = KMD_SNAKE; + if (GCL_GetOption('m')) // model + { + model = GCL_StrToInt(GCL_NextStr()); + } + + GM_InitObject(body, model, BODY_FLAG, OAR_SNAKE); + GM_ConfigObjectJoint(body); + GM_ConfigMotionControl(body, &work->m_ctrl, OAR_SNAKE, work->m_segs1, work->m_segs2, control, work->rots); + GM_ConfigObjectLight(body, work->light); + + GM_PlayerControl = control; + GM_PlayerPosition = work->control.mov; + GM_PlayerBody = body; + + sna_8004EB14(work); + + if (InitLaserSight(work) < 0) + { + return -1; + } + + InitEmpty(work); + + size = &vec; + setVector(size, 300, 650, 300); + + trg = work->field_89C_pTarget = GM_AllocTarget(); + GM_SetTarget(trg, ( TARGET_AVAIL | TARGET_FLAG ), PLAYER_SIDE, size); + GM_SetPowerTarget(trg, POWER_DECREASE, -1, GM_SnakeCurrentHealth, 0, &DG_ZeroVector); + GM_SetCaptureTarget(trg, 0, 0, &work->field_8F4, &work->field_8FC); + GM_MoveTarget(trg, &work->control.mov); + + work->field_A22_snake_current_health = GM_SnakeCurrentHealth; + + InitPacks(work->field_950, 2, DG_GetTexture(PCX_EMPTY2)); + + shadow.vx = 0; + shadow.vy = 6; + shadow.vz = 12; + shadow.pad = 15; + + work->shadow = NewShadow2(control, body, shadow, &work->enable_shadow); + + dword_800ABA1C = 0; + GM_BombSeg = 0; + + for (i = 0; i < 16; i++) + { + tenage_ctrls_800BDD30[i] = NULL; + } + + dword_800BDD28 = 0; + tenage_ctrls_count_800BDD70 = 0; + + hittable = GM_C4Datas; + for (i = 0; i < C4_COUNT; i++, hittable++) + { + hittable->type = WP_C4; + hittable->actor = NULL; + } + + dword_8009F434 = 0; + bakudan_count_8009F42C = 0; + + hittable = GM_ClayDatas; + for (i = 0; i < 8; i++, hittable++) + { + hittable->type = WP_Claymore; + hittable->actor = NULL; + } + + dword_8009F440 = 0; + dword_8009F444 = 0; + counter_8009F448 = 0; + dword_8009F46C[0] = 0; + dword_8009F470 = 0; + dword_8009F474 = 0; + target_800BDF00 = 0; + + for (i = 0; i < 64; i++) + { + gBulNames_800BDC78[i] = 0; + } + + tabako_dword_8009F2C0 = 0; + pVec_800ABBCC = NULL; + + if (model == KMD_SNE_WET2) // wet suit in the docks + { + sna_set_flags1_8004E2F4(work, SNA_FLAG1_UNK29); + } + + return 0; +} + +// Unused strings left in binary for some reason +static const char aPosDDD[] = "pos %d %d %d\n"; +static const char aTurnDDD[] = "turn %d %d %d\n"; +static const char aCeilFloorDD[] = "ceil floor %d %d\n"; +static const char aStatus4x[] = "status %4x\n"; +static const char aWeaponD[] = "weapon %d\n"; +static const char aItemD[] = "item %d\n"; +static const char aFlag4x[] = "flag %4x\n"; +static const char aStanceD[] = "stance %d\n"; +static const char aPadtoD[] = "padto %d\n"; +static const char aTrapCheckD[] = "trap check %d\n"; +static const char aPadX[] = "pad %x\n"; +static const char aKaze[] = "kaze \n"; + +#define EXEC_LEVEL GV_ACTOR_USER + +void *NewSnake(int name, int where, int argc, char **argv) +{ + SnaInitWork *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(SnaInitWork)); + if (!work) + { + return NULL; + } + + GV_SetNamedActor(&work->actor, Act, Die, "../snake_vr/sna_init.c"); + + if (GetResources(work, name, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + snainit_actor_800A9424 = work; + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/snake_vr/sna_init.h b/components/mgs/mgs/source/chara/snake_vr/sna_init.h new file mode 100644 index 00000000..6f0524b5 --- /dev/null +++ b/components/mgs/mgs/source/chara/snake_vr/sna_init.h @@ -0,0 +1,485 @@ +// FIXME: copy-pasta of snake/sna_init.h + +#ifndef __MGS_CHARA_SNA_INIT_H__ +#define __MGS_CHARA_SNA_INIT_H__ + +#include +#include +#include + +#include "libgv/libgv.h" +#include "game/game.h" +#include "game/navi.h" + +typedef struct ACTSTILL +{ + unsigned char stand; + unsigned char setup; + unsigned char squat; + unsigned char crouch; + unsigned char against_wall; + unsigned char squat_against_wall; + unsigned char tap_wall_r; + unsigned char tap_wall_l; +} ACTSTILL; + +typedef struct ACTTRANS +{ + unsigned char crouch; + unsigned char squat; + unsigned char stand; + unsigned char pad; +} ACTTRANS; + +typedef struct ACTMOVE +{ + unsigned char stand; + unsigned char setup; + unsigned char squat; + unsigned char crouch; + unsigned char crouch_backwards; + unsigned char against_wall_r; + unsigned char against_wall_l; + unsigned char aim; + unsigned char aim_stance_l; + unsigned char aim_stance_r; +} ACTMOVE; + +typedef struct ACTDAMAGE +{ + unsigned char field_0; + unsigned char field_1; + unsigned char field_2; + unsigned char field_3; + unsigned char field_4; + unsigned char field_5; + unsigned char field_6; + unsigned char field_7; + unsigned char field_8; + unsigned char field_9; + unsigned char field_A; + unsigned char field_B; +} ACTDAMAGE; + +typedef struct ACTATTACK +{ + unsigned char aim; + unsigned char reload; + unsigned char punch; + unsigned char setup; + unsigned char shoot; + unsigned char field_5; + unsigned char field_6; + unsigned char field_7; + unsigned char field_8; + unsigned char field_9; +} ACTATTACK; + +typedef struct ACTSPECIAL +{ + unsigned char field_0; + unsigned char field_1; + unsigned char field_2; + unsigned char field_3; + unsigned char field_4; + unsigned char field_5; + unsigned char field_6; + unsigned char field_7; +} ACTSPECIAL; + +typedef struct Sna_E6 +{ + unsigned char field_0; + unsigned char field_1; + unsigned char field_2; + unsigned char field_3; +} Sna_E6; + +typedef struct Target_Data +{ + SVECTOR field_0; + SVECTOR field_8_size; + int field_10; + int field_14; + int field_18; + unsigned int field_1C; +} Target_Data; + +typedef struct ACTPACK +{ + ACTSTILL *still; /* 静止モーション */ + ACTMOVE *move; /* 移動モーション */ + ACTTRANS *trans; /* 遷移モーション */ + ACTDAMAGE *damage; /* ダメージモーション */ + ACTATTACK *attack; /* 攻撃モーション */ + Sna_E6 *special1; //u_char *special1 ; /* 特殊モーション1 */ + ACTSPECIAL *special2; //u_char *special2 ; /* 特殊モーション2 */ +} ACTPACK; + +typedef enum +{ + SNA_FLAG1_UNK1 = 0x1, + SNA_FLAG1_UNK2 = 0x2, + SNA_FLAG1_UNK3 = 0x4, + SNA_FLAG1_UNK4 = 0x8, + SNA_FLAG1_UNK5 = 0x10, + SNA_FLAG1_UNK6 = 0x20, + SNA_FLAG1_UNK7 = 0x40, + SNA_FLAG1_UNK8 = 0x80, + SNA_FLAG1_UNK9 = 0x100, + SNA_FLAG1_UNK10 = 0x200, + SNA_FLAG1_UNK11 = 0x400, + SNA_FLAG1_UNK12 = 0x800, + SNA_FLAG1_UNK13 = 0x1000, + SNA_FLAG1_UNK14 = 0x2000, + SNA_FLAG1_UNK15 = 0x4000, + SNA_FLAG1_UNK16 = 0x8000, + SNA_FLAG1_UNK17 = 0x10000, + SNA_FLAG1_UNK18 = 0x20000, + SNA_FLAG1_UNK19 = 0x40000, + SNA_FLAG1_UNK20 = 0x80000, + SNA_FLAG1_UNK21 = 0x100000, + SNA_FLAG1_UNK22 = 0x200000, + SNA_FLAG1_UNK23 = 0x400000, + SNA_FLAG1_UNK24 = 0x800000, + SNA_FLAG1_UNK25 = 0x1000000, + SNA_FLAG1_UNK26 = 0x2000000, + SNA_FLAG1_UNK27 = 0x4000000, + SNA_FLAG1_UNK28 = 0x8000000, + SNA_FLAG1_UNK29 = 0x10000000, + SNA_FLAG1_UNK30 = 0x20000000, + SNA_FLAG1_UNK31 = 0x40000000, + SNA_FLAG1_UNK32 = 0x80000000, +} SnaFlag1; + +typedef enum +{ + SNA_FLAG2_UNK1 = 0x1, + SNA_FLAG2_UNK2 = 0x2, + SNA_FLAG2_UNK3 = 0x4, + SNA_FLAG2_UNK4 = 0x8, + SNA_FLAG2_UNK5 = 0x10, + SNA_FLAG2_UNK6 = 0x20, + SNA_FLAG2_UNK7 = 0x40, + SNA_FLAG2_UNK8 = 0x80, + SNA_FLAG2_UNK9 = 0x100, + SNA_FLAG2_UNK10 = 0x200, + SNA_FLAG2_UNK11 = 0x400, + SNA_FLAG2_UNK12 = 0x800, + SNA_FLAG2_UNK13 = 0x1000, + SNA_FLAG2_UNK14 = 0x2000, + SNA_FLAG2_UNK15 = 0x4000, + SNA_FLAG2_UNK16 = 0x8000, + SNA_FLAG2_UNK17 = 0x10000, + SNA_FLAG2_UNK18 = 0x20000, + SNA_FLAG2_UNK19 = 0x40000, + SNA_FLAG2_UNK20 = 0x80000, + SNA_FLAG2_UNK21 = 0x100000, + SNA_FLAG2_UNK22 = 0x200000, + SNA_FLAG2_UNK23 = 0x400000, + SNA_FLAG2_UNK24 = 0x800000, + SNA_FLAG2_UNK25 = 0x1000000, + SNA_FLAG2_UNK26 = 0x2000000, + SNA_FLAG2_UNK27 = 0x4000000, + SNA_FLAG2_UNK28 = 0x8000000, + SNA_FLAG2_UNK29 = 0x10000000, + SNA_FLAG2_UNK30 = 0x20000000, + SNA_FLAG2_UNK31 = 0x40000000, + SNA_FLAG2_UNK32 = 0x80000000, +} SnaFlag2; + +// sub struct of SnaInitWork +typedef struct UnkSnakeStruct2 +{ + unsigned short field_9E4; + short field_9E6; + short field_9E8; + short field_9EA; + short field_9EC_flags3; + short field_9EE; + short field_9F0; + short field_9F2; + SVECTOR field_9F4; +} UnkSnakeStruct2; + +struct SnaInitWork; +typedef void (*TSnakeFunction)(struct SnaInitWork *, int); + +// TODO: define the rest of the joints +#define SNAKEJOINT_NUM 16 + +// TODO: Many fields and sub structures are not yet recovered/incorrect +typedef struct SnaInitWork +{ + GV_ACT actor; + CONTROL control; + OBJECT body; + MOTION_CONTROL m_ctrl; + MOTION_SEGMENT m_segs1[SNAKEJOINT_NUM + 1]; + MOTION_SEGMENT m_segs2[SNAKEJOINT_NUM + 1]; + SVECTOR rots[SNAKEJOINT_NUM]; + SVECTOR adjust[SNAKEJOINT_NUM]; + int field_798_p_height; + int field_79C; + int field_7A0_msg_count; + GV_MSG field_7A4_msgs[8]; + int field_844; + MATRIX light[2]; + GV_ACT *shadow; + int *enable_shadow; + unsigned short field_890_autoaim_max_dist; + unsigned short field_892_autoaim_min_angle; + SnaFlag1 field_894_flags1; + // field_898_flags2 removed here + TARGET *field_89C_pTarget; + TARGET field_8A0_target; + TARGET *field_8E8_pTarget; + SVECTOR field_8EC_vec; + int field_8F4; + int field_8F8; + SVECTOR field_8FC; + int field_904_frames_last_choke; + GV_ACT *field_908_weapon_actor; + TSnakeFunction field_90C_pWeaponFn; + int field_910; + u_long field_914_trigger; + short *field_918_n_bullets; + int field_91C_weapon_idx; + int field_920_tbl; + short field_924; + short field_926; + DG_TEX *field_928; + DG_PRIM *field_92C; + SVECTOR field_930; + SVECTOR field_938; + SVECTOR field_940; + SVECTOR field_948; + POLY_FT4 field_950[2]; + int field_9A0; + GV_ACT *field_9A4_item_actor; + int field_9A8_current_item; + int field_9AC; + GV_PAD *field_9B0_pad_ptr; + ACTPACK *actpack; + TSnakeFunction field_9B8_fn_anim; + int field_9BC_anim_frame; + TSnakeFunction field_9C0; + int field_9C4; + TSnakeFunction field_9C8_anim_update_fn_3p; // called for every anim/state while in third person + TSnakeFunction field_9CC_anim_update_fn_1p; // same except first person. caller: 8005a7e0 + short field_9D0[10]; + UnkSnakeStruct2 field_9E4; + void *field_9FC; + NAVIGATE field_A00; // auto movement of snake during mini cutscenes. some above fields may also be this + short field_A20; + short field_A22_snake_current_health; + char field_A24_invuln_frames; + char field_A25; + short field_A26_stance; // SNA_STANCE_... + short field_A28; + short field_A2A; + SVECTOR field_A2C; + char field_A34; + char field_A35_vibtime2; + char field_A36; + char field_A37; + short field_A38_local_data; + short field_A3A; + SVECTOR field_A3C; + SVECTOR field_A44; + int field_A4C; + short field_A50; + short field_A52; + // var reused in differenet contexts, union is better than a generic name for all contexts + union { + short choke_count; + short prone_bool_thing; + short wall_thing; + short knockdown_thing; + } field_A54; + short field_A56; + short field_A58; + unsigned short field_A5A; + unsigned short field_A5C; + short field_A5E; + SVECTOR field_A60; + SVECTOR field_A68; + int field_A70; +} SnaInitWork; + +typedef void * (*TSnakeEquipFuncion)(CONTROL *, OBJECT *, int); + +void sna_start_anim_8004E1F4(SnaInitWork *work, void *pFn); +void SetAction_8004E22C(SnaInitWork *work, int action_flag, int interp); +void sna_8004E260(SnaInitWork *work, int a2, int interp, int a4); +int GM_CheckPlayerStatusFlag(PlayerStatusFlag arg0); +int GM_SetPlayerStatusFlag(PlayerStatusFlag arg0); +void GM_ClearPlayerStatusFlag(PlayerStatusFlag flag); +void sna_set_flags1_8004E2F4(SnaInitWork *snake, SnaFlag1 flags); +void sna_clear_flags1_8004E308(SnaInitWork *snake, SnaFlag1 flags); +int sna_check_flags1_8004E31C(SnaInitWork *snake, SnaFlag1 flags); +void sna_set_flags2_8004E330(SnaInitWork *snake, SnaFlag2 flag); +void sna_clear_flags2_8004E344(SnaInitWork *snake, SnaFlag2 flags); +unsigned int sna_sub_8004E358(SnaInitWork *snake, SnaFlag2 param_2); +void CheckSnakeDead_8004E384(SnaInitWork *snake); +void sna_sub_8004E41C(SnaInitWork *snake, unsigned short flags); +int sub_8004E458(short param_1, int param_2); +int sub_8004E4C0(SnaInitWork *work, int param_2); +int sna_line_check(SVECTOR *line, HZD_HDL *hzd, int flag, int exclude); +void sub_8004E588(HZD_HDL *param_1, SVECTOR *param_2, int *levels); +int sub_8004E930(SnaInitWork *snake, int arg1); +void sub_8004E9D0(SnaInitWork *work); +void sub_8004EA50(SnaInitWork *work, int param_2); +int sna_8004EAA8(SnaInitWork *work, int a2); +void sub_8004EB74(SnaInitWork *work); +void sna_8004EC00(SnaInitWork *work); +void sna_8004EC8C(SnaInitWork *work); +void sub_8004ED08(SnaInitWork *work); +void sub_8004EEB0(SnaInitWork *work); +int sub_8004EFE4(SnaInitWork *work, int param_2); +void sna_8004F034(SnaInitWork *work, unsigned int bits); +void sna_act_helper2_helper4_8004F090(SnaInitWork *work, int param_2); +void sub_8004F14C(SnaInitWork *param_1); +void sub_8004F204(SnaInitWork *param_1); +void sna_set_invuln_8004F2A0(SnaInitWork *work, char invuln_frames); +void sna_clear_invuln_8004F2EC(SnaInitWork *snake); +void sub_8004F338(SnaInitWork *param_1); +int sna_act_unk_helper4_8004FA3C(void); +void sna_8004FA74(SnaInitWork *param_1); +void sub_8004FA9C(SnaInitWork *snake); +int sna_current_item_8004FB38(void); +HZD_FLR **sub_8004FB90(void); +int sna_8004E808(SnaInitWork *work, int a2, int a3, int a4, int a5); + +// TODO: move these to game.h even though theyre defined in sna_init.c? +// .. or move the implementation to a static inline in game.h which the definition in sna_init calls? +int GM_Next_BulName_8004FBA0(); +void GM_ClearBulName(int idx); +void GM_CheckShukanReverse(unsigned short *pInput); +void GM_CheckShukanReverseAnalog(unsigned char *pInput); + +void sna_check_knock_800501F8(SnaInitWork *work, int time); +int sna_prone_check_standup_80050398(SnaInitWork *work); +void sna_80050440(SnaInitWork *work); +void sna_80050568(SnaInitWork *work); +int sub_800507D8(SnaInitWork *param_1); +void sna_80051FD0(SnaInitWork *work); +void sna_fn_80052120(SnaInitWork *work, int time); +void sub_8005230C(SnaInitWork *work, int time); +void sub_80052468(SnaInitWork *work, int time); +void sna_fn_80052540(SnaInitWork *work, int time); +void sna_fn_800525F8(SnaInitWork *work, int time); +void sna_act_helper2_helper_helper_800526BC(SnaInitWork *work, int time); +void sna_anim_idle_8005275C(SnaInitWork *work, int time); +void sna_anim_crouch_800527DC(SnaInitWork *param_1, int time); +void sna_anim_prone_idle_800528BC(SnaInitWork *work, int time); +void sna_anim_run_8005292C(SnaInitWork *work, int time); +void sna_anim_prone_move_800529C0(SnaInitWork *work, int time); +void sna_anim_wall_idle_and_c4_80052A5C(SnaInitWork *work, int time); +void sna_anim_wall_move_80052BA8(SnaInitWork *work, int time); +void sna_anim_wall_crouch_80052CCC(SnaInitWork *work, int time); +void sna_fn_80052E58(SnaInitWork *work, int time); +void sna_anim_crouch_helper_80053014(SnaInitWork *work, int time); +void sna_fn_800531F4(SnaInitWork *work, int time); +void sna_fn_nothing_80053B80(SnaInitWork *param_1, int time); +void sna_anim_run_begin_80053B88(SnaInitWork *param_1, int time); +void sna_anim_prone_begin_80053BE8(SnaInitWork *work, int time); +void sna_anim_prone_standup_80053D74(SnaInitWork *work, int time); +void sna_anim_enter_duct_80053E9C(SnaInitWork *work, int time); +void sub_80053FAC(SnaInitWork *work, int time); +void sna_gun_800540D0(SnaInitWork *work, int time); +void sna_bomb_800541A8(SnaInitWork *work); +void sna_anim_chokethrow_begin1_80054210(SnaInitWork *work, int time); +void sna_8005425C(SnaInitWork *work, int time); +void sna_80054318(SnaInitWork *work, int time); +void sna_fn_800543A8(SnaInitWork *work, int time); +void sna_anim_duct_move_80054424(SnaInitWork *work, int time); +void sna_anim_duct_idle_80054488(SnaInitWork *work, int time); +void sna_anim_shot_flinch_800544E0(SnaInitWork *work, int time); +void sna_act_helper2_helper7_80054648(SnaInitWork *work, int time); +void sna_anim_knockdown_80054710(SnaInitWork *work, int time); +void sna_anim_knockdown_idle_80054930(SnaInitWork *work, int time); +void sna_anim_knockdown_getup_80054A10(SnaInitWork *work, int time); +void sna_anim_knockdown_shot_80054B50(SnaInitWork *work); +void sna_anim_knockdown_shot_tank_80054C08(SnaInitWork *work, int time); +void sna_anim_scope_80055334(SnaInitWork *param_1, int time); +void sna_anim_jpegcam_800553CC(SnaInitWork *work, int time); +void sna_anim_box_idle_800553EC(SnaInitWork *work, int time); +void sna_anim_box_move_8005544C(SnaInitWork *work, int time); +void sna_anim_box_stop_800554B4(SnaInitWork *work, int time); +void sub_8005684C(SnaInitWork *work); +void sub_8005688C(SnaInitWork *work, int time); +void sub_80056928(SnaInitWork *param_1, int time); +void sna_knockdown_check_getup_80056A1C(SnaInitWork *work, int time); +void sna_80056A54(SnaInitWork *work, int time); +void sna_fn_80056AD8(SnaInitWork *work, int time); +void sna_anim_shoot_weapon_80056B88(SnaInitWork *work, int time); +void sna_anim_rungun_begin_80056BDC(SnaInitWork *param_1, int time); +void sna_anim_rungun_80056C3C(SnaInitWork *param_1, int time); +void sna_anim_stinger_800570C0(SnaInitWork *work, int time); +void sna_80057118(SnaInitWork *work, int time); +void sna_800571B8(SnaInitWork *work, int time); +void sna_80057378(SnaInitWork *work, int time); +void sna_anim_shoot_weapon_helper_80057590(SnaInitWork *work, int time); +void sna_anim_rungun_begin_helper_800577B4(SnaInitWork *work, int time); +void sna_auto_aim_800579A0(SnaInitWork *work); +STATIC void OP_ShootStinger(SnaInitWork *work, int time); +void sub_80058644(SnaInitWork *work, int time); +void sna_anim_punch_80058C10(SnaInitWork *work, int time); +void sna_anim_chokethrow_begin2_80058C80(SnaInitWork *work, int time); +void sna_anim_choke_80058E88(SnaInitWork *work, int time); +void sna_anim_choke_rechoke_80058EF4(SnaInitWork *work, int time); +void sna_anim_choke_rechoke_80058EF4(SnaInitWork *work, int time); +void sna_anim_choke_kill_80058F88(SnaInitWork *work, int time); +void snake_vr_sna_init_80055500(SnaInitWork *work, int time); +void sna_anim_choke_drag_80059054(SnaInitWork *work, int time); +void sna_anim_choke_hold_80059154(SnaInitWork *work, int time); +void sub_800591BC(SnaInitWork *work); +void sna_anim_choke_helper_8005951C(SnaInitWork *work, int time); +void sna_anim_choke_rechoke_helper_8005961C(SnaInitWork *work, int time); +void sna_kill_8005B52C(SnaInitWork *work); +void sna_anim_psg1_helper_80057FD4(SnaInitWork *work, int a2); +void sna_80051DA0(SnaInitWork *work); +void sna_80053360(SnaInitWork *work, int time); +void sub_80057BF0(SnaInitWork *work, int a2); +void sna_anim_throw_800589C8(SnaInitWork *work, int a2); +void sna_gun_800540D0(SnaInitWork *work, int a2); +void sna_anim_punch_helper_800591F4(SnaInitWork *work, int a2); +int sna_8004FDE8(SnaInitWork *work, Target_Data *pVec); +void sna_8004F8E4(SnaInitWork *work, int a2); +void sna_fn_800535B8(SnaInitWork *work, int time); +void sna_knockdown_getup_80050668(SnaInitWork *work); +void sna_anim_grenade_80058470(struct SnaInitWork *work, int a2); +void sna_anim_claymore_80057474(struct SnaInitWork *work, int a2); +void sna_anim_psg1_80056DDC(struct SnaInitWork *work, int a2); +void sna_8004EE28(SnaInitWork *work); +void sna_8004EB14(SnaInitWork *work); +void sna_anim_wall_idle_and_c4_helper_800537D4(SnaInitWork *work, int time); +void sna_anim_wall_crouch_helper_80053A54(SnaInitWork *work, int time); +void sub_8004FAE8(SnaInitWork *work); +void sna_anim_wall_move_helper_800538CC(SnaInitWork *work, int time); +void sna_knock_80054D68(SnaInitWork *work, int time); +void sna_anim_rungun_helper_80057844(SnaInitWork *work, int time); +int sna_8005009C(SnaInitWork *work); +int sub_80051BA4(SnaInitWork *work); +int sub_8004EFE4(SnaInitWork *work, int param_2); +int sub_8004E5E8(SnaInitWork *work, int param_2); +int sub_8004E808(SnaInitWork *work, int, int, int, int); // dummy signature +int NavigateGetTargetDist(NAVIGATE *, SVECTOR *); +int NavigateTargetNear(NAVIGATE *param_1, SVECTOR *param_2, int param_3); +void sna_anim_claymore_helper_80058780(SnaInitWork *work, int time); +void sna_act_unk2_80051170(TARGET *pTarget); +void sna_act_helper3_helper_80056650(SnaInitWork *work, int time); +void sna_act_unk_helper3_80055DD8(SnaInitWork *work, int time); +void sna_anim_mini_cutscene_800559D8(SnaInitWork *work, int time); +void sna_anim_dying_80055524(SnaInitWork *work, int time); +void NavigateSetTarget(NAVIGATE *autoMove, HZD_HDL *pHzd, SVECTOR *pVec); +int GM_ZoneNavi(NAVIGATE *pAutoMove, CONTROL *pControl); +void sna_80057A90(SnaInitWork *work, int time); +void sna_8004EF14(SnaInitWork *work); +void *NewSnake(int name, int where, int argc, char **argv); +int sna_act_unk_helper2_helper_helper_8005C974(HZD_HDL *pHzd, int, int, CONTROL *pControl); +void sna_act_helper2_helper2_80033054(int id, SVECTOR *vec); +int sna_ration_available_8004FB4C(void); + +#endif // __MGS_CHARA_SNA_INIT_H__ diff --git a/components/mgs/mgs/source/chara/torture/bed.c b/components/mgs/mgs/source/chara/torture/bed.c new file mode 100644 index 00000000..61765a4c --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/bed.c @@ -0,0 +1,138 @@ +#include "bed.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "strcode.h" + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + OBJECT_NO_ROTS body; + MATRIX light[2]; + SVECTOR pos; +} Work; + +#define EXEC_LEVEL GV_ACTOR_USER +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_GBOUND | DG_FLAG_SHADE | DG_FLAG_ONEPIECE ) + +static inline int CheckMessages(Work *work) +{ + int found; + GV_MSG *msg; + int i; + + found = 0; + + msg = &work->control.msg[work->control.n_msg] - 1; + for (i = work->control.n_msg; i > 0; i--) + { + if (msg->message[0] == HASH_TURN) + { + found = 1; + work->control.turn.vx = msg->message[1]; + } + else if (msg->message[0] == HASH_LEAVE2) + { + work->control.mov.vy = 500; + } + + msg--; + } + + return found; +} + +static void Act(Work *work) +{ + int found; + + if (work->control.n_msg == 0) + { + found = 0; + } + else + { + found = CheckMessages(work); + } + + GM_ActControl(&work->control); + GM_ActObject2((OBJECT *)&work->body); + DG_GetLightMatrix(&work->control.mov, work->light); + + if (found != 0) + { + DG_MovePos(&work->pos); + + if (GM_PlayerBody) + { + DG_PutObjs(GM_PlayerBody->objs); + } + } +} + +static void Die(Work *work) +{ + GM_FreeControl(&work->control); + GM_FreeObject((OBJECT *)&work->body); +} + +int BedGetResources_800C6FD8(Work *work, int name, int map) +{ + CONTROL *control; + OBJECT_NO_ROTS *body; + u_short model; + + control = &work->control; + if (GM_InitControl(control, name, map) < 0) + { + return -1; + } + + GM_ConfigControlString(control, GCL_GetOption('p'), GCL_GetOption('d')); + GM_ConfigControlHazard(control, 0, -2, -2); + + body = &work->body; + + if (GCL_GetOption('m')) + { + model = GCL_StrToInt(GCL_NextStr()); + } + else + { + model = GV_StrCode("03b_o1"); + } + + GM_InitObjectNoRots(body, model, BODY_FLAG, 0); + GM_ConfigObjectLight((OBJECT *)body, work->light); + + if (!GCL_GetOption('v')) + { + return -1; + } + + GCL_StrToSV(GCL_NextStr(), (short *)&work->pos); + return 0; +} + +void *NewTortureBed(int name, int where) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "bed.c"); + + if (BedGetResources_800C6FD8(work, name, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/torture/bed.h b/components/mgs/mgs/source/chara/torture/bed.h new file mode 100644 index 00000000..37860cac --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/bed.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_BED_H__ +#define __MGS_CHARA_BED_H__ + +void *NewTortureBed(int name, int where); + +#endif // __MGS_CHARA_BED_H__ diff --git a/components/mgs/mgs/source/chara/torture/boxall.c b/components/mgs/mgs/source/chara/torture/boxall.c new file mode 100644 index 00000000..68d79b71 --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/boxall.c @@ -0,0 +1,362 @@ +#include "boxall.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "sound/g_sound.h" + +typedef struct _BoxallWork +{ + GV_ACT actor; + CONTROL control; + OBJECT_NO_ROTS body; + MATRIX light[2]; + int timer; + int f104; + LINE_F4 text_line[2]; + DG_PRIM *prim; + SVECTOR vertices[4]; + int proc_id; +} Work; + +extern GM_CameraSystemWork GM_Camera; + +#define EXEC_LEVEL GV_ACTOR_USER +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_GBOUND | DG_FLAG_SHADE | DG_FLAG_ONEPIECE ) + +static int GetWorldPosition(SVECTOR *out, SVECTOR *in) +{ + int z; + MATRIX *m; + + m = &DG_Chanl(0)->eye_inv; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(in); + gte_rtps(); + gte_stsxy(out); + + gte_stsz(&z); + return z > 0; +} + +static void DrawItemText(Work *work) +{ + SVECTOR world; + SVECTOR pos; + SVECTOR *mov; + int dx, dz; + OT_TYPE *ot; + LINE_F4 *text_line; + int x, y; + + if (GM_Camera.first_person == 0) + { + work->f104 = 0; + return; + } + + mov = &work->control.mov; + + dx = GM_PlayerControl->mov.vx - mov->vx; + if (dx < 0) + { + dx = -dx; + } + + dz = GM_PlayerControl->mov.vz - mov->vz; + if (dz < 0) + { + dz = -dz; + } + + if (dx >= 2000 || dz >= 2000) + { + return; + } + + if ((work->f104++ % 60) < 21) + { + return; + } + + ot = DG_ChanlOTag(1); + text_line = &work->text_line[GV_Clock]; + + pos = work->control.mov; + pos.vy += 250; + + if (!GetWorldPosition(&world, &pos)) + { + return; + } + + x = world.vx + 160; + y = world.vy + 104; + + if (y < -32 || y > 300) + { + return; + } + + y = MAX(y, 32); + y = MIN(y, FRAME_HEIGHT); + + text_line->x2 = text_line->x1 = x + 16; + text_line->y2 = text_line->y1 = y - 16; + text_line->x0 = x; + text_line->y0 = y; + text_line->y3 = y - 23; + + addPrim(ot, text_line); + + MENU_Color(200, 200, 200); + MENU_Locate(text_line->x1 + 5, text_line->y1 - 8, 0x10); + MENU_Printf("SNAKE'S ITEMS"); + + MENU_Color(1, 1, 1); + MENU_Locate(text_line->x1 + 6, text_line->y1 - 7, 0x10); + text_line->x2 = text_line->x3 = MENU_Printf("SNAKE'S ITEMS") + 3; +} + +static inline void DrawMessage(short *x, short *y, char *message) +{ + MENU_Color(255, 255, 255); + MENU_Locate(*x + 160, *y + 104, 0x12); + MENU_Printf(message); + + MENU_Color(1, 1, 1); + MENU_Locate(*x + 161, *y + 105, 0x12); + MENU_Printf(message); + + MENU_ResetText(); +} + +static void DrawItemTextCollected(Work *work) +{ + SVECTOR pos; + MATRIX *m; + + m = &DG_Chanl(0)->eye_inv; + gte_SetRotMatrix(m); + gte_SetTransMatrix(m); + gte_ldv0(&work->control.mov); + gte_rtps(); + gte_stsxy(&pos); + + if (work->timer < 16) + { + pos.vy -= work->timer; + } + else + { + pos.vy -= 16; + } + + if (work->timer == 2) + { + GM_SeSet2(0, 63, SE_ITEM_GET); + } + + DrawMessage(&pos.vx, &pos.vy, "SNAKE'S ITEMS"); +} + +static int HasCollectedItem(Work *work) +{ + SVECTOR diff; + SVECTOR pos; + SVECTOR player; + int dist; + int py, y, dy; + + pos = work->control.mov; + pos.vy = 0; + + player = GM_PlayerPosition; + player.vy = 0; + + GV_SubVec3(&pos, &player, &diff); + dist = GV_VecLen3(&diff); + + py = GM_PlayerPosition.vy; + y = work->control.mov.vy; + + dy = py - y; + if (dy < 0) + { + dy = y - py; + } + + return dist < 500 && dy < 1000; +} + +static void Act(Work *work) +{ + if (work->timer > 0) + { + if (++work->timer < 20) + { + DrawItemTextCollected(work); + } + else + { + GV_DestroyActor(&work->actor); + } + } + else + { + GM_ActControl(&work->control); + GM_ActObject2((OBJECT *)&work->body); + DG_GetLightMatrix(&work->control.mov, work->light); + + work->control.turn.vy += 64; + work->control.turn.vy &= 4095; + + DrawItemText(work); + + if (HasCollectedItem(work)) + { + work->timer++; + DG_InvisibleObjs(work->body.objs); + GCL_ExecProc(work->proc_id, NULL); + } + } +} + +static void Die(Work *work) +{ + GM_FreeControl(&work->control); + GM_FreeObject((OBJECT *)&work->body); + GM_FreePrim(work->prim); +} + +static int InitPrim(Work *work) +{ + int raise; + DG_PRIM *prim; + DG_TEX *tex; + int i, j; + POLY_FT4 *poly; + SVECTOR *vertices; + + raise = 250; + + prim = GM_MakePrim(DG_PRIM_POLY_FT4, 1, work->vertices, NULL); + if (prim == NULL) + { + return -1; + } + + prim->raise = raise; + + tex = DG_GetTexture(GV_StrCode("shadow")); + if (tex == NULL) + { + return -1; + } + + for (i = 0; i < 2; i++) + { + poly = prim->packs[i]; + + setPolyFT4(poly); + setSemiTrans(poly, 1); + setRGB0(poly, 72, 72, 72); + DG_SetPacketTexture4(poly, tex); + } + + vertices = work->vertices; + vertices[0].vx = -500; + vertices[0].vy = 0; + vertices[0].vz = -500; + vertices[1].vx = 500; + vertices[1].vy = 0; + vertices[1].vz = -500; + vertices[2].vx = -500; + vertices[2].vy = 0; + vertices[2].vz = 500; + vertices[3].vx = 500; + vertices[3].vy = 0; + vertices[3].vz = 500; + + for (i = 0; i < 3; i++) + { + for (j = 0; j < 3; j++) + { + prim->world.m[i][j] = (i == j) ? 4096 : 0; + } + } + + prim->world.t[0] = work->control.mov.vx; + prim->world.t[1] = work->control.mov.vy; + prim->world.t[2] = work->control.mov.vz; + prim->world.t[1] = 0; + + work->prim = prim; + return 0; +} + +static int GetResources(Work *work, int name, int map) +{ + CONTROL *control; + char *pos, *dir; + unsigned short model; + OBJECT_NO_ROTS *body; + int i; + + control = &work->control; + if (GM_InitControl(control, name, map) < 0) + { + return -1; + } + + pos = GCL_GetOption('p'); + dir = GCL_GetOption('d'); + GM_ConfigControlString(control, pos, dir); + GM_ConfigControlHazard(control, 500, -2, -2); + + model = GCL_StrToInt(GCL_GetOption('m')); + work->proc_id = GCL_StrToInt(GCL_GetOption('e')); + + body = &work->body; + GM_InitObjectNoRots(body, model, BODY_FLAG, 0); + GM_ConfigObjectLight((OBJECT *)body, work->light); + + work->timer = 0; + work->f104 = 0; + + for (i = 0; i < 2; i++) + { + setLineF4(&work->text_line[i]); + setRGB0(&work->text_line[i], 255, 255, 255); + } + + if (InitPrim(work) < 0) + { + return -1; + } + + return 0; +} + +void *NewAllItemBox(int name, int where) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "boxall.c"); + + if (GetResources(work, name, where) >= 0) + { + return (void *)work; + } + + GV_DestroyActor(&work->actor); + } + + return NULL; +} diff --git a/components/mgs/mgs/source/chara/torture/boxall.h b/components/mgs/mgs/source/chara/torture/boxall.h new file mode 100644 index 00000000..394b5c97 --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/boxall.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_BOXALL_H__ +#define __MGS_CHARA_BOXALL_H__ + +void *NewAllItemBox(int name, int where); + +#endif // __MGS_CHARA_BOXALL_H__ diff --git a/components/mgs/mgs/source/chara/torture/info.c b/components/mgs/mgs/source/chara/torture/info.c new file mode 100644 index 00000000..f2a974d6 --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/info.c @@ -0,0 +1,195 @@ +#include "info.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" + +typedef struct _InfoWork +{ + GV_ACT actor; + int f20; + int f24; + int f28; + int f2C; + int f30; + int f34; + int f38; + DG_TEX *tex[2]; + POLY_FT4 poly[2][2]; +} Work; + +int info_alive = FALSE; + +#define EXEC_LEVEL GV_ACTOR_AFTER2 + +static void Act(Work *work) +{ + int f24; + OT_TYPE *ot; + POLY_FT4 *poly1; + POLY_FT4 *poly2; + int w, h; + + if (!info_alive) + { + GV_DestroyActor(&work->actor); + return; + } + + f24 = work->f24++; + + ot = DG_ChanlOTag(1); + + poly1 = &work->poly[GV_Clock][0]; + poly2 = &work->poly[GV_Clock][1]; + + switch (work->f20) + { + case 0: + w = f24 * work->f34; + if (work->tex[0]->w < w) + { + w = work->tex[0]->w; + work->f20 = 1; + work->f24 = 0; + } + + work->f28 = w; + break; + + case 1: + w = f24 * work->f38; + if (work->tex[1]->w < w) + { + w = work->tex[1]->w; + } + + work->f2C = w; + + h = f24 * work->f38; + if (work->tex[1]->h < h) + { + h = work->tex[1]->h; + } + + work->f30 = h; + + if (h == work->tex[1]->h) + { + if (w == work->tex[1]->w) + { + work->f20++; + } + } + break; + + default: + work->f24--; + break; + } + + poly1->x1 = poly1->x3 = work->f28 + 188; + poly2->x1 = poly2->x3 = work->f2C + 188; + poly2->y2 = poly2->y3 = work->f30 + 8; + + addPrim(ot, poly2); + addPrim(ot, poly1); +} + +static void Die(Work *work) +{ + /* do nothing */ +} + +static int GetResources(Work *work, u_short name1, u_short name2, int *abe) +{ + DG_TEX **texlist; + POLY_FT4 *poly; + int i; + DG_TEX *tex; + + texlist = work->tex; + texlist[0] = DG_GetTexture(name1); + texlist[1] = DG_GetTexture(name2); + + if (texlist[0] == NULL || texlist[1] == NULL) + { + return -1; + } + + poly = (POLY_FT4 *)work->poly; + for (i = 0; i < 4; i++, poly++) + { + setPolyFT4(poly); + setRGB0(poly, 128, 128, 128); + + if (i & 1) + { + tex = texlist[1]; + } + else + { + tex = texlist[0]; + } + + DG_SetPacketTexture4(poly, tex); + + if (abe[i & 1] != 0) + { + SetSemiTrans(poly, 1); + } + + switch (i & 1) + { + case 0: + poly->x0 = poly->x2 = 188; + poly->y0 = poly->y1 = 9; + poly->x1 = poly->x3 = 188; + poly->y2 = poly->y3 = tex->h + 9; + break; + + case 1: + poly->x0 = poly->x2 = 188; + poly->y0 = poly->y1 = 8; + poly->x1 = poly->x3 = 188; + poly->y2 = poly->y3 = 8; + break; + } + } + + work->f34 = 8; + work->f38 = 8; + work->f20 = 0; + work->f24 = 0; + work->f30 = 0; + work->f2C = 0; + work->f28 = 0; + + info_alive = 1; + return 0; +} + +void *NewTortureInfo(u_short name1, u_short name2, int *abe) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "info.c"); + + if (GetResources(work, name1, name2, abe) >= 0) + { + return (void *)work; + } + + GV_DestroyActor(&work->actor); + } + + return NULL; +} + +void TortureInfoKill(void) +{ + info_alive = 0; +} diff --git a/components/mgs/mgs/source/chara/torture/info.h b/components/mgs/mgs/source/chara/torture/info.h new file mode 100644 index 00000000..bdd64727 --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/info.h @@ -0,0 +1,9 @@ +#ifndef __MGS_CHARA_INFO_H__ +#define __MGS_CHARA_INFO_H__ + +#include + +void *NewTortureInfo(u_short name1, u_short name2, int *abe); +void TortureInfoKill(void); + +#endif // __MGS_CHARA_INFO_H__ diff --git a/components/mgs/mgs/source/chara/torture/jfamas.c b/components/mgs/mgs/source/chara/torture/jfamas.c new file mode 100644 index 00000000..63de9834 --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/jfamas.c @@ -0,0 +1,94 @@ +#include "jfamas.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" +#include "okajima/bullet.h" +#include "anime/animconv/anime.h" +#include "sound/g_sound.h" + +typedef struct _Work +{ + GV_ACT actor; + OBJECT_NO_ROTS body; + CONTROL *root_ctrl; + OBJECT *root_obj; + int unit; + int *trigger; + int side; +} Work; + +#define EXEC_LEVEL GV_ACTOR_USER +#define BODY_MODEL GV_StrCode("famas") +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE | DG_FLAG_GBOUND | DG_FLAG_ONEPIECE ) + +SVECTOR famas_pos = {5, 65036, 80}; + +static void Act(Work *work) +{ + MATRIX world; + int trigger; + + GM_SetCurrentMap(work->root_ctrl->map->index); + DG_GroupObjsEx(work->body.objs); + + trigger = *work->trigger; + + if (work->root_obj->objs->flag & DG_FLAG_INVISIBLE) + { + DG_InvisibleObjs(work->body.objs); + } + else + { + DG_VisibleObjs(work->body.objs); + } + + if (trigger & 2) + { + DG_SetPos(&work->body.objs->world); + DG_MovePos(&famas_pos); + ReadRotMatrix(&world); + NewBullet(&world, work->side, 0, 1); + GM_SeSet(&work->root_ctrl->mov, SE_FAMAS_SHOT); + AN_FamasFlash(&world); + } +} + +static void Die(Work *work) +{ + GM_FreeObject((OBJECT *)&work->body); +} + +static int GetResources(Work *work, OBJECT *root_obj, int unit) +{ + OBJECT_NO_ROTS *body; + + body = &work->body; + GM_InitObjectNoRots(body, BODY_MODEL, BODY_FLAG, 0); + GM_ConfigObjectRoot((OBJECT *)body, root_obj, unit); + return 0; +} + +void *NewJohnnyFamas(CONTROL *root_ctrl, OBJECT *root_obj, int unit, int *trigger) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "jfamas.c"); + if (GetResources(work, root_obj, unit) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + work->root_ctrl = root_ctrl; + work->root_obj = root_obj; + work->unit = unit; + work->trigger = trigger; + work->side = ENEMY_SIDE; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/torture/jfamas.h b/components/mgs/mgs/source/chara/torture/jfamas.h new file mode 100644 index 00000000..0d1545c1 --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/jfamas.h @@ -0,0 +1,8 @@ +#ifndef __MGS_CHARA_JFAMAS_H__ +#define __MGS_CHARA_JFAMAS_H__ + +#include "game/game.h" // for CONTROL, OBJECT + +void *NewJohnnyFamas(CONTROL *root_ctrl, OBJECT *root_obj, int unit, int *trigger); + +#endif // __MGS_CHARA_JFAMAS_H__ diff --git a/components/mgs/mgs/source/chara/torture/johnny.c b/components/mgs/mgs/source/chara/torture/johnny.c new file mode 100644 index 00000000..99bfa5db --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/johnny.c @@ -0,0 +1,3854 @@ +#include "johnny.h" +#include "jfamas.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "libhzd/libhzd.h" +#include "game/game.h" +#include "game/navi.h" +#include "linkvar.h" +#include "game/vibrate.h" +#include "chara/snake/sna_init.h" +#include "sound/g_sound.h" +#include "strcode.h" + +#include "chara/snake/shadow.h" // for NewShadow +#include "enemy/glight.h" // for NewGunLight +#include "okajima/blood.h" // for NewBlood +#include "okajima/hiyoko.h" // for NewHiyoko +#include "takabe/fadeio.h" // for NewFadeInOut +#include "takabe/cineutil.h" // for OpenCinemaScreen, etc. + +struct _Work; +typedef void (*ACTION)(struct _Work *work, int action); + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + OBJECT body; + MOTION_CONTROL m_ctrl; + MOTION_SEGMENT m_segs1[17]; + MOTION_SEGMENT m_segs2[17]; + SVECTOR rots[16]; + SVECTOR adjust[16]; + MATRIX light[2]; + char pad1[0x48]; + TARGET *target; + HOMING *homing; + GV_ACT *weapon; + GV_ACT *gunlight; + int *enable_gunlight; + int trigger; + GV_ACT *shadow; + int unk83C[5]; + SVECTOR unk850[5][16]; + SVECTOR *unkAD0; + int unkAD4; + int unkAD8; + NAVIGATE nav; + SVECTOR unkAFC; + SVECTOR unkB04; + int unkB0C; + int unkB10; + int unkB14; + int unkB18; + int unkB1C; + char padB20[4]; + int unkB24; + char padB28[4]; + int player_status; + SVECTOR unkB30; + ACTION unkB38; + int unkB3C; + char padB40[4]; + ACTION unkB44; + ACTION unkB48; + short unkB4C; + short unkB4E; + short unkB50; + short unkB52; + short unkB54; + short unkB56; + short unkB58; + short unkB5A; + short unkB5C; + short unkB5E; + SVECTOR unkB60; + int unkB68; + unsigned short unkB6C; + unsigned short unkB6E; + unsigned short unkB70; + unsigned short unkB72; + int unkB74; + int vox_ids[18]; + int proc_id[8]; + int unkBE0; + int unkBE4; +} Work; + +#define EXEC_LEVEL GV_ACTOR_USER + +#define SEGMENT_ATR (0) + +#define TARGET_FLAG ( TARGET_POWER | TARGET_CAPTURE | TARGET_PUSH | TARGET_TOUCH | TARGET_SEEK ) + +unsigned char johnny_vibration1_800C32C0[] = {0xFF, 0x0A, 0x00, 0x00}; +unsigned char johnny_vibration2_800C32C4[] = {0xFF, 0x04, 0x00, 0x00}; + +int s03c_dword_800C32C8 = 0x00000000; + +SVECTOR s03c_dword_800C32CC = {0, 0, 7000}; +SVECTOR s03c_dword_800C32D4 = {0, 0, 100}; +SVECTOR s03c_dword_800C32DC = {64512, 0, 0}; +SVECTOR s03c_dword_800C32E4 = {0, 0, 3500, 0}; +SVECTOR s03c_dword_800C32EC = {0, 0, 300}; +SVECTOR target_size = {300, 750, 300}; + +extern GM_SnakeCameraWork GM_SnakeCamera; +extern GM_CameraSystemWork GM_Camera; + +extern int s03c_dword_800C33D8; + +void NavigateSetTargetPlayer(NAVIGATE *pAutoMove); + +void AN_Fog(SVECTOR *svec); +void AN_Sleep( SVECTOR *svec ); +void s03b_boxall_800C9328(void); +void s03b_boxall_800C9404(void); +void s03b_boxall_800C93F0(int, int); +int s03b_boxall_800C93AC(int arg0); +int s03b_boxall_800C95EC(void); +void *AN_Unknown_800CA1EC(MATRIX *mat, int mark); + +static void Johnny_800C6FC0(Work *work, int arg1); +static void Johnny_800C7A64(Work *work, int action); +static void Johnny_800C7160(Work *work, int arg1); +static void Johnny_800C7804(Work *work, int arg1); +static void Johnny_800C7BF8(Work *work, int action); +static void Johnny_800C7F78(Work *work, int action); + +static inline void ClearAdjust(Work *work) +{ + int i; + + for (i = 0; i < 16; i++) + { + work->adjust[i] = DG_ZeroVector; + } +} + +static inline void SetAction(Work *work, int action) +{ + if (work->body.action != action) + { + GM_ConfigObjectAction(&work->body, action, 0, 4); + } +} + +static int Johnny_800C4194(Work *work) +{ + SVECTOR player_pos; + SVECTOR control_mov; + + player_pos = GM_PlayerPosition; + control_mov = work->control.mov; + if (HZD_OnlineHazardCheck(work->control.map->hzd, &player_pos, &control_mov, HZD_CHK_SEGMENT, SEGMENT_ATR) == 0) + { + return -1; + } + + HZD_GetOnlinePoint(&control_mov); + GV_SubVec3(&control_mov, &player_pos, &player_pos); + return GV_VecLen3(&player_pos); +} + +static void Johnny_800C5520(Work *work, int arg1); +static void Johnny_800C588C(Work *work, int arg1); + +static void Johnny_800C424C(Work *work, ACTION func) +{ + if (work->unkB1C & 0x100000) + { + return; + } + + if (work->unkB38 != Johnny_800C5520 && work->unkB38 != Johnny_800C588C && GM_StreamStatus() == -1 && + GV_Time % 320 == 0) + { + work->unkB44 = func; + work->unkB38 = Johnny_800C5520; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } +} + +static void Johnny_800C430C(Work *work) +{ + SVECTOR svec1, svec2; + + work->unkAD4 = 0; + + svec2 = work->control.mov; + svec2.vy = 0; + + GV_SubVec3(work->unkAD0, &svec2, &svec1); + work->control.turn.vy = GV_VecDir2(&svec1); + + if (GV_VecLen3(&svec1) < 320) + { + work->unkAD4 = 1; + } +} + +static int Johnny_800C4388(Work *work, int hash) +{ + int i; + unsigned short *triggers; + + if (work->unkB5C != 0) + { + return 1; + } + + triggers = work->control.evt.inside; + + for (i = work->control.evt.n_inside; i > 0; i--, triggers++) + { + if (*triggers == hash) + { + return 1; + } + } + return 0; +} + +static int Johnny_800C43D0(unsigned short hash) +{ + int i; + unsigned short *triggers; + + triggers = GM_PlayerControl->evt.inside; + + for (i = GM_PlayerControl->evt.n_inside; i > 0; i--, triggers++) + { + if (*triggers == hash) + { + return 1; + } + } + return 0; +} + +static void Johnny_800C4418(Work *work) +{ + int i; + int new_unkB24; + unsigned short *triggers; + + new_unkB24 = 0; + + triggers = GM_PlayerControl->evt.inside; + for (i = GM_PlayerControl->evt.n_inside; i > 0; i--, triggers++) + { + if (*triggers == 0x41A5) + { + new_unkB24 |= 2; + } + if (*triggers == 0x6599) + { + new_unkB24 |= 8; + } + } + + if (GM_PlayerStatus & PLAYER_INTRUDE) + { + new_unkB24 |= 1; + } + + if ((new_unkB24 & 1) != (work->unkB24 & 1)) + { + if (new_unkB24 & 1) + { + if (work->unkB1C & 0x20) + { + work->unkB1C |= 0x40; + } + else + { + work->unkB1C |= 0x10; + } + } + else + { + if (!(work->unkB1C & 0x20)) + { + work->unkB1C |= 0x40000000; + } + work->unkB1C &= ~0x10; + work->unkB1C &= ~0x40; + } + } + + work->unkB24 = new_unkB24; +} + +static void Johnny_800C44F8(Work *work) +{ + if ((work->player_status & PLAYER_KETCHUP) != (GM_PlayerStatus & PLAYER_KETCHUP)) + { + if (GM_PlayerStatus & PLAYER_KETCHUP) + { + if (work->unkB1C & 0x20) + { + if (!(work->unkB1C & 2)) + { + work->unkB1C |= 0x100; + } + } + else + { + work->unkB1C |= 2; + } + } + else + { + if (!(work->unkB1C & 0x20)) + { + work->unkB1C |= 2; + } + work->unkB1C &= ~0x100; + } + } + work->player_status = GM_PlayerStatus; +} + +static void Johnny_800C4588(Work *work) +{ + work->unkB48 = work->unkB38; + work->unkB38 = Johnny_800C7F78; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; +} + +static void Johnny_800C794C(Work *work, int arg1); + +static void Johnny_800C45AC(Work* work) +{ + int fB24; + + fB24 = work->unkB24; + + if (work->unkB70 == 2 && !(work->unkB1C & 0x2)) + { + if (!(work->unkB1C & 0x20) && GM_Items[IT_Ketchup] == -1 && !(GM_PlayerStatus & PLAYER_KETCHUP)) + { + work->unkB1C |= 0x80002; + } + } + + if (fB24 & 0x1) + { + if (work->unkB1C & 0x40) + { + s03b_boxall_800C9328(); + GM_SeSet2(0, 63, SE_EXCLAMATION); + s03b_boxall_800C93F0(work->vox_ids[6], 4); + work->unkB0C = 6; + work->unkB10 = 0; + } + } + else if (work->unkB1C & 0x100) + { + Johnny_800C4588(work); + work->unkB48 = Johnny_800C794C; + } + + else if (work->unkB1C & 0x2 && GM_PlayerStatus & PLAYER_KETCHUP && !(work->unkB1C & 0x80000)) + { + work->unkB1C |= 0x08000000; + Johnny_800C4588(work); + work->unkB1C |= 0x80000; + } +} + +static int Johnny_800C470C(Work *work) +{ + return (work->unkB24 & 1) ^ 1; +} + +static void Johnny_800C4720(Work *work, int arg1) +{ + work->unkAD0 = work->unk850[arg1]; +} + +static void Johnny_800C4734(Work *work) +{ + SVECTOR svec; + SVECTOR *adjust; + int to; + + GV_SubVec3(&s03c_dword_800C32CC, &work->control.mov, &svec); + to = (GV_VecDir2(&svec) - work->control.rot.vy) & 0xFFF; + if (to > 2048) + { + to -= 4096; + } + if (to > 768) + { + to = 768; + } + if (to < -768) + { + to = -768; + } + + adjust = work->adjust; + adjust[6].vy = GV_NearExp4(adjust[6].vy, to); +} + +static void Johnny_800C47C4(Work *work) +{ + SVECTOR pos; + + GV_SubVec3(&GM_PlayerPosition, &work->control.mov, &pos); + work->control.turn.vy = GV_VecDir2(&pos); +} + +static int Johnny_800C4804(Work *work) +{ + SVECTOR diff; + SVECTOR mov; + + mov = work->control.mov; + mov.vy = GM_PlayerPosition.vy; + + GV_SubVec3(&GM_PlayerPosition, &mov, &diff); + return GV_VecLen3(&diff); +} + +static int Johnny_800C4860(Work *work) +{ + SVECTOR svec; + int vecdir; + int rotvy; + + GV_SubVec3(&GM_PlayerPosition, &work->control.mov, &svec); + vecdir = GV_VecDir2(&svec); + rotvy = work->control.rot.vy & 0xFFF; + + if (rotvy > 480 && rotvy < 3615) + { + if (rotvy - 480 < vecdir && vecdir < rotvy + 480) + { + return 1; + } + + return 0; + } + + if (rotvy < 480) + { + if (vecdir < rotvy + 480 || ((rotvy - 480) & 0xFFF) < vecdir) + { + return 1; + } + + return 0; + } + + if (rotvy - 480 < vecdir || vecdir < ((rotvy + 480) & 0xFFF)) + { + return 1; + } + + return 0; +} + +static void Johnny_800C4934(Work *work) +{ + SVECTOR svec1; + SVECTOR svec2; + SVECTOR svec3; + SVECTOR svec4; + SVECTOR *adjust; + int vecdir; + + svec2.vx = GM_PlayerBody->objs->objs[5].world.t[0]; + svec2.vy = GM_PlayerBody->objs->objs[5].world.t[1]; + svec2.vz = GM_PlayerBody->objs->objs[5].world.t[2]; + + svec3.vx = work->body.objs->objs[9].world.t[0]; + svec3.vy = work->body.objs->objs[9].world.t[1]; + svec3.vz = work->body.objs->objs[9].world.t[2]; + + GV_SubVec3(&svec2, &svec3, &svec1); + + svec4.vz = SquareRoot0(svec1.vx * svec1.vx + svec1.vz * svec1.vz); + svec4.vx = svec1.vy; + + vecdir = -GV_VecDir2(&svec4); + if (vecdir < -2048) + { + vecdir += 4096; + } + + adjust = work->adjust; + + adjust[2].vx = GV_NearExp4(adjust[2].vx, vecdir); + adjust[6].vx = GV_NearExp4(adjust[6].vx, vecdir); + adjust[7].vx = GV_NearExp4(adjust[7].vx, vecdir); + adjust[6].vy = 0; +} + +static void Johnny_800C4A64(Work *work) +{ + TARGET *target; + + target = work->target; + target->damaged &= ~TARGET_PUSH; + GM_PushTarget(target); +} + +void JohnnyExecProc_800C4A98(Work *work, int arg) +{ + GCL_ARGS args; + long argv[1]; + + args.argc = 1; + args.argv = argv; + argv[0] = arg; + + GCL_ExecProc(work->proc_id[2], &args); +} + +void JohnnySendEnter_800C4AD0(Work *work, int hash) +{ + GV_MSG msg; + + msg.address = hash; + msg.message[0] = GV_StrCode("入る"); // 入る = enter (HASH_ENTER) + msg.message[1] = work->control.name; + msg.message_len = 2; + + GV_SendMessage(&msg); + if (hash == GV_StrCode("rou_tobira")) + { + work->unkB5C = 1; + JohnnyExecProc_800C4A98(work, 1); + } + else + { + work->unkB5E = 1; + } +} + +void JohnnySendLeave_800C4B58(Work *work, int hash) +{ + GV_MSG msg; + + msg.address = hash; + msg.message[0] = GV_StrCode("出る"); // 出る = leave (HASH_LEAVE) + msg.message[1] = work->control.name; + msg.message_len = 2; + + GV_SendMessage(&msg); + if (hash == GV_StrCode("rou_tobira")) + { + work->unkB5C = 0; + JohnnyExecProc_800C4A98(work, 0); + } + else + { + work->unkB5E = 0; + } +} + +static void Johnny_800C4BDC(Work *work, int a1, int a2, ACTION func) +{ + work->unkB58 = a1; + work->unkB5A = a2; + work->unkB44 = func; +} + +static void Johnny_800C4BEC(Work *work, int index) +{ + MATRIX rot; + + DG_SetPos(&work->body.objs->objs[index].world); + DG_MovePos(&s03c_dword_800C32D4); + DG_RotatePos(&s03c_dword_800C32DC); + ReadRotMatrix(&rot); + NewBlood(&rot, 1); +} + +static int Johnny_800C4C54(Work *work) +{ + unsigned short unkB70; + + unkB70 = work->unkB70; + if (work->unkB6C >= 5) + { + work->unkB70 = 3; + return 1; + } + + switch (unkB70) + { + case 0: + return 0; + + case 2: + if (!(work->unkB1C & 2)) + { + return 0; + } + + default: + if (unkB70 != 1) + { + // FIXME: this is probably just "case 1:", but + // couldn't find a way to match it with it. + return (work->unkB1C & 2) > 0; + } + return 1; + } +} + +static void Johnny_800C4CCC(Work *work) +{ + GCL_ARGS args; + long argv[4]; + + if (work->unkB68 >= 0) + { + args.argc = 4; + args.argv = argv; + + if (work->unkB70 < 3 && (work->unkB1C & 2)) + { + work->unkB70 = 3; + } + + argv[0] = work->unkB70; + argv[1] = work->unkB1C & 1; + argv[2] = work->unkB0C == 3; + argv[3] = (work->unkB1C & 0x1000) > 0; + + GCL_ExecProc(work->proc_id[0], &args); + GV_DestroyActor(&work->actor); + } +} + +void JohnnyExecProc_800C4D8C(Work *work) +{ + GCL_ARGS args; + long argv[1]; + + args.argc = 1; + args.argv = argv; + argv[0] = s03c_dword_800C32C8; + + GCL_ExecProc(work->proc_id[6], &args); +} + +static void Johnny_800C4DCC(Work *work) +{ + if (!(work->unkB1C & 8)) + { + return; + } + work->unkB1C &= ~8; + + if (work->unkB14 < 0) + { + Johnny_800C4CCC(work); + return; + } + + if (work->unkB1C & 0x2000) + { + work->unkB1C = work->unkB1C & ~0x2000; + if (Johnny_800C4C54(work) != 0) + { + Johnny_800C4CCC(work); + return; + } + work->unkB10 = 0; + work->unkB0C = work->unkB14; + } + else + { + work->unkB0C = work->unkB14; + work->unkB10 = 0; + } +} + +static void Johnny_800C4E5C(Work *work) +{ + if (work->unkB72 != 0) + { + if (--work->unkB72 == 0) + { + GM_SeSet(&work->unkB04, SE_DOOR_CLOSE3); + } + } +} + +static void Johnny_800C4E9C(Work *work) +{ + MATRIX mat1; + MATRIX mat2; + SVECTOR svec; + MATRIX *world; + + world = &work->body.objs->objs[6].world; + ReadRotMatrix(&mat1); + DG_TransposeMatrix(&mat1, &mat2); + MulMatrix0(&mat2, world, &mat2); + DG_MatrixRotZYX(&mat2, &svec); + SetRotMatrix(&mat1); + work->control.radar_param.dir = svec.vy + work->control.rot.vy; +} + +static void Johnny_800C4F24(Work *work, int arg1) +{ + if (arg1 != 0) + { + if (!(work->unkB1C & 0x10000000)) + { + GM_EnemyWatchCount++; + } + work->unkB1C |= 0x10000000; + GM_AlertModeSet(3); + return; + } + work->unkB1C &= ~0x10000000; + GM_AlertModeSet(0); + GM_AlertLevel = 0; +} + +static void Johnny_800C4FAC(Work *work) +{ + CONTROL *control; + int frame; + + frame = work->m_ctrl.info1.frame; + control = &work->control; + + switch (work->body.action) + { + case 1: + if (frame == 0) + { + GM_SeSetSize(&control->mov, 160, 1500); + } + if (frame == 12) + { + GM_SeSetSize(&control->mov, 161, 1500); + } + break; + + case 2: + case 33: + if (frame == 0) + { + GM_SeSetSize(&control->mov, 160, 1500); + } + if (frame == 8) + { + GM_SeSetSize(&control->mov, 161, 1500); + } + break; + } +} + +static void Johnny_800C7428(Work *work, int action); +static void Johnny_800C6C10(Work *work, int action); + +static void Johnny_800C5064(Work *work) +{ + if (Johnny_800C4388(work, 0x41A5) != 0 || work->unkB5C == 1) + { + work->unkB38 = Johnny_800C7428; + } + else + { + work->unkB48 = Johnny_800C6C10; + work->unkB38 = Johnny_800C7F78; + } + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; +} + +static void Johnny_800C50D0(Work *work) +{ + if (!(work->unkB1C & 0x10) || !(GM_PlayerStatus & PLAYER_INTRUDE)) + { + work->unkB74 = 0; + return; + } + if (work->unkB74 < 0) + { + return; + } + if (work->unkB74 >= 24) + { + return; + } + work->unkB74++; +} + +// Similar to sna_act_unk2_80051170 +// arg type could be different +static void Johnny_800C5124(TARGET *target) +{ + target->a_mode = 0; + target->damage = 0; + target->damaged &= ~(TARGET_TOUCH | TARGET_POWER | TARGET_CAPTURE); + target->force = DG_ZeroVector; +} + +static void Johnny_800C8400(Work *work, int action); +static void Johnny_800C854C(Work *work, int action); +static void Johnny_800C8654(Work *work, int action); +static void Johnny_800C8B14(Work *work, int action); +static void Johnny_800C9144(Work *work, int action); + +static void Johnny_800C5168(Work *work) +{ + TARGET *target; + int a_mode; + + if (work->unkB1C & 0x1000) + { + return; + } + + if (work->unkB1C < 0) + { + Johnny_800C5124(work->target); + return; + } + + target = work->target; + a_mode = target->a_mode; + + if (target->damaged & 0x4) + { + if (!(work->unkB1C & 0x200)) + { + if (a_mode == 3) + { + work->unkB38 = Johnny_800C8400; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + + GM_SeSet(&work->control.mov, SE_PUNCH_HIT); + GM_SeSet(&work->control.mov, VO_ENEMY_PUNCHED); + + AN_Fog(&work->control.mov); + + if (target->faint <= 0) + { + work->unkB38 = Johnny_800C9144; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + } + else if (a_mode == 4) + { + work->unkB38 = Johnny_800C8400; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + + GM_SeSet(&work->control.mov, SE_KICK_HIT); + GM_SeSet(&work->control.mov, VO_ENEMY_THROWN); + + AN_Fog(&work->control.mov); + + if (target->faint <= 0) + { + work->unkB38 = Johnny_800C9144; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + } + else if (a_mode == 1) + { + work->unkB38 = Johnny_800C8400; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + + work->unkB1C |= 0x400; + + AN_Fog(&work->control.mov); + GM_SeSet(&work->control.mov, VO_ENEMY_PUNCHED); + } + else + { + work->unkB30 = target->force; + + work->unkB38 = Johnny_800C854C; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + + work->unkB30.vx *= 4; + work->unkB30.vz *= 4; + + AN_Fog(&work->control.mov); + GM_SeSet(&work->control.mov, VO_ENEMY_THROWN); + } + } + else + { + Johnny_800C4BEC(work, 6); + } + + Johnny_800C5124(target); + + work->unkB1C &= ~0x800; + UnsetTargetClass( work->target, TARGET_DOWN ); + + s03b_boxall_800C9328(); + } + else if (target->damaged & 0x2) + { + if (a_mode == 36) + { + target->class |= TARGET_DOWN; + + work->unkB38 = Johnny_800C8654; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + + GM_ConfigMotionAdjust(&work->body, NULL); + ClearAdjust(work); + + Johnny_800C5124(target); + s03b_boxall_800C9328(); + } + else if (!(work->unkB1C & 0x800)) + { + target->class |= TARGET_DOWN; + + GM_SeSet(&work->control.mov, VO_ENEMY_GRABBED); + + work->unkBE4 = 0; + work->unkB38 = Johnny_800C8B14; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + + GM_ConfigMotionAdjust(&work->body, NULL); + ClearAdjust(work); + + s03b_boxall_800C9328(); + } + } +} + +static void Johnny_800C54A8(Work *work, int arg1) +{ + if (arg1 == 0) + { + SetAction(work, 0); + Johnny_800C47C4(work); + } + + if (arg1 == 60) + { + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB38 = work->unkB44; + } +} + +static void Johnny_800C5520(Work *work, int action) +{ + int index; + + if (action == 0) + { + index = (work->unkB1C & 0x4000) ? 10 : 0; + + s03b_boxall_800C9328(); + s03b_boxall_800C93F0(work->vox_ids[index], 4); + + work->unkBE0 = work->vox_ids[index]; + + SetAction(work, 0); + + GM_ConfigMotionAdjust(&work->body, work->adjust); + } + + switch (work->unkB4C) + { + case 0: + if (work->unkBE0 == work->vox_ids[10]) + { + if (GM_StreamStatus() == 2) + { + SetAction(work, 15); + work->unkB4C = 1; + } + } + else if (GM_StreamStatus() == 2 && ++work->unkB4E == 42) + { + SetAction(work, 15); + work->unkB4C = 1; + work->unkB4E = 0; + } + break; + + case 1: + if (work->body.action == 15 && work->body.is_end != 0) + { + if (work->unkB1C & 0x4000) + { + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB38 = work->unkB44; + + GM_ConfigMotionAdjust(&work->body, NULL); + ClearAdjust(work); + } + else + { + GM_ConfigObjectAction(&work->body, 0, 0, 4); + work->unkB4C = 2; + } + } + break; + + case 2: + if (GM_StreamStatus() == -1) + { + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB38 = work->unkB44; + work->unkB1C |= 0x4000; + + GM_ConfigMotionAdjust(&work->body, NULL); + ClearAdjust(work); + } + break; + } +} + +static void Johnny_800C5A7C(Work *work, int arg1); + +static void Johnny_800C57D0(Work *work, int arg1) +{ + if (arg1 == 0) + { + work->control.turn.vy = 3072; + work->unkB74 = -1; + work->unkB1C |= 0x20; + SetAction(work, (GV_Time & 1) ? 10 : 11); + } + if (arg1 == 24) + { + GM_SeSet2(0, 0x3F, VO_ENEMY_YAWN); + } + + if (work->body.is_end != 0) + { + work->unkB38 = Johnny_800C5A7C; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } +} + +static void Johnny_800C588C(Work *work, int arg1) +{ + SVECTOR svec; + int status; + + if (arg1 == 0) + { + GV_SubVec3(&GM_PlayerPosition, &work->control.mov, &svec); + work->control.turn.vy = GV_VecDir2(&svec); + + SetAction(work, 0); + + s03b_boxall_800C9328(); + s03b_boxall_800C93F0(work->vox_ids[12], 4); + } + if (arg1 > 16) + { + status = GM_StreamStatus(); + if (status == -1) + { + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB74 = status; + work->unkB38 = work->unkB44; + } + } +} + +static void Johnny_800C594C(Work *work) +{ + int index; + + if (GM_StreamStatus() == -1 && GM_NoisePower != 0) + { + index = (GV_Time & 0x1) ? 14 : 16; + s03b_boxall_800C93AC(work->vox_ids[index]); + } +} + +static void Johnny_800C59B8(Work *work, int arg1) +{ + if (arg1 == 0) + { + work->control.turn.vy = 3072; + SetAction(work, 0); + } + GV_NearExp2V(&work->control.mov.vx, &work->unkAFC.vx, 3); + if ((work->unkB74 ^ (0x10 | 0x8)) == 0) + { + work->unkB44 = Johnny_800C59B8; + work->unkB38 = Johnny_800C588C; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + else if (arg1 == 90) + { + work->unkB44 = Johnny_800C59B8; + work->unkB38 = Johnny_800C54A8; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + else + { + Johnny_800C594C(work); + } +} + +static void Johnny_800C5A7C(Work *work, int action) +{ + SVECTOR pos; + + if (action == 0) + { + work->unkB1C |= 0x20; + work->control.radar_atr &= ~RADAR_SIGHT; + SetAction(work, 12); + work->unkB74 = -1; + } + + if (!(work->unkB1C & 0x10000) && ((action % 100) == 0)) + { + pos.vx = work->body.objs->objs[6].world.t[0]; + pos.vy = work->body.objs->objs[6].world.t[1]; + pos.vz = work->body.objs->objs[6].world.t[2]; + AN_Sleep(&pos); + } + + switch (work->unkB4C) + { + case 0: + if (!(action & 0x3F)) + { + GM_SeSet2(0, 63, VO_ENEMY_SNORE); + } + + if (action == 348) + { + work->unkB4C = 1; + SetAction(work, 13); + } + break; + + case 1: + if (work->body.is_end != 0) + { + work->unkB1C |= 0x10000; + + SetAction(work, 0); + AN_Unknown_800CA1EC(&work->body.objs->objs[6].world, 0); + + if (!(work->unkB1C & 0x2000000)) + { + s03b_boxall_800C93AC(work->vox_ids[3]); + work->unkB4C = 2; + work->unkB1C |= 0x2000000; + } + else + { + work->unkB14 = 2; + work->unkB1C |= 0x8; + work->control.radar_atr |= RADAR_SIGHT; + work->unkB1C &= ~0x10000; + } + + work->unkB1C &= ~0x20; + } + break; + + case 2: + if (action == 511) + { + SetAction(work, 10); + } + + if ((work->body.action == 10) && (work->body.is_end != 0)) + { + GM_ConfigObjectAction(&work->body, 0, 0, 4); + } + + if ((action >= 512) && (GM_StreamStatus() == -1)) + { + work->unkB4C = 3; + s03b_boxall_800C93AC(work->vox_ids[4]); + SetAction(work, 11); + } + break; + + case 3: + if ((work->body.action == 11) && (work->body.is_end != 0)) + { + GM_ConfigObjectAction(&work->body, 0, 0, 4); + } + + if ((work->body.action == 0) && (GM_StreamStatus() == -1)) + { + work->unkB14 = 2; + work->unkB1C |= 0x8; + work->control.radar_atr |= RADAR_SIGHT; + work->unkB1C &= ~0x10000; + } + break; + } +} + +static void Johnny_800C92E0(Work *work, int arg1); + +static void Johnny_800C5DE4(Work *work, int action) +{ + if (action == 0) + { + GM_ConfigMotionAdjust(&work->body, work->adjust); + work->unkAD0++; + SetAction(work, 1); + } + + Johnny_800C4734(work); + Johnny_800C430C(work); + + if (work->unkAD4 != 0) + { + if (((work->unkAD0->pad & 0xC00) >> 10) == 2) + { + if (work->unkB4C == 0) + { + GM_ConfigMotionAdjust(&work->body, NULL); + ClearAdjust(work); + + if (work->unkB6E == 1 || (work->unkB1C & 0x400000) != 0) + { + work->unkB14 = 0; + work->unkB1C |= 0x8; + } + else + { + work->unkB38 = Johnny_800C92E0; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + return; + } + work->unkB4C = 1; + } + } + else + { + GM_ConfigMotionAdjust(&work->body, NULL); + ClearAdjust(work); + + work->unkB44 = Johnny_800C5DE4; + + if (work->unkB1C & 0x20000000) + { + work->unkB38 = Johnny_800C5520; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB1C &= ~0x20000000; + return; + } + + if ((work->unkB74 ^ 0x18) == 0) + { + work->unkB44 = Johnny_800C5DE4; + work->unkB38 = Johnny_800C588C; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + return; + } + + if (GV_RandS(128) > 0 || (work->unkB1C & 0x100000) != 0 || GM_StreamStatus() != -1) + { + work->unkB38 = Johnny_800C54A8; + } + else + { + work->unkB38 = Johnny_800C5520; + } + + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + } + + Johnny_800C594C(work); +} + +static void Johnny_800C6268(Work *work, int action); + +static void Johnny_800C6054(Work *work, int action) +{ + int index; + + if (action == 0) + { + work->unkB74 = -1; + work->unkB1C |= 0x20; + + SetAction(work, 31); + + index = !(work->unkB1C & 0x800000) ? 1 : 2; + work->unkB1C |= 0x800000; + s03b_boxall_800C9328(); + s03b_boxall_800C93F0(work->vox_ids[index], 4); + GM_SeSet2(0, 0x3F, 0xB4); + } + if (work->unkB4C == 0) + { + if (work->body.is_end != 0) + { + SetAction(work, 32); + work->unkB4C = 1; + } + } + else if (GM_StreamStatus() == -1) + { + work->unkB38 = Johnny_800C6268; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } +} + +static void Johnny_800C6170(Work *work, int arg1) +{ + if (arg1 == 0) + { + SetAction(work, 0); + work->control.radar_atr &= ~RADAR_SIGHT; + } + if (arg1 == 300 && work->unkB4C == 0) + { + work->unkB14 = 4; + work->unkB4C = 1; + work->unkB1C |= 0x2000 | 0x8; + } +} + +static void Johnny_800C64B0(Work *work, int action); + +static void Johnny_800C6204(Work *work, int arg1) +{ + if (arg1 == 0) + { + work->unkB1C |= 0x20; + SetAction(work, 0); + work->unkB38 = Johnny_800C64B0; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } +} + +static void Johnny_800C6268(Work *work, int action) +{ + if (action == 0) + { + work->unkB1C |= 0x20; + work->unkAD0++; + SetAction(work, 33); + } + + Johnny_800C430C(work); + + if (work->unkAD4 != 0) + { + if (((work->unkAD0->pad & 0xC00) >> 10) == 2) + { + work->unkB38 = Johnny_800C6170; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + else + { + work->unkAD0++; + } + } +} + +static void Johnny_800C631C(Work *work, int action) +{ + if (action == 0) + { + work->control.turn.vy = 0; + SetAction(work, 10); + s03b_boxall_800C93AC(work->vox_ids[5]); + } + if (work->body.is_end != 0) + { + if (work->body.action == 10) + { + GM_ConfigObjectAction(&work->body, 0, 0, 4); + work->unkB4C++; + } + } + if (work->body.action == 0 && GM_StreamStatus() == -1) + { + work->unkB4C++; + } + if (work->unkB4C == 2) + { + work->unkB38 = Johnny_800C64B0; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB1C |= 0x1000000; + } +} + +static void Johnny_800C6418(Work *work, int action) +{ + work->unkB1C |= 0x20; + SetAction(work, 0); + work->control.mov.vx = 6000; + work->control.mov.vz = 750; + work->control.rot.vy = 1024; + work->control.turn.vy = 1024; + work->unkB38 = Johnny_800C64B0; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkAD0 += 2; + work->unkB1C |= 0x1000000; +} + +static void Johnny_800C64B0(Work *work, int action) +{ + if (action == 0) + { + work->unkB1C |= 0x20; + work->unkAD0++; + + SetAction(work, 1); + + if (!(work->unkB1C & 0x1000000)) + { + GM_SeSet2(0, 0x3F, 0xB3); + GM_SeSet2(0, 0x3F, 0xB6); + } + } + + if (Johnny_800C4388(work, 0x5CBD)) + { + work->unkB1C &= ~0x20; + } + + Johnny_800C430C(work); + + if (work->unkAD4 != 0) + { + if (((work->unkAD0->pad & 0xC00) >> 10) == 2) + { + if (work->unkB4C == 0) + { + work->unkB14 = 0; + work->unkB1C &= ~0x1000000; + work->unkB1C |= 0x8; + } + } + else if (((work->unkAD0->pad & 0xE0) >> 5) == 4) + { + work->unkB38 = Johnny_800C631C; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + else + { + work->unkAD0++; + } + } +} + +static void Johnny_800C69D8(Work *work, int arg1); + +static void Johnny_800C65F8(Work *work, int arg1) +{ + if (arg1 == 0) + { + SetAction(work, 6); + work->control.turn.vy = (work->unkAD0->pad & 0x1F) * 512; + AN_Unknown_800CA1EC(&work->body.objs->objs[6].world, 3); + } + if (Johnny_800C470C(work) != 0) + { + Johnny_800C5064(work); + } + else if (work->body.is_end != 0) + { + work->unkB38 = Johnny_800C69D8; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } +} + +static void Johnny_800C66A4(Work *work, int action) +{ + if (action == 0) + { + work->unkB1C &= ~0x80000000; + GM_ConfigMotionAdjust(&work->body, NULL); + ClearAdjust(work); + + work->control.turn.vy = (work->unkAD0->pad & 0x1F) * 512; + + SetAction(work, 34); + } + + if (work->body.is_end != 0 && work->unkB4C == 0) + { + SetAction(work, 38); + work->unkB4C = 1; + } + + if (work->unkB4C != 0) + { + if (Johnny_800C4860(work)|| action == 240 || (work->unkB24 & 0x2) == 0) + { + work->unkB38 = Johnny_800C7428; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + else if (work->body.is_end != 0) + { + work->control.mov = *work->unkAD0; + AN_Unknown_800CA1EC(&work->body.objs->objs[6].world, 3); + } + } +} + +static void Johnny_800C6850(Work *work, int arg1) +{ + if (arg1 == 0) + { + work->unkB1C |= 0x80000000; + + if (work->unkB5A == 0) + { + JohnnySendLeave_800C4B58(work, work->unkB58); + work->unkB72 = 16; + } + + SetAction(work, 35); + GM_SeSet2(0, 0x3F, 0xB5); + } + if (work->body.is_end != 0) + { + if (work->unkB5A == 1) + { + JohnnySendEnter_800C4AD0(work, work->unkB58); + work->unkB72 = 16; + } + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB38 = work->unkB44; + } +} + +static void Johnny_800C6918(Work *work, int arg1) +{ + OBJECT *object; + + if (arg1 == 0) + { + work->unkB1C &= ~0x80000000; + object = &work->body; + GM_ConfigMotionAdjust(object, work->adjust); + SetAction(work, 2); + NavigateSetTarget(&work->nav, work->control.map->hzd, work->unk850[3]); + } + Johnny_800C4734(work); + if (GM_ZoneNavi(&work->nav, &work->control) < 0) + { + Johnny_800C4720(work, 3); + work->unkB38 = Johnny_800C65F8; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } +} + +static void Johnny_800C69D8(Work *work, int action) +{ + int action1, action2; + + if (action == 0) + { + work->unkB1C |= 0x80000000; + GM_ConfigMotionAdjust(&work->body, work->adjust); + SetAction(work, 2); + work->unkAD0++; + } + + Johnny_800C4734(work); + + if (Johnny_800C470C(work)) + { + GM_ConfigMotionAdjust(&work->body, NULL); + ClearAdjust(work); + + Johnny_800C5064(work); + return; + } + + Johnny_800C430C(work); + + if (work->unkAD4 != 0) + { + action1 = (work->unkAD0->pad & 0xC00) >> 10; + action2 = (work->unkAD0->pad & 0xE0) >> 5; + + if (action1 == 2) + { + work->unkB38 = Johnny_800C66A4; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + else if (action2 == 1) + { + GM_ConfigMotionAdjust(&work->body, NULL); + ClearAdjust(work); + + work->unkB38 = Johnny_800C65F8; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + else if (action2 == 2) + { + GM_ConfigMotionAdjust(&work->body, NULL); + ClearAdjust(work); + + work->control.turn.vy = -1024; + Johnny_800C4BDC(work, GV_StrCode("rou_tobira"), 1, Johnny_800C69D8); + + work->unkB38 = Johnny_800C6850; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + else + { + work->unkAD0++; + } + } +} + +static void Johnny_800C6C10(Work *work, int action) +{ + OBJECT *object; + int unkB1C; + + if (action == 0) + { + object = &work->body; + + work->control.radar_atr &= ~RADAR_UNK4; + + work->unkB1C |= 0x80000000; + + GM_ConfigMotionAdjust(object, work->adjust); + SetAction(work, 1); + NavigateSetTarget(&work->nav, work->control.map->hzd, + work->unk850[0]); + + unkB1C = work->unkB1C; + + work->unkB1C &= ~0x40; + + if (unkB1C & 0x100) + { + work->unkB1C &= ~0x100; + work->unkB1C |= 2; + } + + GM_GameStatus &= ~STATE_PADRELEASE; + + Johnny_800C4F24(work, 0); + } + + Johnny_800C4734(work); + GM_ZoneNavi(&work->nav, &work->control); + + if (NavigateTargetNear(&work->nav, &work->control.mov, 250)) + { + GM_ConfigMotionAdjust(&work->body, NULL); + ClearAdjust(work); + + work->unkB14 = 2; + work->unkB1C |= 8; + } +} + +static void Johnny_800C6D84(Work *work, int action) +{ + SVECTOR pos; + SVECTOR diff; + int len; + + if ((work->unkB24 & 0x8) && !(work->target->class & TARGET_DOWN)) + { + work->unkB38 = Johnny_800C7A64; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + return; + } + + len = Johnny_800C4804(work); + if (GM_PlayerStatus & PLAYER_INTRUDE) + { + pos.vx = -700; + pos.vz = 8000; + pos.vy = work->control.mov.vy; + + GV_SubVec3(&pos, &work->control.mov, &diff); + len = GV_VecLen3(&diff); + } + + if (action == 0) + { + work->unkB1C &= ~0x80000000; + + SetAction(work, 2); + + if ((len < 2500) && ((GM_PlayerStatus & PLAYER_INTRUDE) || (Johnny_800C4194(work) < 0))) + { + work->unkB44 = Johnny_800C6D84; + work->unkB38 = Johnny_800C7160; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + return; + } + + Johnny_800C4F24(work, 1); + work->nav.target_next = -1; + } + + if (GM_PlayerStatus & PLAYER_INTRUDE) + { + pos.vx = -700; + pos.vz = 8000; + pos.vy = 0; + + NavigateSetTarget(&work->nav, work->control.map->hzd, &pos); + } + else + { + NavigateSetTargetPlayer(&work->nav); + } + + GM_ZoneNavi(&work->nav, &work->control); + + if ((len < 1500) && ((GM_PlayerStatus & PLAYER_INTRUDE) || (Johnny_800C4194(work) < 0))) + { + work->unkB44 = Johnny_800C6D84; + work->unkB38 = Johnny_800C7160; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + + if ((work->unkB70 != 4) && (work->unkB24 & 2)) + { + work->unkB38 = Johnny_800C6FC0; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } +} + +static void Johnny_800C6FC0(Work *work, int action) +{ + SVECTOR diff; + int len; + + if (action == 0) + { + work->unkB1C &= ~0x80000000; + + SetAction(work, 2); + + NavigateSetTarget(&work->nav, work->control.map->hzd, &work->unkB04); + Johnny_800C4F24(work, 1); + } + + GM_ZoneNavi(&work->nav, &work->control); + + if (NavigateTargetNear(&work->nav, &work->control.mov, 450)) + { + GV_SubVec3(&GM_PlayerPosition, &work->control.mov, &diff); + len = GV_VecLen3(&diff); + + if ((work->unkB24 & 0x2) && (len > 1500)) + { + work->control.turn.vy = -1024; + Johnny_800C4BDC(work, GV_StrCode("rou_tobira"), 0, Johnny_800C6C10); + work->unkB38 = Johnny_800C6850; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + GM_GameStatus |= STATE_PADRELEASE; + } + else + { + work->unkB38 = Johnny_800C6D84; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + } + else if ((action == 24) && (work->unkB24 & 0x2)) + { + work->unkB44 = Johnny_800C6FC0; + work->unkB38 = Johnny_800C7160; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } +} + +static void Johnny_800C7160(Work *work, int arg1) +{ + int action; + int trigger; + + if (arg1 == 0) + { + work->unkB1C &= ~0x80000000; + + if (GM_PlayerStatus & PLAYER_INTRUDE) + { + work->unkB4E = 1; + action = 41; + GM_ConfigMotionAdjust(&work->body, work->adjust); + } + else + { + action = 8; + GM_ConfigMotionAdjust(&work->body, work->adjust); + } + + SetAction(work, action); + Johnny_800C4F24(work, 1); + } + + Johnny_800C47C4(work); + + if (work->unkB4E == 1) + { + work->adjust[3].vx = -work->adjust[2].vx; + work->control.turn.vy += 320; + } + + Johnny_800C4934(work); + + trigger = 1; + + if (arg1 == 4) + { + work->unkB4C = 1; + + if (work->unkB4E == 0) + { + SetAction(work, 9); + } + } + + if (work->body.action == 41) + { + if (arg1 == 16 || arg1 == 22 || arg1 == 28) + { + trigger |= 2; + } + } + else if (arg1 != 12 && arg1 != 17) + { + if (arg1 == 22) + { + trigger |= 2; + } + } + else + { + trigger |= 2; + } + + if (work->body.is_end != 0 && work->unkB4C == 1) + { + if (GM_PlayerStatus & PLAYER_INTRUDE) + { + work->unkB38 = Johnny_800C7160; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + else + { + GM_ConfigMotionAdjust(&work->body, NULL); + ClearAdjust(work); + + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB38 = work->unkB44; + } + } + else + { + work->trigger = trigger; + } +} + +static void Johnny_800C7378(Work *work, int arg1) +{ + OBJECT *object; + + if (arg1 == 0) + { + work->unkB1C &= ~0x80000000; + object = &work->body; + GM_ConfigMotionAdjust(object, work->adjust); + SetAction(work, 8); + Johnny_800C4F24(work, 1); + } + if (arg1 == 32) + { + work->unkB38 = Johnny_800C7160; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + Johnny_800C47C4(work); +} + +static void Johnny_800C7428(Work *work, int action) +{ + if (action == 0) + { + work->unkB1C &= ~0x80000000; + s03b_boxall_800C9328(); + s03b_boxall_800C93F0(work->vox_ids[13], 4); + GM_SeSet2(0, 0x3F, SE_EXCLAMATION); + NewPadVibration(johnny_vibration1_800C32C0, 2); + AN_Unknown_800CA1EC(&work->body.objs->objs[6].world, 0); + SetAction(work, 5); + work->unkB1C &= ~0x40000000; + } + + Johnny_800C47C4(work); + + if (work->body.is_end != 0) + { + if (Johnny_800C4388(work, 0x41A5) != 0) + { + work->unkB44 = Johnny_800C6FC0; + if (work->unkB24 & 2) + { + work->unkB38 = Johnny_800C7378; + } + else + { + work->unkB38 = Johnny_800C6FC0; + } + } + else + { + work->unkB38 = Johnny_800C6C10; + } + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } +} + +static void Johnny_800C753C(Work *work, int action) +{ + SVECTOR mov; + + if (action == 0) + { + work->unkB1C |= 0x80000000; + s03b_boxall_800C9328(); + + SetAction(work, 2); + + switch (work->unkB50) + { + case 0: + NavigateSetTarget(&work->nav, work->control.map->hzd, &s03c_dword_800C32E4); + break; + + case 1: + NavigateSetTarget(&work->nav, work->control.map->hzd, &work->unkB04); + break; + + case 2: + if (GM_PlayerStatus & PLAYER_INTRUDE) + { + mov.vx = -750; + mov.vz = 8000; + mov.vy = 0; + + NavigateSetTarget(&work->nav, work->control.map->hzd, &mov); + } + else + { + work->nav.target_next = -1; + NavigateSetTargetPlayer(&work->nav); + } + break; + } + } + + if (!(GM_PlayerStatus & PLAYER_KETCHUP)) + { + work->unkB50 = 0; + + work->unkB1C |= 0x2; + work->unkB1C &= ~0x100; + + NewPadVibration(johnny_vibration1_800C32C0, 2); + + work->unkB1C |= 0x80002; + work->unkB1C &= ~0x100; + + if (Johnny_800C4388(work, 0x41A5)) + { + work->unkB44 = Johnny_800C6FC0; + + if (work->unkB24 & 0x2) + { + work->unkB38 = Johnny_800C7378; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + return; + } + + work->unkB38 = Johnny_800C6FC0; + } + else + { + AN_Unknown_800CA1EC(&work->body.objs->objs[6].world, 0); + GM_SeSet2(0, 63, SE_EXCLAMATION); + + s03b_boxall_800C9328(); + s03b_boxall_800C93F0(work->vox_ids[8], 4); + + NewPadVibration(johnny_vibration1_800C32C0, 2); + + GCL_ExecProc(work->proc_id[4], NULL); + work->unkB38 = Johnny_800C6C10; + } + + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + return; + } + + GM_ZoneNavi(&work->nav, &work->control); + + if (NavigateTargetNear(&work->nav, &work->control.mov, 500)) + { + work->unkB50++; + + if (work->unkB50 == 2) + { + work->control.turn.vy = -1024; + Johnny_800C4BDC(work, GV_StrCode("rou_tobira"), 1, Johnny_800C753C); + work->unkB38 = Johnny_800C6850; + } + else if (work->unkB50 == 3) + { + work->unkB38 = Johnny_800C7804; + } + else + { + work->unkB38 = Johnny_800C794C; + } + + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } +} + +static void Johnny_800C7804(Work *work, int arg1) +{ + SVECTOR svec; + + if (arg1 == 0) + { + work->unkB1C &= ~0x80000000; + SetAction(work, 0); + + AN_Unknown_800CA1EC(&work->body.objs->objs[6].world, 3); + + work->unkB60 = GM_PlayerPosition; + work->unkB60.pad = GM_PlayerControl->turn.vy; + } + + GV_SubVec3(&GM_PlayerPosition, &work->unkB60, &svec); + + if (GV_VecLen3(&svec) < 5) + { + if (ABS(work->unkB60.pad - GM_PlayerControl->turn.vy) >= 5) + { + work->unkB38 = Johnny_800C794C; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + } + else + { + work->unkB38 = Johnny_800C794C; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + + if (arg1 == 64) + { + work->unkB38 = Johnny_800C794C; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } +} + +static void Johnny_800C794C(Work *work, int arg1) +{ + if (arg1 == 0) + { + work->unkB1C &= ~0x80000000; + SetAction(work, 5); + } + Johnny_800C47C4(work); + if (work->body.is_end != 0) + { + if (work->unkB50 == 3) + { + work->unkB50 = 0; + work->unkB44 = &Johnny_800C6FC0; + AN_Unknown_800CA1EC(&work->body.objs->objs[6].world, 0); + GM_SeSet2(0, 0x3F, SE_EXCLAMATION); + NewPadVibration(johnny_vibration1_800C32C0, 2); + GCL_ExecProc(work->proc_id[4], NULL); + work->unkB38 = Johnny_800C7378; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB1C |= 0x80000 | 0x2; + work->unkB1C &= ~0x100; + } + else + { + work->unkB38 = &Johnny_800C753C; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + } +} + +static void Johnny_800C7A64(Work *work, int action) +{ + SVECTOR diff; + int len; + + if (action == 0) + { + work->unkB1C |= 0x80000000; + + s03b_boxall_800C9328(); + OpenCinemaScreen(1, 30000); + + GM_GameStatus |= STATE_PADRELEASE | STATE_PAUSE_ONLY; + + SetAction(work, 2); + + work->nav.target_next = -1; + } + + switch (work->unkB4C) + { + case 0: + NavigateSetTargetPlayer(&work->nav); + GM_ZoneNavi(&work->nav, &work->control); + + len = Johnny_800C4804(work); + if (len < 5000) + { + GV_SubVec3(&work->control.mov, &GM_PlayerPosition, &diff); + GM_PlayerControl->turn.vy = GV_VecDir2(&diff); + } + + if ((len < 1500) && (Johnny_800C4194(work) < 0)) + { + SetAction(work, 8); + work->unkB4C = 1; + } + break; + + case 1: + Johnny_800C47C4(work); + + if (++work->unkB4E == 32) + { + work->unkB38 = Johnny_800C7BF8; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + break; + } +} + +static void Johnny_800C7BF8(Work *work, int action) +{ + SVECTOR diff; + SVECTOR pos; + SVECTOR campos; + int len; + + if (action == 0) + { + work->unkB1C |= 0x80000000; + work->target->class = TARGET_AVAIL; + + s03b_boxall_800C9328(); + SetAction(work, 31); + GM_SeSet2(0, 63, 180); + } + + if (action == 4) + { + s03b_boxall_800C93AC(work->vox_ids[2]); + } + + switch (work->unkB4C) + { + case 0: + if (work->body.is_end != 0) + { + SetAction(work, 32); + work->unkB4C = 1; + } + break; + + case 1: + if (GM_StreamStatus() == -1) + { + SetAction(work, 33); + + pos.vx = 6500; + pos.vy = 0; + pos.vz = -1500; + + NavigateSetTarget(&work->nav, work->control.map->hzd, &pos); + work->unkB4C = 2; + } + break; + + case 2: + case 3: + DG_InvisibleObjs(GM_PlayerBody->objs); + + GM_Camera.first_person = 1; + + GM_SnakeCamera.position = GM_PlayerPosition; + GM_SnakeCamera.position.vy += 500; + + GM_SnakeCamera.rotate2.vz = 0; + GM_SnakeCamera.rotate2.vx = 0; + + campos.vx = 5500; + campos.vz = 750; + campos.vy = 0; + + GV_SubVec3(&campos, &GM_PlayerPosition, &diff); + GM_SnakeCamera.rotate2.vy = GV_VecDir2(&diff); + + if (work->unkB4C == 2) + { + GM_ZoneNavi(&work->nav, &work->control); + len = NavigateGetTargetDist(&work->nav, &work->control.mov); + + if (len < 500) + { + work->control.radar_atr &= ~RADAR_SIGHT; + SetAction(work, 0); + work->unkB4C = 3; + } + } + else if (++work->unkB4E == 32) + { + GM_Camera.first_person = 0; + work->unkB4C = 4; + work->unkB4E = 0; + } + break; + + case 4: + if (++work->unkB4E == 6) + { + DG_VisibleObjs(GM_PlayerBody->objs); + CloseCinemaScreen(); + JohnnyExecProc_800C4D8C(work); + Johnny_800C4F24(work, 0); + GCL_ExecProc(work->proc_id[5], 0); + GM_GameStatus &= ~( STATE_PADRELEASE | STATE_PAUSE_ONLY ); + work->unkB4C = 5; + } + break; + + case 5: + if ((GM_StreamStatus() == -1) && Johnny_800C43D0(0xAE93) && (GM_PlayerStatus & PLAYER_CHECK_WALL)) + { + s03b_boxall_800C93F0(work->vox_ids[17], 4); + } + break; + } + +} + +static void Johnny_800C7F78(Work *work, int action) +{ + SVECTOR sp10; + SVECTOR diff; + MATRIX sp20; + MATRIX sp40; + GV_MSG msg; + + if (action == 0) + { + work->unkB1C |= 0x80000000; + + SetAction(work, 0); + + GM_ConfigMotionAdjust(&work->body, NULL); + ClearAdjust(work); + + AN_Unknown_800CA1EC(&work->body.objs->objs[6].world, 0); + GM_SeSet2(0, 63, SE_EXCLAMATION); + + NewPadVibration(johnny_vibration1_800C32C0, 2); + GM_GameStatus |= STATE_PADRELEASE | STATE_PAUSE_ONLY; + } + + DG_VisibleObjs(GM_PlayerBody->objs); + Johnny_800C47C4(work); + + if (work->control.mov.vx >= -2700) + { + sp10.vx = -350; + sp10.vy = 1000; + sp10.vz = -750; + } + else + { + sp10.vx = 450; + sp10.vy = 1000; + sp10.vz = -750; + } + + ReadRotMatrix(&sp20); + DG_MovePos(&sp10); + + ReadRotMatrix(&sp40); + DG_SetPos(&sp20); + + GM_Camera.first_person = 1; + + GM_SnakeCamera.position.vx = sp40.t[0]; + GM_SnakeCamera.position.vy = sp40.t[1]; + GM_SnakeCamera.position.vz = sp40.t[2]; + + GV_SubVec3(&GM_PlayerPosition, &GM_SnakeCamera.position, &diff); + + GM_SnakeCamera.rotate2.vx = 320; + GM_SnakeCamera.rotate2.vz = 0; + GM_SnakeCamera.rotate2.vy = GV_VecDir2(&diff); + + if (work->unkB1C & 0x4000000) + { + if (action == 64) + { + msg.address = GV_StrCode("スネーク"); + msg.message_len = 6; + msg.message[0] = GV_StrCode("run_move"); + msg.message[1] = -250; + msg.message[2] = 0; + msg.message[3] = GM_PlayerPosition.vz; + msg.message[4] = 500; + msg.message[5] = 0x201; + GV_SendMessage(&msg); + } + + if ((action > 64) && !(GM_PlayerStatus & PLAYER_ACT_ONLY)) + { + work->unkB1C &= ~0x4000000; + + GM_GameStatus &= ~( STATE_PADRELEASE | STATE_PAUSE_ONLY ); + GM_Camera.first_person = 0; + + if ((work->unkB48 == Johnny_800C64B0) || (work->unkB48 == Johnny_800C5DE4)) + { + work->unkAD0--; + } + + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB38 = work->unkB48; + } + } + else if (work->unkB1C & 0x40000000) + { + if ((work->unkB48 == Johnny_800C6C10) && (action == 0)) + { + s03b_boxall_800C9328(); + s03b_boxall_800C93F0(work->vox_ids[13], 4); + } + + if ((action == 65) || s03b_boxall_800C95EC()) + { + s03b_boxall_800C9328(); + + GM_Camera.first_person = 0; + GM_GameStatus &= ~( STATE_PADRELEASE | STATE_PAUSE_ONLY ); + + if ((work->unkB48 == Johnny_800C64B0) || (work->unkB48 == Johnny_800C5DE4)) + { + work->unkAD0--; + } + + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB38 = work->unkB48; + work->unkB1C &= ~0x40000000; + } + } + else if (work->unkB1C & 0x8000000) + { + if (action == 0) + { + s03b_boxall_800C9328(); + s03b_boxall_800C93F0(work->vox_ids[8], 4); + GCL_ExecProc(work->proc_id[4], NULL); + } + + GM_GameStatus |= STATE_PADRELEASE | STATE_PAUSE_ONLY; + + if (action == 100) + { + GM_GameStatus &= ~( STATE_PADRELEASE | STATE_PAUSE_ONLY ); + GM_Camera.first_person = 0; + + if ((work->unkB48 == Johnny_800C64B0) || (work->unkB48 == Johnny_800C5DE4)) + { + work->unkAD0--; + } + + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB38 = work->unkB48; + } + } + else + { + if (action == 0) + { + s03b_boxall_800C9328(); + s03b_boxall_800C93F0(work->vox_ids[7], 4); + } + + if (action == 32) + { + GM_Camera.first_person = 0; + work->unkB0C = 7; + work->unkB10 = 0; + GM_GameStatus &= ~( STATE_PADRELEASE | STATE_PAUSE_ONLY ); + } + } +} + +static void Johnny_800C8400(Work *work, int action) +{ + int new_action; + + if (action == 0) + { + if (work->unkB1C & 0x400) + { + work->unkB1C |= 0x200; + work->unkB1C &= ~0x400; + } + + if (GV_Time & 1) + { + new_action = 16; + } + else + { + new_action = 17; + } + + SetAction(work, new_action); + } + if (work->body.is_end != 0) + { + work->unkB1C &= ~0x200; + + if (work->target->vital <= 0) + { + work->unkB38 = Johnny_800C9144; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + return; + } + + if (work->unkB70 != 4 && (work->unkB24 & 2)) + { + work->unkB38 = Johnny_800C6FC0; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + else + { + work->unkB38 = Johnny_800C6D84; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + + if (!(work->unkB1C & 0x10000000)) + { + GM_SeSet2(0, 0x3F, SE_EXCLAMATION); + NewPadVibration(johnny_vibration1_800C32C0, 2); + AN_Unknown_800CA1EC(&work->body.objs->objs[6].world, 0); + } + } +} + +static void Johnny_800C873C(Work *work, int action); +static void Johnny_800C8FE4(Work *work, int action); + +static void Johnny_800C854C(Work *work, int action) +{ + if (action == 0) + { + work->unkB1C |= 0x200; + SetAction(work, 20); + work->target->vital--; + } + + if (action < 12) + { + work->control.turn.vy += 170; + } + + if (work->body.is_end != 0) + { + work->unkB50 = 1; + if (work->target->vital > 0) + { + work->unkB38 = Johnny_800C873C; + } + else + { + work->unkB38 = Johnny_800C8FE4; + } + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + else + { + if (action > 16) + { + GV_NearExp8V(&work->unkB30.vx, &DG_ZeroVector.vx, 3); + } + work->control.step.vx = work->unkB30.vx; + work->control.step.vz = work->unkB30.vz; + } +} + +static void Johnny_800C8654(Work *work, int action) +{ + if (action == 0) + { + SetAction(work, 19); + work->control.turn.vy = GM_PlayerControl->turn.vy + 2048; + work->target->vital--; + } + if (action == 20) + { + GM_SeSet(&work->control.mov, VO_ENEMY_PUNCHED); + GM_SeSet(&work->control.mov, SE_HIT_FLOOR); + } + if (work->body.is_end != 0) + { + work->unkB50 = 2; + if (work->target->vital > 0) + { + work->unkB38 = Johnny_800C873C; + } + else + { + work->unkB38 = Johnny_800C8FE4; + } + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } +} + +static void Johnny_800C88C8(Work *work, int action); + +static void Johnny_800C873C(Work *work, int action) +{ + if (action == 0) + { + work->control.radar_atr &= ~RADAR_SIGHT; + work->unkB1C |= 0x200; + SetTargetClass( work->target, TARGET_DOWN ); + SetAction(work, (work->unkB50 == 1) ? 23 : 24); + } + + if (work->unkB24 & 8) + { + work->target->faint = 0; + } + + if (work->target->faint <= 0) + { + if (work->unkB4E == 0) + { + work->homing->flag = FALSE; + Johnny_800C4F24(work, 0); + GCL_ExecProc(work->proc_id[5], NULL); + GCL_ExecProc(work->proc_id[3], NULL); + work->unkB4E = 1; + NewHiyoko(&work->body.objs->objs[6].world, -1); + UnsetTargetClass( work->target, TARGET_FLAG ); + work->unkB1C |= 0x80000000; + } + if (++work->unkB4C == 36) + { + JohnnyExecProc_800C4D8C(work); + } + } + else if (action == 48) + { + work->unkB38 = Johnny_800C88C8; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } +} + +static void Johnny_800C88C8(Work *work, int action) +{ + if (action == 0) + { + action = (work->unkB50 == 1) ? 25 : 26; + work->unkB50 = 0; + + SetAction(work, action); + } + + if (work->unkB24 & 0x8) + { + OpenCinemaScreen(1, 30000); + GM_GameStatus |= STATE_PADRELEASE | STATE_PAUSE_ONLY; + } + + if (work->body.is_end != 0) + { + UnsetTargetClass( work->target, TARGET_DOWN ); + work->unkB1C &= ~0x200; + + if (work->unkB24 & 0x8) + { + work->unkB38 = Johnny_800C7A64; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + else if ((work->unkB70 != 4) && (work->unkB24 & 0x2)) + { + work->unkB38 = Johnny_800C6FC0; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + else + { + work->unkB38 = Johnny_800C6D84; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + + if (!(work->unkB1C & 0x10000000)) + { + GM_SeSet2(0, 63, SE_EXCLAMATION); + NewPadVibration(johnny_vibration1_800C32C0, 2); + AN_Unknown_800CA1EC(&work->body.objs->objs[6].world, 0); + } + + work->control.radar_atr |= RADAR_SIGHT; + } +} + +static void Johnny_800C8C34(Work *work, int action); +static void Johnny_800C8E84(Work *work, int action); +static void Johnny_800C8CD4(Work *work, int action); +static void Johnny_800C8D58(Work *work, int action); + +static int Johnny_800C8A2C(Work *work, int action) +{ + if (!(work->target->damaged & 2)) + { + work->unkB38 = Johnny_800C8C34; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + return 1; + } + + if (GM_PlayerAction == action) + { + return 0; + } + + switch (GM_PlayerAction) + { + case 7: + work->unkB38 = Johnny_800C8B14; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + break; + + case 38: + work->unkB38 = Johnny_800C8CD4; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + return 1; + + case 39: + work->unkB38 = Johnny_800C8D58; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + return 1; + + case 13: + work->unkB38 = Johnny_800C8E84; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + return 1; + } + + return 0; +} + +static void Johnny_800C8B14(Work *work, int action) +{ + if (action == 0) + { + SetAction(work, 27); + work->unkB1C |= 0x800; + work->control.turn.vy = GM_PlayerControl->turn.vy; + } + + if (++work->unkBE4 > 50) + { + SetAction(work, 42); + + if (!(work->unkBE4 & 7)) + { + NewPadVibration(johnny_vibration2_800C32C4, 2); + } + } + if (Johnny_800C8A2C(work, 7) == 0 && (work->unkB24 & 8)) + { + work->target->captured = 0; + work->target->faint = 0; + GM_SeSet(&work->control.mov, VO_ENEMY_THROWN); + work->unkB38 = Johnny_800C8C34; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } +} + +static void Johnny_800C8C34(Work *work, int action) +{ + if (action == 0) + { + SetAction(work, 30); + } + if (action == 12) + { + GM_SeSet(&work->control.mov, SE_HIT_FLOOR); + } + if (work->body.is_end != 0) + { + work->unkB50 = 2; + work->unkB38 = Johnny_800C873C; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB1C &= ~0x800; + } +} + +static void Johnny_800C8CD4(Work *work, int action) +{ + if (action == 0) + { + GM_SeSet(&work->control.mov, VO_ENEMY_GRABBED); + SetAction(work, 28); + work->unkBE4 = 0; + } + if (work->body.is_end) + { + work->unkB38 = Johnny_800C8B14; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + Johnny_800C8A2C(work, 38); +} + +static void Johnny_800C8D58(Work *work, int action) +{ + SVECTOR svec; + + if (action == 0) + { + work->control.radar_atr &= ~RADAR_SIGHT; + GM_SeSet(&work->control.mov, VO_ENEMY_THROWN); + GM_SeSet(&work->control.mov, VO_ENEMY_SNAPPED); + SetAction(work, 29); + work->target->vital -= 255; + GM_TotalEnemiesKilled++; + } + if (action > 48 && action < 60) + { + svec.vx = svec.vy = 0; + svec.vz = 96; + DG_RotVector(&svec, &work->control.step, 1); + } + if (action == 55) + { + GM_SeSet(&work->control.mov, SE_HIT_FLOOR); + } + if (work->body.is_end != 0) + { + work->unkB50 = 2; + work->unkB38 = Johnny_800C8FE4; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB1C &= ~0x800; + } +} + +static void Johnny_800C8E84(Work *work, int action) +{ + SVECTOR svec; + MATRIX mat; + CONTROL *control; + + if (action == 0) + { + SetAction(work, 27); + } + + if (++work->unkBE4 > 50) + { + SetAction(work, 42); + + if (!(work->unkBE4 & 7)) + { + NewPadVibration(johnny_vibration2_800C32C4, 2); + } + } + if (Johnny_800C8A2C(work, 13) == 0) + { + work->control.turn.vy = GM_PlayerControl->turn.vy; + + DG_SetPos2(&GM_PlayerControl->mov, &GM_PlayerControl->rot); + DG_MovePos(&s03c_dword_800C32EC); + + ReadRotMatrix(&mat); + + control = &work->control; + svec.vx = mat.t[0]; + svec.vy = mat.t[1]; + svec.vz = mat.t[2]; + + GV_SubVec3(&svec, &control->mov, &work->control.step); + + if (work->unkB24 & 8) + { + work->target->captured = 0; + work->target->faint = 0; + + GM_SeSet(&control->mov, VO_ENEMY_THROWN); + + work->unkB38 = Johnny_800C8C34; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + } +} + +static void Johnny_800C8FE4(Work *work, int action) +{ + int action_flag; + + if (action == 0) + { + work->homing->flag = FALSE; + work->control.radar_atr &= ~RADAR_SIGHT; + + if (work->unkB50 == 1) + { + action_flag = 23; + } + else + { + action_flag = 24; + } + + work->unkB50 = 0; + + SetAction(work, action_flag); + GM_ConfigControlAttribute(&work->control, 0); + + SetTargetClass( work->target, TARGET_DOWN ); + + Johnny_800C4F24(work, 0); + GCL_ExecProc(work->proc_id[5], NULL); + + work->unkB1C |= 0x1000; + work->homing->flag = FALSE; + } + if (action & 2) + { + DG_InvisibleObjs(work->body.objs); + *work->enable_gunlight = 0; + } + else + { + DG_VisibleObjs(work->body.objs); + *work->enable_gunlight = 1; + } + + if (action == 36) + { + work->unkB1C |= 0x1000; + GCL_ExecProc(work->proc_id[3], NULL); + JohnnyExecProc_800C4D8C(work); + GM_GameStatus &= ~STATE_PADRELEASE; + GV_DestroyActor(&work->actor); + } +} + +static void Johnny_800C9144(Work *work, int action) +{ + if (action == 0) + { + work->homing->flag = FALSE; + work->control.radar_atr &= ~RADAR_SIGHT; + + SetAction(work, 39); + UnsetTargetClass( work->target, TARGET_FLAG ) ; + SetTargetClass( work->target, TARGET_DOWN ) ; + } + + if (action == 36) + { + JohnnyExecProc_800C4D8C(work); + } + + switch (work->unkB4C) + { + case 0: + if (action == 22) + { + GM_SeSetMode(&work->control.mov, SE_ENEMY_COLLAPSE, GM_SEMODE_BOMB); + } + + if (action == 44) + { + GM_SeSetMode(&work->control.mov, SE_HIT_FLOOR, GM_SEMODE_BOMB); + } + + if (work->body.is_end != 0) + { + Johnny_800C4F24(work, 0); + GCL_ExecProc(work->proc_id[5], NULL); + + SetAction(work, 40); + + work->unkB4C++; + NewHiyoko(&work->body.objs->objs[6].world, -1); + } + break; + + case 1: + if (++work->unkB4E == 32) + { + work->unkB1C |= 0x1000; + GCL_ExecProc(work->proc_id[3], NULL); + } + break; + } +} + +static void Johnny_800C949C(Work *work, int arg1); + +static void Johnny_800C92E0(Work *work, int arg1) +{ + if (arg1 == 0) + { + OpenCinemaScreen(0, 30000); + SetAction(work, 0); + work->unkB1C |= 0x20; + GM_GameStatus |= STATE_PADRELEASE; + NavigateSetTarget(&work->nav, work->control.map->hzd, + &work->unkB04); + } + if (arg1 == 8) + { + GM_SeSetMode(&work->control.mov, 0xC2, GM_SEMODE_BOMB); + } + if (arg1 >= 48) + { + if (arg1 == 48) + { + SetAction(work, 1); + s03b_boxall_800C93AC(work->vox_ids[9]); + GM_ConfigMotionAdjust(&work->body, work->adjust); + } + switch (work->unkB4C) + { + case 0: + GM_ZoneNavi(&work->nav, &work->control); + if (NavigateGetTargetDist(&work->nav, &work->control.mov) < 500) + { + work->unkB4C++; + } + break; + case 1: + SetAction(work, 0); + work->unkB4C++; + break; + case 2: + work->control.turn.vy = -1024; + work->unkB38 = Johnny_800C949C; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + break; + } + } +} + +static void Johnny_800C949C(Work *work, int arg1) +{ + SVECTOR svec; + + CheckCinemaTimeout(); + if (arg1 == 0) + { + if (GM_PlayerStatus & PLAYER_KETCHUP) + { + work->unkB1C |= 2; + } + + work->control.turn.vy = 0; + + SetAction(work, 36); + + GV_SubVec3(&work->control.mov, &GM_PlayerPosition, &svec); + GM_PlayerControl->turn.vy = GV_VecDir2(&svec); + } + if (arg1 == 24) + { + GCL_ExecProc(work->proc_id[1], NULL); + } + switch (work->unkB4C) + { + case 0: + if (GM_StreamStatus() == -1) + { + GM_ConfigMotionAdjust(&work->body, NULL); + ClearAdjust(work); + + NewFadeInOut(FADEIO_MODE_TOBLACK, 32); + CloseCinemaScreen(); + work->unkB4C++; + } + break; + case 1: + if (++work->unkB4E == 80) + { + GM_GameStatus &= ~STATE_PADRELEASE; + Johnny_800C4CCC(work); + } + break; + } +} + +static void Johnny_800C9644(Work *work) +{ + int i; + int message_len; + GV_MSG *msgs; + + if (work->control.n_msg == 0) + { + return; + } + + msgs = &work->control.msg[work->control.n_msg] - 1; + for (i = work->control.n_msg; i > 0; i--, msgs--) + { + message_len = msgs->message_len; + if (message_len >= 2 && msgs->message[0] == 0x10BA) + { + if (msgs->message[1] == HASH_ON) + { + work->unkB1C &= ~0x100000; + } + else if (msgs->message[1] == HASH_OFF) + { + work->unkB1C |= 0x100000; + } + } + } +} + +static void Johnny_800C96F4(Work *work, int field_B10) +{ + if (field_B10 == 0) + { + work->unkB1C |= 0x80000000; + if ((work->unkB1C & 0x400000) && work->unkB54 == 1) + { + work->unkB54 = 0; + work->unkB38 = Johnny_800C92E0; + } + else + { + work->unkB38 = Johnny_800C59B8; + } + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + if (work->unkB38 != Johnny_800C92E0 && work->unkB38 != Johnny_800C949C && work->unkB38 != Johnny_800C7F78 && + work->unkB38 != Johnny_800C6850) + { + Johnny_800C45AC(work); + if (work->unkB38 != Johnny_800C7F78) + { + if (work->unkB1C & 0x40000000) + { + work->unkB1C &= ~0x40000000; // Why not do it unconditionally!? + } + Johnny_800C424C(work, Johnny_800C59B8); + if (work->unkB38 != Johnny_800C5520 && work->unkB38 != Johnny_800C588C && field_B10 >= 480 && + GM_StreamStatus() == -1) + { + work->unkB10 = 0; + if (work->unkB1C & 0x400000) + { + work->unkB1C |= 8; + work->unkB14 = 2; + work->unkB54++; + } + else if (work->unkB1C & 4) + { + work->unkB1C |= 8; + work->unkB14 = 1; + work->unkB1C &= ~4; + } + else + { + work->unkB1C |= 8; + work->unkB14 = 3; + work->unkB1C |= 4; + } + } + } + } +} + +static void Johnny_800C98B0(Work *work, int field_B10) +{ + if (field_B10 == 0) + { + work->unkB38 = Johnny_800C57D0; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB1C |= 0x80000000; + } +} + +static void Johnny_800C98E4(Work *work, int field_B10) +{ + if (field_B10 == 0) + { + work->unkB38 = Johnny_800C5DE4; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB1C |= 0x80000000; + Johnny_800C4720(work, 0); + } + if (work->unkB38 != Johnny_800C7F78) + { + Johnny_800C45AC(work); + if (work->unkB38 != Johnny_800C7F78) + { + if (work->unkB1C & 0x40000000) + { + work->unkB1C &= ~0x40000000; // Why not do it unconditionally!? + } + } + } +} + +static void Johnny_800C998C(Work *work, int field_B10) +{ + if (field_B10 == 0) + { + work->unkB38 = Johnny_800C6054; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB1C |= 0x80000000; + Johnny_800C4720(work, 1); + } +} + +static void Johnny_800C99D8(Work *work, int action) +{ + if (action == 0) + { + work->unkB1C |= 0x80000000; + + work->unkB38 = Johnny_800C6204; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + + work->unkB1C |= 0x24; + work->control.radar_atr |= RADAR_SIGHT; + + if (s03c_dword_800C33D8 == 1) + { + s03c_dword_800C33D8 = 0; + work->unkB38 = Johnny_800C6418; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + + Johnny_800C4720(work, 2); + } + + if ((work->unkB70 == 4) && !(work->unkB24 & 0x2)) + { + work->unkB14 = 8; + work->unkB1C |= 0x8; + } + else if (!(work->unkB1C & 0x20)) + { + if (work->unkB70 == 4) + { + work->unkB1C &= ~0x80000000; + work->unkB1C |= 0x8; + work->unkB14 = 8; + } + else if (work->unkB38 != Johnny_800C7F78) + { + Johnny_800C45AC(work); + + if ((work->unkB38 != Johnny_800C7F78) && (work->unkB1C & 0x40000000)) + { + work->unkB1C &= ~0x40000000; + } + } + } +} + +static void Johnny_800C9B3C(Work *work, int field_B10) +{ + if (field_B10 == 0) + { + work->unkB1C |= 0x80000000 | 0x4; + Johnny_800C4720(work, 2); + work->unkB0C = 4; + work->unkB38 = Johnny_800C64B0; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkAD0 += 2; + work->unkB1C |= 0x20; + } +} + +static void Johnny_800C9BB4(Work *work, int field_B10) +{ + if (field_B10 == 0) + { + s03c_dword_800C32C8 = 1; + work->unkB38 = Johnny_800C6918; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB1C |= 0x80000000; + work->control.radar_atr |= RADAR_UNK4; + } + work->trigger = 0; + Johnny_800C5168(work); + Johnny_800C4A64(work); +} + +static void Johnny_800C9C2C(Work *work, int field_B10) +{ + if (field_B10 == 0) + { + s03c_dword_800C32C8 = 2; + work->unkB38 = Johnny_800C794C; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + work->unkB50 = 0; + work->control.radar_atr |= RADAR_UNK4; + work->unkB1C |= 0x80000000; + } + work->trigger = 0; + Johnny_800C5168(work); + Johnny_800C4A64(work); +} + +static void Johnny_800C9CA8(Work *work, int field_B10) +{ + if (field_B10 == 0) + { + s03c_dword_800C32C8 = 3; + work->control.radar_atr |= RADAR_UNK4; + work->unkB1C &= ~0x80000000; + s03b_boxall_800C9328(); + s03b_boxall_800C93F0(work->vox_ids[13], 4); + NewPadVibration(johnny_vibration1_800C32C0, 2); + GM_SeSet2(0, 0x3F, SE_EXCLAMATION); + AN_Unknown_800CA1EC(&work->body.objs->objs[6].world, 0); + work->unkB38 = Johnny_800C6D84; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB3C = 0; + } + work->trigger = 0; + Johnny_800C5168(work); + Johnny_800C4A64(work); +} + +static void Johnny_800C9D64(Work *work) +{ + int field_B10; + int field_B3C; + ACTION field_B38; + int status; + + Johnny_800C9644(work); + Johnny_800C4418(work); + Johnny_800C44F8(work); + Johnny_800C4DCC(work); + Johnny_800C4E5C(work); + Johnny_800C50D0(work); + s03b_boxall_800C9404(); + if (work->body.objs->adjust) + { + sna_act_helper2_helper2_80033054(GV_StrCode("ジョニー"), &work->adjust[6]); // ジョニー = Joni = Johnny + } + if (work->unkB1C & 0x10000000) + { + GM_AlertLevel = 0xFF; + } + field_B10 = work->unkB10; + if (work->unkB10 < 16000) + { + work->unkB10++; + } + switch (work->unkB0C) + { + case 0: + Johnny_800C96F4(work, field_B10); + break; + case 1: + Johnny_800C98B0(work, field_B10); + break; + case 2: + Johnny_800C98E4(work, field_B10); + break; + case 3: + Johnny_800C998C(work, field_B10); + break; + case 4: + Johnny_800C99D8(work, field_B10); + break; + case 5: + Johnny_800C9B3C(work, field_B10); + break; + case 6: + Johnny_800C9BB4(work, field_B10); + break; + case 7: + Johnny_800C9C2C(work, field_B10); + break; + case 8: + Johnny_800C9CA8(work, field_B10); + break; + } + field_B38 = work->unkB38; + field_B3C = work->unkB3C; + status = GM_StreamStatus(); + if (status == -1 || status == 2) + { + work->unkB3C++; + } + if (work->unkB3C == 0) + { + work->unkB3C = 1; + } + field_B38(work, field_B3C); + Johnny_800C4E9C(work); + Johnny_800C4FAC(work); + if (!(work->unkB1C & 2) && GM_KetchupFlag == 0) + { + work->unkB1C |= 2; + GM_KetchupFlag = -1; + } +} + +static void Act(Work *work) +{ + CONTROL *control; + + GM_ActMotion(&work->body); + control = &work->control; + GM_ActControl(control); + GM_ActObject(&work->body); + GM_MoveTarget(work->target, &control->mov); + DG_GetLightMatrix2(&control->mov, work->light); + work->control.height = work->body.height; + work->nav.addr = HZD_GetAddress(work->control.map->hzd, &control->mov, work->nav.addr); + Johnny_800C9D64(work); + if (work->unkB30.vy < 0 && work->control.grounded != 0) + { + work->unkB30.vy = 0; + } + work->unkB30.vy -= 32; + work->control.step.vy += work->unkB30.vy; +} + +static void Die(Work *work) +{ + GV_ACT *shadow; + GV_ACT *weapon; + GV_ACT *gunlight; + + GM_FreeHomingTarget(work->homing); + shadow = work->shadow; + if (shadow != NULL) + { + GV_DestroyOtherActor(shadow); + } + weapon = work->weapon; + if (weapon != NULL) + { + GV_DestroyOtherActor(weapon); + } + gunlight = work->gunlight; + if (gunlight != NULL) + { + GV_DestroyOtherActor(gunlight); + } + GM_FreeTarget(work->target); + GM_FreeControl(&work->control); + GM_FreeObject(&work->body); + s03b_boxall_800C9328(); +} + +static int InitTarget(Work *work) +{ + TARGET *target; + + work->target = target = GM_AllocTarget(); + if (target == NULL) + { + return -1; + } + + GM_SetTarget(target, ( TARGET_AVAIL | TARGET_FLAG ), ENEMY_SIDE, &target_size); + GM_SetPowerTarget(target, POWER_DECREASE, -1, 192, 10, &DG_ZeroVector); + work->homing = GM_AllocHomingTarget(&work->body.objs->objs[6].world, &work->control); + work->homing->flag = TRUE; + return 0; +} + +static int ReadRoute(HZD_PAT *route, int *n_out, SVECTOR *out) +{ + int n_points; + HZD_PTP *points; + + n_points = *n_out = route->n_points; + points = route->points; + + while (--n_points >= 0) + { + out->vx = points->x; + out->vy = points->y; + out->vz = points->z; + out->pad = points->command; + + points++; + out++; + } + + return 0; +} + +static int InitRoute(Work *work) +{ + HZD_PAT *routes; + int *n_out; + int n_routes; + int i; + + n_out = work->unk83C; + n_routes = work->control.map->hzd->def->n_routes; + routes = work->control.map->hzd->def->routes; + for (i = 0; i < n_routes; routes++, n_out++, i++) + { + if (ReadRoute(routes, n_out, work->unk850[i]) < 0) + { + return -1; + } + } + + work->unkAFC = work->unk850[0][0]; + work->unkB04 = work->unk850[4][0]; + work->unkAD0 = work->unk850[0]; + work->unkB60 = work->unk850[0][0]; + work->unkAD8 = 0; + work->unkAD4 = 0; + return 0; +} + +static void InitExtra(Work *work) +{ + char *option; + + work->weapon = NewJohnnyFamas(&work->control, &work->body, 4, &work->trigger); + work->gunlight = NewGunLight(&work->body.objs->objs[4].world, &work->enable_gunlight); + work->unkB5A = 0; + work->unkB58 = 0; + work->unkB5E = 0; + work->unkB5C = 0; + + option = GCL_GetOption('m'); + if (option != NULL) + { + work->unkB0C = GCL_StrToInt(option); + } + else + { + work->unkB0C = 0; + } + + work->unkB14 = 0; + work->unkB10 = 0; + work->unkB18 = 0; + work->unkB1C = 0; + if (work->unkB0C == 4) + { + work->unkB1C = 0x20; + } + + option = GCL_GetOption('f'); + if (option != NULL) + { + work->unkB70 = GCL_StrToInt(option); + if (work->unkB70 == 3) + { + work->unkB1C |= 2; + } + } + + option = GCL_GetOption('b'); + if (option != NULL) + { + if (GCL_StrToInt(option) == 1) + { + work->unkB1C |= 1; + } + } + + option = GCL_GetOption('c'); + if (option != NULL) + { + work->unkB6C = GCL_StrToInt(option); + } + else + { + work->unkB6C = 0; + } + + option = GCL_GetOption('s'); + if (option != NULL) + { + work->unkB6E = GCL_StrToInt(option); + } + else + { + work->unkB6E = 0; + } + + if (work->unkB6C == 1) + { + if (work->unkB6E == 0) + { + work->unkB1C |= 0x400000; + } + } + else + { + work->unkB1C |= 0x4000; + } + + if (work->unkB6C >= 3U) + { + work->unkB1C |= 0x2000000; + } + + if (work->unkB70 >= 2U) + { + work->unkB1C |= 0x800000 | 0x4000; + } + + work->unkB24 = 0; + work->unkB38 = NULL; + work->unkB3C = 0; + work->unkB44 = 0; + work->unkB4E = 0; + work->unkB4C = 0; + work->unkB52 = 0; + work->unkB50 = 0; + work->unkB56 = 0; + work->unkB54 = 0; + work->unkB74 = 0; + work->unkB1C |= 0x20000000; + work->player_status = GM_PlayerStatus & ~STATE_SHOW_RADAR; + work->unkB30 = DG_ZeroVector; + NagivateInit(&work->nav, work->control.map->hzd, &work->control.mov); +} + +static void InitSounds(Work *work) +{ + int i; + int *out; + char *res; + + if (!GCL_GetOption('v')) + { + return; + } + + i = 0; + out = work->vox_ids; + while ((res = GCL_NextStr())) + { + if (i == 18) + { + break; + } + + *out++ = GCL_StrToInt(res); + i++; + } +} + +static void InitProcs(Work *work) +{ + int i; + int *out; + char *res; + + if (!GCL_GetOption('e')) + { + return; + } + + i = 0; + out = work->proc_id; + while ((res = GCL_NextStr())) + { + if (i == 8) + { + break; + } + + *out++ = GCL_StrToInt(res); + i++; + } +} + +static int GetResources(Work *work, int name, int where) +{ + SVECTOR indices; + CONTROL *control; + OBJECT *object; + RADAR_SIGHT_PARAM *r_param; + + control = &work->control; + if (GM_InitControl(control, name, where) >= 0) + { + GM_ConfigControlString(control, GCL_GetOption('p'), GCL_GetOption('d')); + GM_ConfigControlHazard(control, control->mov.vy, 450, 450); + control->seg_flag = 2; + GM_ConfigControlAttribute(control, 5); + + r_param = &work->control.radar_param; + r_param->dis = 6000; + r_param->range = 1024; + + object = &work->body; + GM_InitObject(object, GV_StrCode("johnny"), 0x32D, GV_StrCode("joh_03c")); + GM_ConfigObjectJoint(object); + GM_ConfigMotionControl(object, &work->m_ctrl, GV_StrCode("joh_03c"), work->m_segs1, work->m_segs2, + control, work->rots); + GM_ConfigObjectLight(object, work->light); + GM_ConfigObjectAction(object, 0, 0, 0); + GM_ConfigControlTrapCheck(control); + if (InitTarget(work) >= 0 && InitRoute(work) >= 0) + { + InitSounds(work); + InitProcs(work); + InitExtra(work); + + indices.vx = 0; + indices.vy = 6; + indices.vz = 12; + indices.pad = 15; + work->shadow = NewShadow(control, object, indices); + if (work->shadow != NULL) + { + s03b_boxall_800C9328(); + return 0; + } + } + } + return -1; +} + +void *NewJohnny(int name, int where) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work == NULL) + { + return NULL; + } + + GV_SetNamedActor(&work->actor, Act, Die, "johnny.c"); + if (GetResources(work, name, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/torture/johnny.h b/components/mgs/mgs/source/chara/torture/johnny.h new file mode 100644 index 00000000..05b9cfbf --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/johnny.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_JOHNNY_H__ +#define __MGS_CHARA_JOHNNY_H__ + +void *NewJohnny(int name, int where); + +#endif // __MGS_CHARA_JOHNNY_H__ diff --git a/components/mgs/mgs/source/chara/torture/johnny2.c b/components/mgs/mgs/source/chara/torture/johnny2.c new file mode 100644 index 00000000..53de9c4e --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/johnny2.c @@ -0,0 +1,205 @@ +#include "johnny2.h" +#include "jfamas.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" + +#include "chara/snake/shadow.h" // for NewShadow +#include "enemy/glight.h" // for NewGunLight + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + OBJECT body; + MOTION_CONTROL m_ctrl; + MOTION_SEGMENT m_segs1[17]; + MOTION_SEGMENT m_segs2[17]; + SVECTOR rots[32]; + MATRIX light[2]; + GV_ACT *weapon; + int trigger; + GV_ACT *gunlight; + int *enable_gunlight; + GV_ACT *shadow; + short field_7EC; + short field_7EE; + char pad1[0x8]; + SVECTOR svec_7F8; +} Work; + +#define EXEC_LEVEL GV_ACTOR_USER + +extern int s03c_dword_800C33D8; + +static void Act(Work *work) +{ + CONTROL *control; + OBJECT *body; + int rand; + + if (s03c_dword_800C33D8 == 1) + { + GV_DestroyActor(&work->actor); + return; + } + + body = &work->body; + GM_ActMotion(body); + + control = &work->control; + GM_ActControl(control); + GM_ActObject(body); + DG_GetLightMatrix2(&control->mov, work->light); + + work->control.height = work->body.height; + + if (work->body.is_end != 0) + { + work->control.mov.vx = 6000; + work->control.mov.vz = 750; + + rand = GV_RandU(64); + if (rand < 16) + { + if (work->body.action != 11) + { + GM_ConfigObjectAction(body, 11, 0, 4); + } + } + else if (rand < 32) + { + if (work->body.action != 15) + { + GM_ConfigObjectAction(body, 15, 0, 4); + } + } + else if (rand < 48) + { + if (work->body.action != 10) + { + GM_ConfigObjectAction(body, 10, 0, 4); + } + } + else if (work->body.action != 9) + { + GM_ConfigObjectAction(body, 9, 0, 4); + } + } + if (work->svec_7F8.vy < 0 && work->control.grounded != 0) + { + work->svec_7F8.vy = 0; + } + + work->svec_7F8.vy -= 32; + + work->control.step.vy = work->control.step.vy + work->svec_7F8.vy; + work->control.radar_param.dir = work->control.rot.vy; +} + +static void Die(Work *work) +{ + if (work->shadow != NULL) + { + GV_DestroyOtherActor(work->shadow); + } + + if (work->weapon != NULL) + { + GV_DestroyOtherActor(work->weapon); + } + + if (work->gunlight != NULL) + { + GV_DestroyOtherActor(work->gunlight); + } + + GM_FreeControl(&work->control); + GM_FreeObject(&work->body); +} + +static void InitExtra(Work *work) +{ + work->weapon = NewJohnnyFamas(&work->control, &work->body, 4, &work->trigger); + work->gunlight = NewGunLight(&work->body.objs->objs[4].world, &work->enable_gunlight); + + work->field_7EE = 0; + work->field_7EC = 0; + + work->svec_7F8 = DG_ZeroVector; +} + +static int GetResources(Work *work) +{ + SVECTOR indices; + CONTROL *control; + OBJECT *obj; + RADAR_SIGHT_PARAM *r_param; + + control = &work->control; + if (GM_InitControl(control, GV_StrCode("ジョニー"), 0) < 0) + { + return -1; + } + + GM_ConfigControlHazard(control, control->mov.vy, -1, -1); + GM_ConfigControlAttribute(control, 5); + + work->control.mov.vx = 6000; + control->mov.vz = 750; + control->mov.vy = 750; + + r_param = &work->control.radar_param; + obj = &work->body; + + control->rot.vy = 1024; + control->turn.vy = 1024; + + r_param->dis = 6000; + r_param->range = 1024; + + GM_InitObject(obj, GV_StrCode("johnny"), 0x32D, GV_StrCode("joh_03c")); + GM_ConfigObjectJoint(obj); + GM_ConfigMotionControl(obj, &work->m_ctrl, GV_StrCode("joh_03c"), work->m_segs1, work->m_segs2, control, + work->rots); + GM_ConfigObjectLight(obj, work->light); + GM_ConfigObjectAction(obj, 10, 0, 0); + + InitExtra(work); + + indices.vx = 0; + indices.vy = 6; + indices.vz = 12; + indices.pad = 15; + work->shadow = NewShadow(control, obj, indices); + + if (work->shadow == NULL) + { + return -1; + } + + return 0; +} + +void *NewJohnny2(void) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work == NULL) + { + return NULL; + } + + GV_SetNamedActor(&work->actor, Act, Die, "johnny2.c"); + + if (GetResources(work) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/torture/johnny2.h b/components/mgs/mgs/source/chara/torture/johnny2.h new file mode 100644 index 00000000..d741daa5 --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/johnny2.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_JOHNNY2_H__ +#define __MGS_CHARA_JOHNNY2_H__ + +void *NewJohnny2(void); + +#endif // __MGS_CHARA_JOHNNY2_H__ diff --git a/components/mgs/mgs/source/chara/torture/ninja.c b/components/mgs/mgs/source/chara/torture/ninja.c new file mode 100644 index 00000000..3d237243 --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/ninja.c @@ -0,0 +1,385 @@ +#include "ninja.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "equip/equip.h" +#include "sound/g_sound.h" + +#include "takabe/spark2.h" // for NewSpark2_800CA714 +#include "takabe/cineutil.h" // for OpenCinemaScreen, etc. + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + OBJECT body; + MOTION_CONTROL m_ctrl; + MOTION_SEGMENT m_segs1[17]; + MOTION_SEGMENT m_segs2[17]; + SVECTOR rots[32]; + MATRIX light[2]; + GV_ACT *shadow; // unused, a guess based on otacom.c + GV_ACT *kogaku; + int bound_where; + SVECTOR field_7E4; + int unused2; + unsigned int mode; + int unused3; + int timer; + int field_7FC[2]; + int procs[4]; +} Work; + +#define EXEC_LEVEL GV_ACTOR_USER + +void AN_Unknown_800CCA40(SVECTOR *pos); + +int s03b_boxall_800C93AC(int arg0); +void s03b_boxall_800C9404(void); +int s03b_boxall_800C95EC(void); + +static void SendMessage(int address, int message1, int message2) +{ + GV_MSG msg; + + msg.address = address; + msg.message_len = 2; + msg.message[0] = message1; + msg.message[1] = message2; + GV_SendMessage(&msg); +} + +static void Update(Work *work, int timer) +{ + SVECTOR svec1; + SVECTOR svec2; + SVECTOR svec3; + MATRIX rot; + + GCL_ARGS args1; + GCL_ARGS args2; + long argv1[1]; + long argv2[1]; + + CONTROL *control; + OBJECT *body; + + int len; + + control = &work->control; + body = &work->body; + + switch (work->mode) + { + case 0: + if (timer == 0) + { + args1.argc = 1; + args1.argv = argv1; + argv1[0] = 0; + GCL_ExecProc(work->procs[1], &args1); + + GV_SubVec3(&work->control.mov, &GM_PlayerPosition, &svec3); + GM_PlayerControl->turn.vy = GV_VecDir2(&svec3); + + GM_GameStatus |= STATE_PADRELEASE | STATE_JAMMING; + + work->kogaku = NewKogaku2(control, body, 0); + } + if (timer == 32) + { + GM_SeSet(&work->field_7E4, SE_PSG1_SHOT); + GM_SetSound(0x01ffff0b, SD_ASYNC); + svec1 = DG_ZeroVector; + svec1.vx = 1024; + svec1.vy = -1024; + svec2 = work->field_7E4; + svec2.vy = 1500; + DG_SetPos2(&svec2, &svec1); + ReadRotMatrix(&rot); + NewSpark2_800CA714(&rot); + svec2.vx -= 250; + AN_Unknown_800CCA40(&svec2); + } + if (timer == 96) + { + SendMessage(work->bound_where, GV_StrCode("入る") /* HASH_ENTER */, GV_StrCode("ninja")); + } + + GV_SubVec3(&work->field_7E4, &GM_PlayerPosition, &svec1); + svec1.vy = 0; + + len = GV_VecLen3(&svec1); + + if (timer == 128) + { + GM_SetSound(0x01000003, SD_ASYNC); + GM_GameStatus &= ~STATE_PADRELEASE; + GM_PlayerControl->turn.vy = GV_VecDir2(&svec1); + GCL_ExecProc(work->procs[2], NULL); + } + if (timer > 128 && len < 1500) + { + GV_SubVec3(&work->control.mov, &GM_PlayerPosition, &svec3); + GM_PlayerControl->turn.vy = GV_VecDir2(&svec3); + OpenCinemaScreen(0, 30000); + GM_GameStatus |= STATE_PADRELEASE; + s03b_boxall_800C93AC(work->field_7FC[0]); + args1.argc = 1; + args1.argv = argv1; + argv1[0] = 1; + GCL_ExecProc(work->procs[1], &args1); + work->timer = 0; + work->mode++; + } + break; + case 1: + s03b_boxall_800C9404(); + if (s03b_boxall_800C95EC()) + { + work->timer = 0; + work->mode++; + } + break; + case 2: + if (timer == 32) + { + GV_DestroyActor(work->kogaku); + GM_SeSetMode(&control->mov, SE_NINJA_STEALTH, GM_SEMODE_BOMB); + } + if (timer == 36) + { + work->kogaku = NewKogaku2(control, body, 0); + } + if (timer == 64) + { + work->timer = 0; + work->mode++; + } + break; + case 3: + if (timer == 0) + { + GV_DestroyActor(work->kogaku); + } + if (timer < 40) + { + if (timer & 1) + { + DG_InvisibleObjs(body->objs); + } + else + { + DG_VisibleObjs(body->objs); + } + } + if (timer == 16) + { + if (work->body.action != 1) + { + GM_ConfigObjectAction(&work->body, 1, 0, 4); + } + } + if (timer == 32) + { + GM_SeSetMode(&control->mov, 196, GM_SEMODE_BOMB); + GM_SetSound(0x01ffff0b, SD_ASYNC); + } + if (timer == 38) + { + work->kogaku = NewKogaku2(control, body, 0); + GM_SeSetMode(&control->mov, SE_NINJA_STEALTH, GM_SEMODE_BOMB); + } + if (timer == 55) + { + DG_InvisibleObjs(body->objs); + } + if (body->is_end) + { + GV_DestroyOtherActor(work->kogaku); + work->timer = 0; + work->mode++; + } + break; + case 4: + s03b_boxall_800C9404(); + if (timer == 0) + { + DG_InvisibleObjs(body->objs); + s03b_boxall_800C93AC(work->field_7FC[1]); + } + if (s03b_boxall_800C95EC()) + { + work->timer = 0; + work->mode++; + CloseCinemaScreen(); + GM_GameStatus &= ~STATE_JAMMING; + GCL_ExecProc(work->procs[2], NULL); + GCL_ExecProc(work->procs[0], NULL); + args2.argc = 1; + args2.argv = argv2; + argv2[0] = 1; + GCL_ExecProc(work->procs[3], &args2); + GM_SetSound(0x01000001, SD_ASYNC); + GV_DestroyActor(&work->actor); + } + break; + case 5: + if (timer == 16) + { + GM_GameStatus &= ~(STATE_JAMMING | STATE_PADRELEASE); + } + break; + } +} + +static void Act(Work *work) +{ + CONTROL *control; + OBJECT *body; + int timer; + + body = &work->body; + GM_ActMotion(body); + + control = &work->control; + GM_ActControl(control); + + GM_ActObject(body); + DG_GetLightMatrix(&control->mov, work->light); + + work->control.height = work->body.height; + + timer = work->timer; + work->timer++; + Update(work, timer); +} + +static void Die(Work *work) +{ + GM_FreeControl(&work->control); + GM_FreeObject(&work->body); + + if (work->shadow) + { + GV_DestroyOtherActor(work->shadow); + } +} + +static void InitSounds(Work *work) +{ + int i; + int *out; + char *res; + + if (!GCL_GetOption('v')) + { + return; + } + + i = 0; + out = work->field_7FC; + while ((res = GCL_NextStr())) + { + if (i == 2) + { + break; + } + + *out++ = GCL_StrToInt(res); + i++; + } +} + +static void InitProc(Work *work) +{ + int i; + int *out; + char *res; + + if (!GCL_GetOption('r')) + { + return; + } + + i = 0; + out = work->procs; + while ((res = GCL_NextStr())) + { + if (i == 4) + { + break; + } + + *out++ = GCL_StrToInt(res); + i++; + } +} + +static int GetResources(Work *work, int name, int where) +{ + CONTROL *control; + OBJECT *body; + int model; + int motion; + + control = &work->control; + if (GM_InitControl(control, name, where) < 0) + { + return -1; + } + + GM_ConfigControlString(control, GCL_GetOption('p'), GCL_GetOption('d')); + + GM_ConfigControlHazard(control, 1000, -1, -1); + + GCL_GetOption('m'); + model = GCL_StrToInt(GCL_NextStr()); + + GCL_GetOption('o'); + motion = GCL_StrToInt(GCL_NextStr()); + + body = &work->body; + GM_InitObject(body, model & 0xFFFF, 0x2D, motion & 0xFFFF); + GM_ConfigObjectJoint(body); + GM_ConfigMotionControl(body, &work->m_ctrl, motion & 0xFFFF, work->m_segs1, work->m_segs2, control, work->rots); + GM_ConfigObjectLight(body, work->light); + GM_ConfigObjectAction(body, 0, 0, 0); + + GCL_GetOption('b'); + work->bound_where = GCL_StrToInt(GCL_NextStr()); + + InitSounds(work); + InitProc(work); + + GCL_GetOption('a'); + GCL_StrToSV(GCL_NextStr(), (short *)&work->field_7E4); + + work->unused3 = 0; + work->timer = 0; + work->shadow = NULL; + + return 0; +} + +void *NewPrisonNinja(int name, int where) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work == NULL) + { + return NULL; + } + + GV_SetNamedActor(&work->actor, Act, Die, "ninja.c"); + if (GetResources(work, name, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/torture/ninja.h b/components/mgs/mgs/source/chara/torture/ninja.h new file mode 100644 index 00000000..d18f4ecc --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/ninja.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_NINJA_H__ +#define __MGS_CHARA_NINJA_H__ + +void *NewPrisonNinja(int name, int where); + +#endif // __MGS_CHARA_NINJA_H__ diff --git a/components/mgs/mgs/source/chara/torture/otacom.c b/components/mgs/mgs/source/chara/torture/otacom.c new file mode 100644 index 00000000..286b600c --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/otacom.c @@ -0,0 +1,552 @@ +#include "johnny2.h" +#include "otacom.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "game/navi.h" +#include "chara/snake/sna_init.h" +#include "chara/snake/shadow.h" +#include "equip/equip.h" +#include "sound/g_sound.h" + +// "It stands for Otaku Comvention." +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + OBJECT object; + MOTION_CONTROL motion; + MOTION_SEGMENT oar1[17]; + MOTION_SEGMENT oar2[17]; + SVECTOR rots[16]; + SVECTOR adjust[16]; + MATRIX light[2]; + GV_ACT *shadow; + GV_ACT *kogaku; + int bound_where; + NAVIGATE nav; + int field_804; + int mode; + int timer; + int field_810[3]; + int procs[3]; +} Work; + +#define EXEC_LEVEL GV_ACTOR_USER + +int s03c_dword_800C33D8 = 0; + +void s03b_boxall_800C9404(void); +int s03b_boxall_800C93AC(int arg0); +int s03b_boxall_800C95EC(void); + +void OtacomSendMessage_800CB3E0(int address, int message) +{ + GV_MSG msg; + + msg.address = address; + msg.message[0] = message; + msg.message[1] = GV_StrCode("otacom"); + msg.message_len = 2; + GV_SendMessage(&msg); +} + +void OtacomSendLampOnOffToGomon_800CB420(int on) +{ + GV_MSG msg; + + msg.address = GV_StrCode("dr_gomon"); + + msg.message[0] = GV_StrCode("on"); + if (on) + { + msg.message[1] = GV_StrCode("dr_lamp_on"); + } + else + { + msg.message[1] = GV_StrCode("dr_lamp_off"); + } + + msg.message_len = 2; + + GV_SendMessage(&msg); +} + +void Otacom_800CB494(Work *work, int timer) +{ + SVECTOR svec1; + SVECTOR indices; + SVECTOR svec2; + SVECTOR svec3; + SVECTOR svec4; + + CONTROL *control; + OBJECT *object; + + control = &work->control; + object = &work->object; + + switch (work->mode) + { + case 0: + if (timer == 0) + { + work->kogaku = NewKogaku2(control, object, 0); + + if (work->object.action != 1) + { + GM_ConfigObjectAction(&work->object, 1, 0, 0); + } + + OtacomSendMessage_800CB3E0(work->bound_where, GV_StrCode("入る")); // 入る = enter (HASH_ENTER) + OtacomSendLampOnOffToGomon_800CB420(1); + } + + if (timer == 64) + { + OtacomSendMessage_800CB3E0(work->bound_where, GV_StrCode("出る")); // 出る = leave (HASH_LEAVE) + OtacomSendLampOnOffToGomon_800CB420(0); + + if (work->object.action != 0) + { + GM_ConfigObjectAction(&work->object, 0, 0, 4); + } + + work->timer = 0; + work->mode++; + } + break; + + case 1: + work->nav.addr = HZD_GetAddress(work->control.map->hzd, &work->control.mov, work->nav.addr); + GM_ZoneNavi(&work->nav, &work->control); + + if (NavigateGetTargetDist(&work->nav, &work->control.mov) < 250) + { + if (work->object.action != 1) + { + GM_ConfigObjectAction(&work->object, 1, 0, 4); + } + + work->control.turn.vy = -1024; + s03b_boxall_800C93AC(work->field_810[0]); + + work->timer = 0; + work->mode++; + } + break; + + case 2: + s03b_boxall_800C9404(); + + if (s03b_boxall_800C95EC() != 0) + { + GV_SubVec3(&work->control.mov, &GM_PlayerPosition, &svec4); + GM_PlayerControl->turn.vy = GV_VecDir2(&svec4); + + GCL_ExecProc(work->procs[1], NULL); + + GM_GameStatus |= STATE_PADRELEASE; + + work->timer = 0; + work->mode++; + } + break; + + case 3: + if (timer == 16) + { + if (work->kogaku) + { + GV_DestroyOtherActor(work->kogaku); + } + work->kogaku = NULL; + + GM_SeSetMode(&work->control.mov, SE_NINJA_STEALTH, GM_SEMODE_BOMB); + GM_ConfigControlAttribute(control, 1); + + indices.vx = 0; + indices.vy = 6; + indices.vz = 12; + indices.pad = 15; + work->shadow = NewShadow(control, object, indices); + } + + if (timer == 64) + { + GCL_ExecProc(work->procs[2], NULL); + + GM_GameStatus &= ~STATE_PADRELEASE; + + work->timer = 0; + work->mode++; + } + break; + + case 4: + svec2 = work->control.mov; + svec2.vy = 0; + + svec3 = GM_PlayerPosition; + svec3.vy = 0; + + GV_SubVec3(&svec2, &svec3, &svec1); + + if (GV_VecLen3(&svec1) < 1500) + { + GV_SubVec3(&work->control.mov, &GM_PlayerPosition, &svec1); + GM_PlayerControl->turn.vy = GV_VecDir2(&svec1); + + GCL_ExecProc(work->procs[2], NULL); + GCL_ExecProc(work->procs[0], NULL); + + GV_DestroyActor(&work->actor); + } + break; + } +} + +void Otacom_800CB838(Work *work, int timer) +{ + SVECTOR indices; + CONTROL *control; + OBJECT *object; + + control = &work->control; + object = &work->object; + + switch (work->mode) + { + case 0: + s03b_boxall_800C9404(); + if (timer == 0) + { + GM_ConfigControlAttribute(control, 1); + + indices.vx = 0; + indices.vy = 6; + indices.vz = 12; + indices.pad = 15; + work->shadow = NewShadow(control, object, indices); + + GCL_ExecProc(work->procs[1], NULL); + + if (work->object.action != 1) + { + GM_ConfigObjectAction(object, 1, 0, 0); + } + + s03b_boxall_800C93AC(work->field_810[1]); + NewJohnny2(); + } + + GM_GameStatus |= STATE_PADRELEASE | STATE_RADIO_OFF; + + if (s03b_boxall_800C95EC()) + { + GM_GameStatus &= ~STATE_PADRELEASE; + + if (work->object.action != 0) + { + GM_ConfigObjectAction(&work->object, 0, 0, 4); + } + + work->timer = 0; + work->mode++; + } + break; + + case 1: + s03b_boxall_800C9404(); + + if (timer == 32) + { + GCL_ExecProc(work->procs[2], NULL); + s03b_boxall_800C93AC(work->field_810[2]); + } + + if (timer == 96) + { + if (work->shadow) + { + GV_DestroyOtherActor(work->shadow); + } + work->shadow = NULL; + + work->kogaku = NewKogaku2(control, object, 0); + + GM_ConfigControlAttribute(control, 0); + GM_SeSetMode(&control->mov, SE_NINJA_STEALTH, GM_SEMODE_BOMB); + } + + work->nav.addr = HZD_GetAddress(work->control.map->hzd, &work->control.mov, work->nav.addr); + + GM_ZoneNavi(&work->nav, &work->control); + + if (NavigateGetTargetDist(&work->nav, &work->control.mov) < 250) + { + if (work->object.action != 1) + { + GM_ConfigObjectAction(&work->object, 1, 0, 4); + } + + work->timer = 0; + work->mode++; + } + break; + + case 2: + if (timer == 0) + { + DG_InvisibleObjs(work->object.objs); + OtacomSendMessage_800CB3E0(work->bound_where, GV_StrCode("入る")); // 入る = enter (HASH_ENTER) + OtacomSendLampOnOffToGomon_800CB420(1); + } + + if (timer == 64) + { + OtacomSendMessage_800CB3E0(work->bound_where, GV_StrCode("出る")); // 出る = leave (HASH_LEAVE) + OtacomSendLampOnOffToGomon_800CB420(0); + } + + if (timer == 128) + { + s03c_dword_800C33D8 = 1; + + GCL_ExecProc(work->procs[0], NULL); + GV_DestroyActor(&work->actor); + } + break; + } +} + +void Otacom_800CBB20(Work *work) +{ + int footstepsFrame; + CONTROL *control; + + if (work->object.action == 0) + { + footstepsFrame = work->motion.info1.frame; + control = &work->control; + if (footstepsFrame == 0) + { + GM_SeSetMode(&control->mov, 0xA0, GM_SEMODE_BOMB); + } + if (footstepsFrame == 0xC) + { + GM_SeSetMode(&control->mov, 0xA1, GM_SEMODE_BOMB); + } + } +} + +void OtacomAct_800CBB8C(Work *work) +{ + CONTROL *control; + OBJECT *object; + int timer; + + object = &work->object; + GM_ActMotion(object); + + control = &work->control; + GM_ActControl(control); + + GM_ActObject(object); + DG_GetLightMatrix(&control->mov, work->light); + + work->control.height = work->object.height; + + timer = work->timer++; + switch (work->field_804) + { + case 0: + Otacom_800CB494(work, timer); + break; + case 1: + Otacom_800CB838(work, timer); + break; + } + + Otacom_800CBB20(work); + sna_act_helper2_helper2_80033054(GV_StrCode("オタコン"), &work->adjust[6]); // オタコン = Otacon +} + +void OtacomDie_800CBC50(Work *work) +{ + if (work->shadow) + { + GV_DestroyOtherActor(work->shadow); + } + + if (work->kogaku) + { + GV_DestroyActorQuick(work->kogaku); + } + + GM_FreeControl(&work->control); + GM_FreeObject(&work->object); + + GM_GameStatus &= ~STATE_RADIO_OFF; +} + +void Otacom_800CBCC4(Work *work) +{ + int i; + int *out; + char *res; + + if (!GCL_GetOption('v')) + { + return; + } + + i = 0; + out = work->field_810; + while ((res = GCL_NextStr())) + { + if (i == 3) + { + break; + } + + *out++ = GCL_StrToInt(res); + i++; + } +} + +void Otacom_800CBD3C(Work *work) +{ + int i; + int *out; + char *res; + + if (!GCL_GetOption('r')) + { + return; + } + + i = 0; + out = work->procs; + while ((res = GCL_NextStr())) + { + if (i == 3) + { + break; + } + + *out++ = GCL_StrToInt(res); + i++; + } +} + +int OtacomGetResources_800CBDB4(Work *work, int arg1, int arg2) +{ + SVECTOR svec; + CONTROL *control; + int model; + int motion; + + control = &work->control; + if (GM_InitControl(control, arg1, arg2) < 0) + { + return -1; + } + + GM_ConfigControlString(control, GCL_GetOption('p'), GCL_GetOption('d')); + GM_ConfigControlHazard(control, 1000, -1, -1); + + if (GCL_GetOption('m')) + { + model = GCL_StrToInt(GCL_NextStr()); + } + else + { + model = GV_StrCode("otacom"); + } + + if (GCL_GetOption('o')) + { + motion = GCL_StrToInt(GCL_NextStr()); + } + else + { + motion = GV_StrCode("ota_03c"); + } + + GM_InitObject(&work->object, model & 0xFFFF, 0x2D, motion & 0xFFFF); + GM_ConfigObjectJoint(&work->object); + GM_ConfigMotionControl(&work->object, &work->motion, motion & 0xFFFF, work->oar1, work->oar2, control, + work->rots); + GM_ConfigObjectLight(&work->object, work->light); + GM_ConfigObjectAction(&work->object, 0, 0, 0); + + if (GCL_GetOption('b')) + { + work->bound_where = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->bound_where = -1; + } + + Otacom_800CBCC4(work); + Otacom_800CBD3C(work); + + NagivateInit(&work->nav, work->control.map->hzd, &work->control.mov); + + if (GCL_GetOption('a')) + { + GCL_StrToSV(GCL_NextStr(), (short *)&svec); + } + else + { + svec = DG_ZeroVector; + } + + NavigateSetTarget(&work->nav, work->control.map->hzd, &svec); + + work->timer = 0; + + if (GCL_GetOption('c')) + { + work->field_804 = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->field_804 = 0; + } + + if (work->field_804) + { + GM_GameStatus |= STATE_PADRELEASE | STATE_RADIO_OFF; + } + + work->shadow = 0; + s03c_dword_800C33D8 = 0; + GM_ConfigMotionAdjust(&work->object, work->adjust); + + return 0; +} + +void *NewPrisonOtacon(int name, int where) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work == NULL) + { + return NULL; + } + + GV_SetNamedActor(&work->actor, OtacomAct_800CBB8C, OtacomDie_800CBC50, "otacom.c"); + if (OtacomGetResources_800CBDB4(work, name, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/torture/otacom.h b/components/mgs/mgs/source/chara/torture/otacom.h new file mode 100644 index 00000000..a862b24f --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/otacom.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_OTACOM_H__ +#define __MGS_CHARA_OTACOM_H__ + +void *NewPrisonOtacon(int name, int where); + +#endif // __MGS_CHARA_OTACOM_H__ diff --git a/components/mgs/mgs/source/chara/torture/revolver.c b/components/mgs/mgs/source/chara/torture/revolver.c new file mode 100644 index 00000000..7a65a071 --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/revolver.c @@ -0,0 +1,1545 @@ +#include "revolver.h" + +#include +#include "common.h" +#include "chara/snake/sna_init.h" +#include "chara/snake/shadow.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "linkvar.h" +#include "strcode.h" + +#include "takabe/fadeio.h" // for NewFadeInOut +#include "takabe/cineutil.h" // for OpenCinemaScreen, etc. + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + OBJECT field_9C; + MOTION_CONTROL field_180; + MOTION_SEGMENT field_1D0; + char pad_1F4[0x264]; + MOTION_SEGMENT field_458; + char pad_47C[0x264]; + SVECTOR field_6E0; + char pad_6E8[0x80]; + SVECTOR field_768; + char pad_770[0x30]; + SVECTOR field_7A0; + char pad_7A8[0x48]; + MATRIX field_7F0_light[2]; + GV_ACT *shadow; + int field_834; + SVECTOR field_838[16]; + SVECTOR *field_8B8; + int field_8BC; + SVECTOR field_8C0; + int field_8C8; + int field_8CC; + int field_8D0[27]; + int field_93C; + int field_940; + int field_944; + int field_948; + int field_94C; + int field_950; + int field_954; + int field_958; + int field_95C; + int field_960; + short *field_964; + char pad_968[0x2]; + short field_96A; + short field_96C; + char pad_96E[0x2]; + short *field_970; + int field_974[12]; + SVECTOR field_9A4; + SVECTOR field_9AC; + short field_9B4; + short field_9B6; +} Work; + +#define EXEC_LEVEL GV_ACTOR_USER +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_GBOUND | DG_FLAG_SHADE) + +/* TODO: type */ +u_short s03b_dword_800C32E4[] = +{ + 0x0000, HASH_MOVE, 0x0002, 0x0000, + 0xFFFF, HASH_MOTION, 0x0000, 0x0001, + 0xFFFF, HASH_TURN, 0x0000, 0x0001 +}; + +int s03b_dword_800C32FC[] = +{ + 0, + 1, + 2, + 24, + 17, + 18, + 19, + 20, + 23, + 21, + 22, + 25, + 26, + -1, +}; + +const char *s03b_dword_800C3334[] = +{ + "v0", + "v1", + "v2", + "v24", + "v17", + "v18", + "v19", + "v20", + "v23", + "v21", + "v22", + "v25", + "v26", + "end" +}; + +// Those functions are not actually in boxall, info +// those are some helper functions (not sure if part of revolver.c) +int s03b_boxall_800C9328(void); +int s03b_boxall_800C93AC(int arg0); +int s03b_boxall_800C95EC(void); +int s03b_boxall_800C95FC(void); +int s03b_boxall_800C961C(int); +int s03b_boxall_800C9654(int); +int s03b_boxall_800C968C(void); + +void s03b_800CA868(void); + +int Revolver_800C8710(Work *work, int arg1); +int Revolver_800C8794(Work *, int); +int Revolver_800C884C(Work *, int); +int Revolver_800C8910(Work *, int); + +void RevolverSendMessage_800C7170(int hash, short *message) +{ + GV_MSG msg; + + msg.address = hash; + msg.message_len = 2; + msg.message[0] = message[1]; + msg.message[1] = message[2]; + + GV_SendMessage(&msg); +} + +void RevolverSendMessage_800C71B0(int hash, int message) +{ + GV_MSG msg; + + msg.address = hash; + msg.message_len = 2; + msg.message[0] = HASH_MOTION; + msg.message[1] = message; + GV_SendMessage(&msg); +} + +int s03b_revolver_800C71E8(int hash, short *messages, int arg2) +{ + int count; + short mes; + + if (messages[0] < 0) + { + return 0; + } + + count = 0; + mes = messages[1]; + if (mes >= arg2) + { + while (1) + { + if (mes == arg2) + { + RevolverSendMessage_800C71B0(hash, messages[0]); + messages += 2; + count += 2; + } + + if (messages[0] < 0 || arg2 < messages[1] || arg2 > messages[1]) + { + break; + } + + mes = messages[1]; + } + } + + return count; +} + +void s03b_revolver_800C72A4(Work *work, int arg1) +{ + short *messages; + int hash; + + messages = work->field_964; + hash = work->control.name; + + while ((unsigned short)messages[1] != HASH_VOICE || arg1 == 0) + { + if (messages[0] == arg1) + { + RevolverSendMessage_800C7170(hash, messages); + work->field_964 += 4; + work->field_960++; + } + else if (messages[0] < 0 && (work->field_948 & (unsigned short)messages[3]) != 0) + { + RevolverSendMessage_800C7170(hash, messages); + work->field_964 += 4; + } + else + { + break; + } + + messages = work->field_964; + } +} + +int s03b_revolver_800C7384(Work *work, int index) +{ + int entry; + int name; + + entry = s03b_dword_800C32FC[index]; + + name = GV_StrCode(s03b_dword_800C3334[index]); + work->field_970 = GV_GetCache(GV_CacheID(name, 'l')); + + s03b_boxall_800C93AC(work->field_8D0[entry]); + work->field_93C = work->field_8D0[entry]; + + if (work->field_9C.action != 0) + { + GM_ConfigObjectAction(&work->field_9C, 0, 0, 4); + } + + return 0; +} + +int s03b_revolver_800C742C(Work *work, int arg1, int arg2, int arg3) +{ + int temp_a1; + + if (arg1 == 0) + { + work->field_954 = 0; + work->field_96C = 0; + } + + temp_a1 = work->field_954++; + switch (work->field_96C) + { + case 0: + case 1: + if (Revolver_800C884C(work, temp_a1)) + { + work->field_954 = 0; + work->field_96C++; + } + break; + + case 2: + if (Revolver_800C8910(work, temp_a1)) + { + work->field_954 = 0; + work->field_96C++; + } + break; + + case 3: + if (Revolver_800C8794(work, temp_a1)) + { + work->field_954 = 0; + work->field_96C++; + } + break; + } + + if (arg1 == arg3 && arg2 >= 0) + { + printf("voice %d\n", work->field_8D0[arg2]); + s03b_boxall_800C93AC(work->field_8D0[arg2]); + } + + if (arg2 >= 0) + { + if (!s03b_boxall_800C9654(work->field_8D0[arg2])) + { + return 0; + } + } + else if (arg1 != arg3) + { + return 0; + } + + work->field_948 &= ~0x200; + return 1; +} + +void s03b_revolver_800C7574(Work *work, int arg1) +{ + short messages[4]; + + int iVar5; + int uVar7; + int iVar4; + + int status; + + int iVar6; + int new_var, new_var3; + + iVar5 = work->field_96A; + uVar7 = work->field_948; + iVar4 = work->field_950; + + status = GM_StreamStatus(); + + if (status == -1 || status == 2) + { + work->field_950++; + } + + if (work->field_950 == 0) + { + work->field_950 = 1; + } + + iVar6 = work->field_958; + + if (status == 2) + { + work->field_958++; + } + + if ((GV_PadData[2].press & PAD_CROSS) != 0) + { + work->field_944 = 5; + + work->field_9A4.vx = -3250; + work->field_9A4.vy = 788; + work->field_9A4.vz = -1125; + + work->field_94C = 0; + work->field_9AC = DG_ZeroVector; + work->field_9B4 = 10; + work->field_9B6 = 1; + return; + } + + switch(iVar5) + { + case 0: + if (iVar4 == 0) + { + work->field_958 = -1; + iVar6 = work->field_958; + + OpenCinemaScreen(0, 60000); + s03b_revolver_800C7384(work, 0); + + work->field_948 &= ~0x100; + GCL_ExecProc(work->field_974[11], 0); + } + + if (s03b_boxall_800C961C(work->field_93C)) + { + GCL_ExecProc(work->field_974[0], 0); + } + goto check; + + case 1: + goto check; + + case 3: + if (s03b_boxall_800C961C(work->field_93C)) + { + GCL_ExecProc(work->field_974[1], 0); + } + goto check; + + case 4: + if (s03b_boxall_800C961C(work->field_93C)) + { + GCL_ExecProc(work->field_974[9], 0); + } + goto check; + + case 5: + if (s03b_boxall_800C961C(work->field_93C)) + { + GCL_ExecProc(work->field_974[10], 0); + } + +check: + new_var = 3; + new_var3 = 4; + if ((uVar7 & 0x2) != 0) + { + if (iVar5 == 0) + { + s03b_revolver_800C7384(work, 1); + } + else if (iVar5 == new_var) + { + s03b_revolver_800C7384(work, 11); + } + else if (iVar5 == new_var3) + { + s03b_revolver_800C7384(work, 12); + } + + + work->field_950 = 0; + work->field_958 = -1; + iVar6 = work->field_958; + + work->field_96A++; + } + + work->field_970 += s03b_revolver_800C71E8(work->control.name, work->field_970, iVar6); + break; + + case 2: + if ((GM_TotalHours * 3600 + GM_TotalSeconds) - (GM_LastSaveHours * 3600 + GM_LastSaveSeconds) < 0 + || (GM_TotalHours * 3600 + GM_TotalSeconds) - (GM_LastSaveHours * 3600 + GM_LastSaveSeconds) > 1800) + { + s03b_revolver_800C7384(work, 2); + work->field_96A = 3; + } + else + { + s03b_revolver_800C7384(work, 11); + work->field_96A = 4; + } + + work->field_950 = 0; + work->field_970 += s03b_revolver_800C71E8(work->control.name, work->field_970, iVar6); + break; + + case 6: + if (Revolver_800C8710(work, iVar4)) + { + work->field_950 = 0; + work->field_96A++; + } + break; + + case 7: + if (s03b_revolver_800C742C(work, iVar4, 8, 48)) + { + NewFadeInOut(FADEIO_MODE_TOBLACK, 16); + CloseCinemaScreen(); + + work->field_950 = 0; + work->field_96A++; + } + break; + + case 8: + if (iVar4 == 18) + { + messages[1] = GV_StrCode("end"); + messages[2] = 0; + RevolverSendMessage_800C7170(GV_StrCode("ch_progcam"), messages); + } + else if (iVar4 == 28) + { + messages[1] = 0x71f1; + messages[2] = 0; + RevolverSendMessage_800C7170(0x62fe, messages); + work->field_948 |= 0x100; + } + break; + + default: + break; + } +} + +void s03b_revolver_800C7958(Work *work, int arg1) +{ + short messages[4]; + int iVar4; + int iVar3; + int uVar6; + int status; + int iVar5; + int index; + + iVar4 = work->field_96A; + iVar3 = work->field_950; + uVar6 = work->field_948; + status = GM_StreamStatus(); + + if (status == -1 || status == 2) + { + work->field_950++; + } + + if (work->field_950 == 0) + { + work->field_950 = 1; + } + + iVar5 = work->field_958; + + if (status == 2) + { + work->field_958++; + } + + if (iVar4 > 1 && (GV_PadData[2].press & PAD_CROSS) != 0) + { + work->field_944 = 5; + + work->field_9A4.vx = -3250; + work->field_9A4.vy = 788; + work->field_9A4.vz = -1125; + + work->field_94C = 0; + work->field_9AC = DG_ZeroVector; + work->field_9B4 = 10; + work->field_9B6 = 1; + return; + } + + switch(iVar4) + { + case 0: + if (iVar3 == 0) + { + OpenCinemaScreen(0, 60000); + } + + if (iVar3 == 120) + { + work->field_950 = 0; + work->field_96A++; + } + break; + + case 1: + if (iVar3 == 0) + { + work->field_958 = -1; + iVar5 = work->field_958; + s03b_revolver_800C7384(work, 9); + work->field_948 &= ~0x100; + } + goto check; + + case 2: + if (s03b_boxall_800C961C(work->field_93C)) + { + GCL_ExecProc(work->field_974[3], 0); + + messages[1] = 0x71f1; + messages[2] = 0; + RevolverSendMessage_800C7170(0x62fe, messages); + } + goto check; + + case 3: + if (work->field_940 > 1) + { + index = 3; + } + else + { + index = 4; + } + + if (s03b_boxall_800C961C(work->field_93C)) + { + GCL_ExecProc(work->field_974[index], 0); + + if (index == 3) + { + messages[1] = 0x71f1; + messages[2] = 0; + RevolverSendMessage_800C7170(0x62fe, messages); + } + } + +check: + if ((uVar6 & 2) != 0) + { + if (iVar4 == 1) + { + s03b_revolver_800C7384(work, 0); + } + else if (iVar4 == 2) + { + s03b_revolver_800C7384(work, 10); + } + + work->field_96A++; + work->field_950 = 0; + + work->field_958 = -1; + iVar5 = work->field_958; + + if (work->field_940 > 1 && iVar4 == 1) + { + work->field_96A++; + } + } + + work->field_970 += s03b_revolver_800C71E8(work->control.name, work->field_970, iVar5); + break; + + case 4: + if (iVar3 == 0) + { + GCL_ExecProc(work->field_974[5], 0); + } + + if (Revolver_800C8710(work, iVar3)) + { + work->field_950 = 0; + work->field_96A++; + } + break; + + case 5: + if (s03b_revolver_800C742C(work, iVar3, 8, 48)) + { + NewFadeInOut(FADEIO_MODE_TOBLACK, 16); + CloseCinemaScreen(); + work->field_950 = 0; + work->field_96A++; + } + break; + + case 6: + if (iVar3 == 18) + { + messages[1] = GV_StrCode("end"); + messages[2] = 0; + RevolverSendMessage_800C7170(GV_StrCode("ch_progcam"), messages); + } + else if (iVar3 == 28) + { + messages[1] = 0x71f1; + messages[2] = 0; + RevolverSendMessage_800C7170(0x62fe, messages); + work->field_948 |= 0x100; + } + break; + } +} + +void s03b_revolver_800C7D04(Work *work, int arg1) +{ + short message[4]; + + CheckCinemaTimeout(); + + if (arg1 == 0) + { + work->field_950 = 0; + work->field_96A = 0; + GM_GameStatus |= STATE_PADMASK; + GV_PadMask = (GV_PadMask & ~0xF810) | 0x40; + } + + if (work->field_940 == 0) + { + s03b_revolver_800C7574(work, arg1); + } + else + { + s03b_revolver_800C7958(work, arg1); + } + + if (work->field_948 & 0x100) + { + message[1] = 4; + message[2] = 0; + RevolverSendMessage_800C7170(GV_StrCode("スネーク"), message); + + GM_GameStatus &= ~STATE_PADRELEASE; + + message[1] = HASH_MODE; + message[2] = 1; + RevolverSendMessage_800C7170(work->control.name, message); + + work->field_948 &= ~0x100; + + GV_PadMask = ~0x800; + GM_GameStatus |= STATE_PADMASK; + } +} + +void Revolver_800C7E2C(Work *work, int arg1) +{ + work->control.mov = *work->field_8B8; + if (work->field_948 & 0x200) + { + s03b_revolver_800C742C(work, arg1, -1, 0x80); + } +} + +void s03b_revolver_800C7E88(Work *work, int arg1) +{ + short messages[4]; + int uVar8; + int uVar1; + int iVar7; + int status; + int iVar6; + + CheckCinemaTimeout(); + + if (arg1 == 0) + { + GM_GameStatus |= STATE_PADMASK; + GV_PadMask &= ~0xf810; + OpenCinemaScreen(0, 60000); + work->field_950 = 0; + work->field_96A = 0; + work->field_9B6 = 0; + } + + if ((GV_PadData[2].press & PAD_CROSS) != 0) + { + s03b_boxall_800C9328(); + GM_GameStatus |= STATE_PADRELEASE; + + if (work->field_8C8 > -1) + { + GCL_ExecProc(work->field_8C8, 0); + } + + work->field_9B6 = 1; + return; + } + + if (work->field_9B6 == 1) + { + return; + } + + uVar8 = work->field_948; + uVar1 = work->field_96A; + iVar7 = work->field_950; + status = GM_StreamStatus(); + + if (status == -1 || status == 2) + { + work->field_950++; + } + + if (work->field_950 == 0) + { + work->field_950 = 1; + } + + iVar6 = work->field_958; + + if (status == 2) + { + work->field_958++; + } + + switch(uVar1) + { + case 0: + if (Revolver_800C8710(work, iVar7)) + { + work->control.turn.vy = 1800; + work->field_958 = -1; + s03b_revolver_800C7384(work, 4); + work->field_950 = 0; + work->field_96A++; + } + break; + + case 1: + if ((uVar8 & 2) != 0) + { + if (work->field_940 < 3) + { + s03b_revolver_800C7384(work, work->field_940 + 5); + work->field_950 = 0; + work->field_96A++; + } + else + { + work->field_950 = 0; + work->field_96A += 2; + } + + work->field_958 = -1; + iVar6 = work->field_958; + } + + work->field_970 += s03b_revolver_800C71E8(work->control.name, work->field_970, iVar6); + break; + + case 2: + if (s03b_boxall_800C961C(work->field_93C)) + { + GCL_ExecProc(work->field_974[work->field_940 + 6], 0); + } + + if ((uVar8 & 2) != 0) + { + work->field_950 = 0; + work->field_958 = -1; + iVar6 = work->field_958; + work->field_96A++; + NewFadeInOut(FADEIO_MODE_TOBLACK, 12); + } + + work->field_970 += s03b_revolver_800C71E8(work->control.name, work->field_970, iVar6); + break; + + case 3: + if (Revolver_800C8710(work, iVar7)) + { + work->field_950 = 0; + work->field_96A++; + } + break; + + case 4: + if (s03b_revolver_800C742C(work, iVar7, -1, 48)) + { + work->field_950 = 0; + GM_GameStatus &= ~STATE_PADRELEASE; + work->field_96A++; + } + + if (iVar7 == 0) + { + messages[1] = GV_StrCode("end"); + messages[2] = 0; + RevolverSendMessage_800C7170(GV_StrCode("ch_progcam"), messages); + } + else if (iVar7 == 8) + { + messages[1] = 0x71f1; + messages[2] = 0; + RevolverSendMessage_800C7170(0x62fe, messages); + + messages[1] = 4; + messages[2] = 0; + RevolverSendMessage_800C7170(GV_StrCode("スネーク"), messages); + } + break; + } +} + +void s03b_revolver_800C81EC(Work *work, int arg1) +{ + if (arg1 == 0) + { + work->field_964 = s03b_dword_800C32E4; + work->field_95C = 3; + work->field_960 = 0; + GM_GameStatus |= STATE_PADRELEASE; + } + + if (work->field_960 == work->field_95C) + { + GM_GameStatus &= ~STATE_PADRELEASE; + } + else + { + s03b_revolver_800C72A4(work, arg1); + } +} + +void s03b_revolver_800C826C(Work *work, int arg1) +{ + short messages[4]; + int uVar1; + int iVar4; + int status; + int iVar2; + + CheckCinemaTimeout(); + + if (arg1 == 0) + { + GM_GameStatus |= STATE_PADRELEASE; + OpenCinemaScreen(0, 60000); + work->field_950 = 0; + work->field_96A = 0; + } + + uVar1 = work->field_96A; + iVar4 = work->field_950; + status = GM_StreamStatus(); + + if (status == -1 || status == 2) + { + work->field_950++; + } + + if (work->field_950 == 0) + { + work->field_950 = 1; + } + + iVar2 = work->field_958; + if (status == 2) + { + work->field_958++; + } + + switch(uVar1) + { + case 0: + if (Revolver_800C8710(work, iVar4)) + { + work->field_958 = -1; + s03b_revolver_800C7384(work, 8); + work->field_950 = 0; + work->field_96A++; + } + break; + + case 1: + if (iVar4 == 254) + { + work->field_950 = 0; + work->field_96A++; + } + + work->field_970 += s03b_revolver_800C71E8(work->control.name, work->field_970, iVar2); + break; + + case 2: + if (s03b_revolver_800C742C(work, iVar4, -1, 48)) + { + work->field_950 = 0; + work->field_96A++; + } + break; + + case 3: + if (GM_StreamStatus() == -1) + { + work->field_950 = 0; + work->field_96A++; + NewFadeInOut(FADEIO_MODE_TOBLACK, 28); + } + break; + + case 4: + if (iVar4 == 34) + { + messages[1] = 0x71f1; + messages[2] = 0; + RevolverSendMessage_800C7170(0x62fe, messages); + } + else if (iVar4 == 28) + { + messages[1] = 4; + messages[2] = 0; + RevolverSendMessage_800C7170(GV_StrCode("スネーク"), messages); + CloseCinemaScreen(); + } + break; + } +} + +void Revolver_800C8488(Work *work, int mode) +{ + short message[3]; + int field_9B4; + + if (mode == 0) + { + NewFadeInOut(FADEIO_MODE_TOBLACK, 28); + s03b_boxall_800C9328(); + CloseCinemaScreen(); + } + + if (mode == 32) + { + message[1] = GV_StrCode("end"); + message[2] = 0; + RevolverSendMessage_800C7170(GV_StrCode("ch_progcam"), message); + + message[1] = 4; + message[2] = 0; + RevolverSendMessage_800C7170(GV_StrCode("スネーク"), message); + + message[1] = HASH_MODE; + message[2] = work->field_9B6; + RevolverSendMessage_800C7170(work->control.name, message); + + work->field_948 &= ~0x100; + + message[1] = 0x71F1; + message[2] = 0; + RevolverSendMessage_800C7170(0x62FE, message); + + work->control.mov = work->field_9A4; + work->control.turn = work->field_9AC; + + field_9B4 = work->field_9B4; + if (work->field_9C.action != field_9B4) + { + GM_ConfigObjectAction(&work->field_9C, work->field_9B4, 0, 4); + } + + GM_GameStatus = (GM_GameStatus & ~STATE_PADRELEASE) | STATE_PADMASK; + GV_PadMask = ~0x800; + } +} + +void s03b_revolver_800C8600(Work *work) +{ + int iVar1; + int uVar2; + + uVar2 = work->field_94C; + iVar1 = s03b_boxall_800C968C(); + + if (iVar1 == -1 || iVar1 == 2) + { + work->field_94C++; + } + + if (work->field_94C == 0) + { + work->field_94C = 1; + } + + switch(work->field_944) + { + case 0: + s03b_revolver_800C7D04(work, uVar2); + break; + + case 1: + Revolver_800C7E2C(work, uVar2); + break; + + case 2: + s03b_revolver_800C7E88(work, uVar2); + break; + + case 3: + s03b_revolver_800C81EC(work, uVar2); + break; + + case 4: + s03b_revolver_800C826C(work, uVar2); + break; + + case 5: + Revolver_800C8488(work, uVar2); + break; + } +} + +int Revolver_800C8710(Work *work, int arg1) +{ + if (arg1 == 0 && work->field_9C.action != 12) + { + GM_ConfigObjectAction(&work->field_9C, 12, 0, 4); + } + if (work->field_9C.is_end) + { + work->control.turn.vy += 2048; + work->control.rot.vy = work->control.turn.vy; + if (work->field_9C.action != 0) + { + GM_ConfigObjectAction(&work->field_9C, 0, 0, 0); + } + return 1; + } + + return 0; +} + +int Revolver_800C8794(Work *work, int arg1) +{ + if (arg1 == 0) + { + work->control.turn.vy = 0; + if (work->field_9C.action != 8) + { + GM_ConfigObjectAction(&work->field_9C, 8, 0, 4); + } + } + + GV_NearExp4V(&work->control.mov.vx, &work->field_8B8->vx, 3); + if (arg1 == 24) + { + GM_SeSet(&work->control.mov, 179); + } + + if (work->field_9C.is_end) + { + if (work->field_9C.action != 0) + { + GM_ConfigObjectAction(&work->field_9C, 0, 0, 4); + } + return 1; + } + + return 0; +} + +int Revolver_800C884C(Work *work, int arg1) +{ + if (arg1 == 0) + { + work->control.turn.vy = 0; + if (work->field_9C.action != 9) + { + GM_ConfigObjectAction(&work->field_9C, 9, 0, 4); + } + } + + GV_NearExp4V(&work->control.mov.vx, &work->field_8B8->vx, 3); + if (arg1 == 10 || arg1 == 44) + { + GM_SeSet(&work->control.mov, 180); + } + + if (work->field_9C.is_end) + { + if (work->field_9C.action != 0) + { + GM_ConfigObjectAction(&work->field_9C, 0, 0, 4); + } + return 1; + } + + return 0; +} + +int Revolver_800C8910(Work *work, int arg1) +{ + if (arg1 == 0) + { + work->control.turn.vy = 0; + if (work->field_9C.action != 10) + { + GM_ConfigObjectAction(&work->field_9C, 10, 0, 4); + } + } + + GV_NearExp4V(&work->control.mov.vx, &work->field_8B8->vx, 3); + if (arg1 == 32) + { + GM_SeSet(&work->control.mov, 180); + } + + if (work->field_9C.is_end) + { + if (work->field_9C.action != 0) + { + GM_ConfigObjectAction(&work->field_9C, 0, 0, 4); + } + return 1; + } + + return 0; +} + +void s03b_revolver_800C89C8(Work *work) +{ + int n_msgs; + GV_MSG *msg; + int i; + int flag; + int len; + int code; + int action; + + n_msgs = work->control.n_msg; + if (n_msgs == 0) + { + return; + } + + msg = &work->control.msg[n_msgs] - 1; + for (i = n_msgs; i > 0; i--, msg--) + { + flag = 1; + len = msg->message_len; + + if (len < 2) + { + continue; + } + + code = msg->message[0]; + action = msg->message[1]; + + switch (code) + { + case HASH_MOTION: + work->field_9C.action = -1; + + if (action != -1) + { + GM_ConfigObjectAction(&work->field_9C, action, 0, 4); + } + break; + + case HASH_MOVE: + work->field_8B8 = &work->field_838[action]; + + if (work->field_9C.action != flag) + { + GM_ConfigObjectAction(&work->field_9C, 1, 0, 4); + } + + work->field_948 |= 0x20; + break; + + case HASH_VOICE: + s03b_boxall_800C93AC(work->field_8D0[action]); + break; + + case HASH_TURN: + work->control.turn.vy = action; + break; + + case HASH_MODE: + work->field_944 = action; + work->field_94C = 0; + break; + + case HASH_OPERATION: + work->field_94C = 0; + work->field_948 |= 0x200; + break; + } + } +} + +void Revolver_800C8B5C(Work *work) +{ + SVECTOR svec2, svec1; + + work->field_948 &= ~0x7; + if (work->field_9C.is_end) + { + work->field_948 |= 4; + } + + if ((work->field_948 & 0x10) && s03b_boxall_800C95EC()) + { + work->field_948 = (work->field_948 | 0x2) & ~0x10; + } + + if (s03b_boxall_800C95FC()) + { + GM_ConfigMotionAdjust(&work->field_9C, &work->field_768); + work->field_948 |= 0x10; + } + + sna_act_helper2_helper2_80033054(GV_StrCode("オセロット"), &work->field_7A0); + + if (work->field_948 & 0x20) + { + svec1 = work->control.mov; + svec1.vy = 0; + GV_SubVec3(work->field_8B8, &svec1, &svec2); + work->control.turn.vy = GV_VecDir2(&svec2); + if (GV_VecLen3(&svec2) < 0x80) + { + work->field_948 = (work->field_948 | 1) & ~0x20; + if (work->field_9C.action != 0) + { + GM_ConfigObjectAction(&work->field_9C, 0, 0, 4); + } + } + } +} + +void Revolver_800C8CA8(Work *work) +{ + s03b_800CA868(); + s03b_revolver_800C89C8(work); + Revolver_800C8B5C(work); + s03b_revolver_800C8600(work); +} + +void RevolverAct_800C8CE4(Work *work) +{ + GM_ActMotion(&work->field_9C); + GM_ActControl(&work->control); + GM_ActObject(&work->field_9C); + + DG_GetLightMatrix(&work->control.mov, work->field_7F0_light); + + work->control.height = work->field_9C.height; + + Revolver_800C8CA8(work); + + if (work->field_8C0.vy < 0 && work->control.grounded != 0) + { + work->field_8C0.vy = 0; + } + work->field_8C0.vy -= 32; + + work->control.step.vy += work->field_8C0.vy; +} + +void RevolverDie_800C8D8C(Work *work) +{ + GV_DestroyOtherActor(work->shadow); + GM_FreeControl(&work->control); + GM_FreeObject(&work->field_9C); + s03b_boxall_800C9328(); +} + +int s03b_revolver_800C8DD0(HZD_PAT *route, int *n_out, SVECTOR *out) +{ + int n_points; + HZD_PTP *points; + + n_points = *n_out = route->n_points; + points = route->points; + + while (--n_points >= 0) + { + out->vx = points->x; + out->vy = points->y; + out->vz = points->z; + out->pad = points->command; + + points++; + out++; + } + + return 0; +} + +int Revolver_800C8E34(Work *work) +{ + HZD_PAT *routes; + int route_idx; + char *opt; + + opt = GCL_GetOption('r'); + route_idx = 0; + if (opt) + { + route_idx = GCL_StrToInt(GCL_NextStr()); + } + + routes = work->control.map->hzd->def->routes; + routes += route_idx; // Why? + + if (s03b_revolver_800C8DD0(routes, &work->field_834, work->field_838) < 0) + { + return -1; + } + + work->field_8B8 = work->field_838; + work->field_8BC = 0; + return 0; +} + +void s03b_revolver_800C8EC0(Work *work) +{ + int i; + int *out; + char *res; + + if (GCL_GetOption('v')) + { + i = 0; + out = work->field_8D0; + while ((res = GCL_NextStr())) + { + if (i == 27) + { + break; + } + + *out++ = GCL_StrToInt(res); + i++; + } + + work->field_8CC = i; + } + else + { + work->field_8CC = 0; + } +} + +void s03b_revolver_800C8F4C(Work *work) +{ + int i; + int *out; + char *res; + + if (!GCL_GetOption('a')) + { + return; + } + + i = 0; + out = work->field_974; + while ((res = GCL_NextStr())) + { + if (i == 12) + { + break; + } + + *out++ = GCL_StrToInt(res); + i++; + } +} + +void Revolver_800C8FC4(Work *work) +{ + work->field_964 = NULL; + work->field_95C = 0; + work->field_960 = 0; +} + +int RevolverGetResources_800C8FD4(Work *work, int arg1, int arg2) +{ + SVECTOR indices; + OBJECT *object; + short motion; + CONTROL *control; + + if (GCL_GetOption('o')) + { + motion = GCL_StrToInt(GCL_NextStr()); + } + else + { + motion = GV_StrCode("rev03bd"); + } + + if (GCL_GetOption('m')) + { + GCL_StrToInt(GCL_NextStr()); + } + else + { + GV_StrCode("rev_v_ct"); + } + + control = &work->control; + + if (GM_InitControl(control, arg1, arg2) < 0) + { + return -1; + } + + GM_ConfigControlString(control, GCL_GetOption('p'), GCL_GetOption('d')); + GM_ConfigControlHazard(control, control->mov.vy, -1, -1); + control->seg_flag = 2; + GM_ConfigControlAttribute(control, 1); + + object = &work->field_9C; + + GM_InitObject(object, GV_StrCode("rev_v_ct"), BODY_FLAG, motion); + GM_ConfigObjectJoint(object); + GM_ConfigMotionControl(object, &work->field_180, motion, &work->field_1D0, &work->field_458, control, + &work->field_6E0); + GM_ConfigObjectLight(object, work->field_7F0_light); + GM_ConfigObjectAction(object, 0, 0, 0); + + if (Revolver_800C8E34(work) < 0) + { + return -1; + } + + s03b_revolver_800C8EC0(work); + s03b_revolver_800C8F4C(work); + Revolver_800C8FC4(work); + + work->field_8C0 = DG_ZeroVector; + work->field_944 = 0; + work->field_948 = 0; + work->field_94C = 0; + work->field_958 = -1; + + if (GCL_GetOption('e') != 0) + { + work->field_8C8 = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->field_8C8 = -1; + } + + if (GCL_GetOption('c') != 0) + { + work->field_940 = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->field_940 = 0; + } + + if (GCL_GetOption('f') && GCL_StrToInt(GCL_NextStr())) + { + work->field_948 |= 0x400; + } + + indices.vx = 0; + indices.vy = 7; + indices.vz = 13; + indices.pad = 16; + + work->shadow = NewShadow(control, object, indices); + if (work->shadow == NULL) + { + return -1; + } + + return 0; +} + +void *NewTortureOcelot(int arg0, int arg1) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work == NULL) + { + return NULL; + } + + GV_SetNamedActor(&work->actor, RevolverAct_800C8CE4, RevolverDie_800C8D8C, "revolver.c"); + if (RevolverGetResources_800C8FD4(work, arg0, arg1) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/torture/revolver.h b/components/mgs/mgs/source/chara/torture/revolver.h new file mode 100644 index 00000000..40a10290 --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/revolver.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_REVOLVER_H__ +#define __MGS_CHARA_REVOLVER_H__ + +void *NewTortureOcelot(int arg0, int arg1); + +#endif // __MGS_CHARA_REVOLVER_H__ diff --git a/components/mgs/mgs/source/chara/torture/sne_03c.c b/components/mgs/mgs/source/chara/torture/sne_03c.c new file mode 100644 index 00000000..36792218 --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/sne_03c.c @@ -0,0 +1,389 @@ +#include "sne_03c.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" + +#include "takabe/fadeio.h" // for NewFadeInOut +#include "takabe/cineutil.h" // for OpenCinemaScreen, etc. + +// There are two actors in this file + +// Created by NewPrisonSnake +typedef struct Snake03c1Work +{ + GV_ACT actor; + CONTROL control; + OBJECT object; + MOTION_CONTROL motion; + MOTION_SEGMENT oar1[17]; + MOTION_SEGMENT oar2[17]; + SVECTOR rots[32]; + MATRIX light[2]; + GV_ACT *other_actor; // practically unused, did it store Snake03c2Work*? + SVECTOR svec7DC; + int mode; + int field_7E8; + int field_7EC; + int procs[4]; +} Snake03c1Work; + +// Created by NewPrisonSnake2 +typedef struct Snake03c2Work +{ + GV_ACT actor; + int mode; + int field_24; + int procs[4]; +} Snake03c2Work; + +#define EXEC_LEVEL GV_ACTOR_USER + +extern GM_SnakeCameraWork GM_SnakeCamera; + +void Snake03c1Act_800CD698(Snake03c1Work *work) +{ + SVECTOR svec; + CONTROL *control; + OBJECT *object; + int field_7E8; + + object = &work->object; + GM_ActMotion(object); + + control = &work->control; + GM_ActControl(control); + + GM_ActObject(object); + + DG_GetLightMatrix(&control->mov, work->light); + + work->control.mov.vy = work->object.height; + work->control.height = work->object.height; + + GM_SnakeCamera.position = work->control.mov; + + field_7E8 = work->field_7E8++; + + CheckCinemaTimeout(); + + switch (work->mode) + { + case 0: + if (field_7E8 == 0) + { + NewFadeInOut(FADEIO_MODE_FROMBLACK, 48); + GM_GameStatus |= STATE_PADRELEASE; + OpenCinemaScreen(0, 60000); + GCL_ExecProc(work->procs[1], NULL); + } + if (--work->field_7EC == 0) + { + if (work->object.action != 1) + { + GM_ConfigObjectAction(object, 1, 0, 4); + } + work->field_7E8 = 0; + work->mode++; + } + break; + + case 1: + if (work->object.is_end != 0) + { + GM_GameStatus &= ~STATE_PADRELEASE; + CloseCinemaScreen(); + GCL_ExecProc(work->procs[3], NULL); + GCL_ExecProc(work->procs[0], NULL); + GV_DestroyActor(&work->actor); + } + break; + + case 2: + GV_SubVec3(&work->svec7DC, &control->mov, &svec); + work->control.turn.vy = GV_VecDir2(&svec); + svec.vy = 0; + if (GV_VecLen3(&svec) < 250) + { + if (work->object.action != 2) + { + GM_ConfigObjectAction(object, 2, 0, 4); + } + work->field_7E8 = 0; + work->mode++; + } + break; + + case 3: + if (field_7E8 == 64) + { + GM_GameStatus &= ~STATE_PADRELEASE; + CloseCinemaScreen(); + GCL_ExecProc(work->procs[3], NULL); + GCL_ExecProc(work->procs[0], NULL); + GV_DestroyActor(&work->actor); + } + break; + } +} + +void Snake03c1Die_800CD8CC(Snake03c1Work *work) +{ + GM_FreeControl(&work->control); + GM_FreeObject(&work->object); + if (work->other_actor != NULL) + { + GV_DestroyActor(work->other_actor); + } +} + +void Snake03c1_800CD914(Snake03c1Work *work) +{ + int i; + int *out; + char *res; + + if (!GCL_GetOption('e')) + { + return; + } + + i = 0; + out = work->procs; + while ((res = GCL_NextStr())) + { + if (i == 4) + { + break; + } + + *out++ = GCL_StrToInt(res); + i++; + } +} + +int Snake03c1GetResources_800CD98C(Snake03c1Work *work, int arg1, int arg2) +{ + CONTROL *control; + int motion; + int model; + + control = &work->control; + if (GM_InitControl(control, arg1, arg2) < 0) + { + return -1; + } + + GM_ConfigControlString(control, GCL_GetOption('p'), GCL_GetOption('d')); + GM_ConfigControlHazard(control, control->mov.vy, -2, -2); + + model = GCL_StrToInt(GCL_GetOption('m')); + motion = GCL_StrToInt(GCL_GetOption('o')); + + GCL_StrToSV(GCL_GetOption('t'), (short *)&work->svec7DC); + + GM_InitObject(&work->object, model & 0xFFFF, 0x2D, motion & 0xFFFF); + GM_ConfigObjectJoint(&work->object); + GM_ConfigMotionControl(&work->object, &work->motion, motion & 0xFFFF, work->oar1, work->oar2, control, + work->rots); + GM_ConfigObjectLight(&work->object, work->light); + GM_ConfigObjectAction(&work->object, 0, 0, 0); + Snake03c1_800CD914(work); + work->field_7E8 = 0; + work->mode = 0; + work->field_7EC = GCL_StrToInt(GCL_GetOption('c')); + work->other_actor = NULL; + return 0; +} + +void *NewPrisonSnake(int name, int where) +{ + Snake03c1Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Snake03c1Work)); + if (work == NULL) + { + return NULL; + } + + GV_SetNamedActor(&work->actor, Snake03c1Act_800CD698, Snake03c1Die_800CD8CC, "sne_03c.c"); + if (Snake03c1GetResources_800CD98C(work, name, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + return (void *)work; +} + +// +// Snake03c1 ends here, Snake03c2 starts here +// + +int Snake03c2GetRaise_800CDB78(DG_MDL *mdl) +{ + unsigned int flags; + int raise; + + flags = mdl->flags; + raise = 0; + if (flags & 0x300) + { + raise = (4 - ((flags >> 12) & 3)) * 250; + if (!(flags & 0x100)) + { + raise = -raise; + } + } + return raise; +} + +int Snake03c2_800CDBC8() +{ + DG_MDL *mdl, *mdl2; + DG_OBJ *obj; + DG_OBJS *playerBodyObjs; + int count; + + playerBodyObjs = GM_PlayerBody->objs; + count = playerBodyObjs->n_models; + obj = playerBodyObjs->objs; + for (; count > 0; count--, obj++) + { + DG_FreeObjPacket(obj, 0); + DG_FreeObjPacket(obj, 1); + } + + playerBodyObjs->def = GV_GetCache(GV_CacheID(GV_StrCode("snake"), 'k')); + + count = playerBodyObjs->def->n_models; + mdl = playerBodyObjs->def->model; + mdl2 = &playerBodyObjs->def->model[0]; + obj = &playerBodyObjs->objs[0]; + for (; count > 0; obj++, mdl2++, mdl++, count--) + { + obj->model = mdl; + + if (mdl2->extend < 0) + { + obj->extend = NULL; + } + else + { + obj->extend = &playerBodyObjs->objs[mdl2->extend]; + } + + obj->raise = Snake03c2GetRaise_800CDB78(mdl); + obj->n_packs = mdl2->n_faces; + } + + return 0; +} + +void Snake03c2Act_800CDCE8(Snake03c2Work *work) +{ + int field_24; + + field_24 = work->field_24++; + switch (work->mode) + { + case 0: + if (field_24 == 0) + { + OpenCinemaScreen(0, 30000); + GM_GameStatus |= STATE_PADRELEASE; + GCL_ExecProc(work->procs[0], NULL); + } + if (field_24 == 48) + { + GCL_ExecProc(work->procs[1], NULL); + work->field_24 = 0; + work->mode++; + } + return; + case 1: + if (!(GM_PlayerStatus & PLAYER_ACT_ONLY)) + { + GM_SeSet2(0xF0, 0x3F, 0xB8); + Snake03c2_800CDBC8(); + work->field_24 = 0; + work->mode++; + } + break; + case 2: + if (field_24 == 32) + { + GCL_ExecProc(work->procs[2], NULL); + work->mode++; + work->field_24++; + } + break; + case 3: + if (!(GM_PlayerStatus & PLAYER_ACT_ONLY)) + { + GM_GameStatus &= ~STATE_PADRELEASE; + GM_PlayerControl->turn.vy = 1024; + CloseCinemaScreen(); + GCL_ExecProc(work->procs[3], NULL); + GV_DestroyActor(&work->actor); + } + break; + } +} + +void Snake03c2Die_800CDE78(void) +{ +} + +void Snake03c2GetResources_800CDE80(Snake03c2Work *work) +{ + int i; + int *out; + char *res; + + if (!GCL_GetOption('e')) + { + return; + } + + i = 0; + out = work->procs; + while ((res = GCL_NextStr())) + { + if (i == 4) + { + break; + } + + *out++ = GCL_StrToInt(res); + i++; + } +} + +int Snake03c2GetResources_800CDEF8(Snake03c2Work *work) +{ + Snake03c2GetResources_800CDE80(work); + return 0; +} + +void *NewPrisonSnake2() +{ + Snake03c2Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Snake03c2Work)); + if (work == NULL) + { + return NULL; + } + + GV_SetNamedActor(&work->actor, Snake03c2Act_800CDCE8, Snake03c2Die_800CDE78, "sne_03c.c"); + if (Snake03c2GetResources_800CDEF8(work) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/torture/sne_03c.h b/components/mgs/mgs/source/chara/torture/sne_03c.h new file mode 100644 index 00000000..58df8f04 --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/sne_03c.h @@ -0,0 +1,7 @@ +#ifndef __MGS_CHARA_SNE_03C_H__ +#define __MGS_CHARA_SNE_03C_H__ + +void *NewPrisonSnake(int name, int where); +void *NewPrisonSnake2(); + +#endif // __MGS_CHARA_SNE_03C_H__ diff --git a/components/mgs/mgs/source/chara/torture/torture.c b/components/mgs/mgs/source/chara/torture/torture.c new file mode 100644 index 00000000..605e8d85 --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/torture.c @@ -0,0 +1,2141 @@ +#include "info.h" +#include "torture.h" + +#include "common.h" +#include "chara/snake/sna_init.h" +#include "libgcl/libgcl.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" +#include "linkvar.h" +#include "game/over.h" +#include "game/vibrate.h" +#include "sound/g_sound.h" +#include "strcode.h" + +#include "takabe/fadeio.h" // for NewFadeInOut +#include "takabe/cineutil.h" // for OpenCinemaScreen, etc. + +struct _Work; +typedef void (*TTortureFn)(struct _Work *, int); + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + OBJECT body; + MOTION_CONTROL m_ctrl; + MOTION_SEGMENT anims[34]; + SVECTOR rots[16]; + SVECTOR adjust[16]; + MATRIX light[2]; + char pad[0x24]; + short f7FC; + short f7FE; + short f800; + short f802; + short f804; + short f806; + TTortureFn f808; + int f80C; + int f810; + int f814; + short f818; + short f81A; + short f81C; + short f81E; + short f820; + short f822; + SVECTOR f824; + SVECTOR f82C; + GV_PAD *f834; + int f838; + int f83C; + int f840; + int f844; + unsigned short f848; + unsigned short f84A; + unsigned short f84C; + char pad4[0x2]; + MENU_BAR_CONF time_conf; + unsigned short f85C; + unsigned short f85E; + unsigned short f860[2][6]; + int f878; + int f87C[8]; + SVECTOR f89C; + short f8A4; + short f8A6; + short f8A8; + short f8AA; + SVECTOR f8AC; + short f8B4; + short f8B6; + int f8B8; + SVECTOR f8BC[8]; + GV_ACT *f8FC; + GV_ACT *f900; +} Work; + +extern GM_SnakeCameraWork GM_SnakeCamera; +extern GM_CameraSystemWork GM_Camera; + +// unused +int s03b_dword_800C3290 = 0x027F0200; +int s03b_dword_800C3294 = 0x00000000; +int s03b_dword_800C3298 = 0x00000E46; + +char s03b_dword_800C329C[] = +{ + 0x00, 0x02, + 0x7F, 0x06, + 0x00, 0x14, + 0x7F, 0x04, + 0x00, 0x16, + 0x7F, 0x04, + 0x00, 0x00 +}; + +char s03b_dword_800C32AC[] = +{ + 0x5A, 0x0E, + 0x28, 0x0C, + 0x46, 0x0E, + 0x28, 0x0C, + 0x50, 0x14, + 0x00, 0x00 +}; + +char s03b_dword_800C32B8[] = +{ + 0x00, 0x01, + 0x7F, 0x01, + 0x00, 0x76, + 0x7F, 0x02, + 0x00, 0x00, +}; + +char s03b_dword_800C32C4[] = +{ + 0x55, 0x04, + 0x32, 0x0A, + 0x28, 0x6A, + 0x7D, 0x04, + 0x00, 0x00, +}; + +char s03b_dword_800C32D0[] = +{ + 0x7F, 0x07, + 0x00, 0x32, + 0x7F, 0x02, + 0x00, 0x00 +}; + +char s03b_dword_800C32D8[] = +{ + 0xFF, 0x04, + 0x3C, 0x0A, + 0x28, 0x0C, + 0x1E, 0x1C, + 0xA0, 0x04, + 0x00, 0x00 +}; + +char SECTION(".bss") s03b_dword_800D32F0[16]; + +void *NewPlasma_800CD1A4(OBJECT *, int, int, int, int, int); +void *NewBlurSet(int, int, int); /* in okajima/blur.c */ + +void s03b_boxall_800C9328(void); +int s03b_boxall_800C93AC(int arg0); +void s03b_boxall_800C93F0(int, int); +void s03b_boxall_800C9404(void); +int s03b_boxall_800C95EC(void); +int s03b_boxall_800C9654(int); + +void s03b_torture_800C4C48(Work *work, int); +void s03b_torture_800C5AF8(Work *work, int); +void s03b_torture_800C5E48(Work *work, int); + +#define EXEC_LEVEL GV_ACTOR_USER +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_GBOUND | DG_FLAG_SHADE ) + +void s03b_torture_800C3E80(Work *work) +{ + int index; + + index = work->f80C; + work->f80C++; + work->f808(work, index); +} + +void s03b_torture_800C3EB8(Work *work) +{ + int f802; + int index; + + f802 = work->f802; + if (f802 & 0x2) + { + index = work->f810; + work->f810++; + s03b_torture_800C5E48(work, index); + } +} + +void s03b_torture_800C3EF8(Work *work) +{ + if (work->f814 == 0) + { + return; + } + + if (work->f814 > 0) + { + if (GM_Camera.first_person == 0 && --work->f814 == 0) + { + DG_VisibleObjs(work->body.objs); + } + } + else + { + if (GM_Camera.first_person != 0 && ++work->f814 == 0) + { + DG_InvisibleObjs(work->body.objs); + } + } +} + +int s03b_torture_800C3F7C(GV_PAD *pad) +{ + char *analog; + int i; + char adjust; + + if (pad->analog == 0) + { + return 0; + } + + if (!(pad->status & PAD_UDLR)) + { + return 0; + } + + analog = &pad->right_dx; + for (i = 0; i < 4; i++) + { + adjust = *analog - 64; + if (adjust > 128) + { + return 1; + } + + analog++; + } + + return 0; +} + +void s03b_torture_800C3FE4(Work *work) +{ + short status; + unsigned char ldy; + GV_PAD *pad; + + int delta; + int f800, f802; + char tmp1, tmp2; + + f800 = work->f800; + if (!(f800 & 1)) + { + work->control.turn.vy = 0; + work->control.turn.vz = 0; + work->control.turn.vx = work->f806; + work->control.rot = work->control.turn; + work->f82C = work->control.rot; + return; + } + + f802 = work->f802; + if (!(f802 & 0x4000)) + { + pad = work->f834; + status = pad->status; + ldy = pad->left_dy; + + GM_CheckShukanReverse(&status); + GM_CheckShukanReverseAnalog(&ldy); + + if (pad->press & (PAD_UP | PAD_DOWN | PAD_LEFT | PAD_RIGHT)) + { + GM_SeSetMode(&work->control.mov, 186, GM_SEMODE_BOMB); + } + + if (s03b_torture_800C3F7C(pad)) + { + delta = work->f806; + if (status & PAD_UP) + { + tmp1 = 64 - ldy; + delta -= 10 * tmp1; + } + else if (status & PAD_DOWN) + { + tmp2 = 64 + ldy; + delta += 10 * tmp2; + } + work->f82C.vx = delta; + + delta = 0; + if (status & PAD_LEFT) + { + tmp1 = 64 - pad->left_dx; + delta = tmp1 * 5; + } + else if (status & PAD_RIGHT) + { + tmp2 = 64 + pad->left_dx; + delta = -(tmp2 * 5); + } + work->f82C.vy = delta; + } + else + { + delta = work->f806; + if (status & PAD_UP) + { + delta -= 640; + } + else if (status & PAD_DOWN) + { + delta += 640; + } + work->f82C.vx = delta; + + delta = 0; + if (status & PAD_LEFT) + { + delta = 320; + } + else if (status & PAD_RIGHT) + { + delta = -320; + } + work->f82C.vy = delta; + } + + if (work->f82C.vx < -1000) + { + work->f82C.vx = -1000; + } + } +} + +void s03b_torture_800C421C(Work *work) +{ + work->f800 |= 0x1; + + if (GM_Camera.first_person <= 0) + { + GM_Camera.first_person = 1; + work->f814 = -4; + } + else + { + work->f814 = -1; + } +} + +void s03b_torture_800C4260(Work *work) +{ + work->f800 &= ~0x1; + GM_Camera.first_person = 0; + work->f814 = 4; +} + +void s03b_torture_800C4284(Work *work) +{ + int f802; + int f800; + + f802 = work->f802; + if ((f802 & 0x2000) || (((f802 & 0x1) == 0) && (work->f834->status & PAD_TRIANGLE))) + { + f800 = work->f800; + if ((f800 & 0x1) == 0) + { + s03b_torture_800C421C(work); + } + } + else + { + f800 = work->f800; + if (f800 & 0x1) + { + s03b_torture_800C4260(work); + } + } +} + +int s03b_torture_800C4314(Work *work) +{ + int f800; + + f800 = work->f800; + if ((f800 & 0x4) || (work->f834->press & PAD_SELECT) == 0) + { + return 0; + } + + work->f7FC = 6; + work->f7FE = 0; + return 1; +} + +void s03b_torture_800C435C(Work *work, int vx) +{ + GV_MSG msg; + int f802; + + f802 = work->f802; + if (f802 & 0x8) + { + msg.address = GV_StrCode("拷問台"); + msg.message_len = 2; + msg.message[0] = GV_StrCode("turn"); + + if (abs(vx) >= 2048) + { + msg.message[1] = work->control.rot.vx; + } + else + { + msg.message[1] = vx; + } + + GV_SendMessage(&msg); + } +} + +void s03b_torture_800C43F0(void) +{ + GV_MSG msg; + + msg.address = GV_StrCode("拷問台"); + msg.message_len = 1; + msg.message[0] = GV_StrCode("leave"); + + GV_SendMessage(&msg); +} + +void s03b_torture_800C4438(Work *work, int message) +{ + GV_MSG msg; + + msg.address = GV_StrCode("リキッド"); + msg.message_len = 1; + msg.message[0] = message; + + GV_SendMessage(&msg); +} + +void s03b_torture_800C447C(Work *work, int arg1, int arg2) +{ + GV_MSG msg; + + msg.address = GV_StrCode("オセロット"); + msg.message_len = 2; + msg.message[0] = arg1; + msg.message[1] = arg2; + + GV_SendMessage(&msg); +} + +void s03b_torture_800C44D0(Work *work, int arg1, int arg2) +{ + int max, now; + + max = work->f860[0][work->f85C]; + + if (arg2 < 0) + { + now = arg1 * 4; + + if (now > max) + { + now = max; + } + + max = now; + } + else if (arg2 == 0) + { + now = max - arg1; + + if (max == now) + { + now = max - 1; + } + } + else + { + now = 0; + } + + max *= 5; + now *= 5; + + if (max > 1024) + { + work->time_conf.right[1] = (0x10000 / max) + 63; + work->time_conf.right[2] = (0xFC00 / max) - 64; + } + + MENU_DrawBar2(28, now, now, max, &work->time_conf); +} + +int s03b_torture_800C45E4(Work *work) +{ + MENU_BAR_CONF *conf; + + memcpy(s03b_dword_800D32F0, "Time", 5); + + conf = &work->time_conf; + conf->name = s03b_dword_800D32F0; + // Set the left and right RGB values for the torture time bar gradient + conf->left[0] = 31; + conf->left[1] = 63; + conf->left[2] = 192; + + conf->right[0] = 31; + conf->right[1] = 127; + conf->right[2] = 255; + + conf->height = 1; + return 0; +} + +void s03b_torture_800C4654(Work *work) +{ + int n_msgs; + GV_MSG *msg; + + if (work->control.n_msg == 0) + { + return; + } + + n_msgs = work->control.n_msg; + msg = &work->control.msg[n_msgs] - 1; + + for (; n_msgs > 0; n_msgs--, msg--) + { + if (msg->message[0] == 4) + { + work->f802 |= 0x4; + } + } +} + +void s03b_torture_800C46B8(Work *work, int arg1) +{ + if (arg1 == 0) + { + NewFadeInOut(FADEIO_MODE_TOBLACK, 28); + s03b_boxall_800C9328(); + + work->f820 = 0; + + if (work->f8FC != NULL) + { + GV_DestroyOtherActor(work->f8FC); + } + + work->f8FC = NULL; + } + + if (arg1 == 32) + { + GM_Camera.first_person = 0; + + if (work->f83C >= 0) + { + GCL_ExecProc(work->f83C, NULL); + } + } +} + +// TODO: This is wrong. +// All other accesses to f802 are doing weird things (lh vs lhu), +// but we have been working around it by assigning them to ints. +static inline char s03b_torture_helper_800C4740(Work *work) +{ + return LLOAD(&work->f802) >> 8; +} + +void s03b_torture_800C4740(Work *work) +{ + int status; + int vox_stream; + int f802; + + CheckCinemaTimeout(); + + if (GV_PadData[2].press & PAD_CROSS) + { + work->f808 = s03b_torture_800C46B8; + work->f81A = 0; + work->f818 = 0; + work->f80C = 0; + return; + } + + switch(work->f818) + { + case 0: + if (work->f81A == 0) + { + OpenCinemaScreen(1, 60000); + } + + if (work->f8FC == 0) + { + work->f8FC = NewBlurSet(1, 0, 0); + } + + if (++work->f81A == 200) + { + work->f81A = 0; + work->f818++; + } + break; + + case 1: + s03b_boxall_800C93AC(work->f87C[work->f820 + 3]); + work->f818 = 2; + work->f820++; + break; + + case 2: + if (s03b_boxall_800C95EC()) + { + if (work->f820 == 4) + { + CloseCinemaScreen(); + work->f802 |= 0x4; + } + else + { + work->f818 = 3; + } + } + break; + + case 3: + if ((work->f802 & 0x700) != 0x700) + { + work->f81A = 150; + work->f818 = 4; + } + else + { + s03b_torture_800C4438(work, work->f820 - 1); + work->f81A = 0; + work->f818 = 1; + } + break; + + case 4: + if (work->f81A-- == 1) + { + work->f818 = 1; + s03b_torture_800C4438(work, work->f820 - 1); + break; + } + + status = work->f834->status; + if (status == 0) + { + break; + } + + vox_stream = -1; + if ((status & 0xf000) != 0 && (f802 = work->f802, (s03b_torture_helper_800C4740(work) & 0x1) == 0)) + { + work->f802 |= 0x100; + vox_stream = work->f87C[2]; + } + else if ((status & 0xf) != 0 && (f802 = work->f802, (f802 & 0x200) == 0)) + { + work->f802 |= 0x200; + vox_stream = work->f87C[0]; + } + else if ((status & 0xe0) != 0 && (f802 = work->f802, (f802 & 0x400) == 0)) + { + work->f802 |= 0x400; + vox_stream = work->f87C[1]; + } + + if (vox_stream >= 0) + { + s03b_boxall_800C93AC(vox_stream); + work->f818 = 5; + } + break; + + case 5: + if (s03b_boxall_800C95EC()) + { + work->f81A -= 200; + if (work->f81A < 1) + { + s03b_torture_800C4438(work, work->f820 - 1); + work->f81A = 0; + work->f818 = 1; + } + else + { + work->f818 = 4; + } + } + break; + } +} + +void s03b_torture_800C4A08(Work *work) +{ + if (work->f848 == 0) + { + s03b_torture_800C4740(work); + } + else if (++work->f81A == 90) + { + work->f802 |= 0x4; + } +} + +void s03b_torture_800C4A70(Work *work) +{ + s03b_torture_800C4654(work); +} + +void s03b_torture_800C4A90(Work *work) +{ + s03b_torture_800C4A70(work); +} + +void s03b_torture_800C4AB0(Work *work, int arg1) +{ + OBJECT *body; + int i; + int rnd; + + if (arg1 == 0) + { + body = &work->body; + GM_ConfigMotionAdjust(body, work->adjust); + + GM_GameStatus |= STATE_PADRELEASE; + + s03b_boxall_800C9328(); + s03b_boxall_800C93F0(work->f87C[2], 4); + GM_SetSound(0xff0000fe, SD_ASYNC); + OpenCinemaScreen(0, 10000); + + if (work->body.action != 2) + { + GM_ConfigObjectAction(body, 2, 0, 4); + } + } + + if (s03b_boxall_800C9654(work->f87C[2])) + { + work->f808 = s03b_torture_800C4C48; + work->f81A = 0; + work->f818 = 0; + work->f80C = 0; + } + + if (work->body.is_end != 0 && work->body.action == 2) + { + GM_ConfigObjectAction(&work->body, 3, 0, 4); + } + + if ((work->f800 & 0x2) != 0 && (arg1 & 0xF) == 0) + { + for (i = 0; i < work->f8B8; i++) + { + rnd = GV_RandU(64) % 15; + NewPlasma_800CD1A4(&work->body, rnd, rnd + 1, 16, 0, 1000); + } + } +} + +void s03b_torture_800C4C48(Work *work, int arg1) +{ + GCL_ARGS args; + long data[1]; + + if (arg1 == 0) + { + s03b_torture_800C447C(work, GV_StrCode("mode"), 4); + + args.argc = 1; + args.argv = data; + data[0] = 0; + GCL_ExecProc(work->f840, &args); + } + + s03b_torture_800C4654(work); + + if (arg1 == 300) + { + if (work->body.action != 0) + { + GM_ConfigObjectAction(&work->body, 0, 0, 15); + } + + args.argc = 1; + args.argv = data; + data[0] = 11; + GCL_ExecProc(work->f840, &args); + } + + if (arg1 >= 330) + { + if (arg1 == 330) + { + work->f802 |= 0x8; + NewPadVibration(s03b_dword_800C32B8, 1); + NewPadVibration(s03b_dword_800C32C4, 2); + GM_SeSet(&work->control.mov, 176); + } + + work->control.turn.vx = (arg1 - 330) * -7; + if (work->control.turn.vx < -760) + { + if (work->f818 == 0) + { + work->f818 = 1; + } + + work->control.turn.vx = -760; + } + + work->f806 = work->control.turn.vx; + work->f82C.vx = work->f806; + } + + if (arg1 == 420) + { + GM_Camera.first_person = 2; + work->f802 |= 0x6000; + } + + if (arg1 == 490) + { + GM_SeSet2(0, 63, 182); + } +} + +void s03b_torture_800C4DF0(Work *work, int arg1) +{ + if (arg1 == 0) + { + NewFadeInOut(FADEIO_MODE_FROMBLACK, 128); + + if (work->body.action != 0) + { + GM_ConfigObjectAction(&work->body, 0, 0, 1); + } + + work->f806 = -760; + work->control.rot.vx = -760; + work->control.turn.vx = -760; + work->f802 |= 0x2000; + } + + s03b_torture_800C4A08(work); +} + +void s03b_torture_800C4E64(Work *work, int arg1) +{ + if (arg1 == 0) + { + work->f802 |= 0x2000; + + if (work->body.action != 0) + { + GM_ConfigObjectAction(&work->body, 0, 0, 4); + } + + NewPadVibration(s03b_dword_800C32B8, 1); + NewPadVibration(s03b_dword_800C32C4, 2); + + GM_SeSet(&work->control.mov, 176); + + GM_GameStatus |= STATE_NOSLOW; + } + + work->f806 = (arg1 * 7) - 760; + if (work->f806 > 0) + { + if (work->f818 == 0) + { + work->f818 = 1; + + if (work->f848 != 0) + { + NewFadeInOut(FADEIO_MODE_TOBLACK, 28); + } + } + + work->f806 = 0; + } + + work->control.turn.vx = work->f806; +} + +void s03b_torture_800C4F54(Work *work, int arg1) +{ + if (arg1 == 0) + { + if (work->body.action != 0) + { + GM_ConfigObjectAction(&work->body, 0, 0, 4); + } + + NewPadVibration(s03b_dword_800C32B8, 1); + NewPadVibration(s03b_dword_800C32C4, 2); + + GM_SeSet(&work->control.mov, 176); + } + + work->f806 = arg1 * -7; + if (work->f806 < -760) + { + if (work->f818 == 0) + { + work->f818 = 1; + } + + work->f806 = -760; + } + + work->control.turn.vx = work->f806; + + if (arg1 == 90) + { + work->f802 |= 0x2000; + GM_Camera.first_person = 2; + } + + if (arg1 == 200) + { + work->f900 = NewFadeInOut(FADEIO_MODE_TOBLACK, 32); + } + + if (arg1 == 160) + { + GM_GameStatus |= STATE_PADRELEASE; + GM_SeSet2(0, 63, 182); + } + + if (arg1 == 250) + { + work->f802 |= 0x4; + } +} + +void s03b_torture_800C50A8(Work *work, int arg1) +{ + GV_MSG msg; + CONTROL *control; + int which; + int dir; + + if (arg1 == 0) + { + control = &work->control; + + GM_GameStatus |= STATE_PADRELEASE; + + if (work->body.action != 0) + { + GM_ConfigObjectAction(&work->body, 0, 0, 4); + } + + GM_Camera.first_person = 2; + work->f802 |= 0x6000; + + GM_ConfigControlHazard(control, control->mov.vy, -2, -2); + + control->mov.vx = -2000; + control->mov.vy = 450; + control->mov.vz = -1000; + + control->step = DG_ZeroVector; + + control->turn.vx = 320; + control->rot.vx = 320; + + control->turn.vz = 0; + control->rot.vz = 0; + + control->turn.vy = 2048; + control->rot.vy = 2048; + + s03b_torture_800C447C(work, GV_StrCode("mode"), 3); + + work->f820 = 0xB7; + work->f802 |= 0x8; + + s03b_torture_800C435C(work, -1023); + + work->f802 &= ~0x8; + work->f82C = control->rot; + } + + if (arg1 == 1) + { + msg.address = 0x62FE; + msg.message_len = arg1; + msg.message[0] = 0x71F1; + GV_SendMessage(&msg); + + work->f802 &= ~0x8; + } + + which = work->f818 & 0x1; + + if (work->f818 == 10) + { + NewFadeInOut(FADEIO_MODE_TOBLACK, 64); + } + + if (work->f818 == 12) + { + work->f802 |= 0x4; + return; + } + + switch (which) + { + case 0: + if (work->f81A == 0) + { + if (work->f820 == 183) + { + NewPadVibration(s03b_dword_800C329C, 1); + NewPadVibration(s03b_dword_800C32AC, 2); + } + + GM_SeSet(&work->control.mov, work->f820); + + if (++work->f820 == 186) + { + work->f820 = 183; + } + } + + work->control.step.vz = 32; + + dir = (GV_Clock != 0) ? -1 : 1; + GM_SnakeCamera.position.vy += dir * GV_RandU(8); + + case 1: + if (++work->f81A == 16) + { + work->f81A = 0; + work->f818++; + } + break; + } + + if (arg1 > 60 && work->f82C.vx > 0) + { + work->f82C.vx -= 6; + } + + if (arg1 > 148) + { + GM_Camera.zoom -= 6; + } +} + +void s03b_torture_800C53C8(Work *work, int arg1) +{ + if (arg1 == 0) + { + work->f802 &= ~0x2000; + + if (work->body.action != 0) + { + GM_ConfigObjectAction(&work->body, 0, 0, 4); + } + + work->f806 = 0; + } + + s03b_torture_800C4A90(work); +} + +void s03b_torture_800C5CC8(Work *work, int arg1); +void s03b_torture_800C59FC(Work *work, int arg1); + +void s03b_torture_800C5420(Work *work, int arg1) +{ + int abe[2]; + int var_v1; + int temp_a0; + int randval; + int temp_s2; + int var_a1; + int action; + int var_s0; + int var_v0_2; + int tmp; + + if (arg1 == 0) + { + work->f806 = 0; + work->f802 &= ~0x2000; + + s03b_torture_800C447C(work, HASH_MOTION, 9); + + work->f8AC = work->f89C; + work->f8AC.vz = 0; + + GCL_ExecProc(work->f844, NULL); + + GM_CameraSetTrack(work->f89C.vz); + GM_CameraSetRotation(&work->f8AC); + + GM_SnakeCamera.position.vx = work->body.objs->objs[6].world.t[0]; + GM_SnakeCamera.position.vy = work->body.objs->objs[6].world.t[1]; + GM_SnakeCamera.position.vz = work->body.objs->objs[6].world.t[2]; + + sub_8003081C(); + + work->f81C = 0; + } + + temp_s2 = work->f85C == 0 ? 32 : 0; + + if (arg1 == temp_s2 + 32) + { + if (work->f85C == 0) + { + abe[0] = 1; + abe[1] = 0; + NewTortureInfo(GV_StrCode("gou_bg"), GV_StrCode("goumon"), abe); + } + GM_SeSet2(0, 0x3F, 0xB4); + } + else if (arg1 == temp_s2 + 42 || arg1 == temp_s2 + 64) + { + GM_SeSet2(0, 0x3F, 0xB4); + } + + if (arg1 == temp_s2 + 108) + { + GM_SeSet2(0, 0x3F, 0xB2); + } + + var_v0_2 = arg1 - 136; + var_a1 = var_v0_2 - temp_s2; + if (var_a1 < 0) + { + s03b_torture_800C44D0(work, arg1, -1); + } + else + { + s03b_torture_800C44D0(work, var_a1, 0); + } + + if (arg1 >= temp_s2 + 136) + { + work->f800 |= 2; + + if (!(arg1 & 0xF)) + { + for (var_s0 = 0; var_s0 < work->f8B8; var_s0++) + { + randval = GV_RandU(64) % 15; + NewPlasma_800CD1A4(&work->body, randval, randval + 1, 16, 0, 1000); + } + } + + if (arg1 == temp_s2 + 136) + { + work->f84C = work->f860[0][work->f85C] + 64; + work->f802 |= 2; + GM_SeSet(&work->control.mov, 128); + + work->control.mov = work->f824; + + if (GV_Clock) + { + action = 1; + } + else + { + action = 7; + } + + if (work->body.action != action) + { + GM_ConfigObjectAction(&work->body, action, 0, 4); + } + } + + temp_a0 = temp_s2 + 136; + if (arg1 - temp_a0 == (arg1 - temp_a0) / 30 * 30) + { + work->f804 -= work->f860[1][work->f85C]; + } + + if (arg1 == temp_s2 + 136 + work->f860[0][work->f85C]) + { + GM_SetSound(0xff0000fe, SD_ASYNC); + work->f800 = (unsigned short)work->f800 & ~0x2; + if (work->f804 <= 0) + { + work->f810 = 0; + work->f808 = s03b_torture_800C5CC8; + work->f81A = 0; + work->f818 = 0; + work->f80C = 0; + work->f802 = (unsigned short)work->f802 & ~0x2; + return; + } + work->f808 = s03b_torture_800C59FC; + work->f81A = 0; + work->f818 = 0; + work->f80C = 0; + return; + } + GM_PadVibration2 = GV_RandU(64) + 192; + if (work->f81A == 0) + { + GM_SeSet2(0, 0x3F, 177); + work->f81A = (GV_RandU(4096) % 11) + 2; + } + else + { + work->f81A--; + } + + switch (work->f818) + { + case 0: + tmp = (work->f8A6 - work->f89C.vy) / work->f8B6; + var_v1 = work->f8AC.vy + tmp; + if (var_v1 > work->f8A6) + { + var_v1 = work->f8A6; + work->f818++; + } + work->f8AC.vy = var_v1; + break; + case 1: + if (++work->f81C == 32) + { + work->f81C = 0; + work->f818++; + } + break; + case 2: + tmp = (work->f89C.vy - work->f8A6) / work->f8B6; + var_v1 = work->f8AC.vy + tmp; + if (work->f89C.vy > var_v1) + { + var_v1 = work->f89C.vy; + work->f818++; + } + + work->f8AC.vy = var_v1; + break; + case 3: + if (++work->f81C == 32) + { + work->f818 = 0; + work->f81C = 0; + } + break; + } + GM_CameraSetRotation(&work->f8AC); + if (work->body.is_end != 0) + { + work->control.mov = work->f824; + } + } +} + +void s03b_torture_800C59FC(Work *work, int arg1) +{ + char pad[0x20]; + + int f85E; + + if (arg1 == 0) + { + s03b_torture_800C447C(work, HASH_MOTION, 7); + + if (work->body.action != 2) + { + GM_ConfigObjectAction(&work->body, 2, 0, 4); + } + + GM_SeSet(&work->control.mov, 181); + } + + s03b_torture_800C44D0(work, 0, 1); + + if (arg1 < 8) + { + GV_NearExp4V((short *)&work->control.mov, (short *)&work->f824, 3); + } + + if (work->body.is_end != 0) + { + work->f85C++; + + if (work->f85C > 0) + { + // Identical cases + f85E = work->f85E; + if (work->f85C != f85E) + { + s03b_torture_800C447C(work, HASH_MOTION, 7); + s03b_torture_800C447C(work, HASH_VOICE, work->f85C + 2); + } + } + else + { + // Identical cases + f85E = work->f85E; + if (work->f85C != f85E) + { + s03b_torture_800C447C(work, HASH_MOTION, 7); + s03b_torture_800C447C(work, HASH_VOICE, work->f85C + 2); + } + } + + work->f808 = s03b_torture_800C5AF8; + work->f81A = 0; + work->f818 = 0; + work->f80C = 0; + } +} + +void s03b_torture_800C5AF8(Work *work, int arg1) +{ + GCL_ARGS args; + long data; + int f85E; + int f802; + int f81A; + + f85E = work->f85E; + if (arg1 == 0) + { + if (work->body.action != 6) + { + GM_ConfigObjectAction(&work->body, 6, 0, 15); + } + + work->f806 = 0; + + if (work->f840 >= 0) + { + args.argc = 1; + args.argv = &data; + + if (work->f85C == f85E) + { + data = 0; + } + else + { + data = work->f85C; + } + + GCL_ExecProc(work->f840, &args); + } + } + + if (work->f85C != f85E) + { + if (work->f818 == 0) + { + f802 = work->f802; + if ((f802 & 0x2) == 0) + { + f81A = work->f81A; + if (f81A == 1) + { + GV_RandU(16); + + s03b_torture_800C447C(work, HASH_OPERATION, 0); + s03b_torture_800C447C(work, HASH_VOICE, work->f85C + 8); + + work->f818 = f81A; + } + } + } + + if (s03b_boxall_800C95EC()) + { + work->f81A++; + } + + if (work->f81A == 2) + { + work->f808 = s03b_torture_800C5420; + work->f81A = 0; + work->f818 = 0; + work->f80C = 0; + } + } + else if (work->f818 == 0) + { + f802 = work->f802; + if ((f802 & 0x2) == 0) + { + work->f800 |= 0x4; + + GM_GameStatus |= STATE_LIFEBAR_OFF; + TortureInfoKill(); + + s03b_torture_800C447C(work, HASH_MODE, 2); + + work->f818 = 1; + } + + if (work->f818 != 0) + { + s03b_torture_800C4654(work); + } + } + else + { + s03b_torture_800C4654(work); + } +} + +void s03b_torture_800C5CC8(Work *work, int arg1) +{ + char pad[32]; + + if (arg1 == 0) + { + NewPadVibration(s03b_dword_800C32D0, 1); + NewPadVibration(s03b_dword_800C32D8, 2); + + GM_GameStatus |= STATE_PADRELEASE; + GM_SeSet2(0, 63, SE_PLAYEROUT); + + if (work->body.action != 4) + { + GM_ConfigObjectAction(&work->body, 4, 0, 4); + } + + work->f802 |= 0x10; + } + + if (arg1 < 8) + { + GV_NearExp4V((short *)&work->control.mov, (short *)&work->f824, 3); + } + + switch (work->f818) + { + case 0: + if (work->body.is_end != 0) + { + work->f818++; + + if (work->body.action != 5) + { + GM_ConfigObjectAction(&work->body, 5, 0, 4); + } + } + break; + + case 1: + if (++work->f81A == 32) + { + work->f818++; + + GM_GameStatus &= ~STATE_PADRELEASE; + GM_CallSystemCallbackProc(0, 0); + + GM_GameStatus |= STATE_MENU_OFF | STATE_PAUSE_OFF | STATE_RADIO_OFF; + NewGameOver(FALSE); + } + break; + } +} + +void s03b_torture_800C5E48(Work *work, int arg1) +{ + if (work->f834->press & PAD_CIRCLE) + { + work->f804 += work->f84A; + } + + if (work->f804 > GM_SnakeMaxHealth) + { + work->f804 = GM_SnakeMaxHealth; + } + + if (arg1 == work->f84C) + { + work->f810 = 0; + work->f802 &= ~0x2; + } +} + +void s03b_torture_800C5EC4(Work *work) +{ + GV_MSG *msg; + int i; + int n_msgs; + int f802; + + f802 = work->f802; + if (f802 & 0x40) + { + return; + } + + if (work->control.n_msg == 0) + { + return; + } + + n_msgs = work->control.n_msg; + msg = &work->control.msg[n_msgs] - 1; + + for (; n_msgs > 0; n_msgs--, msg--) + { + switch (msg->message[0]) + { + case 0xE530: + work->f85C = 0; + work->f85E = msg->message[1]; + work->f802 |= 0x40; + break; + case 0xF999: + for (i = 1; i < msg->message_len; i++) + { + work->f860[0][i - 1] = msg->message[i]; + } + work->f802 |= 0x40; + break; + case 0xE314: + for (i = 1; i < msg->message_len; i++) + { + work->f860[1][i - 1] = msg->message[i]; + } + work->f802 |= 0x40; + break; + case 0xFE25: + work->f84A = msg->message[1]; + work->f802 |= 0x40; + break; + } + } +} + +void s03b_torture_800C6024(Work *work) +{ + int f802; + + if (work->f7FE == 0) + { + work->f808 = s03b_torture_800C4DF0; + work->f81A = 0; + work->f818 = 0; + work->f80C = 0; + work->f802 |= 0x8; + } + + f802 = work->f802; + if (f802 & 0x4) + { + work->f802 &= ~0x4; + work->f7FC = 1; + work->f7FE = 0; + } +} + +void s03b_torture_800C6080(Work *work) +{ + int comp; + + if (work->f7FE < 2) + { + if (work->f8FC) + { + GV_DestroyOtherActor(work->f8FC); + work->f8FC = NULL; + } + + work->f808 = s03b_torture_800C4E64; + work->f81A = 0; + work->f818 = 0; + work->f80C = 0; + work->f802 |= 0x8; + } + + if (work->f848 == 0 && work->f7FE == 120) + { + NewFadeInOut(FADEIO_MODE_TOBLACK, 28); + } + + comp = (work->f848 == 0) ? 150 : 180; + if (work->f7FE == comp) + { + if (work->f848 == 0 && work->f83C >= 0) + { + GM_Camera.first_person = 0; + GCL_ExecProc(work->f83C, NULL); + GV_DestroyActor(&work->actor); + GM_GameStatus &= ~STATE_NOSLOW; + } + else + { + work->f7FC = 2; + work->f7FE = 0; + work->f802 &= ~0x8; + } + } +} + +void s03b_torture_800C61A4(Work *work) +{ + int f802; + + if (work->f7FE < 2) + { + work->f808 = s03b_torture_800C4F54; + work->f81A = 0; + work->f818 = 0; + work->f80C = 0; + work->f802 |= 0x8; + } + + f802 = work->f802; + if (f802 & 4) + { + work->f802 &= ~0xC; + work->f7FC = 7; + work->f7FE = 0; + } +} + +void s03b_torture_800C6204(Work *work) +{ + int f802; + + if (work->f7FE < 2) + { + s03b_torture_800C43F0(); + work->f808 = s03b_torture_800C50A8; + work->f81A = 0; + work->f818 = 0; + work->f80C = 0; + GM_GameStatus |= STATE_NOSLOW; + } + + f802 = work->f802; + if (f802 & 0x4) + { + work->f802 &= ~0x4; + + GM_GameStatus &= ~STATE_NOSLOW; + GM_GameStatus &= ~STATE_PADRELEASE; + + if (work->f83C >= 0) + { + GCL_ExecProc(work->f83C, NULL); + GV_DestroyActor(&work->actor); + } + } +} + +void s03b_torture_800C62C4(Work *work) +{ + int f802; + + if (work->f7FE < 2) + { + work->f808 = s03b_torture_800C53C8; + work->f81A = 0; + work->f818 = 0; + work->f80C = 0; + GM_GameStatus |= STATE_NOSLOW; + } + + if (work->f7FE == 0x1E) + { + s03b_torture_800C4260(work); + } + + f802 = work->f802; + if (f802 & 0x4) + { + work->f802 &= ~0x4; + work->f7FC = 3; + work->f7FE = 0; + GM_GameStatus &= ~STATE_PADRELEASE; + GM_GameStatus &= ~STATE_NOSLOW; + } +} + +void Torture_800C6380(Work *work) +{ + int f802; + + if (work->f7FE < 2) + { + work->f808 = s03b_torture_800C5420; + work->f81A = 0; + work->f818 = 0; + work->f80C = 0; + } + + if (!s03b_torture_800C4314(work)) + { + f802 = work->f802; + if (f802 & 0x4) + { + work->f802 &= ~0x4; + work->f820 = 0; + work->f7FC = 5; + work->f7FE = 0; + } + } +} + +void Torture_800C6400(Work *work) +{ + int f802; + + if (work->f7FE < 2) + { + TortureInfoKill(); + work->f808 = s03b_torture_800C4AB0; + work->f81A = 0; + work->f818 = 0; + work->f80C = 0; + GM_GameStatus |= STATE_PADRELEASE | STATE_LIFEBAR_OFF; + work->f802 |= 0x8; + } + + f802 = work->f802; + if (f802 & 0x4) + { + work->f7FC = 7; + work->f7FE = 0; + work->f83C = work->f838; + work->f802 &= ~0xC; + GM_GameStatus &= ~STATE_PADRELEASE; + } +} + +void Torture_800C64BC(Work *work) +{ + s03b_torture_800C5EC4(work); + + switch (work->f7FC) + { + case 0: + s03b_torture_800C6024(work); + break; + + case 1: + s03b_torture_800C6080(work); + break; + + case 2: + s03b_torture_800C62C4(work); + break; + + case 3: + Torture_800C6380(work); + + if (work->f7FC == 6) + { + work->f7FE = 1; + work->f802 &= ~0x4; + goto next; + } + break; + + case 4: + break; + + case 5: + s03b_torture_800C61A4(work); + break; + + case 6: + next: + Torture_800C6400(work); + break; + + case 7: + s03b_torture_800C6204(work); + break; + } + + if (work->f7FE < 16000) + { + work->f7FE++; + } + + s03b_boxall_800C9404(); + + sna_act_helper2_helper2_80033054(GV_StrCode("スネーク"), &work->adjust[6]); + + s03b_torture_800C435C(work, 4000); + s03b_torture_800C3EF8(work); + s03b_torture_800C3E80(work); + s03b_torture_800C3EB8(work); + s03b_torture_800C4284(work); + s03b_torture_800C3FE4(work); +} + +void TortureAct_800C6600(Work *work) +{ + GM_SnakeCameraWork *cam; + int f800; + + GV_PadMask &= ~0x800; + GM_GameStatus |= STATE_PADMASK; + + GM_ActMotion(&work->body); + + work->control.mov.vy += work->body.height - work->control.height; + + GM_ActControl(&work->control); + GM_ActObject(&work->body); + + DG_GetLightMatrix(&work->control.mov, work->light); + + GM_PlayerPosition = work->control.mov; + + cam = &GM_SnakeCamera; + cam->position.vx = work->body.objs->objs[6].world.t[0]; + cam->position.vy = work->body.objs->objs[6].world.t[1]; + cam->position.vz = work->body.objs->objs[6].world.t[2]; + + GM_SnakeCurrentHealth = (work->f804 > 0) ? work->f804 : 0; + + work->control.height = work->body.height; + + Torture_800C64BC(work); + + f800 = work->f800; + if (f800 & 0x1) + { + GV_NearExp2PV((short *)&cam->rotate2, (short *)&work->f82C, 3); + } + else + { + cam->rotate2 = work->control.rot; + } +} + +void TortureDie_800C6774(Work *work) +{ + GM_FreeControl(&work->control); + GM_FreeObject(&work->body); + + GM_PlayerStatus &= ~PLAYER_MENU_DISABLE; + + CloseCinemaScreen(); + s03b_boxall_800C9328(); + + GM_SnakeCurrentHealth = GM_SnakeMaxHealth; + + if (GM_PlayerControl == &work->control) + { + GM_PlayerControl = NULL; + } + + if (GM_PlayerBody == &work->body) + { + GM_PlayerBody = NULL; + } +} + +void Torture_800C6814(Work *work) +{ + int params[7]; + int *iter; + char *res; + + if (!GCL_GetOption('b')) + { + return; + } + + iter = params; + while ((res = GCL_NextStr())) + { + *iter++ = GCL_StrToInt(res); + } + + work->f89C.vx = params[1]; + work->f89C.vy = params[2]; + work->f89C.vz = params[0]; + work->f8A4 = params[3]; + work->f8A6 = params[4]; + work->f8B4 = params[5]; + work->f8B6 = params[6]; + + if (GCL_GetOption('z')) + { + work->f844 = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->f844 = -1; + } +} + +void Torture_800C68E8(Work *work) +{ + int count; + SVECTOR *iter; + + if (!GCL_GetOption('x')) + { + return; + } + + count = 0; + iter = work->f8BC; + while (GCL_NextStr()) + { + GCL_StrToSV(GCL_NextStr(), (short *)iter); + iter++; + count++; + } + + work->f8B8 = count; +} + +void Torture_800C695C(Work *work) +{ + char *opt; + + work->f7FE = 0; + work->f800 = 0; + work->f802 = 0; + work->f804 = GM_SnakeMaxHealth; + work->f810 = 0; + work->f814 = 0; + work->f81A = 0; + work->f818 = 0; + work->f822 = 0; + work->f820 = 0; + work->f824 = work->control.mov; + work->f834 = &GV_PadData[2]; + work->f82C = work->control.rot; + work->f8FC = NULL; + work->f900 = NULL; + + opt = GCL_GetOption('m'); + if (opt != NULL) + { + work->f7FC = GCL_StrToInt(opt); + } + else + { + work->f7FC = 0; + } + + if (work->f7FC < 2) + { + GM_Camera.first_person = 2; + work->f802 |= 0x2000; + } + else + { + GM_Camera.first_person = 0; + } + + opt = GCL_GetOption('c'); + if (opt != NULL) + { + work->f848 = GCL_StrToInt(opt); + } + else + { + work->f848 = 0; + } + + opt = GCL_GetOption('f'); + if (opt != NULL && GCL_StrToInt(opt) != 0) + { + work->f802 |= 0x80; + } +} + +void Torture_800C6AB0(Work *work) +{ + int count; + int *iter; + char *res; + + if (GCL_GetOption('v')) + { + count = 0; + iter = work->f87C; + while ((res = GCL_NextStr())) + { + if (count == 8) + { + break; + } + + *iter++ = GCL_StrToInt(res); + count++; + } + + work->f878 = count; + } + else + { + work->f878 = 0; + } +} + +int TortureGetResources_800C6B3C(Work *work, int name, int map) +{ + CONTROL *control; + char *pos; + char *dir; + OBJECT *body; + int model; + int motion; + int oar; + int proc; + + control = &work->control; + if (GM_InitControl(control, name, map) < 0) + { + return -1; + } + + pos = GCL_GetOption('p'); + dir = GCL_GetOption('d'); + GM_ConfigControlString(control, pos, dir); + + GM_ConfigControlHazard(control, control->mov.vy, -2, -2); + GM_ConfigControlAttribute(control, RADAR_VISIBLE); + + body = &work->body; + + model = GV_StrCode("sne_nude"); + motion = GV_StrCode("sne_03b"); + GM_InitObject(body, model, BODY_FLAG, motion); + GM_ConfigObjectJoint(body); + + oar = GV_StrCode("sne_03b"); + GM_ConfigMotionControl(body, &work->m_ctrl, oar, &work->anims[0], &work->anims[17], control, work->rots); + GM_ConfigMotionAdjust(body, work->adjust); + GM_ConfigObjectLight(body, work->light); + GM_ConfigObjectAction(body, 0, 0, 0); + + if (GCL_GetOption('e')) + { + work->f83C = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->f83C = -1; + } + + if (GCL_GetOption('s')) + { + work->f838 = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->f838 = -1; + } + + if (GCL_GetOption('a')) + { + work->f840 = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->f840 = -1; + } + + Torture_800C68E8(work); + Torture_800C6814(work); + Torture_800C6AB0(work); + s03b_torture_800C45E4(work); + Torture_800C695C(work); + + GM_PlayerControl = control; + GM_PlayerBody = body; + + GM_SnakeCamera.rotate2 = control->rot; + GM_SnakeCamera.position = control->mov; + + work->body.objs->objs[6].world.t[0] = GM_SnakeCamera.position.vx; + work->body.objs->objs[6].world.t[1] = GM_SnakeCamera.position.vy; + work->body.objs->objs[6].world.t[2] = GM_SnakeCamera.position.vz; + + GM_AlertMode = 10; + GM_PlayerStatus |= PLAYER_MENU_DISABLE; + + s03b_boxall_800C9328(); + + GM_ActMotion(body); + + work->control.height = work->body.height; + + GM_ActControl(control); + GM_ActObject(body); + + proc = GCL_StrToInt(GCL_GetOption('n')); + GCL_ExecProc(proc, NULL); + + GV_PadMask = ~0x810; + GM_GameStatus |= STATE_PADMASK; + + return 0; +} + +void *NewTorture(int name, int where) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, TortureAct_800C6600, TortureDie_800C6774, "torture.c"); + + if (TortureGetResources_800C6B3C(work, name, where) >= 0) + { + return (void *)work; + } + + GV_DestroyActor(&work->actor); + } + + return NULL; +} diff --git a/components/mgs/mgs/source/chara/torture/torture.h b/components/mgs/mgs/source/chara/torture/torture.h new file mode 100644 index 00000000..a18a248e --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/torture.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_TORTURE_H__ +#define __MGS_CHARA_TORTURE_H__ + +void *NewTorture(int name, int where); + +#endif // __MGS_CHARA_TORTURE_H__ diff --git a/components/mgs/mgs/source/chara/torture/unknown5.c b/components/mgs/mgs/source/chara/torture/unknown5.c new file mode 100644 index 00000000..f5dd53dd --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/unknown5.c @@ -0,0 +1,167 @@ +#include +#include "game/game.h" + +static int s03b_dword_800D3300[6]; + +char s03b_dword_800C336C = 0; +int s03b_dword_800C3370 = 0; +int s03b_dword_800C3374 = -1; +int s03b_dword_800C3378 = -1; +char s03b_dword_800C337C = 0; +char s03b_dword_800C337D = 0; +int s03b_dword_800C3380 = 0; +int s03b_dword_800C3384 = 0; +int s03b_dword_800C3388 = 0; +int s03b_dword_800C338C = 0; + +void s03b_boxall_800C9328(void) +{ + s03b_dword_800C336C = 0; + s03b_dword_800C3370 = -1; + s03b_dword_800C3374 = -1; + s03b_dword_800C3378 = -1; + s03b_dword_800C337C = 0; + s03b_dword_800C337D = 0; + s03b_dword_800C3380 = -1; + s03b_dword_800C3384 = -1; + s03b_dword_800C338C = 0; + s03b_dword_800C3388 = 0; + + if (GM_StreamStatus() != -1) + { + GM_StreamPlayStop(); + } +} + +int s03b_boxall_800C93AC(int arg0) +{ + int index; + + if (s03b_dword_800C336C == 6) + { + return -1; + } + + index = s03b_dword_800C336C; + s03b_dword_800D3300[index] = arg0; + s03b_dword_800C336C++; + return 0; +} + +void s03b_boxall_800C93F0(int arg0, int arg1) +{ + s03b_dword_800C3388 = arg0; + s03b_dword_800C338C = arg1; +} + +void s03b_boxall_800C9404(void) +{ + int temp_s1; + int temp_s2; + int timeout; + int i; + + if (s03b_dword_800C338C != 0 && --s03b_dword_800C338C <= 0) + { + s03b_boxall_800C93AC(s03b_dword_800C3388); + s03b_dword_800C338C = 0; + } + + temp_s1 = s03b_dword_800C3370; + + temp_s2 = GM_StreamStatus(); + s03b_dword_800C3370 = temp_s2; + + s03b_dword_800C337C = 0; + s03b_dword_800C337D = 0; + + if (s03b_dword_800C3380 >= 0 && s03b_dword_800C336C != 0 && temp_s1 != -1) + { + if (temp_s1 == 2) + { + s03b_dword_800C3380++; + } + + if (++s03b_dword_800C3384 > 5400) + { + timeout = 1; + printf("timeout %d\n", s03b_dword_800C3374); + GM_StreamPlayStop(); + } + else + { + timeout = 0; + } + + if (temp_s2 == -1 || timeout == 1) + { + s03b_dword_800C3374 = -1; + s03b_dword_800C3378 = s03b_dword_800D3300[0]; + s03b_dword_800C337C = 1; + + for (i = 0; i < 5; i++) + { + s03b_dword_800D3300[i] = s03b_dword_800D3300[i + 1]; + } + + s03b_dword_800C3384 = -1; + s03b_dword_800C3380 = -1; + s03b_dword_800C336C--; + } + } + + if (temp_s2 == -1 && s03b_dword_800C336C != 0) + { + s03b_dword_800C3384 = 0; + s03b_dword_800C3380 = 0; + s03b_dword_800C3374 = s03b_dword_800D3300[0]; + s03b_dword_800C337D = 1; + GM_VoxStream(s03b_dword_800C3374, 0); + } +} + +int s03b_boxall_800C95DC(void) +{ + return s03b_dword_800C3374; +} + +int s03b_boxall_800C95EC(void) +{ + return s03b_dword_800C337C; +} + +int s03b_boxall_800C95FC(void) +{ + return s03b_dword_800C337D; +} + +int s03b_boxall_800C960C(void) +{ + return s03b_dword_800C3380 > 0; +} + +int s03b_boxall_800C961C(int arg0) +{ + if (s03b_dword_800C3374 == arg0 && s03b_dword_800C3380 == 1) + { + return 1; + } + + return 0; +} + +int s03b_boxall_800C9654(int arg0) +{ + if (s03b_dword_800C337C == 1 && s03b_dword_800C3378 == arg0) + { + s03b_dword_800C3378 = -1; + return 1; + } + + return 0; +} + +int s03b_boxall_800C968C(void) +{ + return s03b_dword_800C3370; +} diff --git a/components/mgs/mgs/source/chara/torture/unknown7.c b/components/mgs/mgs/source/chara/torture/unknown7.c new file mode 100644 index 00000000..ed97253f --- /dev/null +++ b/components/mgs/mgs/source/chara/torture/unknown7.c @@ -0,0 +1,144 @@ +#include "libgv/libgv.h" +#include "game/game.h" +#include "strcode.h" + +extern GM_CameraSystemWork GM_Camera; + +SECTION(".bss") int s03b_dword_800D3318; +SECTION(".bss") int s03b_dword_800D331C; +SECTION(".bss") SVECTOR s03b_dword_800D3320; +SECTION(".bss") SVECTOR s03b_dword_800D3328; +SECTION(".bss") SVECTOR s03b_dword_800D3330; +SECTION(".bss") SVECTOR s03b_dword_800D3338; +SECTION(".bss") GV_MSG *s03b_dword_800D3340; +SECTION(".bss") int s03b_dword_800D3344; + +int s03b_dword_800C33A0 = 0; + +void s03b_800CA5DC(void) +{ + SVECTOR diff; + SVECTOR offset; + int scale; + int len; + + if (s03b_dword_800C33A0 != 0) + { + scale = s03b_dword_800D3330.pad; + if (scale < 0) + { + s03b_dword_800D3320 = s03b_dword_800D3330; + } + else + { + GV_SubVec3(&s03b_dword_800D3330, &s03b_dword_800D3320, &diff); + len = GV_VecLen3(&diff); + if (scale < len) + { + GV_LenVec3(&diff, &offset, len, scale); + GV_AddVec3(&s03b_dword_800D3320, &offset, &s03b_dword_800D3320); + } + } + + scale = s03b_dword_800D3338.pad; + if (scale < 0) + { + s03b_dword_800D3328 = s03b_dword_800D3338; + } + else + { + GV_SubVec3(&s03b_dword_800D3338, &s03b_dword_800D3328, &diff); + len = GV_VecLen3(&diff); + if (scale < len) + { + GV_LenVec3(&diff, &offset, len, scale); + GV_AddVec3(&s03b_dword_800D3328, &offset, &s03b_dword_800D3328); + } + } + + GM_Camera.type = 0; + GM_Camera.position = s03b_dword_800D3320; + GM_Camera.target = s03b_dword_800D3328; + } +} + +void s03b_800CA794(void) +{ + if (s03b_dword_800C33A0 == 0) + { + GM_Camera.flag = 2; + GM_Camera.interp = 0; + GM_Camera.interp_mode = 0; + s03b_dword_800D3318 = GM_Camera.type; + GM_SetCameraCallbackFunc(1, s03b_800CA5DC); + s03b_dword_800C33A0 = 1; + } +} + +void s03b_800CA7FC(void) +{ + if (s03b_dword_800C33A0 != 0) + { + GM_Camera.interp_mode = 2; + GM_Camera.interp = 0; + GM_Camera.type = s03b_dword_800D3318; + GM_Camera.flag &= ~0x2; + GM_SetCameraCallbackFunc(1, NULL); + s03b_dword_800C33A0 = 0; + } +} + +void s03b_800CA868(void) +{ + int n_msgs; + GV_MSG *msg; + int i; + int code; + int len; + + n_msgs = GV_ReceiveMessage(0xCED3, &s03b_dword_800D3340); + s03b_dword_800D3344 = n_msgs; + if (n_msgs == 0) + { + return; + } + + msg = &s03b_dword_800D3340[n_msgs] - 1; + for (i = n_msgs; i > 0; i--, msg--) + { + code = msg->message[0]; + len = msg->message_len; + + switch (code) + { + case 0xA3AC: + if (len == 7) + { + s03b_dword_800D3330.vx = msg->message[4]; + s03b_dword_800D3330.vy = msg->message[5]; + s03b_dword_800D3330.vz = msg->message[6]; + + s03b_dword_800D3338.vx = msg->message[1]; + s03b_dword_800D3338.vy = msg->message[2]; + s03b_dword_800D3338.vz = msg->message[3]; + } + break; + + case 0xA86D: + if (len >= 3) + { + s03b_dword_800D3330.pad = msg->message[1]; + s03b_dword_800D3338.pad = msg->message[2]; + } + break; + + case HASH_START: + s03b_800CA794(); + break; + + case 0xA225: + s03b_800CA7FC(); + break; + } + } +} diff --git a/components/mgs/mgs/source/chara/wake/wake.c b/components/mgs/mgs/source/chara/wake/wake.c new file mode 100644 index 00000000..50fd8bed --- /dev/null +++ b/components/mgs/mgs/source/chara/wake/wake.c @@ -0,0 +1,243 @@ +#include "wake.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "chara/snake/sna_init.h" + +extern GM_CameraSystemWork GM_Camera; +extern GM_SnakeCameraWork GM_SnakeCamera; + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_USER + +#define MESG_UNK_7E11 0x7e11 + +typedef struct _Work +{ + GV_ACT actor; + SVECTOR field_20; + SVECTOR player_pos; + SVECTOR field_30; + SVECTOR field_38; + GV_PAD *pad; + int unused1; + int unused2; + int where; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void CheckMessage(Work *work) +{ + GV_MSG *msg; + int count; + + count = GV_ReceiveMessage(work->where, &msg); + if (count > 0) + { + for (count--; count >= 0; count--, msg++) + { + if (msg->message[0] == MESG_UNK_7E11 && msg->message_len >= 4) + { + work->field_38.vx = msg->message[1]; + work->field_38.vy = msg->message[2]; + work->field_38.vz = msg->message[3]; + } + } + } +} + +// Modified s03b_torture_800C3F7C +static int CheckPadDirInput(GV_PAD *pad) +{ + char *analog; + int i; + char adjust; + + if (pad->analog == 0) + { + return 0; + } + + if (!(pad->status & PAD_UDLR)) + { + return 0; + } + + analog = &pad->left_dx; + for (i = 0; i < 2; i++) + { + adjust = *analog - 64; + if (adjust > 128) + { + return 1; + } + + analog++; + } + + return 0; +} + +static void CheckPad(Work *work) +{ + short status; + unsigned char left_dy; + GV_PAD *pad, *pad2; + int coord; + + pad = work->pad; + status = pad->status; + + pad2 = work->pad; + left_dy = pad2->left_dy; + + GM_CheckShukanReverse(&status); + GM_CheckShukanReverseAnalog(&left_dy); + + if (CheckPadDirInput(pad)) + { + coord = work->field_38.vx; + if (status & PAD_UP) + { + coord -= work->field_20.vx * ((64 - left_dy) & 0xFF) / 64; + } + else if (status & PAD_DOWN) + { + coord += work->field_20.vy * ((left_dy + 64) & 0xFF) / 64; + } + work->field_30.vx = coord; + + coord = work->field_38.vy; + if (status & PAD_LEFT) + { + coord += work->field_20.vz * ((64 - pad->left_dx) & 0xFF) / 64; + } + else if (status & PAD_RIGHT) + { + coord -= work->field_20.pad * ((pad->left_dx + 64) & 0xFF) / 64; + } + work->field_30.vy = coord; + } + else + { + coord = work->field_38.vx; + if (status & PAD_UP) + { + coord -= work->field_20.vx; + } + else if (status & PAD_DOWN) + { + coord += work->field_20.vy; + } + work->field_30.vx = coord; + + coord = work->field_38.vy; + if (status & PAD_LEFT) + { + coord += work->field_20.vz; + } + else if (status & PAD_RIGHT) + { + coord -= work->field_20.pad; + } + work->field_30.vy = coord; + } + + if (work->field_30.vx < -1000) + { + work->field_30.vx = -1000; + } + + GV_NearExp4PV(&GM_SnakeCamera.rotate2.vx, &work->field_30.vx, 3); + GM_SnakeCamera.position = work->player_pos; +} + +/*---------------------------------------------------------------------------*/ + +static void Act(Work *work) +{ + if (GM_PlayerStatus & PLAYER_SECOND_CONTROLLER) + { + work->pad = &GV_PadData[1]; + } + else + { + work->pad = &GV_PadData[0]; + } + CheckMessage(work); + CheckPad(work); + GM_PlayerPosition = work->player_pos; +} + +static void Die(Work *work) +{ + GM_PlayerStatus &= ~PLAYER_MENU_DISABLE; +} + +static int GetResources(Work *work, int where) +{ + if (!GCL_GetOption('b')) + { + return -1; + } + work->field_20.vx = GCL_StrToInt(GCL_NextStr()); + work->field_20.vy = GCL_StrToInt(GCL_NextStr()); + work->field_20.vz = GCL_StrToInt(GCL_NextStr()); + work->field_20.pad = GCL_StrToInt(GCL_NextStr()); + + if (!GCL_GetOption('p')) + { + return -1; + } + GCL_StrToSV(GCL_NextStr(), (short *)&work->player_pos); + + if (!GCL_GetOption('d')) + { + return -1; + } + + GCL_StrToSV(GCL_NextStr(), (short *)&work->field_30); + work->field_38 = work->field_30; + + work->unused1 = 0; + work->unused2 = 0; + work->where = where; + + GM_Camera.first_person = 2; + GM_SnakeCamera.rotate2 = work->field_30; + + GM_PlayerStatus |= PLAYER_MENU_DISABLE; + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewWake(int where) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work == NULL) + { + return NULL; + } + + GV_SetNamedActor(&work->actor, Act, Die, "wake.c"); + + if (GetResources(work, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/chara/wake/wake.h b/components/mgs/mgs/source/chara/wake/wake.h new file mode 100644 index 00000000..a2c2d700 --- /dev/null +++ b/components/mgs/mgs/source/chara/wake/wake.h @@ -0,0 +1,6 @@ +#ifndef __MGS_CHARA_WAKE_H__ +#define __MGS_CHARA_WAKE_H__ + +void *NewWake(int where); + +#endif // __MGS_CHARA_WAKE_H__ diff --git a/components/mgs/mgs/source/data/bss.c b/components/mgs/mgs/source/data/bss.c new file mode 100644 index 00000000..57bc8a71 --- /dev/null +++ b/components/mgs/mgs/source/data/bss.c @@ -0,0 +1,609 @@ +#define __BSSDEFINE__ + +#include +#include +#include +#include // for SpuVoiceAttr + +#include "mts/mts_new.h" +#include "mts/mts_pad.h" +#include "mts/terminal.h" +#include "sound/sd_cli.h" +#include "sound/sd_incl.h" + +#include "common.h" +#include "libfs/libfs.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "libhzd/libhzd.h" +#include "memcard/memcard.h" + +#include "game/game.h" +#include "game/jimctrl.h" +#include "chara/snake/sna_init.h" +#include "equip/equip.h" +#include "bullet/bakudan.h" + +#define BSS SECTION(".bss") +#define gap char BSS + +// simply add a new var at any random place below the line then run bss.py and it will automatically put it in then +// correct place and re-adjust the gaps, sizes, and byte size comments. the only thing allowed below the line are +// var declarations. put everything else in a header. all comments below the line will also be deleted. +// you must use "BSS" instead of SECTION, and for EVERY var. + +// WARNING: +// If any headers included by this file have externs to variables defined here, +// the build won't match!! DO NOT declare any BSS variables in a header without +// wrapping them with #if !defined(__BSSDEFINE__). +// +// The __BSSDEFINE__ flag needs to be defined here and nowhere else. +// This lets us properly declare BSS variables for the rest of the codebase. + +// -------------------------------------------------------------------------------------------------------------------- + +/* main/main.obj */ +char BSS GameStack[2048]; // 0x800 (2048) bytes +char BSS SdStack[2048]; // 0x800 (2048) bytes + +/* libfs/select.obj */ +int BSS FS_DiskNum; // 0x4 (4) bytes +int BSS pad_800ACBF4; // 0x4 (4) bytes + +/* libgv/gvd.obj */ +GV_ACT BSS GV_Work; // 0x20 (32) bytes + +/* libgv/actor.obj */ +AList BSS ActorList[GV_ACTOR_LEVEL]; // 0x264 (612) bytes + +gap gap_800ACE7C[0x4]; // 4 bytes + +/* libgv/cache.obj */ +GV_LOADFUNC BSS Loaders[MAX_LOADERS]; // 0x68 (104) bytes + +gap gap_800ACEE8[0x8]; // 8 bytes + +CACHE BSS Caches[MAX_CACHES]; // 0x400 (1024) bytes + +/* libgv/memory.obj */ +GV_HEAP BSS MemorySystems_800AD2F0[3]; // 0x3030 (12336) bytes + +/* libgv/message.obj */ +MESSAGE_LIST BSS message_list[2]; // 0x288 (648) bytes + +/* libgv/pad.obj */ +int BSS dword_800B05A8[6]; // 0x18 (24) bytes +GV_PAD BSS GV_PadData[4]; // 0x40 (64) bytes + +/* libdg/display.c */ +DISPENV BSS g_dispenv; // 0x14 (20) bytes + +gap gap_800B0614[0xC]; // 12 bytes + +VECTOR BSS DG_RightVector; // 0x10 (16) bytes + +/* libdg/chanl.obj */ +OT_TYPE BSS ot_background[68]; // 0x110 (272) bytes +OT_TYPE BSS ot_primitive[516]; // 0x810 (2064) bytes +OT_TYPE BSS ot_overlay[4]; // 0x10 (16) bytes + +void *BSS obj_queue_background[8]; // 0x20 (32) bytes +void *BSS obj_queue_primitive[256]; // 0x400 (1024) bytes + +DR_ENV BSS bg_drawenv[2]; // 0x80 (128) bytes + +OT_TYPE BSS ptr_800B1400[256]; // 0x400 (1024) bytes + +DG_CHANL BSS DG_Chanls[3]; // 0x5C4 (1476) bytes + +gap gap_800B1DC4[0x4]; // 4 bytes + +unsigned short BSS gOldRootCnt_800B1DC8[32]; // 0x40 (64) bytes + +/* libdg/light.obj */ +DG_FixedLight BSS gFixedLights_800B1E08[8]; // 0x40 (64) bytes +DG_TmpLightList BSS LightSystems_800B1E48[2]; // 0x108 (264) bytes + +/* libdg/text.obj */ +DG_TEX BSS TexSets[DG_MAX_TEXTURES]; // 0x1800 (6144) bytes + +/* libdg/dgd.obj */ +GV_ACT BSS DG_WorkFirst; // 0x20 (32) bytes +GV_ACT BSS DG_WorkLast; // 0x20 (32) bytes +int BSS dword_800B3790; // 0x4 (4) bytes + +gap gap_800B3794[0x4]; // 4 bytes + +/* libdg/loader.obj */ +unsigned char BSS pcxBuffer_800B3798[128]; // 0x80 (128) bytes + +/* libdg/palette.obj (?) */ +u_long BSS DG_PaletteBuffer[256]; // 0x400 (1024) bytes + +/* libgcl/command.obj */ +GCL_SCRIPT BSS current_script; // 0xC (12) bytes + +gap gap_800B3C24[0x4]; // 4 bytes + +/* libgcl/parse.obj */ +int BSS argbuffer[32]; // 0x80 (128) bytes +char *BSS commandlines[8]; // 0x20 (32) bytes + +/* libgcl/variable.obj */ +GCL_Vars BSS gGcl_vars_800B3CC8; // 0x800 (2048) bytes +short BSS sv_linkvarbuf[0x60]; // 0xC0 (192) bytes +GCL_Vars BSS gGcl_memVars_800b4588; // 0x800 (2048) bytes +char BSS gStageName_800B4D88[16]; // 0x10 (16) bytes +short BSS linkvarbuf[0x60]; // 0xC0 (192) bytes + +/* libfs/cdbios.obj */ +CDBIOS_TASK BSS cd_bios_task_800B4E58; // 0x24 (36) bytes + +gap gap_800B4E7C[0xC]; // 12 bytes + +char BSS cd_bios_stack_800B4E88[1024]; // 0x400 (1024) bytes +FS_STAGE_INFO *BSS fs_stage_info; // 0x4 (4) bytes +int BSS gLoaderStartTime_800B528C; // 0x4 (4) bytes +int BSS gOverlayBinSize_800B5290; // 0x4 (4) bytes +int BSS FS_ResidentCacheDirty; // 0x4 (4) bytes +int BSS fs_stream_ref_count; // 0x4 (4) bytes +int BSS fs_stream_read; // 0x4 (4) bytes +int BSS fs_stream_sector; // 0x4 (4) bytes +void *BSS fs_stream_heap; // 0x4 (4) bytes +char *BSS fs_stream_heap_end; // 0x4 (4) bytes +int BSS fs_stream_heap_size; // 0x4 (4) bytes +int BSS fs_stream_unused; // 0x4 (4) bytes +char *BSS fs_stream_top; // 0x4 (4) bytes +int *BSS fs_stream_write_ptr;; // 0x4 (4) bytes +char *BSS fs_stream_bottom; // 0x4 (4) bytes +unsigned int BSS fs_stream_task_state; // 0x4 (4) bytes + +gap gap_800B52C4[0x4]; // 4 bytes + +/* memcard/memcard.obj */ +long BSS gHardware_end_io; // 0x4 (4) bytes +long BSS gHardware_end_write; // 0x4 (4) bytes +long BSS gHardware_timeout; // 0x4 (4) bytes +long BSS gHardware_new_device; // 0x4 (4) bytes +long BSS gSoftware_end_io; // 0x4 (4) bytes +long BSS gSoftware_end_write; // 0x4 (4) bytes +long BSS gSoftware_timeout; // 0x4 (4) bytes +long BSS gSoftware_new_device; // 0x4 (4) bytes +TMemCardFunc BSS gHwCard_do_op; // 0x4 (4) bytes +TMemCardFunc BSS gSwCard_do_op; // 0x4 (4) bytes +volatile int BSS gSwCardLastOp; // 0x4 (4) bytes +volatile int BSS gHwCardLastOp; // 0x4 (4) bytes +MEM_CARD BSS gMemCards[2]; // 0x350 (848) bytes +volatile long BSS gMemCard_io_size; // 0x4 (4) bytes + +gap gap_800B564C[0x4]; // 4 bytes + +/* game/control.obj */ +CONTROL BSS DummyPlayer; // 0x7C (124) bytes + +gap gap_800B56CC[0x4]; // 4 bytes + +CONTROL *BSS GM_WhereList[96]; // 0x180 (384) bytes + +/* game/area.obj */ +AreaHistory BSS area_history; // 0x10 (16) bytes + +/* game/gamed.obj */ +char BSS exe_name[32]; // 0x20 (32) bytes +gameWork BSS GameWork; // 0x28 (40) bytes + +gap gap_800B58A8[0x8]; // 8 bytes + +DG_TEX BSS gMenuTextureRec_800B58B0; // 0xC (12) bytes + +gap gap_800B58BC[0x4]; // 4 bytes + +unsigned short BSS GM_SystemCallbackProc[6]; // 0xC (12) bytes + +gap gap_800B58CC[0x14]; // 20 bytes + +/* game/script.obj */ +HZD_BND BSS gBindsArray_800b58e0[128]; // 0xC00 (3072) bytes + +/* game/target.obj */ +TARGET BSS GM_TargetDatas[TARGET_MAX]; // 0x1200 (4608) bytes + +/* game/alert.obj */ +char BSS noise_sounds[4][3]; // 0xC (12) bytes + +gap gap_800B76EC[0x4]; // 4 bytes + +/* game/camera.obj */ +UnkCameraStruct2 BSS gUnkCameraStruct2_800B76F0; // 0x24 (36) bytes + +gap gap_800B7714[0x4]; // 4 bytes + +CAMERA BSS GM_CameraList[8]; // 0xA0 (160) bytes +GM_SnakeCameraWork BSS GM_SnakeCamera; // 0x30 (48) bytes +GM_CameraSystemWork BSS GM_Camera; // 0x7C (124) bytes + +gap gap_800B7864[0x4]; // 4 bytes + +UnkCameraStruct2 BSS gUnkCameraStruct2_800B7868; // 0x24 (36) bytes + +gap gap_800B788C[0x4]; // 4 bytes + +/* game/map.obj */ +DG_OBJS *BSS StageObjs[32]; // 0x80 (128) bytes +MAP BSS gMapRecs_800B7910[16]; // 0x140 (320) bytes + +/* libdg/pshade.obj */ +DG_LitVertex BSS DG_LitVertices_800B7A50[84]; // 0x7E0 (2016) bytes + +/* game/homing.obj */ +HOMING BSS homing_targets[NUM_HOMING_TARGETS]; + +/* game/strctrl.obj */ +StreamCtrlWork BSS strctrl_work; // 0x40 (64) bytes + +/* game/jimctrl.obj */ +JimakuCtrlWork BSS jimctrl_work; // 0x104C (4172) bytes +array_800B933C_child BSS array_800B933C[array_800B933C_SIZE]; // 0x1C (28) bytes +int BSS dword_800B9358; // 0x4 (4) bytes + +gap gap_800B935C[0x4]; // 4 bytes + +/* menu/menuman.obj */ +unsigned char BSS menu_primbuffers[2][8192]; // 0x4000 (16384) bytes +MenuWork BSS gMenuWork_800BD360; // 0x220 (544) bytes + +/* menu/radar.obj */ +MATRIX BSS gRadarScaleMatrix_800BD580; // 0x20 (32) bytes + +/* menu/item.obj */ +PANEL_TEXTURE BSS gMenuLeftItems_800BD5A0[MENU_ITEM_COUNT]; // 0x1A4 (420) bytes + +gap gap_800BD744[0x4]; // 4 bytes + +/* menu/weapon.obj */ +array_800BD748_child BSS array_800BD748[9]; // 0xD8 (216) bytes + +gap gap_800BD820[0x8]; // 8 bytes + +array_800BD748_child BSS array_800BD828[4]; // 0x60 (96) bytes +PANEL_TEXTURE BSS gMenuRightItems_800BD888[MENU_WEAPON_COUNT]; // 0xDC (220) bytes + +gap gap_800BD964[0x4]; // 4 bytes + +/* menu/life.obj */ +KCB BSS font_800BD968; // 0x2C (44) bytes + +gap gap_800BD994[0x4]; // 4 bytes + +SPRT BSS gMenuSprt_800bd998; // 0x14 (20) bytes + +gap gap_800BD9AC[0x4]; // 4 bytes + +/* menu/radio.obj */ +SPRT BSS gRadioNumberSprt_800bd9b0; // 0x14 (20) bytes + +gap gap_800BD9C4[0xC]; // 12 bytes + +SPRT BSS gRadioNumberSprt2_800bd9d0; // 0x14 (20) bytes + +gap gap_800BD9E4[0xC]; // 12 bytes + +SPRT BSS gRadioStringSprt_800BD9F0; // 0x14 (20) bytes + +gap gap_800BDA04[0xC]; // 12 bytes + +PANEL_TEXTURE BSS dword_800BDA10; // 0x14 (20) bytes + +gap gap_800BDA24[0xC]; // 12 bytes + +PANEL_TEXTURE BSS dword_800BDA30; // 0x14 (20) bytes + +gap gap_800BDA44[0x4]; // 4 bytes + +/* menu/radioanim.obj */ +menu_0x14 BSS stru_800BDA48[2]; // 0x28 (40) bytes + +/* menu/jimaku.obj */ +UnkJimakuStruct BSS gUnkJimakuStruct_800BDA70; // 0x44 (68) bytes + +gap gap_800BDAB4[0x4]; // 4 bytes + +/* menu/radiotable.obj */ +radio_table BSS gRadioBaseTable_800BDAB8; // 0x40 (64) bytes +radio_table BSS gRadioOverTable_800BDAF8; // 0x40 (64) bytes + +/* menu/radiomem.obj */ +RadioMemory BSS gRadioMemory_800BDB38[RADIO_MEMORY_COUNT]; // 0x140 (320) bytes + +/* chara/snake/sna_init.obj */ +unsigned char BSS gBulNames_800BDC78[64]; // 0x40 (64) bytes +short BSS snake_mag_size_800BDCB8; // 0x2 (2) bytes +short BSS snake_weapon_idx_800BDCBA; // 0x2 (2) bytes +short BSS snake_weapon_max_ammo_800BDCBC; // 0x2 (2) bytes +short BSS unk_800BDCBE; // 0x2 (2) bytes +short BSS word_800BDCC0; // 0x2 (2) bytes + +gap gap_800BDCC4[0x4]; // 4 bytes + +#ifndef VR_EXE +/* equip/jpegcam.obj */ +int BSS dword_800BDCC8; // 0x4 (4) bytes +int BSS dword_800BDCCC; // 0x4 (4) bytes +int BSS dword_800BDCD0; // 0x4 (4) bytes +gap gap_800BDCD4[0x4]; // 4 bytes +TMat8x8B BSS gJpegcamMatrix2_800BDCD8; // 0x40 (64) bytes +#endif + +/* weapon/rfsight.obj */ +DVECTOR BSS dvector_800BDD18[3]; // 0xC (12) bytes + +gap gap_800BDD24[0x4]; // 4 bytes + +/* bullet/tenage.obj */ +int BSS dword_800BDD28; // 0x4 (4) bytes + +gap gap_800BDD2C[0x4]; // 4 bytes + +CONTROL *BSS tenage_ctrls_800BDD30[16]; // 0x40 (64) bytes +int BSS tenage_ctrls_count_800BDD70; // 0x4 (4) bytes + +gap gap_800BDD74[0x4]; // 4 bytes + +/* bullet/bakudan.obj */ +HITTABLE BSS GM_C4Datas[C4_COUNT]; // 0x100 (256) bytes + +/* bullet/jirai.obj */ +HITTABLE BSS GM_ClayDatas[8]; // 0x80 (128) bytes + +/* bullet/rmissile.obj */ +int BSS dword_800BDEF8[2]; // 0x8 (8) bytes + +/* ??? */ +TARGET *BSS target_800BDF00; // 0x4 (4) bytes + +gap gap_800BDF04[0x4]; // 4 bytes + +/* okajima/claymore.obj */ +MAP *BSS claymore_MAP_800bdf08; // 0x4 (4) bytes + +gap gap_800BDF0C[0x4]; // 4 bytes + +/* okajima/spark.obj */ +short BSS gSparkRandomTable[0x40]; // 0x80 (128) bytes + +/* okajima/stngrnd.obj */ +SVECTOR BSS stru_800BDF90; // 0x8 (8) bytes + +/* okajima/chafgrnd.obj */ +int BSS dword_800BDF98; // 0x4 (4) bytes +int BSS dword_800BDF9C; // 0x4 (4) bytes +int BSS okajima_CHAF_LIFE; // 0x4 (4) bytes + +gap gap_800BDFA4[0x4]; // 4 bytes + +/* takabe/goggleir.obj */ +int BSS dword_800BDFA8; // 0x4 (4) bytes + +gap gap_800BDFAC[0x4]; // 4 bytes + +/* takabe/palette.obj */ +void BSS (*pfn_800BDFB0)(); // 0x4 (4) bytes +unsigned short BSS (*pfn_800BDFB4)(unsigned short); // 0x4 (4) bytes + +/* libfs/memfile.obj */ +int BSS dword_800BDFB8; // 0x4 (4) bytes +int BSS dword_800BDFBC; // 0x4 (4) bytes +FS_MEMFILE *BSS dword_800BDFC0; // 0x4 (4) bytes +int BSS dword_800BDFC4; // 0x4 (4) bytes + +/* sound.lib */ + +char BSS sd_main_stack[2048]; // 0x800 (2048) bytes +char BSS sd_int_stack[2048]; // 0x800 (2048) bytes + +gap gap_800BEFC8[0x4]; // 4 bytes + +int BSS dword_800BEFCC; // 0x4 (4) bytes + +gap gap_800BEFD0[0x4]; // 4 bytes + +int BSS sd_debug_mode; // 0x4 (4) bytes +int BSS str_pause_wait; + +gap gap_800BEFDC[0x8]; // 12 bytes + +char *BSS str_data_ptr; // 0x4 (4) bytes +int BSS str_mono_offset; // 0x4 (4) bytes +int BSS mute_l_r_fg; // 0x4 (4) bytes +int BSS str_mute_fg; // 0x4 (4) bytes +unsigned int BSS str_int_ctr; // 0x4 (4) bytes +int BSS song_end_last; // 0x4 (4) bytes +#ifdef VR_EXE +gap vrgap1[0x4]; // 4 bytes +#endif +int BSS se_stop_fg; // 0x4 (4) bytes +int BSS sd_print_fg; // 0x4 (4) bytes +int BSS se_tracks; // 0x4 (4) bytes +#ifdef VR_EXE +gap vrgap2[0x4]; // 4 bytes +#endif +int BSS sng_syukan_vol; // 0x4 (4) bytes +unsigned int BSS blank_data_addr; // 0x4 (4) bytes +char* BSS cdload_buf; // 0x4 (4) bytes +int BSS se_fp; // 0x4 (4) bytes +int BSS sd_sng_code_buf[16]; // 0x40 (64) bytes +char* BSS str_header; // 0x4 (4) bytes +int BSS sng_fadein_time; // 0x4 (4) bytes +int BSS spu_bgm_start_ptr_l; // 0x4 (4) bytes +int BSS eons; // 0x4 (4) bytes +SEPLAYTBL BSS se_playing[8]; // 0x60 (96) bytes +int BSS spu_bgm_start_ptr_r; // 0x4 (4) bytes +int BSS str_fadein_time; // 0x4 (4) bytes +unsigned long BSS mdata1; // 0x4 (4) bytes +unsigned long BSS mdata2; // 0x4 (4) bytes +unsigned long BSS mdata3; // 0x4 (4) bytes +unsigned long BSS mdata4; // 0x4 (4) bytes +SEPLAYTBL BSS se_request[8]; // 0x60 (96) bytes +unsigned int BSS spu_load_offset; // 0x4 (4) bytes +int BSS vox_rev_on; // 0x4 (4) bytes + +gap gap_800BF148[0xC]; // 12 bytes + +int BSS vox_on_vol; // 0x4 (4) bytes +int BSS sng_status; // 0x4 (4) bytes +unsigned int BSS str_volume; // 0x4 (4) bytes +int BSS str_vox_on; // 0x4 (4) bytes +int BSS str_play_offset; // 0x4 (4) bytes +int BSS str_unload_size; // 0x4 (4) bytes +unsigned int BSS str_status; // 0x4 (4) bytes +int BSS str_read_status; + +gap gap_800BF174[0x0C]; // 16 bytes + +int BSS se_pan_800BF180[8]; // 0x20 (32) bytes +int BSS stop_jouchuu_se; // 0x4 (4) bytes +int BSS dword_800BF1A4; // 0x4 (4) bytes +int BSS dword_800BF1A8; // 0x4 (4) bytes +int BSS str_unplay_size; // 0x4 (4) bytes +unsigned long BSS key_fg; // 0x4 (4) bytes + +gap gap_800BF1B4[0x4]; // 4 bytes + +int BSS se_pan[8]; // 0x20 (32) bytes +int BSS sng_fp; // 0x4 (4) bytes +int BSS str_mute_status; // 0x4 (4) bytes +WAVE_W* BSS wave_header; // 0x4 (4) bytes + +gap gap_800BF1E4[0x4]; // 4 bytes + +int BSS bgm_idx; // 0x4 (4) bytes +unsigned long BSS mtrack; // 0x4 (4) bytes +int BSS se_vol[8]; // 0x20 (32) bytes +int BSS eoffs; // 0x4 (4) bytes + +gap gap_800BF214[0x4]; // 4 bytes + +SpuVoiceAttr BSS sd_blank_attr; // 0x40 (64) bytes +int BSS str_fp; // 0x4 (4) bytes +int BSS sng_fout_fg; // 0x4 (4) bytes +unsigned long BSS keyons; // 0x4 (4) bytes +int BSS str_off_idx; // 0x4 (4) bytes +int BSS str_mono_fg; // 0x4 (4) bytes +int BSS str_fout_fg; // 0x4 (4) bytes +int BSS dword_800BF270; // 0x4 (4) bytes +unsigned int BSS wave_unload_size; // 0x4 (4) bytes +int BSS str_mute_off_idx; +unsigned int BSS dword_800BF27C; // 0x4 (4) bytes +int BSS str_trans_offset; // 4 bytes +char* BSS se_data; // 0x4 (4) bytes +int BSS sd_code_read; // 0x4 (4) bytes +unsigned int BSS se_load_code; // 0x4 (4) bytes +int BSS sng_kaihi_fg; // 0x4 (4) bytes +int BSS wave_data; // 0x4 (4) bytes +int BSS sng_pause_fg; // 0x4 (4) bytes +unsigned long BSS keyoffs; // 0x4 (4) bytes +int BSS str_read_idx; + +gap gap_800BF2A4[0x4]; // 8 bytes + +SOUND_W BSS sound_w[21]; // 0x1164 (4452) bytes +int BSS str_play_idx; // 0x4 (4) bytes +int BSS dword_800C0410; // 0x4 (4) bytes +int BSS str_next_idx; // 0x4 (4) bytes +int BSS str_mute_ctr; // 0x4 (4) bytes +int BSS sng_fadein_fg; // 0x4 (4) bytes +unsigned char *BSS sng_data; // 0x4 (4) bytes + +gap gap_800C0424[0x4]; // 4 bytes + +int BSS sng_load_code; // 0x4 (4) bytes + +gap gap_800C042C[0x4]; // 4 bytes + +int BSS sng_fade_time[14]; // 0x38 (56) bytes +char BSS sd_path[128]; // 0x80 (128) bytes +unsigned int BSS song_end; // 0x4 (4) bytes +int BSS str_fadein_fg; // 0x4 (4) bytes +int BSS str_load_code; // 0x4 (4) bytes +int BSS str_fade_time; // 0x4 (4) bytes +unsigned int BSS sng_play_code; // 0x4 (4) bytes + +gap gap_800C04FC[0x4]; // 4 bytes + +int BSS dword_800C0500; // 0x4 (4) bytes +int BSS str_freq; // 0x4 (4) bytes +char* BSS wave_load_ptr; // 0x4 (4) bytes +int BSS sound_mono_fg; // 0x4 (4) bytes +int BSS sng_syukan_fg; // 0x4 (4) bytes +char* BSS str_trans_buf; // 0x4 (4) bytes +int BSS sng_fout_term; // 0x4 (4) bytes +int BSS str_wave_size; // 0x4 (4) bytes +unsigned char *BSS se_header; // 0x4 (4) bytes +unsigned long BSS keyd; // 0x4 (4) bytes +unsigned int BSS wave_load_code; // 0x4 (4) bytes +unsigned int BSS spu_wave_start_ptr; // 0x4 (4) bytes +WAVE_W *BSS voice_tbl; // 0x4 (4) bytes + +gap gap_800C0534[0x4]; // 4 bytes + +int BSS sng_fade_value[13]; // 0x34 (52) bytes +unsigned char BSS wavs; // 0x2 (2) bytes - padded + +gap gap_800C0570[0x0]; // 0 bytes + +unsigned char *BSS mptr; // 0x4 (4) bytes +int BSS se_rev_on; // 0x4 (4) bytes +unsigned long BSS wave_save_code; // 0x4 (4) bytes +SOUND_W *BSS sptr; // 0x4 (4) bytes +int BSS dword_800C0580; // 0x4 (4) bytes +unsigned int BSS str_fade_value; // 0x4 (4) bytes +char BSS spu_malloc_rec[SPU_MALLOC_RECSIZ * (24 + 1)]; // 0xC8 (200) bytes +unsigned int BSS wave_load_size; // 0x4 (4) bytes +int BSS dword_800C0654; // 0x4 (4) bytes +SPU_TRACK_REG BSS spu_tr_wk[23]; // 0x564 (1380) bytes + +gap gap_800C0BBC[0x4]; // 4 bytes + +int BSS sng_kaihi_time; // 0x4 (4) bytes + +gap gap_800C0BC4[0x4]; // 4 bytes + +int BSS sng_master_vol[13]; // 0x34 (52) bytes +volatile int BSS sd_task_status; // 0x4 (4) bytes + +/* mts.lib (mts_new.obj) */ + +MTS_ITASK *BSS D_800C0C00; // 0x4 (4) bytes +MTS_ITASK *BSS D_800C0C04; // 0x4 (4) bytes + +gap gap_800C0C08[0x8]; // 8 bytes + +signed char BSS mts_semaphore_waiters_800C0C10[MTS_MAX_SEMAPHORE]; // 0x20 (32) bytes +MTS_TASK BSS mts_tasks_800C0C30[MTS_NR_TASK]; // 0x180 (384) bytes +int BSS mts_active_task_800C0DB0; // 0x4 (4) bytes +int BSS mts_ready_tasks_800C0DB4; // 0x4 (4) bytes + +gap gap_800C0DB8[0x8]; // 8 bytes + +unsigned int BSS mts_system_task_stack_800C0DC0[128]; // 0x200 (512) bytes +unsigned int BSS mts_sio_task_stack_800C0FC0[256]; // 0x400 (1024) bytes +volatile int BSS mts_active_task_idx_800C13C0; // 0x4 (4) bytes + +gap gap_800C13C4[0xC]; // 12 bytes + +MTS_ITASK BSS mts_intr_tasks_800C13D0[MTS_NR_INT_TASK]; // 0xA0 (160) bytes + +/* mts.lib (mts_pad.obj) */ + +char BSS param_800C1470[2][6]; // 0xC (12) bytes + +gap gap_800C147C[0x4]; // 4 bytes + +PAD_RECV_BUF BSS padbuf_800C1480[2]; // 0x48 (72) bytes + +gap gap_800C14C8[0x8]; // 8 bytes + +unsigned char BSS sendbuf_800C14D0[2][8]; // 0x10 (16) bytes +MTS_PAD_IN BSS pad_800C14E0[2]; // 0x10 (16) bytes +int BSS pad_state_800C14F0[2]; // 0x8 (8) bytes diff --git a/components/mgs/mgs/source/enemy/action.c b/components/mgs/mgs/source/enemy/action.c new file mode 100644 index 00000000..896e5c6a --- /dev/null +++ b/components/mgs/mgs/source/enemy/action.c @@ -0,0 +1,2094 @@ +#include "enemy.h" + +#include +#include +#include +#include + +#include "common.h" +#include "linkvar.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/item.h" +#include "okajima/bullet.h" +#include "sound/g_sound.h" + +// from takabe/mosaic.h +extern void *NewMosaic( MATRIX *, int, int, int ); + +short ActTable_800C3358[54] = +{ + 0x13, 0x14, 0x0A, 0x16, 0x09, 0x22, 0x23, 0x24, 0x2D, + 0x0D, 0x0C, 0x1A, 0x0F, 0x0E, 0x10, 0x1B, 0x08, 0x2E, + 0x32, 0x15, 0x1E, 0x1F, 0x20, 0x21, 0x25, 0x34, 0x35, + 0x19, 0x05, 0x02, 0x2A, 0x04, 0x27, 0x28, 0x06, 0x07, + 0x26, 0x03, 0x29, 0x11, 0x12, 0x2B, 0x17, 0x18, 0x1C, + 0x1D, 0x00, 0x01, 0x33, 0x11, 0x12, 0x2B, 0x16, 0x0B +}; + +SVECTOR s00a_dword_800C33C4 = { -150, 0, 300 }; +SVECTOR s00a_dword_800C33CC = { 0, -550, 950 }; + +extern ENEMY_COMMAND EnemyCommand; +extern SVECTOR COM_NO_POINT; +extern TOPCOMMAND_STRUCT TOPCOMMAND; +extern int COM_EYE_LENGTH; +extern unsigned int COM_GameStatus; +extern int COM_VibTime; + +extern int GV_NearExp4P(int from, int to); + +/***Inlines***********************************************************************************************/ +static inline void UnsetMode( WatcherWork *work ) +{ + extern short ActTable_800C3358[]; + work->unknown.last_unset = 0; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3358[STANDSTILL], 0, ACTINTERP, 0 ); + + work->action2 = 0; + work->time2 = 0; + work->unknown.last_unset = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; +} + +static inline void UnsetMode2( WatcherWork *work ) +{ + extern short ActTable_800C3358[]; + work->unknown.last_unset = 0; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3358[STANDSTILL], 0, ACTINTERP, 0 ); + + work->action2 = 0; + work->time2 = 0; + work->unknown.last_unset = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; + + if ( work->mosaic ) + { + GV_DestroyOtherActor( work->mosaic ); + work->mosaic = 0; + } + +} + +static inline void SetAction( WatcherWork *work, int n_action, int interp ) +{ + extern short ActTable_800C3358[]; + work->unknown.last_set = n_action ; + GM_ConfigObjectAction( &( work->body ), ActTable_800C3358[n_action], 0, interp ); +} + +static inline void UnsetAction( WatcherWork *work, int n_action ) +{ + extern short ActTable_800C3358[]; + + work->unknown.last_unset = n_action; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3358[n_action], 0, ACTINTERP, 0x3FE ); +} + +static inline void UnsetActionManual( WatcherWork *work, int n_action, int a4 ) +{ + extern short ActTable_800C3358[]; + + work->unknown.last_unset = n_action; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3358[n_action], 0, ACTINTERP, a4 ); +} + +static inline void UnsetAction2( WatcherWork *work ) +{ + extern short ActTable_800C3358[]; + + work->unknown.last_unset = 0; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3358[STANDSTILL], 0, ACTINTERP, 0 ); + GV_DestroyOtherActor( work->subweapon ); +} +/**********************************************************************************************************/ + + +void ActStandStill_800C5C84(WatcherWork* work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + work->act_status |= 1; + + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->target->class |= TARGET_C4 ; + } + + work->vision.length = COM_EYE_LENGTH ; + + if ( time == 0 ) + { + SetAction( work, STANDSTILL, ACTINTERP ); + } + + if ( CheckDamage_800C5424( work ) || CheckPad_800C5A60( work ) ) + { + UnsetMode2( work ) ; + return ; + } + + if ( work->pad.dir >= 0 ) + { + if ( GV_DiffDirAbs( work->control.rot.vy, work->pad.dir ) < 0x100 ) + { + if ( work->pad.mode & 0x1 ) + { + SetAction( work, ACTION2, ACTINTERP ); + } + else + { + SetAction( work, ACTION1, ACTINTERP ); + } + SetMode( work, s00a_command_800C5E48 ); + + } + else + { + work->control.turn.vy = work->pad.dir; + } + } + work->vision.facedir = GV_NearExp4P( work->vision.facedir, work->control.rot.vy ); +} + +void s00a_command_800C5E48( WatcherWork* work, int time ) +{ + int x, z; + int s0; + int interval; + int tmp; + int dist; + CONTROL * ctrl; + int dir; + int field_8E0; + SVECTOR* svec; + + SetTargetClass( work->target, TARGET_FLAG ); + + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->target->class |= TARGET_C4 ; + } + + work->vision.length = COM_EYE_LENGTH ; + work->act_status |= 0x100; + work->control.step = DG_ZeroVector; + + if ( CheckDamage_800C5424( work ) || CheckPad_800C5A60( work ) ) + { + UnsetMode2( work ) ; + return ; + } + + dist = -1; + ctrl = &(work->control ); + dir = work->pad.dir; + field_8E0 = work->unknown.last_set; + svec = work->control.vecs; + + if ( (work->pad.mode & 0x1) && ( work->unknown.last_set != ACTION2 ) ) + { + SetAction( work, ACTION2, ACTINTERP ); + } + + if ( dir >= 0 ) + { + s0 = ctrl->n_touches; + if ( s0 > 0 ) + { + dist = GV_VecDir2( svec ); + + if ( s0 >= 2 ) + { + tmp = GV_VecDir2( &ctrl->vecs[1] ); + if ( GV_DiffDirAbs( dir, tmp ) < GV_DiffDirAbs( dir, dist ) ) + { + dist = tmp; + } + } + } + } + + if ( dir < 0 ) + { + SetMode( work, ActStandStill_800C5C84); + UnsetMode2( work ) ; + return; + } + + if ( time != 1 ) + { + switch ( s00a_command_800C580C( dir, dist ) ) + { + case 1: + dir = (dist - 1024) & 0xFFF; + break; + case 2: + dir = (dist + 1024) & 0xFFF; + break; + } + } + + if ( !work->body.objs->bound_mode ) + { + ctrl->rot.vy = dir; + ctrl->turn.vy = dir; + work->vision.facedir = GV_NearExp4P( work->vision.facedir, work->control.rot.vy ); + } + else + { + ctrl->turn.vy = dir; + work->vision.facedir = work->control.rot.vy; + } + + interval = 0; + + if ( field_8E0 == 1 ) + { + interval = 50; + } + else if ( field_8E0 == 2 ) + { + interval = 100; + } + + x = interval * rsin( dir ); + + if ( x < 0 ) + { + x += 0xFFF; + } + + ctrl->step.vx = x >> 12; + + z = interval * rcos( dir ); + + if ( z < 0 ) + { + z += 0xFFF; + } + + ctrl->step.vz = z >> 12; + + if ( !time ) + { + work->unknown.field_1E = 0; + } +} + +void s00a_command_800C615C( WatcherWork *work, int time ) +{ +} + +void s00a_command_800C6164( WatcherWork *work, int time ) +{ + CONTROL *ctrl; + + ctrl = &( work->control ); + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = COM_EYE_LENGTH ; + + if ( time == 0 ) + { + SetAction( work, ACTION16, ACTINTERP ); + } + + if ( CheckDamage_800C5424( work ) ) + { + return ; + } + + if ( work->body.is_end || !( work->pad.press & 0x20 ) ) + { + SetMode( work, ActStandStill_800C5C84 ); + return; + } + + ctrl->turn.vy = work->sn_dir; + ctrl->step.vx = 0; + ctrl->step.vz = 0; + work->vision.facedir = work->control.rot.vy; +} + +void s00a_command_800C624C( WatcherWork *work, int time ) +{ + CONTROL *ctrl; + + ctrl = &( work->control ); + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = COM_EYE_LENGTH ; + + if ( time == 0 ) + { + SetAction( work, ACTION13, ACTINTERP ); + } + + if ( CheckDamage_800C5424( work ) ) + { + return ; + } + + if ( !( work->pad.press & 0x2000000 ) ) + { + SetMode( work, ActStandStill_800C5C84 ); + return; + } + + ctrl->turn.vy = work->pad.dir; + ctrl->step.vx = 0; + ctrl->step.vz = 0; + work->vision.facedir = work->control.rot.vy; +} + +void s00a_command_800C6320( WatcherWork *work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ) ; + work->vision.length = COM_EYE_LENGTH; + work->unknown.field_1E = 0; + + if ( CheckDamage_800C5424( work ) ) + { + UnsetCameraActCall( ); + COM_GameStatus &= ~COM_ST_DANBOWL ; + printf(" ~COM_ST_DANBOWL 0 !! \n ") ; + return; + } + + if ( !(work->pad.press & SP_DANBOWLKERI) ) + { + if ( !(CheckPad_800C5A60( work )) ) + { + SetMode( work, ActStandStill_800C5C84 ) ; + } + UnsetCameraActCall( ); + COM_GameStatus &= ~COM_ST_DANBOWL ; + printf(" ~COM_ST_DANBOWL 1 !! \n ") ; + return ; + } + + if ( time == 0 ) { + SetMode2( work, ActOverScoutD_800C85DC ) ; + SetAction( work, STANDSTILL, ACTINTERP ) ; + SetCameraActCall( ); + } + + if ( time == 60 ) { + UnsetMode2( work ) ; + SetAction( work, DANBOWLKERI, ACTINTERP ) ; + } + + if ( time == 78 ) { + extern void *NewBoxKeri( MATRIX *, SVECTOR * ) ; + NewBoxKeri( &(GM_PlayerBody->objs[ 0 ].world), &( work->control.mov ) ) ; + } + + if ( time == 100 ) { + //AN_HeadMarkMini( &(GM_PlayerBody->objs->objs[ 1 ].world), 2 ) ; + SetAction( work, DANBOWLPOSE, ACTINTERP ) ; + } + + if ( time == 130 ) { + GM_CurrentItemId = IT_None ; //Actually should be named GM_Item and IT_None + } + + if ( time > 150 ) { + UnsetCameraActCall( ); + work->actend = 1 ; + SetMode( work, ActStandStill_800C5C84 ) ; + COM_GameStatus &= ~COM_ST_DANBOWL ; + printf(" ~COM_ST_DANBOWL 2 !! \n "); + return ; + } + work->control.turn.vy = work->sn_dir; + work->vision.facedir = work->control.rot.vy; +} + + +void s00a_command_800C65A8( WatcherWork* work, int time ) +{ + int press; + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = COM_EYE_LENGTH ; + press = work->pad.press; + + if ( time == 0 ) + { + if ( press & 0x10000 ) + { + SetAction( work, ACTION3, ACTINTERP ) ; + } + else + { + SetAction( work, ACTION5, ACTINTERP ); + } + GM_ConfigMotionAdjust( &( work->body ), work->adjust ) ; + } + + s00a_command_800C5860( work ); + if ( CheckDamage_800C5424( work ) ) + { + return ; + } + + if ( !(press & 0x30000) ) + { + SetMode( work, ActStandStill_800C5C84 ) ; + return ; + } + + if ( press & 0x40000 ) + { + SetModeFields( work, s00a_command_800C6724) ; + return; + } + + if ( press & 0x80000 ) + { + SetModeFields( work, s00a_command_800C67E4) ; + return; + } + + if ( press & 0x100000 ) + { + SetMode( work, ActGrenade_800C67EC ) ; + return; + } + + if ( press & 0x200000 ) + { + SetModeFields( work, s00a_command_800C6A40 ) ; + return; + } + + work->control.turn.vy = work->sn_dir; + work->control.step.vx = 0; + work->control.step.vz = 0; + work->vision.facedir = work->control.rot.vy; +} + +void s00a_command_800C6724( WatcherWork* work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = COM_EYE_LENGTH ; + + if ( time == 0 ) + { + SetAction( work, ACTION4, 0 ) ; + ENE_SetPutChar( work, 3 ) ; + GM_ConfigMotionAdjust( &( work->body ), work->adjust ) ; + } + + s00a_command_800C5860( work ); + work->control.turn.vy = work->sn_dir; + + if ( CheckDamage_800C5424( work ) ) + { + return ; + } + + SetModeFields( work, s00a_command_800C65A8 ); + work->control.step.vx = 0; + work->control.step.vz = 0; +} + +void s00a_command_800C67E4( WatcherWork *work, int time ) +{ + +} + +void ActGrenade_800C67EC( WatcherWork *work, int time ) +{ + int check = 0; + SetTargetClass( work->target, TARGET_FLAG ) ; + work->vision.length = COM_EYE_LENGTH ; + + if ( time == 0 ) + { + extern void *NewGrenadeEnemy( CONTROL *, OBJECT *, int, u_long *, SVECTOR *, int ) ; + + work->subweapon = NewGrenadeEnemy( &(work->control), &(work->body), 9, + &(work->trigger), &GM_PlayerPosition, ENEMY_SIDE ) ; + + if ( GM_PlayerStatus & PLAYER_INTRUDE ) + { + SetAction( work, GRENADE, ACTINTERP ); + } + else + { + SetAction( work, STANDSTILL, ACTINTERP ); + UnsetAction( work, GRENADE ); + } + } + + if ( time > ACTINTERP ) + { + work->trigger |= WEAPON_TAKE ; + } + + if ( time == 17 ) + { + GM_SeSet( &( work->control.mov ), SE_PINNUKI ) ; + } + + if ( time == 45 ) + { + if ( work->unknown.last_set == 7 ) + { + work->trigger |= WEAPON_TRIG2 ; + } + else + { + work->trigger |= WEAPON_TRIG ; + } + } + + if ( CheckDamage_800C5424( work ) ) + { + UnsetAction2( work ); + return ; + } + + if ( work->unknown.last_set == 7 ) + { + if ( work->body.is_end ) + { + check = 1; + } + } + else + { + if (work->body.time2) + { + check = 1; + } + } + + if ( check ) + { + work->actend = 1; + UnsetAction2( work ); + SetMode( work, s00a_command_800C65A8 ); + } +} + +void s00a_command_800C6A40( WatcherWork* work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = COM_EYE_LENGTH ; + work->unknown.field_1E = 0; + + if ( time == 0 ) + { + SetAction( work, ACTION8, 0 ); + } + + if ( time == 3 ) + { + if ( AttackForce_800C58E8( work ) ) + { + GM_SeSet( &( work->control.mov ), SE_HIT ); + } + } + + work->control.turn.vy = work->sn_dir; + + if ( CheckDamage_800C5424( work ) ) + { + return ; + } + + if ( work->body.is_end ) + { + SetMode( work, s00a_command_800C65A8 ); + } + + work->control.step.vx = 0; + work->control.step.vz = 0; +} + +void s00a_command_800C6B24( WatcherWork* work, int time ) +{ + work->vision.length = 0; + + if ( time == 0 ) + { + SetAction( work, ACTION21, ACTINTERP ) ; + } + + if ( CheckDamage_800C5424( work ) ) + { + return ; + } + + SetTargetClass( work->target, TARGET_FLAG ); + + if ( work->body.is_end ) + { + work->actend = 1; + SetMode( work, ActStandStill_800C5C84 ); + } +} + +//Enemey getting flipped over +void s00a_command_800C6BCC( WatcherWork* work, int time ) +{ + CONTROL *ctrl; + TARGET *target; + + target = work->target; + ctrl = &work->control; + work->unknown.field_1E = 0; + work->act_status |= 0x08; + + if ( time == 0 ) + { + work->unknown.field_14 = 6; + SetAction( work, ACTION31, ACTINTERP ); + } + + if ( time - 8 < 12u ) + { + s00a_command_800C59F8( work ); + } + + if ( work->unknown.last_set == 31 ) + { + if ( time == 22 ) + { + if ( ctrl->mov.vy - ctrl->levels[0] < 2000 ) + { + GM_SeSetMode( &ctrl->mov, VO_ENEMY_THROWN, GM_SEMODE_BOMB ) ; + GM_SeSetMode( &ctrl->mov, SE_HIT_FLOOR, GM_SEMODE_BOMB ) ; + ENE_PutBlood( work, 6, 0 ) ; + GM_SetNoise( 0x64, 4, &work->control.mov ) ; + } + //if they fall from a height? + else if ( ctrl->mov.vy - ctrl->levels[0] > 3000 ) + { + work->target->vital = 0; + GM_SeSetMode( &ctrl->mov, VO_ENEMY_THROWN, GM_SEMODE_BOMB ) ; + } + } + if ( work->body.is_end ) + { + if ( !ctrl->grounded ) + { + SetAction( work, ACTION40, ACTINTERP ); + } + else + { + SetMode( work, s00a_command_800C7498 ); + } + } + } + else + { + if ( ctrl->grounded ) + { + GM_SeSetMode( &ctrl->mov, SE_HIT_FLOOR, GM_SEMODE_BOMB ) ; + GM_SetNoise( 0x64, 4, &work->control.mov ) ; + ENE_PutBlood( work, 6, 1 ) ; + + if ( work->target->vital <= 0 ) + { + SetMode( work, s00a_command_800C8054 ); + } + else + { + GM_SeSetMode( &ctrl->mov, VO_ENEMY_THROWN, GM_SEMODE_BOMB ) ; + SetMode( work, s00a_command_800C7498 ); + } + } + } + if ( time < 24 ) + { + ctrl->turn = *target->field_1C; + } + + if ( time < 22 ) + { + work->control.hzd_height = -32767; + } +} + +void s00a_command_800C6EC8( WatcherWork* work ) +{ + CONTROL *ctrl; + SVECTOR svec; + + ctrl = &work->control; + work->control.turn = GM_PlayerControl->rot; + DG_SetPos2(&GM_PlayerPosition, &GM_PlayerControl->rot); + DG_PutVector(&s00a_dword_800C33C4, &svec, 1); + GV_SubVec3(&svec, &ctrl->mov, &work->control.step); + + if ( !( ctrl->map->index & GM_PlayerMap ) ) + { + printf(" map change \n"); + work->control.step = DG_ZeroVector; + } +} + +//enemy being held by snake +void s00a_command_800C6FA8( WatcherWork* work, int time ) +{ + int a0; + int v1; + int s2; + TARGET *target; + + target = work->target; + s2 = work->unknown.last_set; + + work->unknown.field_1E = 0; + work->act_status |= 0x0C; + + if ( time == 0 ) + { + work->field_B5A = target->faint; + GM_SeSet( &work->control.mov, VO_ENEMY_GRABBED ); + } + + if ( work->field_B5A != target->faint ) + { + GM_SeSet( &work->control.mov, VO_ENEMY_GRABBED ); + work->field_B5A = target->faint; + } + + if ( target->damaged & TARGET_POWER ) + { + ENE_PutBlood( work, 5, 0 ); + GM_SeSet( &work->control.mov, VO_ENEMY_GRABBED ); + target->force = DG_ZeroVector; + target->damage = 0; + target->damaged = TARGET_STALE; + + if ( target->vital <= 0 ) + { + v1 = target->a_mode; + if ( v1 == 1 ) + { + work->field_C48 = v1; + } + work->unknown.field_14 = 5; + target->side = ENEMY_SIDE; + SetMode( work, s00a_command_800C78E0 ); + target->captured = 0; + } + } + switch ( GM_PlayerAction ) + { + case 0x7: + case 0xD: + if( s2 != 0x1B && s2 != 0x30 ) + { + target->side = PLAYER_SIDE; + SetAction( work, ACTION27, ACTINTERP ); + work->field_B5C = 0; + } + + if ( work->field_B5C == 0x32 && target->faint > 0 ) + { + SetAction( work, ACTION48, ACTINTERP ); + } + + if ( s2 == 0x30 && !( work->field_B5C & 7 ) ) + { + COM_VibTime = 4; + } + work->control.turn = GM_PlayerControl->rot; + s00a_command_800C6EC8( work ); + work->field_B5C++; + break; + case 0x26: + if ( s2 != 0x1C ) + { + target->side = PLAYER_SIDE; + SetAction( work, ACTION28, ACTINTERP ); + } + work->control.turn = GM_PlayerControl->rot; + s00a_command_800C6EC8( work ); + break; + case 0x27: + work->unknown.field_14 = 4; + target->side = ENEMY_SIDE; + target->vital = 0; + SetMode( work, s00a_command_800C78E0 ); + return; + default: + work->unknown.field_14 = 5; + target->side = ENEMY_SIDE; + SetMode( work, s00a_command_800C78E0 ); + return; + } + + a0 = GM_PlayerPosition.vy - work->control.mov.vy; + if ( a0 < 0 ) + { + a0 = work->control.mov.vy - GM_PlayerPosition.vy; + } + + if ( work->sn_dis > 800 || a0 > 500 ) + { + target->captured = 0; + } + if ( !( work->control.map->index & GM_PlayerMap ) ) + { + target->captured = 0; + } + work->target->class |= ( TARGET_SEEK | TARGET_POWER) ; + work->vision.facedir = work->control.rot.vy; +} + +void s00a_command_800C7354( WatcherWork* work, int time ) +{ + CONTROL *ctrl; + int time_offset; + + ctrl = &work->control; + work->unknown.field_1E = 0; + work->vision.length = 0; + work->act_status |= 0x8; + + if ( time == 0 ) + { + SetAction( work, ACTION32, ACTINTERP ) ; + } + + if ( CheckDamage_800C5424( work ) ) + { + return ; + } + + time_offset = 10; + + if ( time == time_offset ) + { + work->unknown.field_14 = 1; + SetAction( work, ACTION37, ACTINTERP ) ; + } + else + { + SetTargetClass( work->target, TARGET_FLAG ); + } + + if ( time == time_offset + 24 ) + { + GM_SeSet( &ctrl->mov, SE_ENEMY_COLLAPSE ); + } + + if ( time == time_offset + 46 ) + { + GM_SeSet( &ctrl->mov, SE_HIT_FLOOR ); + } + + if ( time >= time_offset + 50 && work->body.is_end ) + { + SetMode( work, s00a_command_800C7498 ) ; + } +} + +//enemy on the floor +void s00a_command_800C7498( WatcherWork* work, int time ) +{ + int x; + SVECTOR svec; + + work->act_status |= 0x20; + + if ( time == 0 ) + { + + if ( work->unknown.field_14 < 3 ) + { + if ( work->unknown.field_14 == 1 ) + { + SetAction( work, ACTION41, ACTINTERP ); + } + else + { + SetAction( work, ACTION39, ACTINTERP ); + } + } + else + { + SetAction( work, ACTION40, ACTINTERP ); + } + + if ( work->target->faint <= 0 ) + { + x = work->control.mov.vx % 60; + work->field_B5A = abs(x) + 90; + + svec.vx = work->body.objs->objs[6].world.t[0]; + svec.vy = work->body.objs->objs[6].world.t[1]; + svec.vz = work->body.objs->objs[6].world.t[2]; + AN_Piyopiyo( &svec ); + } + else + { + x = work->control.mov.vx % 30; + work->field_B5A = abs(x) + 10; + } + } + + if (s00a_command_800C513C( work ) ) + { + SetMode( work, s00a_command_800C76BC ); + } + else if ( work->field_B5A < time ) + { + if ( work->target->faint <= 0 ) + { + work->target->faint = work->param_faint; + } + + SetMode( work, s00a_command_800C76C4 ); + } +} + +void s00a_command_800C76BC( WatcherWork* work, int time ) +{ + +} + +//enemy getting up +void s00a_command_800C76C4( WatcherWork* work, int time ) +{ + work->unknown.field_1E = 0; + work->act_status |= 0x08; + + if ( time == 0 ) + { + if ( work->unknown.field_14 < 3 ) + { + if ( work->field_B5A >= 0x5A ) + { + SetAction( work, ACTION44, ACTINTERP ); + } + else + { + SetAction( work, ACTION42, ACTINTERP ); + } + } + else + { + if ( work->field_B5A >= 0x5A ) + { + SetAction( work, ACTION45, ACTINTERP ); + } + else + { + SetAction( work, ACTION43, ACTINTERP ); + } + } + } + + if ( work->body.is_end ) + { + SetMode( work, ActStandStill_800C5C84 ); + } +} + +void s00a_command_800C77C8( WatcherWork* work, int time ) +{ + TARGET* target; + + work->unknown.field_1E = 0; + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = COM_EYE_LENGTH ; + work->act_status |= 0x08; + + if ( CheckDamage_800C5424( work ) ) + { + return ; + } + + target = work->target; + if (time == 0 ) + { + SetAction( work, ACTION15, ACTINTERP ) ; + + if ( target->a_mode == 3 ) + { + GM_SeSet( &work->control.mov, SE_PUNCH_HIT ); + ENE_PutFog( work ); + } + else + { + ENE_PutBlood( work, 5, 0 ); + } + GM_SeSetMode( &work->control.mov, VO_ENEMY_PUNCHED, GM_SEMODE_BOMB ); + } + + if ( work->body.is_end) + { + SetMode( work, ActStandStill_800C5C84 ) ; + } + + work->control.step.vx = 0; + work->control.step.vz = 0; +} + +void s00a_command_800C78E0( WatcherWork *work, int time ) +{ + CONTROL* ctrl; + WatcherUnk *unk; + + unk = &work->unknown; + work->unknown.field_1E = 0; + work->act_status |= 0x8; + work->control.step = work->target->force; + + ctrl = &work->control; + if ( time == 0 ) + { + switch( unk->field_14 ) + { + case 0: + GM_SeSet( &ctrl->mov, SE_PUNCH_HIT ); + SetAction( work, ACTION34, ACTINTERP ); + GM_SeSetMode( &ctrl->mov, VO_ENEMY_THROWN, GM_SEMODE_BOMB ); + ENE_PutBlood( work, 5, 0 ); + work->field_B5A = 17; + break; + case 1: + SetAction( work, ACTION37, ACTINTERP ); + if ( work->target->vital <= 0 ) + { + if ( GM_CurrentWeaponId == WP_Rifle ) + { + ENE_PutBlood( work, 6, 2 ); + } + else + { + ENE_PutBlood( work, 6, 1 ); + } + GM_SeSetMode( &ctrl->mov, VO_ENEMY_KILLED, GM_SEMODE_BOMB ); + work->field_B5A = 46; + } + else + { + ENE_PutBlood( work, 5, 0 ); + GM_SeSetMode( &ctrl->mov, VO_ENEMY_THROWN, GM_SEMODE_BOMB ); + if ( work->target->a_mode == 3 ) + { + GM_SeSet( &work->control.mov, SE_PUNCH_HIT ); + } + work->field_B5A = 46; + } + break; + case 3: + GM_SeSetMode( &ctrl->mov, VO_ENEMY_THROWN, GM_SEMODE_BOMB ); + SetAction( work, ACTION35, ACTINTERP ); + ENE_PutBlood( work, 5, 0 ); + work->field_B5A = 17; + break; + case 2: + GM_SeSetMode( &ctrl->mov, VO_ENEMY_THROWN, GM_SEMODE_BOMB ); + SetAction( work, ACTION36, ACTINTERP ); + ENE_PutBlood( work, 5, 0 ); + work->field_B5A = 37; + break; + case 4: + GM_SeSet( &ctrl->mov, VO_ENEMY_SNAPPED ); + SetAction( work, ACTION29, ACTINTERP ); + work->field_B5A = 67; + break; + case 5: + SetAction( work, ACTION30, ACTINTERP ); + work->field_B5A = 15; + break; + } + } + + switch( unk->field_14 ) + { + case 0: + if ( time < 12 ) + { + ctrl->turn.vy += 170; + } + + if ( time - 7 < 23u ) + { + s00a_command_800C59F8( work ); + } + + if ( time < 20 ) + { + work->control.hzd_height = -32767; + } + break; + case 1: + if ( time == 24 ) + { + GM_SeSet( &ctrl->mov, SE_ENEMY_COLLAPSE ); + } + break; + case 3: + if ( time - 7 < 23u ) + { + s00a_command_800C59F8( work ); + } + if ( time < 15 ) + { + ctrl->hzd_height = -32767; + } + break; + case 2: + if ( time - 7 < 23u ) + { + s00a_command_800C59F8( work ); + } + if ( time < 20 ) + { + ctrl->hzd_height = -32767; + } + break; + case 4: + case 5: + work->act_status |= 0x04; + break; + } + + if ( time > 16 && ctrl->grounded ) + { + ctrl->step = DG_ZeroVector; + } + + if ( time == work->field_B5A ) + { + if (ctrl->mov.vy - ctrl->levels[0] < 2000) + { + GM_SeSetMode( &ctrl->mov, SE_HIT_FLOOR, GM_SEMODE_BOMB ) ; + GM_SetNoise( 0x64, 4, &ctrl->mov ) ; + ENE_PutBlood( work, 6, 0 ) ; + } + else + { + if (ctrl->mov.vy - ctrl->levels[0] > 3000) + { + work->target->vital = 0; + SetMode( work, s00a_command_800C7E28 ); + return; + } + } + } + + if ( work->body.is_end ) + { + work->unknown.field_1E = 1; + work->target->force = DG_ZeroVector; + if ( work->target->vital <= 0 ) + { + SetMode( work, s00a_command_800C8054 ); + } + else + { + SetMode( work, s00a_command_800C7498 ); + } + } +} + +void s00a_command_800C7E28( WatcherWork* work, int time ) +{ + CONTROL *ctrl; + + ctrl = &work->control; + work->unknown.field_1E = 0; + work->act_status |= 0x8; + + if ( time == 0 && work->unknown.field_14 != 2 ) + { + GM_SeSetMode( &ctrl->mov, VO_ENEMY_KILLED, GM_SEMODE_BOMB ); + } + + if ( time > 16 && ctrl->grounded ) + { + ctrl->step = DG_ZeroVector; + } + + if ( work->unknown.last_set < 39 ) + { + ctrl->step = work->target->force; + if ( work->body.is_end ) + { + if ( work->unknown.field_14 < 3 ) + { + if ( work->unknown.field_14 == 1 ) + { + SetAction( work, ACTION41, ACTINTERP ); + } + else + { + SetAction( work, ACTION39, ACTINTERP ); + } + } + else + { + SetAction( work, ACTION40, ACTINTERP ); + } + } + } + else + { + if ( !ctrl->n_touches ) + { + ctrl->step = work->target->force; + } + + if ( ctrl->grounded ) + { + work->unknown.field_1E = 1; + work->target->force = DG_ZeroVector; + GM_SeSetMode( &ctrl->mov, SE_HIT_FLOOR, GM_SEMODE_BOMB ); + ENE_PutBlood( work, 6, 1 ); + SetMode( work, s00a_command_800C8054 ); + } + } + ctrl->hzd_height = -32767; +} + +//enemy dead +void s00a_command_800C8054( WatcherWork *work, int time ) +{ + work->act_status |= 0x40; + if ( time == 0 ) + { + if ( work->unknown.field_14 < 3 ) + { + if ( work->unknown.field_14 == 1 ) + { + SetAction( work, ACTION51, ACTINTERP ); + } + else + { + SetAction( work, ACTION49, ACTINTERP ); + } + } + else + { + SetAction( work, ACTION50, ACTINTERP ); + } + GM_ConfigControlAttribute( &work->control, 0 ); + work->alert_level = 0; + work->sn_dis = 30000; + GM_TargetBody( work->target, NULL ); + } + + if ( time == 4 && TOPCOMMAND.mode == 0 ) + { + ENE_PutItem( work ); + } + + if ( time & 2 ) + { + work->visible = 0; + } + else + { + work->visible = 1; + } + + if ( time > 8 ) + { + SetMode( work, s00a_command_800C818C ); + } +} + +//after enemy dies +void s00a_command_800C818C( WatcherWork *work, int time ) +{ + work->act_status |= 0x40; + + if ( time == 0 ) + { + work->visible = 0; + work->control.mov = COM_NO_POINT; + EnemyCommand.field_0xC8[ work->field_B78 ].field_04 = 1; + + if ( !work->field_C48 ) + { + GM_TotalEnemiesKilled++; + } + + if ( work->field_C3C >= 0 ) + { + GCL_ExecProc( work->field_C3C, NULL ); + } + } + + if ( EnemyCommand.field_0xC8[ work->field_B78 ].field_04 == 2 ) + { + s00a_command_800C55B0( work ); + printf("enemy reset max=%d num=%d \n", EnemyCommand.reset_enemy_max, EnemyCommand.reset_enemy_num); + SetMode( work, ActStandStill_800C5C84) ; + } +} + +void s00a_command_800C82B0( WatcherWork *work ) +{ + short v0; + int time_prev; + ACTION action; + CONTROL *ctrl; + WatcherUnk *unk; + + work->trigger = 0; + work->vision.length = 0; + work->target->class = TARGET_AVAIL; + + action = work->action; + ctrl = &work->control; + + work->actend = 0; + work->act_status = 0; + work->field_C30 = 0; + + ctrl->height = work->body.height; + ctrl->hzd_height = ctrl->levels[0] + 750; + + unk = &work->unknown; + + time_prev = work->time; + work->time++; + + if ( !action ) + { + action = ActStandStill_800C5C84; + work->action = ActStandStill_800C5C84; + } + + action( work, time_prev ); + action = work->action2; + + if ( action ) + { + time_prev = work->time2; + work->time2++; + action( work, time_prev ); + } + + if ( !unk->field_1E ) + { + ctrl->r_sphere = GV_NearExp2( ctrl->r_sphere, unk->field_1C ); + } + else + { + ctrl->r_sphere = -1; + } + + if ( work->target->class & TARGET_POWER ) + { + work->hom->flag = TRUE; + } + else + { + work->hom->flag = FALSE; + } + + if ( unk->field_04 < 0 && ctrl->grounded ) + { + unk->field_04 = 0; + } + + v0 = unk->field_04 - 16; + unk->field_04 = v0; + ctrl->step.vy = v0; + + if ( work->mark_time > 0 ) + { + work->mark_time--; + } +} + +void s00a_command_800C841C( WatcherWork* work, int time) +{ + if ( time == 0 ) + { + UnsetAction( work, ACTION9 ); + } + + if ( !( work->pad.press & 1 ) ) + { + UnsetMode2( work ); + } + else + { + if ( time < 4 ) + { + work->vision.facedir = ( work->control.rot.vy - ( time * 256 ) ) & 0xFFF; + } + else + { + work->vision.facedir = ( work->control.rot.vy - 1024 ) & 0xFFF; + } + } +} + +void s00a_command_800C84FC( WatcherWork* work, int time) +{ + if ( time == 0 ) + { + UnsetAction( work, ACTION10 ); + } + + if ( !( work->pad.press & 2 ) ) + { + UnsetMode2( work ); + } + else + { + if ( time < 4 ) + { + work->vision.facedir = ( work->control.rot.vy + ( time * 256 ) ) & 0xFFF; + } + else + { + work->vision.facedir = ( work->control.rot.vy + 1024 ) & 0xFFF; + } + } +} + +void ActOverScoutD_800C85DC( WatcherWork* work, int time ) +{ + work->vision.length = 3000; + + if ( time == 0 ) + { + UnsetAction( work, ACTION11 ); + } + + if ( !( work->pad.press & 4 ) ) + { + UnsetMode2( work ); + } +} + +void s00a_command_800C8688( WatcherWork* work, int time ) +{ + work->vision.length = 3000; + + if ( time == 0 ) + { + UnsetAction( work, ACTION14 ); + } + + if ( !( work->pad.press & 0x2000 ) ) + { + UnsetMode2( work ); + } +} + +void s00a_command_800C8734( WatcherWork* work, int time ) +{ + work->vision.length = 0; + + if ( time == 0 ) + { + UnsetAction( work, ACTION24 ); + GM_SeSet( &work->control.mov, VO_ENEMY_SNEEZE ); + } + + if ( work->body.time2 || !( work->pad.press & 0x40 ) ) + { + work->pad.time = 0; + UnsetMode2( work ); + } +} + +void s00a_command_800C87FC( WatcherWork* work, int time ) +{ + work->vision.length = 0; + + if ( time == 0 ) + { + UnsetAction( work, ACTION22 ); + } + + if ( time == 60 ) + { + GM_SeSet( &work->control.mov, VO_ENEMY_YAWN ); + } + + if ( work->body.time2 || !( work->pad.press & 0x80 ) ) + { + work->pad.time = 0; + UnsetMode2( work ); + } +} + +void s00a_command_800C88D8( WatcherWork* work, int time ) +{ + if ( time == 0 ) + { + UnsetAction( work, ACTION23 ); + } + + if ( work->body.time2 || !( work->pad.press & 0x400 ) ) + { + work->pad.time = 0; + UnsetMode2( work ); + } +} + +void s00a_command_800C8990( WatcherWork* work, int time ) +{ + work->vision.length = 0; + + if ( time == 0 ) + { + UnsetAction( work, ACTION19 ); + } + + if ( time == 90 ) + { + GM_SeSet( &work->control.mov, VO_ENEMY_YAWN ); + } + + if ( work->body.time2 || !( work->pad.press & 0x800 ) ) + { + work->pad.time = 0; + UnsetMode2( work ); + } +} + +void s00a_command_800C8A6C( WatcherWork *work, int time ) +{ + SVECTOR mov; + work->vision.length = 0; + + if ( time == 0 ) + { + UnsetAction( work, ACTION19 ); + } + + if ( time == 90 ) + { + GM_SeSet( &work->control.mov, VO_ENEMY_YAWN ); + } + + if ( time == 110 ) + { + UnsetAction( work, ACTION20 ); + } + + if ( time > 110 ) + { + work->act_status |= 0x10; + if ( work->m_ctrl.info2.frame == 30 ) + { + mov = work->control.mov; + mov.vy += 500; + AN_Sleep( &mov ); + GM_SeSet( &work->control.mov, VO_ENEMY_SNORE ); + + if ( work->sn_dis < 1000 && ( GM_PlayerStatus & PLAYER_WATCH ) ) + { + int res = GV_RandU( 12 ); + if ( res > 10 ) + { + work->pad.sound = 240; + } + } + } + if ( work->pad.press & 0x1000 ) + { + SetMode( work, s00a_command_800C6B24 ); + UnsetMode2( work ); + return; + } + } + + if ( !( work->pad.press & 0x200 ) ) + { + UnsetMode2( work ); + } +} + +extern void AN_Unknown_800C3B7C( MATRIX *matrix ); + +void s00a_command_800C8C58( WatcherWork* work ) +{ + MATRIX mat; + + DG_SetPos( &work->body.objs->objs[6].world ); + DG_MovePos( &s00a_dword_800C33CC ); + ReadRotMatrix( &mat ); + AN_Unknown_800C3B7C( &mat ); +} + +void s00a_command_800C8C98( WatcherWork *work, int time ) +{ + work->vision.length = 3000; + work->act_status |= 0x80; + + if ( time == 0 ) + { + UnsetAction( work, ACTION25 ); + if ( work->mosaic == NULL ) + { + work->mosaic = NewMosaic(&work->body.objs->objs[0].world, 300, 4, -250 ); + } + GM_SeSet( &work->control.mov, 0xB9 ); + } + + if ( time > 30 ) + { + GV_RandU( 4 ); + if ( time == ( ( time / 5 ) * 5 ) ) + { + GM_SeSet( &work->control.mov, 0xB1 ); + } + if ( (time & 3) && time > 45 ) + { + s00a_command_800C8C58( work ); + } + } + + if ( !( work->pad.press & 0x800000 ) ) + { + UnsetMode2( work ); + } +} + +void s00a_command_800C8DF8( WatcherWork *work, int time ) +{ + work->vision.length = 3000; + + if ( time == 0 ) + { + UnsetAction( work, ACTION25 ); + if ( work->mosaic == NULL ) + { + work->mosaic = NewMosaic(&work->body.objs->objs[0].world, 300, 4, -250 ); + } + } + + if ( time == 186 ) + { + UnsetActionManual( work, ACTION26, 0xFFFF ); + } + + if ( time == 320 ) + { + GM_SeSet( &work->control.mov, 0xB9 ); + } + + //? + if ( time == 0 || time == 4 || time == 8 || time == 12 || + time == 20 || time == 28 || time == 36 || time == 48 || + time == 60 || time == 120 || time == 122 || time == 124 || + time == 126 || time == 180 || time == 182 || time == 184 || + time == 186 ) + { + GM_SeSet( &work->control.mov, 0xB1 ); + s00a_command_800C8C58( work ); + } + + if ( ( time > 186 && work->body.time2 ) || !( work->pad.press & 0x1000000 ) ) + { + GM_SeSet( &work->control.mov, 0xB4 ); + work->pad.time = 0; + UnsetMode2( work ); + } +} + +SVECTOR s00a_dword_800C33D4 = { 0, 0, 100 }; +SVECTOR s00a_dword_800C33DC = { -1024, 0, 0 }; +SVECTOR s00a_dword_800C33E4 = { 0, 0, 100 }; +SVECTOR s00a_dword_800C33EC = { -1024, 0, 0 }; + +#ifdef __psyz +/* patched in place at runtime (digits spliced into the message), so it + * cannot live in read-only memory on a target with a real .rodata */ +char *s00a_off_800C33F4[3] = { +#else +const char *s00a_off_800C33F4[3] = { +#endif + "RATION", + "SOCOM/BULLET * 12", + "FA-MAS/BULLET * 25" +}; + +void ENE_PutSound( WatcherWork* work ); +void ENE_PutBreath( WatcherWork *work, int arg1 ); +void ENE_PutLSight( WatcherWork* work ); +void ENE_PutBulletEx( WatcherWork *work ); + +void *PutFuncList_800C3400[4] = { + ENE_PutSound, + ENE_PutBreath, + ENE_PutLSight, + ENE_PutBulletEx +}; + +SVECTOR s00a_dword_800C3410 = { 5, -500, 80, 0 }; + +extern short s00a_dword_800E0F12; + +extern void AN_BulletSmoke( MATRIX *, int ); +extern void AN_FamasFlash( MATRIX * ); +extern void NewBlood( MATRIX *, int ); +extern void NewLSight( SVECTOR *from, SVECTOR *to, int color ) ; +extern void AN_Breath( MATRIX * ); + +void ENE_PutBlood( WatcherWork* work, int obj_idx, int count ) +{ + MATRIX mat; + DG_SetPos(&work->body.objs->objs[obj_idx].world); + DG_MovePos( &s00a_dword_800C33D4 ); + DG_RotatePos( &s00a_dword_800C33DC ); + ReadRotMatrix( &mat ); + NewBlood( &mat, count ); +} + +void ENE_PutFog( WatcherWork* work ) +{ + MATRIX mat; + SVECTOR svec; + + DG_SetPos( &work->body.objs->objs[1].world ); + DG_MovePos( &s00a_dword_800C33E4 ); + DG_RotatePos( &s00a_dword_800C33EC ); + ReadRotMatrix( &mat ); + + svec.vx = mat.t[ 0 ]; + svec.vy = mat.t[ 1 ]; + svec.vz = mat.t[ 2 ]; + + AN_Fog( &svec ); +} + +void ENE_PutItem( WatcherWork *work ) +{ + int a1; + int a2; + int rand; + SVECTOR svec; + CONTROL *ctrl; + Item_Info item; + svec = work->unknown.field_0C; + rand = 8; + ctrl = &work->control; + svec.vx += GV_RandU( rand ); + svec.vy += 100; + rand = GV_RandU( rand ); + svec.vz += rand; + + switch ( work->local_data ) + { + case 0: + rand = GV_RandU( 4 ); + break; + case 1: + rand = 3; + break; + case 2: + rand = 0; + break; + case 3: + rand = 1; + break; + case 4: + rand = 2; + break; + } + + a1 = 1; + switch ( rand ) + { + default: + break; + case 3: + return; + case 0: + //ration + item.type = 4; + item.id = IT_Ration; + item.num = 1; + item.message = s00a_off_800C33F4[0]; + break; + case 1: + //socom + if ( GM_SocomFlag < 0 ) + { + item.type = 4; + item.id = IT_Ration; + item.num = 1; + item.message = s00a_off_800C33F4[0]; + } + else + { + //Socom bullets + item.type = 2; + item.id = WP_Socom; + if ( work->local_data2 == 0 ) + { + item.num = 12; + item.message = s00a_off_800C33F4[1]; + } + else + { + a1 = 9; + if ( work-> local_data2 > 99 ) + { + a2 = a1; + } + else + { + a1 = work->local_data2 / 10; + a2 = work->local_data2 % 10; + } + if ( a1 == 0 ) + { + s00a_off_800C33F4[1][15] = 0x20; + } + else + { + s00a_off_800C33F4[1][15] = a1 + 0x30; + } + s00a_off_800C33F4[1][16] = a2 + 0x30; + item.message = s00a_off_800C33F4[1]; + item.num = work->local_data2; + } + } + break; + case 2: + //famas + if ( GM_FamasFlag < 0 || GM_DifficultyFlag < 0 ) + { + item.type = 4; + item.id = IT_Ration; + item.num = 1; + item.message = s00a_off_800C33F4[0]; + } + else + { + item.type = 2; + item.id = WP_Famas; + item.num = 25; + item.message = s00a_off_800C33F4[2]; + } + break; + } + item.time = 450; + if ( work->param_item == 1 ) + { + NewItemPut( &ctrl->mov, &svec, &item ); + } +} + +void ENE_PutMark( WatcherWork *work, int mark ) +{ + MATRIX *mat; + if ( !( work->control.map->index & GM_PlayerMap ) ) + { + return; + } + + mat = &work->body.objs->objs[6].world; + if( mark == 0 ) + { + s00a_command_800CEC40( &work->control.mov , 0x10 ); + } + + if ( work->mark_time ) + { + GV_DestroyOtherActor( work->mark ); + } + + work->mark = AN_Unknown_800CA1EC( mat , mark ) ; + work->mark_time = 30; +} + +void ENE_PutSound( WatcherWork* work ) +{ + int a1, a3; + int a2; + int v1; + if ( !EnemyCommand.field_0x17A ) return; + + a3 = work->unknown.last_set; + a2 = work->m_ctrl.info1.frame; + + v1 = ( ( work->field_B78 % 4 ) * 2 ) + 0xA0; + a1 = ( ( work->field_B78 % 4 ) * 2 ) + 0xA1; + + if( a3 == 1 ) + { + if ( a2 == 22 ) + { + GM_SeSet( &work->control.mov, a1 ); + } + else if ( a2 == 11 ) + { + GM_SeSet( &work->control.mov, v1 ); + } + } + else if ( a3 == 2 ) + { + if ( a2 == 16 ) + { + GM_SeSet( &work->control.mov, a1 ); + } + else if ( a2 == 8 ) + { + GM_SeSet( &work->control.mov, v1 ); + } + } +} + +void ENE_PutBreath( WatcherWork *work, int arg1 ) +{ + int frame; + if ( EnemyCommand.mode == TOP_COMM_ALERT ) + { + return; + } + + if ( !( work->control.map->index & GM_PlayerMap ) ) + { + return; + } + + if ( work->unknown.last_unset == 20 ) + { + frame = work->m_ctrl.info2.frame; + if ( frame == 31 ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } + else if ( work->unknown.last_unset == 22 ) + { + frame = work->m_ctrl.info2.frame; + if ( ( frame == 15 ) || ( frame == 35 ) || ( frame == 50 ) || ( frame == 60 ) || + ( frame == 70 ) || ( frame == 74 ) || ( frame == 78 ) ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } + else if ( work->unknown.last_unset == 19 ) + { + frame = work->m_ctrl.info2.frame; + if ( ( frame == 30 ) || ( frame == 40 ) || ( frame == 50 ) || ( frame == 60 ) || + ( frame == 70 ) || ( frame == 80 ) || ( frame == 90 ) || ( frame == 95 ) || + ( frame == 100 ) || ( frame == 105 ) ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } + else + { + if ( ( GV_Time % 64 ) == ( work->field_B78 * 16 ) ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } +} + +void ENE_PutLSight( WatcherWork* work ) +{ + if ( work->vision.field_B92 == 2 ) + { + NewLSight( &GM_PlayerPosition, &work->control.mov, 0x00008F ); + } +} + +void ENE_PutBulletEx( WatcherWork *work ) +{ + int damage; + MATRIX* mat; + SVECTOR svec; + MATRIX local_mat; + + svec = DG_ZeroVector; + svec.vz = GV_RandU( 128 ); + mat = &work->body.objs->objs[4].world; + + DG_SetPos( mat ); + DG_MovePos( &s00a_dword_800C3410 ); + DG_RotatePos( &svec ); + + svec.vx = GV_RandS( 16 ) + 1024; + svec.vz = 0; + + DG_RotatePos( &svec ); + ReadRotMatrix( &local_mat ); + + switch ( GM_DifficultyFlag ) + { + case 1: + damage = 96; + break; + case 2: + case 3: + damage = 128; + break; + default: + damage = 64; + break; + } + + if ( GV_Time & 3 ) + { + NewBulletEx( BULLET_RECOILSPARK, &local_mat, 2, 1, 0, 0xA, damage, 0x2710, 0x2EE); + } + else + { + NewBulletEx( BULLET_RECOILSPARK | BULLET_NO_HZD, &local_mat, 2, 1, 0, 0xA, damage, 0x2710, 0x2EE); + } + + GM_SeSetMode( &work->control.mov, SE_ENEMY_SHOT, GM_SEMODE_BOMB ); + AN_BulletSmoke( mat, 0 ); + AN_FamasFlash( &local_mat ); + ENE_ClearPutChar( work, ENE_PutBulletEx ); +} + +int ENE_SetPutChar( WatcherWork *work, int idx ) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( work->field_B00[ i ] == NULL ) + { + work->field_B00[ i ] = PutFuncList_800C3400[ idx ]; + return 1; + } + } + return 0; +} + +int ENE_ClearPutChar( WatcherWork *work, void *func ) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( work->field_B00[ i ] == func ) + { + work->field_B00[ i ] = 0; + return 1; + } + } + return 0; +} + +void ENE_ExecPutChar( WatcherWork* work ) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( work->field_B00[i] ) + { + PUTFUNC func = work->field_B00[i]; + func( work ); + } + } +} diff --git a/components/mgs/mgs/source/enemy/asiato.c b/components/mgs/mgs/source/enemy/asiato.c new file mode 100644 index 00000000..bc88002b --- /dev/null +++ b/components/mgs/mgs/source/enemy/asiato.c @@ -0,0 +1,459 @@ +#include "asiato.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libhzd/libhzd.h" +#include "game/game.h" +#include "strcode.h" + +extern CONTROL *GM_WhereList[96]; + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_PREV + +#define SEGMENT_ATR ( HZD_SEG_NO_NAVIGATE ) + +typedef struct _Work +{ + GV_ACT actor; + DG_PRIM *prim; + DG_TEX *tex; + SVECTOR pos[4]; + int field_48; + int field_4C; + int field_50; +} Work; + +/*---------------------------------------------------------------------------*/ + +int SECTION(".bss") NearAsiatoDist; +int SECTION(".bss") s00a_dword_800E0F94; +SVECTOR SECTION(".bss") AsiatoPositions[49]; + +/*---------------------------------------------------------------------------*/ + +static void AsiatoChar_ShadePacks(DG_PRIM *prim, DG_TEX *tex, int r, int g, int b) +{ + POLY_FT4 *poly; + + poly = prim->packs[0]; + setRGB0(poly, r, g, b); + + poly = prim->packs[1]; + setRGB0(poly, r, g, b); +} + +static void AsiatoChar_SetPos(SVECTOR *vec, short x, short y, short z) +{ + vec->vx = x; + vec->vy = y; + vec->vz = z; +} + +static void AsiatoChar_Act(Work *work) +{ + int shade; + if (++work->field_48 > 390) + { + GV_DestroyActor(&work->actor); + } + shade = 48 - work->field_48 * 48 / 690; + AsiatoChar_ShadePacks(work->prim, work->tex, shade, shade, shade); +} + +static void AsiatoChar_InitPacks(POLY_FT4 *poly, DG_TEX *tex, int abr, int r, int g, int b) +{ + setPolyFT4(poly); + setRGB0(poly, r, g, b); + + if (abr < 4) + { + setSemiTrans(poly, 1); + DG_SetPacketTexture4(poly, tex); + poly->tpage = (poly->tpage & ~0x60) | (abr << 5); + } + else + { + DG_SetPacketTexture4(poly, tex); + } +} + +static int AsiatoChar_GetResources(Work *work, MATRIX *world, int arg2, int vy) +{ + SVECTOR svec1, svec2; + DG_PRIM *prim; + DG_TEX *tex; + + AsiatoChar_SetPos(&svec1, 0, (*GM_WhereList)->rot.vy, 0); + if (arg2 == 1) + { + AsiatoChar_SetPos(&work->pos[1], -70, 0, -140); + AsiatoChar_SetPos(&work->pos[0], 70, 0, -140); + AsiatoChar_SetPos(&work->pos[3], -70, 0, 140); + AsiatoChar_SetPos(&work->pos[2], 70, 0, 140); + } + else + { + AsiatoChar_SetPos(&work->pos[0], -70, 0, -140); + AsiatoChar_SetPos(&work->pos[1], 70, 0, -140); + AsiatoChar_SetPos(&work->pos[2], -70, 0, 140); + AsiatoChar_SetPos(&work->pos[3], 70, 0, 140); + } + + prim = GM_MakePrim(DG_PRIM_POLY_FT4, 1, &work->pos[0], NULL); + work->prim = prim; + if (prim != NULL) + { + svec2.vx = world->t[0]; + svec2.vy = world->t[1]; + svec2.vz = world->t[2]; + svec2.vy = vy; + work->tex = tex = DG_GetTexture(PCX_ASIATO); + if (tex) + { + AsiatoChar_InitPacks(prim->packs[0], tex, 2, 48, 48, 48); + AsiatoChar_InitPacks(prim->packs[1], tex, 2, 48, 48, 48); + DG_SetPos2(&svec2, &svec1); + DG_PutPrim(work->prim); + return 0; + } + } + return -1; +} + +static void AsiatoChar_Die(Work *work) +{ + GM_FreePrim(work->prim); + + AsiatoPositions[work->field_4C].pad = 0; + AsiatoPositions[48].vy--; +} + +/*---------------------------------------------------------------------------*/ + +void *NewAsiatoChar(MATRIX *world, int arg1, int arg2, int arg3, int arg4) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, AsiatoChar_Act, AsiatoChar_Die, "asiato.c"); + work->field_4C = arg3; + if (AsiatoChar_GetResources(work, world, arg1, arg2) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + work->field_48 = arg4; + } + return (void *)work; +} + +/*---------------------------------------------------------------------------*/ + +void AsiatoPos(int idx, SVECTOR *out) +{ + *out = AsiatoPositions[idx]; +} + +int NextAsiato(HZD_HDL *hdl, int idx, SVECTOR *svec2) +{ + SVECTOR *vec; + + if (++idx >= 48) + { + idx = 0; + } + + vec = &AsiatoPositions[idx]; + if (GV_DiffVec3(svec2, vec) >= 5000) + { + return -1; + } + + if (HZD_OnlineHazardCheck(hdl, svec2, vec, HZD_CHK_ALL, SEGMENT_ATR)) + { + return -1; + } + + if (vec->pad == 1) + { + return idx; + } + + return -1; +} + +void CleanAsiato(void) +{ + int i; + + for (i = 0; i < 48; i++) + { + AsiatoPositions[i].pad = 0; + } +} + +int NearAsiato(void) +{ + return NearAsiatoDist; +} + +int SearchNearAsiato( HZD_HDL *hzd, SVECTOR *mov, int facedir, int vision_unk, int length ) +{ + int i; + int len; + int max_len; + SVECTOR svec; + + int s4; + max_len = 100000000; + + s4 = 0; + for ( i = 0; i < 48 ; i++ ) + { + if (AsiatoPositions[i].pad == 1 ) + { + GV_SubVec3( &AsiatoPositions[i], mov, &svec ); + svec.vy = 0; + len = GV_VecLen3( &svec ); + + if ( len < max_len && len < length && GV_DiffDirAbs( facedir, GV_VecDir2(&svec) ) < vision_unk && + !HZD_OnlineHazardCheck(hzd, mov, &AsiatoPositions[i], HZD_CHK_ALL, SEGMENT_ATR) ) + { + max_len = len; + s4 = i; + } + } + } + + if ( max_len == 100000000 ) + { + NearAsiatoDist = -1; + return -1; + } + + NearAsiatoDist = s4; + return s4; +} + +static int asiato_800D1500( HZD_HDL *hzd, SVECTOR *pos, int name ) +{ + int i; + int test; + HZD_TRG *triggers; + + triggers = hzd->grp->triggers; + + for ( i = 0 ; i < hzd->grp->n_triggers ; i++ ) + { + if ( triggers->trap.name_id == name ) + { + test = pos->vx; + if ( test >= triggers->trap.b1.x && triggers->trap.b2.x >= test ) + { + test = pos->vz; + if ( test >= triggers->trap.b1.z && triggers->trap.b2.z >= test ) + { + test = pos->vy; + if ( test >= triggers->trap.b1.y && triggers->trap.b2.y >= test ) + { + return 1; + } + } + } + } + + triggers++; + } + return 0; +} + +static int asiato_800D15D8( HZD_HDL *hzd, SVECTOR *pos ) +{ + int i; + int test; + HZD_TRG *triggers; + + triggers = hzd->grp->triggers; + + for ( i = 0 ; i < hzd->grp->n_triggers ; i++ ) + { + if ( triggers->trap.name_id == HASH_ASIATO || triggers->trap.name_id == HASH_POOLATO ) + { + test = pos->vx; + if ( test >= triggers->trap.b1.x && triggers->trap.b2.x >= test ) + { + test = pos->vz; + if ( test >= triggers->trap.b1.z && triggers->trap.b2.z >= test ) + { + test = pos->vy; + if ( test >= triggers->trap.b1.y && triggers->trap.b2.y >= test ) + { + return 1; + } + } + } + } + + triggers++; + } + return 0; +} + +int AsiatoCheck( HZD_HDL *hzd, SVECTOR *pos ) +{ + if ( AsiatoPositions[48].vy <= 0 ) + { + return 0; + } + + return asiato_800D15D8( hzd, pos ); +} + +/*---------------------------------------------------------------------------*/ + +typedef struct _Work2 +{ + GV_ACT actor; + int field_20; + int field_24; +} Work2; + +static int asiato_800D16F8(Work2 *work, HZD_HDL *hdl, SVECTOR *pos) +{ + work->field_20 = 0; + + if (asiato_800D1500(hdl, pos, HASH_POOL)) + { + work->field_24 = 60; + return 0; + } + + if (work->field_24 > 0) + { + work->field_20 = 360 - work->field_24 * 2; + work->field_24--; + return 1; + } + + return asiato_800D1500(hdl, pos, HASH_ASIATO); +} + +static int AsiatoIsAllowedOnMove_800D179C() +{ + // If player is moving and not on wall or prone + return (GM_PlayerStatus & (PLAYER_CAUTION | PLAYER_MOVE | PLAYER_GROUND)) == PLAYER_MOVE; +} + +static int asiato_800D17BC(Work2 *work) +{ + HZD_HDL *hdl; + + hdl = GM_WhereList[0]->map->hzd; + if (!AsiatoIsAllowedOnMove_800D179C() || !asiato_800D16F8(work, hdl, &GM_PlayerPosition)) + { + return 0; + } + + if (dword_800AB9D4 != 2) + { + return dword_800AB9D4 == 10 ? 2 : 0; + } + return 1; +} + +static void RecordAsiatoPosition(void) +{ + SVECTOR *svec; + + svec = &AsiatoPositions[AsiatoPositions[48].vx]; + *svec = GM_PlayerPosition; + AsiatoPositions[AsiatoPositions[48].vx].pad = 1; + AsiatoPositions[48].vy++; + AsiatoPositions[48].vx++; + if (AsiatoPositions[48].vx >= 48) + { + AsiatoPositions[48].vx = 0; + } +} + +static void Act(Work2 *work) +{ + int temp_v0; + DG_OBJ *obj; + + temp_v0 = asiato_800D17BC(work); + if ((temp_v0 != 0) && (AsiatoPositions[48].vy < 48)) + { + if (temp_v0 == 1) + { + obj = &GM_PlayerBody->objs->objs[12]; + } + else + { + obj = &GM_PlayerBody->objs->objs[15]; + } + RecordAsiatoPosition(); + GM_CurrentMap = (*GM_WhereList)->map->index; + NewAsiatoChar(&obj->world, temp_v0, (*GM_WhereList)->levels[0], AsiatoPositions[48].vx - 1, work->field_20); + } +} + +static void Die(Work2 *work) +{ + /* do nothing */ +} + +static void GetResources(Work2 *work) +{ + int i; + + for (i = 0; i < 48; i++) + { + AsiatoChar_SetPos(&AsiatoPositions[i], 0, 0, 0); + AsiatoPositions[i].pad = 0; + } + + AsiatoPositions[48].vx = 0; + AsiatoPositions[48].vy = 0; + work->field_24 = 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewAsiato(int name, int where, int argc, char **argv) +{ + Work2 *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work2)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "asiato.c"); + GetResources(work); + } + + return (void *)work; +} + +void *NewAsiatoKun(int name, int where, int argc, char **argv) +{ + // Identical to NewAsiato + Work2 *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work2)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "asiato.c"); + GetResources(work); + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/asiato.h b/components/mgs/mgs/source/enemy/asiato.h new file mode 100644 index 00000000..2ee85fff --- /dev/null +++ b/components/mgs/mgs/source/enemy/asiato.h @@ -0,0 +1,21 @@ +#ifndef __MGS_ENEMY_ASIATO_H__ +#define __MGS_ENEMY_ASIATO_H__ + +#include +#include +#include +#include "libhzd/libhzd.h" // for HZD_HDL + +// void *NewAsiatoChar(MATRIX *world, int arg1, int arg2, int arg3, int arg4); + +void AsiatoPos(int idx, SVECTOR *out); +int NextAsiato(HZD_HDL *hdl, int idx, SVECTOR *svec2); +void CleanAsiato(void); +int NearAsiato(void); +int SearchNearAsiato( HZD_HDL *hzd, SVECTOR *mov, int facedir, int vision_unk, int length ); +int AsiatoCheck( HZD_HDL *hzd, SVECTOR *pos ); + +void *NewAsiato(int name, int where, int argc, char **argv); +void *NewAsiatoKun(int name, int where, int argc, char **argv); + +#endif // __MGS_ENEMY_ASIATO_H__ diff --git a/components/mgs/mgs/source/enemy/asiato2.c b/components/mgs/mgs/source/enemy/asiato2.c new file mode 100644 index 00000000..55973e6e --- /dev/null +++ b/components/mgs/mgs/source/enemy/asiato2.c @@ -0,0 +1,330 @@ +#include "asiato2.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_PREV + +typedef struct _Work +{ + GV_ACT actor; + DG_PRIM *prim; + DG_TEX *tex; + SVECTOR pos[4]; + int f48; + int f4C; + int f50; + int f54; +} Work; + +/*---------------------------------------------------------------------------*/ + +typedef struct _Asiato2Global +{ + SVECTOR vec[128]; + short index; + short total; +} Asiato2Global; + +Asiato2Global SECTION(".bss") asiato2_800E4FC0; + +/*---------------------------------------------------------------------------*/ + +static void Asiato2Char_ShadePacks(DG_PRIM *prim, DG_TEX *tex, int r, int g, int b) +{ + POLY_FT4 *poly; + + poly = prim->packs[0]; + setRGB0(poly, r, g, b); + + poly = prim->packs[1]; + setRGB0(poly, r, g, b); +} + +static void Asiato2Char_SetPos(SVECTOR *vec, int x, int y, int z) +{ + vec->vx = x; + vec->vy = y; + vec->vz = z; +} + +static void Asiato2Char_Act(Work *work) +{ + int t; + int shade; + + if (++work->f48 > (work->f54 - 300)) + { + GV_DestroyActor(&work->actor); + } + + t = work->f54 - work->f48; + if (t < 690) + { + shade = 48 - ((690 - t) * 48) / 690; + Asiato2Char_ShadePacks(work->prim, work->tex, shade, shade, shade); + } + else + { + Asiato2Char_ShadePacks(work->prim, work->tex, 48, 48, 48); + } +} + +static void Asiato2Char_InitPacks(POLY_FT4 *poly, DG_TEX *tex, int abr, int r, int g, int b) +{ + setPolyFT4(poly); + setRGB0(poly, r, g, b); + + if (abr < 4) + { + setSemiTrans(poly, 1); + DG_SetPacketTexture4(poly, tex); + poly->tpage = (poly->tpage & ~0x60) | (abr << 5); + } + else + { + DG_SetPacketTexture4(poly, tex); + } +} + +static int Asiato2Char_GetResources(Work *work, MATRIX *world, int arg2, int height, int arg4) +{ + SVECTOR rot; + SVECTOR pos; + DG_PRIM *prim; + DG_TEX *tex; + + Asiato2Char_SetPos(&rot, 0, arg4, 0); + + if (arg2 == 1) + { + Asiato2Char_SetPos(&work->pos[1], -70, 0, -140); + Asiato2Char_SetPos(&work->pos[0], 70, 0, -140); + Asiato2Char_SetPos(&work->pos[3], -70, 0, 140); + Asiato2Char_SetPos(&work->pos[2], 70, 0, 140); + } + else + { + Asiato2Char_SetPos(&work->pos[0], -70, 0, -140); + Asiato2Char_SetPos(&work->pos[1], 70, 0, -140); + Asiato2Char_SetPos(&work->pos[2], -70, 0, 140); + Asiato2Char_SetPos(&work->pos[3], 70, 0, 140); + } + + prim = GM_MakePrim(DG_PRIM_POLY_FT4, 1, &work->pos[0], NULL); + work->prim = prim; + if (prim == NULL) + { + return -1; + } + + pos.vx = world->t[0]; + pos.vy = world->t[1]; + pos.vz = world->t[2]; + + pos.vy = height; + + tex = DG_GetTexture(PCX_ASIATO); + work->tex = tex; + if (tex == NULL) + { + return -1; + } + + Asiato2Char_InitPacks(prim->packs[0], tex, 2, 48, 48, 48); + Asiato2Char_InitPacks(prim->packs[1], tex, 2, 48, 48, 48); + + DG_SetPos2(&pos, &rot); + DG_PutPrim(work->prim); + + return 0; +} + +static void Asiato2Char_Die(Work *work) +{ + GM_FreePrim(work->prim); + + asiato2_800E4FC0.vec[work->f4C].pad = 0; + asiato2_800E4FC0.total--; +} + +/*---------------------------------------------------------------------------*/ + +void *NewAsiato2Char(MATRIX *world, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Asiato2Char_Act, Asiato2Char_Die, "asiato2.c"); + + work->f4C = arg3; + work->f54 = arg6; + + if (Asiato2Char_GetResources(work, world, arg1, arg2, arg5) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + work->f48 = arg4; + } + + return (void *)work; +} + +/*---------------------------------------------------------------------------*/ + +typedef struct _Work2 +{ + GV_ACT actor; + CONTROL *control; + OBJECT *object; + int f28; + int f2C; + int f30; + int *f34; +} Work2; + +static int asiato2_800DD308(Work2 *work, int name) +{ + HZD_EVT *events; + int i; + + events = &work->control->evt; + for (i = 0; i < events->n_inside; i++) + { + if (events->inside[i] == name) + { + return 1; + } + } + + return 0; +} + +static int asiato2_800DD358(Work2 *work) +{ + work->f28 = 0; + + if (asiato2_800DD308(work, 0xCA85)) + { + work->f2C = 360; + return 0; + } + + if (work->f2C > 0) + { + work->f28 = 360 - work->f2C; + work->f2C--; + return 1; + } + + return asiato2_800DD308(work, 0xDC55); +} + +static int asiato2_800DD3D4(Work2 *work) +{ + int temp_s0; + + temp_s0 = *work->f34 & 0x3; + if (temp_s0 == 0) + { + return 0; + } + + if (!asiato2_800DD358(work)) + { + return 0; + } + + return temp_s0; +} + +static void asiato2_800DD42C(Work2 *work) +{ + asiato2_800E4FC0.vec[asiato2_800E4FC0.index] = work->control->mov; + asiato2_800E4FC0.vec[asiato2_800E4FC0.index].pad = 1; + + asiato2_800E4FC0.total++; + + if (++asiato2_800E4FC0.index >= 128) + { + asiato2_800E4FC0.index = 0; + } +} + +static void Act(Work2 *work) +{ + int which; + MATRIX *world; + + which = asiato2_800DD3D4(work); + if (which == 0 || asiato2_800E4FC0.total >= 128) + { + return; + } + + if (which == 1) + { + world = &work->object->objs->objs[12].world; + } + else + { + world = &work->object->objs->objs[15].world; + } + + asiato2_800DD42C(work); + + GM_CurrentMap = work->control->map->index; + + NewAsiato2Char(world, which, work->control->levels[0], asiato2_800E4FC0.index - 1, work->f28, work->control->rot.vy, work->f30); +} + +static void Die(Work2 *work) +{ +} + +static void GetResources(Work2 *work) +{ + int i; + + for (i = 0; i < 128; i++) + { + Asiato2Char_SetPos(&asiato2_800E4FC0.vec[i], 0, 0, 0); + asiato2_800E4FC0.vec[i].pad = 0; + } + + asiato2_800E4FC0.index = 0; + asiato2_800E4FC0.total = 0; + + work->f2C = 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewAsiato2(CONTROL *control, OBJECT *object, int arg2, int *arg3) +{ + Work2 *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work2)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "asiato2.c"); + + work->control = control; + work->object = object; + work->f30 = arg2; + work->f34 = arg3; + + GetResources(work); + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/asiato2.h b/components/mgs/mgs/source/enemy/asiato2.h new file mode 100644 index 00000000..fad754f4 --- /dev/null +++ b/components/mgs/mgs/source/enemy/asiato2.h @@ -0,0 +1,12 @@ +#ifndef __MGS_ENEMY_ASIATO2_H__ +#define __MGS_ENEMY_ASIATO2_H__ + +#include +#include +#include +#include "game/game.h" // for CONTROL, OBJECT + +// void *NewAsiatoChar2(MATRIX *world, int arg1, int arg2, int arg3, int arg4, int arg5, int arg6); +void *NewAsiato2(CONTROL *control, OBJECT *object, int arg2, int *arg3); + +#endif // __MGS_ENEMY_ASIATO2_H__ diff --git a/components/mgs/mgs/source/enemy/asioto.c b/components/mgs/mgs/source/enemy/asioto.c new file mode 100644 index 00000000..3d35fd14 --- /dev/null +++ b/components/mgs/mgs/source/enemy/asioto.c @@ -0,0 +1,253 @@ +#include "asioto.h" + +#include +#include "common.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "asioto.h" + +typedef struct _Work +{ + GV_ACT actor; + int hash[4]; + int count; + int se[4][2]; + int se2[4][2]; + int se_duct[4][2]; + int noise[4]; +} Work; + +#define EXEC_LEVEL GV_ACTOR_PREV + +int asioto_800C38AC(Work *work) +{ + HZD_EVT *event; + int i; + int j; + + event = &GM_PlayerControl->evt; + for (i = 0; i < event->n_inside; i++) + { + for (j = 0; j < work->count; j++) + { + if (event->inside[i] == work->hash[j]) + { + return j; + } + } + } + + return -1; +} + +int asioto_800C392C(Work *work) +{ + return asioto_800C38AC(work); +} + +int asioto_800C394C() +{ + return (GM_PlayerStatus & (PLAYER_CAUTION | PLAYER_GROUND | PLAYER_MOVE)) == PLAYER_MOVE; +} + +int asioto_800C396C(Work *work) +{ + if (asioto_800C394C() == 0) + return -1; + + if (dword_800AB9D4 == 8) + { + return 0; + } + if (dword_800AB9D4 == 0x10) + { + return 1; + } + return -1; +} + +int asioto_800C39B8(void) +{ + if (dword_800AB9D4 == 0xC) + return 0; + if (dword_800AB9D4 == 0x18) + return 1; + return -1; +} + +void AsiotoAct_800C39E8(Work *work) +{ + int index; + int bank; + int bank2; + + if (!GM_PlayerControl) + { + return; + } + + do + { + if (GM_PlayerStatus & PLAYER_INTRUDE) + { + if (GM_PlayerStatus & PLAYER_MOVE) + { + index = asioto_800C39B8(); + if (index < 0) + { + return; + } + + bank = GM_PlayerControl->vecs[0].pad & 3; + GM_SeSet(&GM_PlayerPosition, work->se_duct[bank][index]); + } + } + else + { + index = asioto_800C396C(work); + if (index < 0) + { + return; + } + + bank2 = asioto_800C392C(work); + if (bank2 >= 0) + { + GM_SeSet(&GM_PlayerPosition, work->se[bank2][index]); + + if (work->noise[bank2] != 0) + { + GM_SetNoise(100, 4, &GM_PlayerPosition); + } + } + else + { + bank = GM_PlayerControl->vecs[0].pad & 3; + GM_SeSet(&GM_PlayerPosition, work->se2[bank][index]); + } + } + } while (0); +} + +void AsiotoDie_800C3B8C(Work *work) +{ +} + +int AsiotoGetResources_800C3B94(Work *work) +{ + int *hash; + char *result; + int i; + + hash = work->hash; + work->count = 0; + + if (GCL_GetOption('t')) + { + while ((result = GCL_NextStr())) + { + *hash++ = GCL_StrToInt(result); + work->count++; + } + } + + i = 0; + if (GCL_GetOption('s')) + { + for (; i < work->count; i++) + { + if (!GCL_NextStr()) + { + break; + } + + work->se[i][0] = GCL_StrToInt(GCL_NextStr()); + work->se[i][1] = GCL_StrToInt(GCL_NextStr()); + } + + if (i != work->count) + { + printf(" asioto se set err \n"); + return -1; + } + } + + work->se2[0][0] = 169; + work->se2[0][1] = 168; + + i = 0; + if (GCL_GetOption('f')) + { + for (; i < 4; i++) + { + if (!GCL_NextStr()) + { + break; + } + + work->se2[i][0] = GCL_StrToInt(GCL_NextStr()); + work->se2[i][1] = GCL_StrToInt(GCL_NextStr()); + } + } + + i = 0; + if (GCL_GetOption('n')) + { + for (; i < work->count; i++) + { + if (!GCL_NextStr()) + { + break; + } + + work->noise[i] = GCL_StrToInt(GCL_NextStr()); + } + } + + if (i != work->count) + { + printf(" asioto se noise set err \n"); + return -1; + } + + work->se_duct[0][0] = SE_CRAWL_LEFT; + work->se_duct[0][1] = SE_CRAWL_RIGHT; + + i = 0; + if (GCL_GetOption('i')) + { + for (; i < 4; i++) + { + if (!GCL_NextStr()) + { + break; + } + + work->se_duct[i][0] = GCL_StrToInt(GCL_NextStr()); + work->se_duct[i][1] = GCL_StrToInt(GCL_NextStr()); + + printf(" migi sodesuri %d \n", work->se_duct[i][0]); + } + } + + return 0; +} + +void *NewAsiotoKun(int name, int where, int argc, char **argv) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, AsiotoAct_800C39E8, AsiotoDie_800C3B8C, "asioto.c"); + if (AsiotoGetResources_800C3B94(work) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/asioto.h b/components/mgs/mgs/source/enemy/asioto.h new file mode 100644 index 00000000..83d14a67 --- /dev/null +++ b/components/mgs/mgs/source/enemy/asioto.h @@ -0,0 +1,6 @@ +#ifndef __MGS_ENEMY_ASIOTO_H__ +#define __MGS_ENEMY_ASIOTO_H__ + +void *NewAsiotoKun(int name, int where, int argc, char **argv); + +#endif // __MGS_ENEMY_ASIOTO_H__ diff --git a/components/mgs/mgs/source/enemy/boxkeri.c b/components/mgs/mgs/source/enemy/boxkeri.c new file mode 100644 index 00000000..efba2380 --- /dev/null +++ b/components/mgs/mgs/source/enemy/boxkeri.c @@ -0,0 +1,204 @@ +#include "boxkeri.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_PREV + +#define BOX_MODEL GV_StrCode("cb_box") +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE \ + | DG_FLAG_GBOUND | DG_FLAG_ONEPIECE ) + +typedef struct _Work +{ + GV_ACT actor; + OBJECT_NO_ROTS body; + SVECTOR field_44; + SVECTOR field_48; + MATRIX world; + short step; + short field_76; + short field_78; + short field_7A; + MATRIX light[2]; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void Act(Work *work) +{ + int step; + + step = work->step; + if (step > 40) + { + GV_DestroyActor(&work->actor); + return; + } + if (step < 9) + { + work->field_7A += 80; + work->field_78 += 256; + } + else if (step < 12) + { + work->field_78 -= 128; + } + else if (step < 15) + { + work->field_78 += 64; + } + else if (step < 18) + { + work->field_78 -= 32; + } + else if (step < 19) + { + work->field_78 += 32; + } + else if (step & 1) + { + DG_InvisibleObjs(work->body.objs); + } + else + { + DG_VisibleObjs(work->body.objs); + } + + switch (work->field_76) + { + case 0: + if (step < 9) + { + work->field_7A += 5; + } + work->field_48.vz = -work->field_7A; + work->field_44.vx = -work->field_78; + break; + + case 1: + work->field_48.vx = -work->field_7A; + work->field_44.vz = work->field_78; + break; + + case 2: + if (step < 9) + { + work->field_7A += 100; + } + work->field_48.vz = work->field_7A; + work->field_44.vx = work->field_78; + break; + + case 3: + work->field_48.vx = work->field_7A; + work->field_44.vz = -work->field_78; + break; + } + + GM_CurrentMap = GM_PlayerMap; + + DG_SetPos(&work->world); + DG_MovePos(&work->field_48); + DG_RotatePos(&work->field_44); + DG_PutObjs(work->body.objs); + DG_GetLightMatrix2(&GM_PlayerPosition, work->light); + + work->step++; +} + +static void Die(Work *work) +{ + GM_FreeObject((OBJECT *)&work->body); +} + +static void SendMessage(void) +{ + GV_MSG msg; + + msg.address = GV_StrCode("スネーク"); + msg.message[0] = GV_StrCode("段ボール"); + msg.message[1] = 1; + msg.message_len = 2; + GV_SendMessage(&msg); +} + +static int s00a_boxkeri_800D2440(SVECTOR *pos, SVECTOR *svec2) +{ + SVECTOR svec; + + GV_SubVec3(svec2, pos, &svec); + return GV_VecDir2(&svec); +} + +static int GetResources(Work *work, MATRIX *world, SVECTOR *arg2) +{ + OBJECT_NO_ROTS *obj; + int dir; + + obj = &work->body; + GM_CurrentMap = GM_PlayerMap; + GM_InitObjectNoRots(obj, BOX_MODEL, BODY_FLAG, 0); + GM_ConfigObjectLight((OBJECT *)obj, work->light); + + work->body.objs->objs[0].raise = 500; + + work->field_44 = DG_ZeroVector; + work->field_48 = DG_ZeroVector; + + work->field_78 = 0; + work->field_7A = 0; + + dir = GV_DiffDirU(GM_PlayerControl->rot.vy, + s00a_boxkeri_800D2440(&GM_PlayerPosition, arg2)); + if (dir < 512 || dir >= 3607) + { + work->field_76 = 0; + } + else + { + if (dir >= 1536) + { + if (dir >= 2584) + { + work->field_76 = 3; + } + else + { + work->field_76 = 2; + } + } + else + { + work->field_76 = 1; + } + } + + work->world = *world; + work->step = 0; + SendMessage(); + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewBoxKeri(MATRIX *world, SVECTOR *svec) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "boxkeri.c"); + if (GetResources(work, world, svec) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/boxkeri.h b/components/mgs/mgs/source/enemy/boxkeri.h new file mode 100644 index 00000000..382456c5 --- /dev/null +++ b/components/mgs/mgs/source/enemy/boxkeri.h @@ -0,0 +1,10 @@ +#ifndef __MGS_ENEMY_BOXKERI_H__ +#define __MGS_ENEMY_BOXKERI_H__ + +#include +#include +#include + +void *NewBoxKeri(MATRIX *world, SVECTOR *svec); + +#endif // __MGS_ENEMY_BOXKERI_H__ diff --git a/components/mgs/mgs/source/enemy/camera.c b/components/mgs/mgs/source/enemy/camera.c new file mode 100644 index 00000000..86dc8693 --- /dev/null +++ b/components/mgs/mgs/source/enemy/camera.c @@ -0,0 +1,1120 @@ +#include "camera.h" + +#include "common.h" +#include "mts/mts.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "linkvar.h" +#include "bullet/blast.h" +#include "okajima/spark.h" +#include "enemy/enemy.h" +#include "sound/g_sound.h" +#include "strcode.h" + +#include "takabe/spark2.h" // for NewSpark2_800CA714 + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + OBJECT_NO_ROTS body; + OBJECT_NO_ROTS field_C0; + SVECTOR field_E4; + SVECTOR field_EC; + MATRIX field_F4; + int field_114; + int field_118; + int field_11C; + int field_120; + int field_124; + int field_128; + int field_12C; + int field_130; + SVECTOR rots[1]; // TODO: are there 2 rots? + int field_13C; + int field_140; + MATRIX light; + int field_164; + int field_168; + int field_16C; + int field_170; + int field_174; + int field_178; + int field_17C; + int field_180; + SVECTOR field_184; + int field_18C; + int field_190; + DG_PRIM *field_194; + DG_TEX *field_198; + short field_19C; + short field_19E; + SVECTOR field_1A0; + SVECTOR field_1A8; + int field_1B0; + int field_1B4; + SVECTOR lens_shift; + SVECTOR field_1C0; + int field_1C8; + TARGET *target; + SVECTOR field_1D0; + int field_1D8; + int field_1DC; + int field_1E0; + int field_1E4; + int field_1E8; + unsigned int field_1EC; + int field_1F0; + short field_1F4; + short field_1F6; + int field_1F8; + SVECTOR field_1FC[8]; + SVECTOR field_23C[8]; + unsigned short field_27C; + unsigned short field_27E; + unsigned short field_280; + unsigned short field_282; + short field_284; + unsigned short field_286; + short field_288; + short field_28A; + int field_28C; +} Work; + +#define EXEC_LEVEL GV_ACTOR_PREV + +#define SEGMENT_ATR ( HZD_SEG_NO_NAVIGATE ) + +#define LENS_DX 0 +#define LENS_DY 175 +#define LENS_DZ 600 + +RECT camera_rect_800C3B68 = {120, 120, 240, 240}; +SVECTOR Size_800C3B70 = {300, 300, 300, 0}; + +extern char s01a_dword_800E44CC[]; + +extern TOPCOMMAND_STRUCT TOPCOMMAND; +extern int COM_VibTime; + +extern CONTROL *GM_WhereList[96]; + +void AN_Unknown_800D6EB0(SVECTOR *pos); + +// duplicate of s03e_guncame_800C7118 +void s01a_camera_800D4CFC(DG_PRIM *prim, DG_TEX *tex, int r, int g, int b) +{ + POLY_FT4 *poly; + + poly = prim->packs[0]; + setRGB0(poly, r, g, b); + + poly = prim->packs[1]; + setRGB0(poly, r, g, b); +} + +int s01a_camera_800D4D28(char *opt, SVECTOR *svecs) +{ + int count; + unsigned char *param; + + for (count = 0; (param = GCL_NextStr()); svecs++, count++) + { + GCL_StrToSV(param, (short *)svecs); + } + return count; +} + +void s01a_camera_800D4D7C(CONTROL *arg0, SVECTOR *arg1, SVECTOR *arg2) +{ + SVECTOR svec; + int vy; + int ratan2val; + + GV_SubVec3(arg1, &arg0->mov, &svec); + arg2->vy = ratan2(svec.vx, svec.vz) & 0xFFF; + vy = svec.vy; + svec.vy = 0; + ratan2val = ratan2(GV_VecLen3(&svec), vy) & 0xFFF; + arg2->vx = ratan2val - 1024; + if (arg2->vx < 0) + { + arg2->vx = ratan2val + 3072; + } +} + +int s01a_camera_800D4E08(Work *work) +{ + SVECTOR svec; + CONTROL *ctrl; + int dir; + int dir2; + + if (GM_CurrentItemId == IT_Stealth) + { + work->field_1DC = 0; + return 0; + } + + ctrl = &work->control; + if (!(ctrl->map->index & GM_PlayerMap)) + { + work->field_1DC = 0; + return 0; + } + + s01a_camera_800D4D7C(ctrl, &GM_PlayerPosition, &svec); + dir = GV_DiffDirAbs(svec.vy, ctrl->rot.vy); + dir2 = GV_DiffDirAbs(svec.vx, ctrl->rot.vx); + + if (work->field_280 < dir2 || work->field_280 < dir || + GV_DiffVec3(&ctrl->mov, &GM_PlayerPosition) > work->field_27E) + { + work->field_1DC = 0; + return 0; + } + if (work->field_1C8 != 0) + { + if (HZD_OnlineHazardCheck(ctrl->map->hzd, &ctrl->mov, &GM_PlayerPosition, HZD_CHK_ALL, SEGMENT_ATR) != 0) + { + work->field_1DC = 0; + return 0; + } + } + if (TOPCOMMAND.mode != 1) + { + if (GM_PlayerStatus & PLAYER_CB_BOX) + { + if (work->field_1DC == 0) + { + work->field_1D0 = GM_PlayerPosition; + work->field_1D8 = GM_WhereList[0]->rot.vy; + work->field_1DC = 1; + return 0; + } + if (GV_DiffVec3(&work->field_1D0, &GM_PlayerPosition) < 50) + { + if (work->field_1D8 == GM_WhereList[0]->rot.vy) + { + work->field_1DC = 1; + return 0; + } + } + } + } + work->field_1DC = 1; + return 1; +} + +void s01a_camera_800D4FE8(SVECTOR *arg0, SVECTOR *arg1, int arg2) +{ + int dir; + + if (arg2 < 8) + { + arg1->vx = arg0->vx; + } + else if (arg1->vx > 768) + { + arg1->vx = 768; + } + + dir = GV_DiffDirS(arg0->vy, arg1->vy); + if (arg2 < dir) + { + arg1->vy = (arg0->vy + arg2) & 0xFFF; + } + else if (dir < -arg2) + { + arg1->vy = (arg0->vy - arg2) & 0xFFF; + } +} + +void s01a_camera_800D509C(Work *work) +{ + s01a_camera_800D4D7C(&work->control, &GM_PlayerPosition, &work->control.turn); + s01a_camera_800D4FE8(&work->field_1C0, &work->control.turn, work->field_282); +} + +void s01a_camera_800D50EC(Work *work) +{ + s01a_camera_800D4D7C(&work->control, &GM_PlayerPosition, &work->control.turn); + s01a_camera_800D4FE8(&work->field_1C0, &work->control.turn, work->field_282); + work->control.rot = work->control.turn; +} + +int s01a_camera_800D515C(Work *work) +{ + int field_1EC; + + field_1EC = work->field_1EC; + if (field_1EC == 0) + { + GM_SeSet(&work->control.mov, SE_CAMERA_LENS); + s01a_camera_800D4CFC(work->field_194, work->field_198, 0xFF, 0, 0); + } + s01a_camera_800D509C(work); + if (field_1EC == 15) + { + s00a_command_800CEC40(&work->control.mov, 32); + AN_Unknown_800CA1EC(&work->body.objs[1].world, 0); + COM_VibTime = 10; + s01a_camera_800D50EC(work); + } + if (field_1EC == 35) + { + ENE_SetTopCommAL(255); + ENE_SetGopointLast(); + return 1; + } + + work->field_1EC++; + return 0; +} + +void s01a_camera_800D522C(Work *work) +{ + if (mts_get_tick_count() - work->field_286 >= 13) + { + if (work->field_28A != 0) + { + GM_SeSet(&work->control.mov, SE_CAMERA_SCAN); + } + work->field_286 = mts_get_tick_count(); + } +} + +int s01a_camera_800D528C(Work *work) +{ + SVECTOR *turn; + + if (work->field_1EC > 60) + { + if (!(work->field_1EC & 3)) + { + s01a_camera_800D522C(work); + } + if (work->field_282 >= 8) + { + turn = &work->control.turn; + turn->vy = (turn->vy + 8) & 0xFFF; + if (GV_DiffDirAbs(work->field_1C0.vy, turn->vy) >= work->field_282) + { + return 1; + } + } + else + { + return 0; + } + } + work->field_1EC++; + return 0; +} + +// Same as s01a_camera_800D528C, but with - 8 instead of + 8 +int s01a_camera_800D5338(Work *work) +{ + SVECTOR *turn; + + if (work->field_1EC > 60) + { + if (!(work->field_1EC & 3)) + { + s01a_camera_800D522C(work); + } + if (work->field_282 >= 8) + { + turn = &work->control.turn; + turn->vy = (turn->vy - 8) & 0xFFF; + if (GV_DiffDirAbs(work->field_1C0.vy, turn->vy) >= work->field_282) + { + return 1; + } + } + else + { + return 0; + } + } + work->field_1EC++; + return 0; +} + +void s01a_camera_800D53E4(Work *work) +{ + CONTROL *ctrl; + SVECTOR *turn; + int dir1, dir2; + int temp_v0; + + ctrl = &work->control; + if (!(work->field_1EC & 0x3F)) + { + temp_v0 = GV_RandU(16); + turn = &work->control.turn; + work->control.interp = 48; + work->field_1EC += temp_v0; + temp_v0 = work->field_1C0.vy - (1024 - GV_RandU(2048)); + turn->vy = temp_v0 & 0xFFF; + turn->vx = GV_RandU(768); + s01a_camera_800D4FE8(&work->field_1C0, turn, work->field_282); + work->field_1F4 = ctrl->rot.vx; + work->field_1F6 = ctrl->rot.vy; + } + dir1 = GV_DiffDirAbs(work->field_1F4, ctrl->rot.vx); + dir2 = GV_DiffDirAbs(work->field_1F6, ctrl->rot.vy); + if ((dir1 > 16 || dir2 > 16) && work->field_28A != 0) + { + GM_SeSet(&ctrl->mov, SE_CAMERA_SCAN); + } + work->field_1F4 = ctrl->rot.vx; + work->field_1F6 = ctrl->rot.vy; + work->field_1EC++; +} + +// Copy of s01a_camera_800D53E4, but with different GM_SeSet +void s01a_camera_800D5504(Work *work) +{ + CONTROL *ctrl; + SVECTOR *turn; + int dir1, dir2; + int temp_v0; + + ctrl = &work->control; + if (!(work->field_1EC & 0x3F)) + { + temp_v0 = GV_RandU(16); + turn = &work->control.turn; + work->control.interp = 48; + work->field_1EC += temp_v0; + temp_v0 = work->field_1C0.vy - (1024 - GV_RandU(2048)); + turn->vy = temp_v0 & 0xFFF; + turn->vx = GV_RandU(768); + s01a_camera_800D4FE8(&work->field_1C0, turn, work->field_282); + work->field_1F4 = ctrl->rot.vx; + work->field_1F6 = ctrl->rot.vy; + } + dir1 = GV_DiffDirAbs(work->field_1F4, ctrl->rot.vx); + dir2 = GV_DiffDirAbs(work->field_1F6, ctrl->rot.vy); + if ((dir1 > 16 || dir2 > 16) && work->field_28A != 0) + { + GM_SeSet(&ctrl->mov, SE_CAMERA_JAMMED); + } + work->field_1F4 = ctrl->rot.vx; + work->field_1F6 = ctrl->rot.vy; + work->field_1EC++; +} + +int s01a_camera_800D5624(Work *work) +{ + CONTROL *ctrl; + SVECTOR *turn; + SVECTOR *svec1; + int dir, dir2, dir3; + + svec1 = &work->field_1C0; + turn = &work->control.turn; + ctrl = &work->control; + + dir = GV_DiffDirS(svec1->vy, turn->vy); + if (dir < -10) + { + turn->vy += 8; + } + else if (dir < 10) + { + turn->vy = svec1->vy; + } + else + { + turn->vy -= 8; + } + turn->vy &= 0xFFF; + + dir = GV_DiffDirS(svec1->vx, turn->vx); + if (dir < -10) + { + turn->vx += 8; + } + else if (dir < 10) + { + turn->vx = svec1->vx; + } + else + { + turn->vx -= 8; + } + turn->vx &= 0xFFF; + + if (turn->vx == svec1->vx && turn->vy == svec1->vy && work->field_1EC > 96) + { + return 1; + } + + dir2 = GV_DiffDirAbs(work->field_1F4, ctrl->rot.vx); + dir3 = GV_DiffDirAbs(work->field_1F6, ctrl->rot.vy); + if ((dir2 > 16 || dir3 > 16) && work->field_28A != 0) + { + GM_SeSet(&work->control.mov, SE_CAMERA_SCAN); + } + + work->field_1F4 = ctrl->rot.vx; + work->field_1F6 = ctrl->rot.vy; + work->field_1EC++; + return 0; +} + +void s01a_camera_800D57CC(Work *work) +{ + switch (work->field_1E8) + { + case 1: + if (s01a_camera_800D528C(work)) + { + work->field_1E8 = 2; + work->field_1EC = 0; + } + if (s01a_camera_800D4E08(work)) + { + work->field_1E8 = 5; + work->field_1EC = 0; + } + break; + case 2: + if (s01a_camera_800D5338(work)) + { + work->field_1E8 = 1; + work->field_1EC = 0; + } + if (s01a_camera_800D4E08(work)) + { + work->field_1E8 = 5; + work->field_1EC = 0; + } + break; + case 3: + work->field_1E8 = 1; + work->field_1EC = 0; + break; + case 5: + if (s01a_camera_800D515C(work)) + { + work->field_1E0 = 1; + work->field_1E8 = 4; + work->field_1EC = 0; + return; + } + break; + case 7: + if (s01a_camera_800D5624(work)) + { + if (work->field_27C == 2) + { + work->field_1E8 = 3; + } + else + { + work->field_1E8 = 1; + } + work->field_1EC = 0; + s01a_camera_800D4CFC(work->field_194, work->field_198, 0, 0xFF, 0); + } + break; + } + + if (TOPCOMMAND.mode == 1) + { + work->field_1E0 = TOPCOMMAND.mode; + work->field_1E8 = 0; + work->field_1EC = 0; + s01a_camera_800D4CFC(work->field_194, work->field_198, 0xFF, 0, 0); + } + if (GM_GameStatus & STATE_CHAFF) + { + work->field_1E0 = 3; + work->field_1E8 = 6; + work->field_1EC = 0; + s01a_camera_800D4CFC(work->field_194, work->field_198, 0xFF, 0xFF, 0); + } +} + +void s01a_camera_800D5970(Work *work) +{ + if (work->field_1E8 != 0) + { + if (work->field_1E8 == 4) + { + s01a_camera_800D509C(work); + ENE_SetTopCommAL(255); + ENE_SetGopointLast(); + if (!s01a_camera_800D4E08(work)) + { + work->field_1E8 = 0; + } + } + } + else if (s01a_camera_800D4E08(work)) + { + work->field_1E8 = 4; + } + if (TOPCOMMAND.mode != 1) + { + work->field_1E0 = 2; + work->field_1E8 = 6; + work->field_1EC = 0; + s01a_camera_800D4CFC(work->field_194, work->field_198, 0xFF, 0xFF, 0); + } + if (GM_GameStatus & STATE_CHAFF) + { + work->field_1E0 = 3; + work->field_1E8 = 6; + work->field_1EC = 0; + s01a_camera_800D4CFC(work->field_194, work->field_198, 0xFF, 0xFF, 0); + } +} + +void s01a_camera_800D5A68(Work *work) +{ + if (work->field_1E8 == 6) + { + s01a_camera_800D53E4(work); + if (TOPCOMMAND.mode == 0) + { + work->field_1E0 = 0; + work->field_1E8 = 7; + work->field_1EC = 0; + } + } + if (s01a_camera_800D4E08(work)) + { + ENE_SetTopCommAL(255); + ENE_SetGopointLast(); + COM_VibTime = 0xA; + s00a_command_800CEC40(&work->control.mov, 0x20); + AN_Unknown_800CA1EC(&work->body.objs[1].world, 0); + s01a_camera_800D50EC(work); + work->field_1E0 = 1; + work->field_1E8 = 4; + work->field_1EC = 0; + s01a_camera_800D4CFC(work->field_194, work->field_198, 0xFF, 0, 0); + } + else if (TOPCOMMAND.mode == 1) + { + work->field_1E0 = TOPCOMMAND.mode; + work->field_1E8 = 0; + work->field_1EC = 0; + s01a_camera_800D4CFC(work->field_194, work->field_198, 0xFF, 0, 0); + } + if (GM_GameStatus & STATE_CHAFF) + { + work->field_1E0 = 3; + work->field_1E8 = 6; + work->field_1EC = 0; + s01a_camera_800D4CFC(work->field_194, work->field_198, 0xFF, 0xFF, 0); + } +} + +void s01a_camera_800D5B9C(Work *work) +{ + int field_1E8; + + field_1E8 = work->field_1E8; + if (field_1E8 == 6) + { + if (GM_GameStatus & STATE_CHAFF) + { + s01a_camera_800D5504(work); + return; + } + switch (TOPCOMMAND.mode) + { + case 0: + work->field_1E0 = 0; + work->field_1E8 = 7; + work->field_1EC = 0; + break; + case 1: + work->field_1E0 = TOPCOMMAND.mode; + work->field_1E8 = 0; + work->field_1EC = 0; + s01a_camera_800D4CFC(work->field_194, work->field_198, 0xFF, 0, 0); + break; + case 2: + work->field_1E0 = TOPCOMMAND.mode; + work->field_1E8 = field_1E8; + work->field_1EC = 0; + s01a_camera_800D4CFC(work->field_194, work->field_198, 0xFF, 0xFF, 0); + break; + } + } +} + +void s01a_camera_800D5C7C(Work *work) +{ + switch (work->field_1E0) + { + case 0: + s01a_camera_800D57CC(work); + break; + case 1: + s01a_camera_800D5970(work); + break; + case 2: + s01a_camera_800D5A68(work); + break; + case 3: + s01a_camera_800D5B9C(work); + break; + } +} + +void s01a_camera_800D5D10(Work *work) +{ + work->control.radar_param.dir = work->control.rot.vy; +} + +void s01a_camera_800D5D1C(Work *work) +{ + MATRIX mat; + SVECTOR svec; + int i; + + if (++work->field_1F0 >= 10) + { + svec.vx = GV_RandU(1024); + svec.vy = GV_RandU(2048); + svec.vz = 0; + + DG_SetPos2(&work->control.mov, &svec); + ReadRotMatrix(&mat); + + if (!(work->field_1F0 & 0x7)) + { + NewSpark(&mat, GV_RandU(2)); + } + if (work->field_1F0 == work->field_1F0 / 12 * 12) + { + NewSpark2_800CA714(&mat); + } + } + + work->body.objs->flag |= 0x80; + work->field_C0.objs->flag |= 0x80; + s01a_camera_800D4CFC(work->field_194, work->field_198, 0, 0, 0); + + for (i = 0; i < 8; i++) + { + work->field_1FC[i].vy -= 15; + work->field_23C[i].vx += work->field_1FC[i].vx; + work->field_23C[i].vy += work->field_1FC[i].vy; + work->field_23C[i].vz += work->field_1FC[i].vz; + AN_Unknown_800D6EB0(&work->field_23C[i]); + } + if (work->field_1F0 >= 40) + { + GV_DestroyActor(&work->actor); + } +} + +int s01a_camera_800D5EC0(Work *work) +{ + GV_MSG *msg; + int type; + + if (GV_ReceiveMessage(work->control.name, &msg) <= 0) + { + return -1; + } + + // They just randomly decided not to use a switch here... + type = msg->message[0]; + if (type == HASH_LEAVE) + { + work->field_284 = 0; + work->control.radar_atr &= ~RADAR_SIGHT; + } + else if (type == HASH_ENTER) + { + do + { + work->field_284 = 1; + work->control.radar_atr |= RADAR_SIGHT; + } while (0); + } + else if (type == HASH_SOUND_ON) + { + work->field_28A = 1; + } + else if (type == HASH_SOUND_OFF) + { + work->field_28A = 0; + } + return type; +} + +void CameraAct_800D5F64(Work *work) +{ + MATRIX mat; + SVECTOR svec1; + SVECTOR svec2; + CONTROL *ctrl; + TARGET *target; + int i; + + s01a_camera_800D5EC0(work); + ctrl = &work->control; + if (work->field_1F0 == 0) + { + GM_ActControl(ctrl); + GM_ActObject2((OBJECT *)&work->body); + DG_PutPrim(work->field_194); + if (work->body.objs->bound_mode != 0) + { + DG_GetLightMatrix2(&ctrl->mov, &work->light); + } + + target = work->target; + GM_MoveTarget(target, &ctrl->mov); + if (target->damaged & TARGET_POWER) + { + if (target->a_mode != 2) + { + target->damage = 0; + target->damaged = 0; + } + else + { + if (work->field_28C >= 0) + { + GCL_ExecProc(work->field_28C, NULL); + } + work->field_1F0 = 1; + AN_Blast_Mini(&ctrl->mov); + for (i = 0; i < 8; i++) + { + svec1.vz = 0; + svec2.vx = GV_RandS(128); + svec2.vy = GV_RandU(256) + 32; + svec2.vz = 0; + svec1.vx = GV_RandU(256) + 64; + + svec1.vy = work->control.rot.vy + GV_RandS(1024); + RotMatrixYXZ_gte(&svec1, &mat); + ApplyMatrixSV(&mat, &svec2, &work->field_1FC[i]); + work->field_23C[i] = work->control.mov; + } + } + } + } + else + { + s01a_camera_800D5D1C(work); + } + if (work->field_284 != 0) + { + s01a_camera_800D5C7C(work); + s01a_camera_800D5D10(work); + DG_VisiblePrim(work->field_194); + work->control.radar_atr |= RADAR_SIGHT; + } + else + { + DG_InvisiblePrim(work->field_194); + work->control.radar_atr &= ~RADAR_SIGHT; + } +} + +void s01a_camera_800D6174(Work *work) +{ + CONTROL *ctrl; + RADAR_SIGHT_PARAM *r_param; + + ctrl = &work->control; + ctrl->radar_atr |= RADAR_UNK3; + + r_param = &work->control.radar_param; + r_param->dir = 0; + r_param->dis = work->field_27E; + r_param->range = work->field_280 * 2; + r_param->r = 0; +} + +int s01a_camera_800D61AC(Work *work, int arg1, int arg2) +{ + char *opt; + + opt = GCL_GetOption('m'); + if (opt) + { + // field_27C is set to 0 after the if... + work->field_27C = GCL_StrToInt(opt); + } + else + { + work->field_27C = 0; + } + work->field_27C = 0; // lol + + opt = GCL_GetOption('l'); + if (opt) + { + work->field_27E = GCL_StrToInt(opt); + } + else + { + work->field_27E = 0; + } + + opt = GCL_GetOption('w'); + if (opt) + { + work->field_280 = GCL_StrToInt(opt); + if (work->field_280 > 512) + { + work->field_280 = 512; + } + } + else + { + work->field_280 = 0; + } + + work->field_1C8 = 0; + if (GCL_GetOption('h')) + { + work->field_1C8 = 1; + } + + opt = GCL_GetOption('x'); + if (opt) + { + work->field_282 = GCL_StrToInt(opt); + if (work->field_282 > 1024) + { + work->field_282 = 1024; + } + } + else + { + work->field_282 = 0; + } + + work->control.mov.vy += 25; + work->control.mov.vz += 450; + + work->field_EC = work->control.rot; + work->field_1C0 = work->field_EC; + if (work->field_1C0.vx > 512) + { + work->field_1C0.vx = 512; + } + work->field_EC.vx = 0; + s01a_camera_800D4CFC(work->field_194, work->field_198, 0, 0xFF, 0); + DG_SetPos2(&work->control.mov, &work->field_EC); + DG_MovePos(&work->field_E4); + GM_ActObject2((OBJECT *)&work->field_C0); + opt = GCL_GetOption('r'); + if (opt) + { + work->field_19C = s01a_camera_800D4D28(opt, &work->field_1A0); + work->field_19E = 0; + } + else + { + work->field_19C = 0; + } + if (work->field_27C == 2) + { + work->field_1A8 = work->field_1A0; + } + if (GCL_GetOption('e')) + { + work->field_28C = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->field_28C = -1; + } + s01a_camera_800D6174(work); + work->field_1E0 = 0; + if (work->field_27C == 2) + { + work->field_1E8 = 3; + } + else + { + work->field_1E8 = 1; + } + work->field_1EC = 0; + work->field_1F0 = 0; + return 0; +} + +int InitArm_800D640C(Work *work) +{ + OBJECT_NO_ROTS *obj; + + obj = &work->field_C0; + GM_InitObjectNoRots(obj, GV_StrCode("cam_arm"), 0x36D, 0); + GM_ConfigObjectLight((OBJECT *)obj, &work->field_F4); + DG_GetLightMatrix2(&work->control.mov, &work->field_F4); + work->field_E4.vy = -25; + work->field_E4.vx = 0; + work->field_E4.vz = -450; + + return 0; +} + +// duplicate of s03e_guncame_800C8E04 +void s01a_camera_800D648C(POLY_FT4 *poly, DG_TEX *tex, int col) +{ + char height; + char width; + unsigned char x_offset; + unsigned char y_offset; + + setPolyFT4(poly); + setRGB0(poly, col, col, col); + setSemiTrans(poly, 1); + + x_offset = tex->off_x; + width = x_offset + tex->w; + + y_offset = tex->off_y; + height = y_offset + tex->h; + + setUV4(poly, x_offset, y_offset, width, y_offset, x_offset, height, width, height); + + poly->tpage = tex->tpage; + poly->clut = (unsigned short)tex->clut; +} + +int InitLed_800D6504(Work *work) +{ + DG_PRIM *prim; + DG_TEX *tex; + + work->field_184.vx = 50; + work->field_184.vy = 300; + work->field_184.vz = 350; + prim = GM_MakePrim(DG_PRIM_RECTANGLE | DG_PRIM_POLY_FT4, 1, &work->field_184, &camera_rect_800C3B68); + work->field_194 = prim; + if (prim != NULL) + { + prim->raise = 500; + work->field_198 = tex = DG_GetTexture(GV_StrCode("camera_l")); + if (tex != NULL) + { + s01a_camera_800D648C(prim->packs[0], tex, 0x80); + s01a_camera_800D648C(prim->packs[1], tex, 0x64); + return 0; + } + } + return -1; +} + +int CameraGetResources_800D65EC(Work *work, int arg1, int arg2) +{ + TARGET *target; + int type; + char *opt; + CONTROL *ctrl; + OBJECT_NO_ROTS *body; + + ctrl = &work->control; + if (GM_InitControl(ctrl, arg1, arg2) < 0) + { + return -1; + } + + GM_ConfigControlString(ctrl, GCL_GetOption('p'), GCL_GetOption('d')); + GM_ConfigControlAttribute(ctrl, 0x49); + GM_ConfigControlHazard(ctrl, -1, -2, -1); + + work->control.step = DG_ZeroVector; + + type = 0x41; + body = &work->body; + + opt = GCL_GetOption('n'); + if (opt) + { + type = GCL_StrToInt(opt); + } + + // Dead code (if with identical cases): + if (type == 0x41) + { + opt = "s_camera"; + } + else + { + opt = "s_camera"; + } + GM_InitObjectNoRots(body, GV_StrCode(opt), 0x32D, 0); + + body->objs->rots = work->rots; + GM_ConfigObjectLight((OBJECT *)body, &work->light); + GM_ConfigObjectStep((OBJECT *)body, &work->control.step); + DG_GetLightMatrix2(&ctrl->mov, &work->light); + + work->lens_shift.vx = LENS_DX; + work->lens_shift.vy = LENS_DY; + work->lens_shift.vz = LENS_DZ; + + InitArm_800D640C(work); + InitLed_800D6504(work); + +/* +#ifdef DEBUGVIEW + InitView( work ); +#endif +*/ + +/* ターゲット確保 */ + work->target = target = GM_AllocTarget(); + if (target) + { + GM_SetTarget(target, 20, ENEMY_SIDE, &Size_800C3B70); + } + else + { + return -1; + } + + work->field_284 = 1; + work->field_286 = 0; + work->field_288 = 1; + work->field_28A = 1; + return 0; +} + +void CameraDie_800D678C(Work *work) +{ + GM_FreeControl(&work->control); + GM_FreeObject((OBJECT *)&work->body); + GM_FreeObject((OBJECT *)&work->field_C0); + GM_FreePrim(work->field_194); + GM_FreeTarget(work->target); +} + +void *NewCamera_800D67F8(int name, int where, int argc, char **argv) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, CameraAct_800D5F64, CameraDie_800D678C, "camera.c"); + if (CameraGetResources_800D65EC(work, name, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + s01a_camera_800D61AC(work, name, where); + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/camera.h b/components/mgs/mgs/source/enemy/camera.h new file mode 100644 index 00000000..8afa85cc --- /dev/null +++ b/components/mgs/mgs/source/enemy/camera.h @@ -0,0 +1,6 @@ +#ifndef __MGS_ENEMY_CAMERA_H__ +#define __MGS_ENEMY_CAMERA_H__ + +void *NewCamera_800D67F8(int name, int where, int argc, char **argv); + +#endif // __MGS_ENEMY_ENEMY_CAMERA_H__ diff --git a/components/mgs/mgs/source/enemy/check.c b/components/mgs/mgs/source/enemy/check.c new file mode 100644 index 00000000..87ee6488 --- /dev/null +++ b/components/mgs/mgs/source/enemy/check.c @@ -0,0 +1,436 @@ +#include "enemy.h" + +#include +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" + +extern void s00a_command_800C6BCC( WatcherWork *work, int time ); +extern void s00a_command_800C6FA8( WatcherWork *work, int time ); + +extern ENEMY_COMMAND EnemyCommand; +extern SVECTOR ENEMY_TARGET_SIZE; +extern SVECTOR ENEMY_TARGET_FORCE; +extern SVECTOR ENEMY_ATTACK_SIZE; +extern SVECTOR ENEMY_ATTACK_FORCE; +extern SVECTOR ENEMY_TOUCH_SIZE; +extern SVECTOR ENEMY_TOUCH_FORCE; +extern TOPCOMMAND_STRUCT TOPCOMMAND; +extern SVECTOR COM_NO_POINT; +extern int COM_EYE_LENGTH; +extern int COM_PlayerAddressOne[8]; + +int s00a_command_800C50B0( WatcherWork *work ) +{ + TARGET *target; + + target= work->target; + if ( target->damaged & 2 ) + { + if ( target->a_mode == 0x24 ) + { + SetMode( work, s00a_command_800C6BCC ) ; + } + else + { + SetMode( work, s00a_command_800C6FA8 ) ; + } + target->damaged = 0; + return 1; + } + return 0; +} + +int s00a_command_800C513C( WatcherWork* work ) { + int check; + check = work->target->damaged & 0x20; + return check > 0; +} + +int s00a_command_800C5158( SVECTOR* svec ) +{ + return abs( svec->vx ) + abs( svec->vy ) + abs( svec->vz ) ; +} + +extern int s00a_command_800C5158( SVECTOR* svec ) ; + +extern int s00a_command_800C5194( WatcherWork *work ); + +int s00a_command_800C5194( WatcherWork *work ) { + short val; + TARGET *target; + + target = work->target; + if ( !( target->damaged & TARGET_POWER ) ) return 0; + + val = target->a_mode - 1; + switch ( val ) + { + case 2: + target->force = DG_ZeroVector; + if ( target->faint <= 0 ) + { + work->unknown.field_14 = 1; + SetMode( work, s00a_command_800C78E0 ) ; + } + else + { + SetMode( work, s00a_command_800C77C8 ) ; + } + break; + case 3: + work->unknown.field_14 = 0; + SetMode( work, s00a_command_800C78E0 ) ; + break; + case 0: + target->force = DG_ZeroVector; + if ( target->vital <= 0 ) + { + work->unknown.field_14 = 1; + SetMode( work, s00a_command_800C78E0 ) ; + } + else + { + SetMode( work, s00a_command_800C77C8 ) ; + } + break; + case 1: + if ( s00a_command_800C5158( &target->force ) < 100 ) + { + work->unknown.field_14 = 3; + } + else + { + work->unknown.field_14 = 2; + } + SetMode( work, s00a_command_800C78E0 ) ; + break; + case 4: + work->unknown.field_14 = 3; + SetMode( work, s00a_command_800C78E0 ) ; + break; + case 8: + SetMode( work, s00a_command_800C7354 ) ; + break; + } + + target->damage = 0; + target->damaged = 0; + return 1; +} + +const char aPlayxresetdwarpd_800DFD1C[] = " play = %x reset = %d warp = %d \n"; +const char aErrnozoneidingcl_800DFD40[] = "!!!!!!Err No Zone ID In Gcl !!!!!!!!\n"; +const char aErrerrenemyresetnumerrcallkorekadoooooooooo_800DFD68[] = " Err Err Enemy Reset Num Err CALL KOREKADOOOOOOOOOO!!\n"; + +const SVECTOR rp_shift_800DFDA0 = { -200, 100, 600 }; /* 右パンチ */ +const SVECTOR force_800DFDA8 = { 5, 0, 100 }; +const SVECTOR size_800DFDB0 = { 400, 1000, 400 }; + +int s00a_command_800C5370( WatcherWork * work ) +{ + if ( work->target->class & TARGET_POWER && GM_GameStatus & STATE_STUN ) + { + SetMode( work, s00a_command_800C7354 ) ; + + if ( TOPCOMMAND.mode == TOP_COMM_TRAVEL ) + { + work->target->faint = 0; + } + else + { + work->target->faint -= 5; + } + + return 1; + } + + return 0; +} + +int CheckDamage_800C5424( WatcherWork * work ) +{ + if ( s00a_command_800C5194( work ) || s00a_command_800C50B0( work ) || s00a_command_800C5370( work ) ) + { + work->target->class = TARGET_AVAIL ; + return 1; + } + return 0; +} + +void InitTarget_800C5484( WatcherWork *work ) +{ + TARGET *target; + TARGET *target2; + int life; + int faint; + + target = work->target; + life = work->param_life; + faint = work->param_faint; + + GM_SetTarget( target, TARGET_FLAG, ENEMY_SIDE, &ENEMY_TARGET_SIZE ); + GM_SetPowerTarget( target, POWER_DECREASE, -1, life, faint, &ENEMY_TARGET_FORCE ); + GM_SetCaptureTarget( target, -1, faint, NULL, NULL); + + GM_TargetBody( target, &( work->body.objs->objs[1].world ) ); + + target2 = &work->field_904; + GM_SetTarget( target2, TARGET_POWER, PLAYER_SIDE, &ENEMY_ATTACK_SIZE ); + GM_SetPowerTarget( target2, POWER_CONST | POWER_EXPLODE, 5, 0, 3, &ENEMY_ATTACK_FORCE ); + + target2 = &work->field_94C; + GM_SetTarget( target2, ( TARGET_TOUCH ), ENEMY_SIDE, &ENEMY_TOUCH_SIZE ); + GM_SetPowerTarget( target2, POWER_CONST | POWER_EXPLODE, 5, 0, 0, &ENEMY_TOUCH_FORCE ); +} + +void s00a_command_800C55B0( WatcherWork* work ) +{ + int val; + int warp; + char reset_num; + HZD_ZON *zone; + CONTROL *ctrl; + MAP *map; + ctrl = &work->control; + + work->visible = 1; + GM_ConfigControlAttribute( ctrl, 0xD ); + InitTarget_800C5484( work ); + + warp = HZD_BoundOutZone( EnemyCommand.map->hzd, (char)COM_PlayerAddressOne[ work->field_B78 ], EnemyCommand.field_0x58 [ EnemyCommand.c_reset_pos ], 200 ); + + printf( (char *)aPlayxresetdwarpd_800DFD1C, (char)COM_PlayerAddressOne[ work->field_B78 ], EnemyCommand.field_0x58 [ EnemyCommand.c_reset_pos ], warp ); + + zone = &ctrl->map->hzd->def->zones[ warp ]; + ctrl->mov.vx = zone->x; + ctrl->mov.vy = zone->y + 1000; + ctrl->mov.vz = zone->z; + + map = GM_FindMapZone( 1 << zone->padding ); + + if ( map ) + { + ctrl->map = map; + } + else + { + printf((char *)aErrnozoneidingcl_800DFD40); + } + + work->think1 = 0; + work->think2 = 0; + work->think3 = 0; + work->think4 = 0; + work->count3 = 0; + + work->next_node = work->n_nodes - 1; + work->target_addr = work->start_addr; + work->target_pos = work->start_pos; + + work->vision.angle = 512; + work->vision.length = COM_EYE_LENGTH; + + work->alert_level = 0; + work->vision.facedir = 0; + work->pad.sound = 0; + work->pad.mode = 1; + work->field_C48 = 0; + work->target_map = work->start_map; + work->field_C14 = work->start_pos; + + val = work->start_addr; + work->field_C08 = val; + work->field_BF0 = val; + + work->field_BA4 = COM_NO_POINT; + work->field_BA2 = 0; + GM_TargetBody( work->target, &( work->body.objs->objs[1].world ) ); + + reset_num = work->field_B81; + if ( reset_num != 0xFF ) + { + if ( !reset_num ) + { + printf ( (char *)aErrerrenemyresetnumerrcallkorekadoooooooooo_800DFD68 ) ; + } + else + { + work->field_B81 = reset_num - 1; + } + } +} + +int s00a_command_800C580C( int dir, int dist ) +{ + int diff ; + if ( dist < 0 || dir < 0 ) return 0 ; + + diff = GV_DiffDirS( dir, dist ) ; + + if ( diff + 0x380 > 0x700u ) return 0 ; + if ( diff >= 0 ) return 1 ; + + return 2 ; +} + +void s00a_command_800C5860( WatcherWork* work ) +{ + int trans; + int near; + + trans = ( ( GM_PlayerBody->objs->objs[6].world.t[1] - work->body.objs->objs[6].world.t[1] ) ); + trans = ratan2( work->sn_dis, trans ) & 0xFFF; + + near = GV_NearExp8( work->adjust[2].vx, trans - 0x400 ); + work->adjust[2].vx = near; + work->adjust[6].vx = near; + + if ( near < 0 ) + { + work->adjust[7].vx = near * 3 ; + } + else + { + work->adjust[7].vx = ( near * 3 ) / 2 ; + } +} + +/* + 打撃攻撃 + ヒットすれば1を返す + */ +int AttackForce_800C58E8( WatcherWork * work ) +{ + TARGET *target; + SVECTOR svec; + SVECTOR rp_shift = rp_shift_800DFDA0; /* 右パンチ */ + SVECTOR force = force_800DFDA8; + SVECTOR size = size_800DFDB0; + + target = &work->punch; + GM_SetTarget( target, 4, ENEMY_SIDE, &size ); + DG_SetPos2( &work->control.mov, &work->control.rot ); + DG_RotVector( &force, &svec, 1 ); + GM_SetPowerTarget( target, POWER_ONCE, 2, 32, 1, &svec ); + DG_PutVector( &rp_shift, &work->punch.center, 1 ); + return GM_PowerTarget( target ); +} + +void s00a_command_800C59F8( WatcherWork *work ) +{ + TARGET* target; + + target = &work->field_904; + GM_SetPowerTarget(target, POWER_CONST | POWER_EXPLODE, 5, 0, 3, &ENEMY_ATTACK_FORCE); + GM_MoveTarget( target, &work->control.mov ); + GM_PowerTarget( target ); +} + +int CheckPad_800C5A60( WatcherWork *work ) +{ + int press = work->pad.press; + + if ( press & 0x01 ) + { + SetMode2( work, s00a_command_800C841C ); + return 0; + } + + if ( press & 0x02 ) + { + SetMode2( work, s00a_command_800C84FC ); + return 0; + } + + if ( press & 0x04 ) + { + SetMode2( work, ActOverScoutD_800C85DC ); // //ActOverScoutD_800C85DC + return 0; + } + + if ( press & 0x2000 ) + { + SetMode2( work, s00a_command_800C8688 ); + return 0; + } + + if ( press & 0x40 ) + { + SetMode2( work, s00a_command_800C8734 ); + return 0; + } + + if ( press & 0x80 ) + { + SetMode2( work, s00a_command_800C87FC ); + return 0; + } + + if ( press & 0x400 ) + { + SetMode2( work, s00a_command_800C88D8 ); + return 0; + } + + if ( press & 0x800 ) + { + SetMode2( work, s00a_command_800C8990 ); + return 0; + } + + if ( press & 0x200 ) + { + SetMode2( work, s00a_command_800C8A6C ); + return 0; + } + + if ( press & 0x800000 ) + { + SetMode2( work, s00a_command_800C8C98 ); + return 0; + } + + if ( press & 0x1000000 ) + { + SetMode2( work, s00a_command_800C8DF8 ); + return 0; + } + + if ( press & 0x100 ) + { + SetMode( work, s00a_command_800C615C ); + return 1; + } + + if ( press & 0x20 ) + { + SetMode( work, s00a_command_800C6164 ); + return 1; + } + + if ( press & 0x2000000 ) + { + SetMode( work, s00a_command_800C624C ); + return 1; + } + + if ( press & SP_DANBOWLKERI ) + { + SetMode( work, s00a_command_800C6320 ); + return 1; + } + + if ( press & 0x30000 ) + { + SetMode( work, s00a_command_800C65A8 ); + work->adjust[2].vx = 0; + return 1; + } + + return 0; +} diff --git a/components/mgs/mgs/source/enemy/command.c b/components/mgs/mgs/source/enemy/command.c new file mode 100644 index 00000000..f32ffd6d --- /dev/null +++ b/components/mgs/mgs/source/enemy/command.c @@ -0,0 +1,1325 @@ +#include "command.h" +#include "enemy.h" + +#include +#include +#include +#include + +#include "common.h" +#include "mts/mts.h" // for fprintf +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "game/vibrate.h" +#include "linkvar.h" +#include "sound/g_sound.h" +#include "strcode.h" + +int SECTION(".bss") s00a_dword_800E0CA0; +int SECTION(".bss") s00a_dword_800E0CA4; +int SECTION(".bss") s00a_dword_800E0CA8; +int SECTION(".bss") s00a_dword_800E0CAC; + +GM_CameraSystemWork SECTION(".bss") s00a_dword_800E0CB0; + +int SECTION(".bss") s00a_dword_800E0D2C; +int SECTION(".bss") s00a_dword_800E0D30; +int SECTION(".bss") s00a_dword_800E0D34; + +SVECTOR SECTION(".bss") s00a_dword_800E0D38; + +int SECTION(".bss") COM_PlayerOnZone; + +int SECTION(".bss") COM_NoiseMinDisID; + +SVECTOR SECTION(".bss") COM_PlayerPositionOne[8]; + +int SECTION(".bss") COM_SHOOTRANGE; +int SECTION(".bss") COM_EYE_LENGTH; +int SECTION(".bss") COM_PlayerAddress; +unsigned int SECTION(".bss") ENE_SPECIAL_FLAG; + +ENEMY_COMMAND SECTION(".bss") EnemyCommand; + +int SECTION(".bss") COM_PlayerMap; + +TOPCOMMAND_STRUCT SECTION(".bss") TOPCOMMAND; + +TOPCOMMAND_STRUCT SECTION(".bss") s00a_dword_800E0F28; + +SVECTOR SECTION(".bss") COM_PlayerPosition; + +int SECTION(".bss") COM_NOISEMODE_DIS; + +unsigned int SECTION(".bss") COM_GameStatus; + +int SECTION(".bss") COM_PlayerAddressOne[8]; + +int SECTION(".bss") COM_ALERT_DECREMENT; + +int SECTION(".bss") GM_GameFlag; + +int SECTION(".bss") COM_VibTime; + +int SECTION(".bss") s00a_dword_800E0F6C; + +int SECTION(".bss") COM_PlayerMapOne[8]; + +extern int GM_event_camera_flag; +extern CONTROL *GM_WhereList[94]; +extern GM_CameraSystemWork GM_Camera; + +SVECTOR ENEMY_TARGET_SIZE = { 300, 1000, 300 }; +SVECTOR ENEMY_TARGET_FORCE = { 0, 0, 0 }; +SVECTOR ENEMY_ATTACK_SIZE = { 800, 500, 800 }; +SVECTOR ENEMY_ATTACK_FORCE = { 100, 0, 0 }; +SVECTOR ENEMY_TOUCH_SIZE = { 300, 800, 300 }; +SVECTOR ENEMY_TOUCH_FORCE = { 0, 0, 0, 0 }; +SVECTOR COM_NO_POINT = { 30000, 30000, 30000 }; + +unsigned char s00a_dword_800C35DC[4] = { 1, 1, 0, 0 }; +unsigned char s00a_dword_800C35E0[4] = { 255, 1, 0, 0 }; + +int s00a_dword_800C35E4[] = { + 0x000009C4, + 0x00000DAC, + 0x00000FA0, + 0x00001388, + 0x0000157C, + 0x000015E0, + 0x00001644, + 0x000016A8 +}; + +const char aCresetposd_800E07FC[] = " c_reset_pos = %d \n"; +const char aKottida_800E0810[] = "kottida !!\n"; + +#define EXEC_LEVEL GV_ACTOR_PREV + +int s00a_command_800CEA2C( WatcherWork *work ) +{ + int i; + for ( i = 0 ; i < 8 ; i++ ) + { + if ( EnemyCommand.field_0xC8[i].watcher == 0 ) + { + EnemyCommand.field_0xC8[i].watcher = work; //update to be a struct with watcher ptr as 4th attribute + EnemyCommand.field_0xC8[i].field_04 = 2; + COM_PlayerAddressOne[i] = -1; + return i; + } + } + + return -1; +} + +void s00a_command_800CEA84( int idx, int val ) +{ + EnemyCommand.field_0x148[ idx ] = val; +} + +int s00a_command_800CEA9C( int val ) +{ + int i; + for ( i = 0 ; i < EnemyCommand.field_0x08 ; i++ ) + { + if ( EnemyCommand.field_0x148[i] == val ) + { + return 1; + } + } + return 0; +} + +void ENE_SetTopCommAL( int alert ) +{ + TOPCOMMAND.alert = alert; +} + +void ENE_SetTopCommMD( int mode ) +{ + TOPCOMMAND.mode = mode; +} + +void ENE_SetGopointLast(void) +{ + EnemyCommand.com_addr = COM_PlayerAddress; + EnemyCommand.com_pos = COM_PlayerPosition; + EnemyCommand.com_map = COM_PlayerMap; + EnemyCommand.field_0x40 = 1; +} + +void s00a_command_800CEB54(void) +{ + EnemyCommand.com_addr = HZD_GetAddress( GM_WhereList[0]->map->hzd, &GM_NoisePosition, -1 ); + EnemyCommand.com_pos = GM_NoisePosition; + EnemyCommand.com_map = GM_PlayerMap; +} + +int s00a_command_800CEBCC( int map_id, int val ) +{ + int i; + A4_STRUCT *unk = &EnemyCommand.field_0xA4; + + if ( unk->map_id == map_id ) + { + for ( i = 0 ; i < unk->n_entry ; i++ ) + { + if ( unk->field_04[i].field_00 == val ) + { + return unk->field_04[i].field_02; + } + } + goto end; + } + return 0; +end: + return -1; +} + +void s00a_command_800CEC40( SVECTOR *mov , int flag ) +{ + s00a_dword_800E0D38.vx = mov->vx; + s00a_dword_800E0D30 |= 1 | flag; + s00a_dword_800E0D38.vy = mov->vy; + s00a_dword_800E0D38.vz = mov->vz; + + if ( s00a_dword_800E0CA0 == 0 ) + { + s00a_dword_800E0CA0 = 32; + } +} + +void s00a_command_800CEC90( void ) +{ + if ( COM_VibTime > 0 ) + { + NewPadVibration( s00a_dword_800C35DC, 1 ); + NewPadVibration( s00a_dword_800C35E0, 2 ); + COM_VibTime--; + return; + } + + COM_VibTime = 0; +} + +void s00a_command_800CECF4(void) +{ + if ( s00a_dword_800E0CA0 == 32 ) + { + GM_SeSet2( 0, 0x3F, SE_EXCLAMATION ); + } + + s00a_dword_800E0CA0--; + + if ( s00a_dword_800E0CA0 < 0 ) + { + s00a_dword_800E0CA0 = 0; + } +} + +void s00a_command_800CED48( void ) +{ + int i; + for ( i = 0; i < EnemyCommand.field_0x08 ; i++ ) + { + EnemyCommand.field_0x148[i] = 255; + } +} + +int s00a_command_800CED88( char *ops, A4_STRUCT *unk ) +{ + int i; + i = 0; + + do { + unk->field_04[i].field_00 = GCL_StrToInt( GCL_NextStr() ); + unk->field_04[i].field_02 = GCL_StrToInt( GCL_NextStr() ); + i++; + } while ( GCL_NextStr() != NULL ); + + return i; +} + +int s00a_command_800CEDE8( char *ops, short *addr, int map_id ) +{ + int i; + MAP *map; + SVECTOR svec; + unsigned char *res; + map = GM_GetMap( map_id ); + + for ( i = 0; ( res = GCL_NextStr() ) && i < 4 ; i++ ) + { + GCL_StrToSV( res, (short *)&svec ); + + if ( svec.vy < 30000 ) + { + addr[i] = HZD_GetAddress( map->hzd, &svec, -1 ) & 0xFF; + } + else + { + addr[i] = 255; + } + } + + return i; +} + +void s00a_command_800CEE98(void) +{ + int i; + int s0; + int dist2; + int total; + int delta; + short dist1; + int reset_pos; + HZD_ZON *zone; + SVECTOR svec; + + i = 0; + delta = GV_Time % EnemyCommand.field_0x54; + total = EnemyCommand.field_0x54; + reset_pos = 0; + + for ( ; i < total ; i++ ) + { + s0 = i + delta; + if ( s0 >= total ) + { + s0 -= total; + } + + zone = &EnemyCommand.map->hzd->def->zones[ EnemyCommand.field_0x58[ s0 ] ]; + + svec.vx = zone->x; + svec.vy = GM_PlayerPosition.vy; + svec.vz = zone->z; + + dist1 = 10000; + dist2 = GV_DiffVec3( &svec, &GM_PlayerPosition ); + if ( dist1 < dist2 ) + { + reset_pos = s0; + break; + } + + } + + EnemyCommand.c_reset_pos = reset_pos; + fprintf( 1, aCresetposd_800E07FC, reset_pos ); +} + +int s00a_command_800CEFE4( int val ) +{ + return EnemyCommand.field_0x0C % val; +} + +void s00a_command_800CF024( int *val ) +{ + if ( COM_GameStatus & 1 ) + { + *val = VO_ENEMY_LETSGO; + } + else + { + *val = VO_ENEMY_WHOSTHAT; + } +} + +void s00a_command_800CF050( int *val ) +{ + *val = VO_ENEMY_THATNOISE; +} + +void s00a_command_800CF05C( int *val ) +{ + *val = VO_ENEMY_JUSTABOX; +} + +void s00a_command_800CF068( int *val ) +{ + *val = VO_ENEMY_FOOTPRINTS; +} + +void s00a_command_800CF074( int *val ) +{ + *val = VO_ENEMY_NOTHINGHERE; +} + +void s00a_command_800CF080( int* val ) +{ + switch( s00a_command_800CEFE4( 3 ) ) + { + case 0: + *val = VO_ENEMY_LETSGO; + break; + case 1: + case 2: + *val = VO_ENEMY_THISWAY; + break; + } +} + +void s00a_command_800CF0CC( int *val ) +{ + *val = VO_ENEMY_RETURN; +} + +void s00a_command_800CF0D8( int* val ) +{ + switch ( s00a_command_800CEFE4( 2 ) ) + { + case 0: + fprintf( 1, aKottida_800E0810 ); + *val = VO_ENEMY_THISWAY; + return; + case 1: + *val = 0; + return; + } +} + +int s00a_command_800CF13C( int val ) +{ + switch( val ) + { + case 240: + s00a_command_800CF024( &val ); + break; + case 241: + s00a_command_800CF050( &val ); + break; + case 242: + s00a_command_800CF05C( &val ); + break; + case 243: + s00a_command_800CF068( &val ); + break; + case 244: + s00a_command_800CF074( &val ); + break; + case 245: + s00a_command_800CF0CC( &val ); + break; + case 246: + s00a_command_800CF080( &val ); + break; + case 247: + s00a_command_800CF0D8( &val ); + break; + } + + return val; +} + +void s00a_command_800CF200(void) +{ + if ( EnemyCommand.field_0x170 < 6000 && ( mts_get_tick_count() - EnemyCommand.field_0x174 ) > 40 ) + { + if ( EnemyCommand.field_0x17A && GM_CurrentWeaponId != WP_Rifle ) + { + GM_SeSetMode( &GM_PlayerPosition, SE_HEARTBEAT, GM_SEMODE_REAL ); + } + EnemyCommand.field_0x174 = mts_get_tick_count(); + } +} + +void s00a_command_800CF298( ENEMY_COMMAND* command ) +{ + int i; + int t2; + int t3; + int t4; + int dists[8]; + int temp1, temp2; + int temp3, temp4; + WatcherWork *work; + + dists[0] = 0x7530; + + for ( i = 0 ; i < command->field_0x08 ; i ++ ) + { + //WatcherWork *work; + work = command->field_0xC8[ EnemyCommand.field_0x68[ i ] ].watcher; + + if ( work->act_status & 0x10000000 ) + { + dists[i] = 0x7530; + } + else + { + dists[i] = work->sn_dis; + } + } + + for ( t3 = command->field_0x08 - 1 ; t3 > -1 ; t3 = t4 ) + { + t4 = -1; + for ( i = 1 ; t3 >= i ; i ++ ) + { + //TODO, fix without using dowhile + do + { + temp2 = dists[ i - 1 ]; + t2 = i - 1; + } while (0); + + temp1 = dists[ i ]; + + if ( temp1 < temp2 ) + { + dists[ i - 1 ] = temp1; + dists[ i ] = temp2; + + temp4 = EnemyCommand.field_0x68[ i - 1 ]; + temp3 = EnemyCommand.field_0x68[ i ]; + t4 = t2; + + EnemyCommand.field_0x68[ i - 1 ] = temp3; + EnemyCommand.field_0x68[ i ] = temp4; + } + } + } + + EnemyCommand.field_0x170 = dists[0]; + + for ( i = 0 ; i < command->field_0x08 ; i++ ) + { + //WatcherWork *work; + work = command->field_0xC8[ EnemyCommand.field_0x68[ i ] ].watcher; + work->field_BFC = s00a_dword_800C35E4[ i ]; + work->field_C00 = i; + } +} + +void s00a_command_800CF420( ENEMY_COMMAND* command ) +{ + int i; + int dis; + int sound; + WatcherWork *work; + EnemyCommand.field_0x1C--; + if ( EnemyCommand.field_0x1C < 0 ) + { + EnemyCommand.field_0x1C = 0; + } + + if ( EnemyCommand.field_0x1C > 0 ) + { + return; + } + + sound = 0; + dis = 100000; + for ( i = 0 ; i < command->field_0x08 ; i++ ) + { + if ( command->field_0xC8[ i ].field_04 == 2 ) + { + work = command->field_0xC8[ i ].watcher; + if ( work->pad.sound && work->sn_dis < dis ) + { + sound = work->pad.sound; + dis = work->sn_dis; + } + work->pad.sound = SE_DUMMY; + } + } + + if ( sound ) + { + EnemyCommand.field_0x1C = 0x1E; + GM_SeSet( NULL, s00a_command_800CF13C( sound ) ); + } +} + +const char s00a_aResetmaxdnumd_800E083C[] = "reset max=%d num=%d \n"; +const char aGmenemywatchcountd_800E0854[] = " GM_EnemyWatchCount = [%d] \n"; +const char aNowzonedrzoned_800E0874[] = " now zone = %d r_zone=%d\n"; +const char aNotrestrctedaread_800E0890[] = " ? ? ? Not Restrcted Area [%d] !!!!\n"; +const char aErrerrerrnotlinkroutedtod_800E08B8[] = " Err Err Err Not Link Route [%d] to [%d] !!!!\n"; +const char aCommanderrnozoneidingclzdidd_800E08E8[] = "command:!!!Err No Zone ID In Gcl z%d id%d!!!!!!!!\n"; +const char aCommandcwhereissnake_800E091C[] = "command.c: Where Is Snake ????\n"; +const char aCom_noisemode_disD_800E0940[] = " COM_NOISEMODE_DIS =%d \n"; +const char aEeeDDDTD_800E095C[] = "eee %d %d %d t %d \n"; + +void s00a_command_800CF504( WATCHER *struct_c8 , int i ) +{ + int x = struct_c8->field_08; + switch ( x ) + { + case 0: + if ( struct_c8->field_04 == 1 ) + { + struct_c8->field_08 = 1; + struct_c8->field_00 = ((WatcherWork *)(struct_c8->watcher))->field_B78 + 1; + } + return; + case 1: + struct_c8->field_00--; + if ( !struct_c8->field_00 && ((WatcherWork *)(struct_c8->watcher))->field_B81 ) + { + struct_c8->field_08 = 2; + struct_c8->field_00 = 0; + EnemyCommand.reset_enemy_num++; + } + return; + case 2: + switch ( EnemyCommand.mode ) + { + case TOP_COMM_TRAVEL: + if ( EnemyCommand.field_0x168 & 1 ) + { + s00a_command_800CEE98(); + struct_c8->field_04 = x; + struct_c8->field_08 = 0; + } + return; + case TOP_COMM_ALERT: + if ( EnemyCommand.reset_enemy_num < EnemyCommand.reset_enemy_max + 1 || EnemyCommand.reset_enemy_max == 255 ) + { + if ( EnemyCommand.reset_enemy_num < 0 ) + { + EnemyCommand.reset_enemy_num = 0; + } + + s00a_command_800CEE98(); + struct_c8->field_04 = x; + struct_c8->field_08 = 0; + printf( (char *)s00a_aResetmaxdnumd_800E083C, EnemyCommand.reset_enemy_max, EnemyCommand.reset_enemy_num ); + } + return; + } + return; + } +} + +int s00a_command_800CF688( int a0, int a1 ) +{ + if ( a0 < a1 ) + { + a0 = a1; + } + + return a0; +} + +void s00a_command_800CF6A0( int val, ENEMY_COMMAND* command ) +{ + if ( command->alert - val > 4 ) + { + command->alert -= 4; + } + else + { + command->alert = val; + } + + if ( command->alert < TOPCOMMAND.alert ) + { + command->alert = TOPCOMMAND.alert; + } + + ENE_SetTopCommAL( 0 ); +} + +void s00a_command_800CF704( ENEMY_COMMAND *command ) +{ + int alert; + + switch ( command->mode ) + { + default: + TOPCOMMAND.mode = command->mode; + return; + case 0: + COM_EYE_LENGTH = EnemyCommand.field_0x88.vx; + COM_SHOOTRANGE = EnemyCommand.field_0x88.vx + 500; + if ( command->alert >= 255 ) + { + command->alert = 255; + GM_AlertModeSet(3); + command->mode = 1; + EnemyCommand.reset_enemy_num = 0; + COM_GameStatus |= 0x1; + + if ( !( COM_GameStatus & 2 ) ) + { + GM_EnemyWatchCount ++; + printf( (char *)aGmenemywatchcountd_800E0854, GM_EnemyWatchCount ); + } + EnemyCommand.field_0x182 = 0; + } + break; + case 1: + COM_EYE_LENGTH = EnemyCommand.field_0x88.vy; + COM_SHOOTRANGE = EnemyCommand.field_0x88.vy + 500; + + if ( command->alert <= 0 ) + { + GM_AlertModeSet(2); + command->mode = 2; + command->field_0x10 = 300; + GM_SetAlertMax( 0x100 ); + s00a_command_800CED48(); + EnemyCommand.field_0x182 = 1; + return; + } + alert = command->alert; + if ( alert >= 0x101 ) + { + alert = 0x100; + } + GM_SetAlertMax( alert ); + break; + case 2: + COM_EYE_LENGTH = EnemyCommand.field_0x88.vz; + COM_SHOOTRANGE = EnemyCommand.field_0x88.vz + 500; + command->field_0x10--; + if ( command->field_0x10 <= 0 ) + { + GM_AlertModeSet(0); + command->mode = 0; + command->field_0x10 = 0; + EnemyCommand.field_0x180 = 0; + EnemyCommand.field_0x182 = 0; + } + if ( command->alert >= 255 ) + { + command->alert = 0xFF; + GM_AlertModeSet(3); + command->mode = 1; + } + alert = command->field_0x10; + if ( alert >= 0x101 ) + { + alert = 0x100; + } + GM_SetAlertMax( alert ); + s00a_command_800CF200(); + break; + } + + TOPCOMMAND.mode = command->mode; +} + +int s00a_command_800CF940( HZD_HDL *hzd, SVECTOR *pos, SVECTOR *pos2 ) +{ + int from; + int to; + + from = HZD_GetAddress( hzd, pos, -1 ); + + //TODO: fix + do + { + to = HZD_GetAddress( hzd, pos2, -1 ); + do { + return HZD_ZoneDistance( hzd, from & 0xFF, to & 0xFF ); + } while (0); + } while (0); +} + +int s00a_command_800CF9A0( WatcherWork *work, int dis, int idx ) +{ + CONTROL *ctrl; + int x; + + ctrl = &work->control; + + if ( TOPCOMMAND.mode == 1 ) + { + goto exit; + } + + if ( GM_NoisePower != 0x64 ) + { + goto exit; + } + + if ( ((WatcherWork *)EnemyCommand.field_0xC8[ idx ].watcher)->act_status & 0x10000028 ) + { + goto exit; + } + + if ( GV_DiffVec3( &GM_NoisePosition, &ctrl->mov ) >= COM_NOISEMODE_DIS ) + { + goto exit; + } + + x = s00a_command_800CF940( ctrl->map->hzd, &ctrl->mov, &GM_NoisePosition ); + + if ( x < 300 && x < dis ) + { + COM_NoiseMinDisID = idx; + dis = x; + } + + exit: + return dis; +} + +void s00a_command_800CFA94( CommanderWork* work ) +{ + int i; + int dis; + int alert; + WatcherWork *watcher; + + alert = 0; + dis = 0x7530; + + COM_NoiseMinDisID = -1; + EnemyCommand.field_0x98 = 0; + + for ( i = 0; i < EnemyCommand.field_0x08; i++ ) + { + if ( EnemyCommand.field_0xC8[ i ].field_04 == 2 ) + { + watcher = EnemyCommand.field_0xC8[ i ].watcher; + alert = s00a_command_800CF688( alert, watcher->alert_level ); + dis = s00a_command_800CF9A0( watcher, dis, i ); + if ( !watcher->faseout ) + { + EnemyCommand.field_0x98++; + } + } + else if ( EnemyCommand.field_0xC8[ i ].field_04 == 1 ) + { + s00a_command_800CF504( &EnemyCommand.field_0xC8[ i ], i ); + } + } + + s00a_command_800CF420( &EnemyCommand ); + s00a_command_800CF6A0( alert, &EnemyCommand ); + s00a_command_800CF704( &EnemyCommand ); + s00a_command_800CF298( &EnemyCommand ); + + if ( EnemyCommand.field_0x98 == 0 ) + { + s00a_dword_800E0D30 |= 2; + if ( EnemyCommand.field_0x17C >= 0 ) + { + GCL_ExecProc( EnemyCommand.field_0x17C, NULL ); + EnemyCommand.field_0x17C = -1; + } + } + + EnemyCommand.field_0x168 = 0; +} + +int s00a_command_800CFC04( WatcherWork *work, HZD_ZON* zone ) +{ + int i; + for ( i = 0; i < work->field_C34 ; i++ ) + { + if ( zone->padding == work->field_C35[i] ) + { + return 1; + } + } + return 0; +} + +int s00a_command_800CFC4C( WatcherWork* work, int r_zone ) +{ + int addr; + SVECTOR pos; + HZD_HDL *hzd; + CONTROL *ctrl; + HZD_ZON *zone; + int route_addr; + int l_zone_shift; + int r_zone_shift; + + ctrl = &work->control; + hzd = ctrl->map->hzd; + addr = HZD_GetAddress( hzd, &ctrl->mov, -1 ) & 0xFF; + + zone = &hzd->def->zones[ addr ]; + + pos.vx = zone->x; + pos.vy = zone->y; + pos.vz = zone->z; + + r_zone_shift = HZD_ZoneAddress( r_zone ); + + for (;;) + { + printf( (char *)aNowzonedrzoned_800E0874, addr, r_zone ); + + l_zone_shift = HZD_ZoneAddress( addr ); + + route_addr = HZD_Navigate( hzd, l_zone_shift, r_zone_shift, &pos ); + + if ( route_addr == addr ) + { + if ( addr == r_zone ) + { + printf( (char *)aNotrestrctedaread_800E0890, addr ); + return addr; + } + + printf( (char *)aErrerrerrnotlinkroutedtod_800E08B8, addr, r_zone ); + return addr; + } + + zone = &hzd->def->zones[ route_addr ]; + + if ( !s00a_command_800CFC04( work, zone ) ) + { + pos.vx = zone->x; + pos.vy = zone->y; + pos.vz = zone->z; + addr = route_addr; + } + else + { + return addr; + } + } +} + +void s00a_command_800CFDC8( WatcherWork* work, int addr, int idx ) +{ + MAP *map; + SVECTOR *pos; + HZD_ZON *zone; + HZD_ZON *zone2; + HZD_HDL *hzd; + COM_PlayerAddressOne[ idx ] = (addr | addr << 8); + + hzd = work->control.map->hzd; + zone2 = &hzd->def->zones[ addr ]; + zone = zone2; + + pos = &COM_PlayerPositionOne[ idx ]; + + pos->vx = zone->x; + pos->vy = zone->y; + pos->vz = zone->z; + + map = GM_FindMapZone( 1 << zone->padding ); + + if ( map != NULL ) + { + COM_PlayerMapOne[ idx ] = map->index; + } + else + { + printf( (char *)aCommanderrnozoneidingclzdidd_800E08E8, addr, zone->padding ); + } +} + +void s00a_command_800CFEA8( void ) +{ + int i; //s2 + int addr; + int addr2; + int check; + int ret_addr; + WatcherWork *work; + + addr = GM_PlayerAddress & 0xFF; + addr2 = ( GM_PlayerAddress >> 8 ) & 0xFF; + + if ( addr == addr2 && addr != 0xFF ) + { + COM_PlayerAddress = GM_PlayerAddress; + COM_PlayerPosition = GM_PlayerPosition; + COM_PlayerMap = GM_PlayerMap; + } + + if ( addr == 0xFF || addr2 == 0xFF ) + { + COM_PlayerOnZone = 0; + } + else + { + COM_PlayerOnZone = 1; + } + + for ( i = 0; i < EnemyCommand.field_0x08 ; i++ ) + { + work = EnemyCommand.field_0xC8[ i ].watcher; + + if ( addr == addr2 && addr != 0xFF ) + { + + check = 0; + if ( !work->field_C34 || !s00a_command_800CFC04( work, &work->control.map->hzd->def->zones[ addr ] ) ) + { + check = 1; + } + + if ( check ) + { + COM_PlayerAddressOne[ i ] = GM_PlayerAddress; + COM_PlayerPositionOne[ i ] = GM_PlayerPosition; + COM_PlayerMapOne[ i ] = GM_PlayerMap; + } + } + + if ( COM_PlayerAddressOne[ i ] == -1 ) + { + if ( addr != 0xFF ) + { + check = s00a_command_800CFC04( work, &work->control.map->hzd->def->zones[ addr ] ); + + if ( check ) + { + ret_addr = s00a_command_800CFC4C( work, addr ); + s00a_command_800CFDC8( work, ret_addr, i ); + } + else + { + s00a_command_800CFDC8( work, addr, i ); + } + } + else + { + printf( (char *)aCommandcwhereissnake_800E091C ); + } + } + + } +} + +int s00a_command_800D0128( char *arg0 ) +{ + int i; + unsigned char* res; + int proc_id; + + res = arg0; + i = 0; + if ( res ) + { + do + { + proc_id = GCL_StrToInt( res ); + res = GCL_NextStr(); + GCL_ExecProc( proc_id, NULL ); + i++; + } while ( res ); + } + return i; +} + +void s00a_command_800D018C( CommanderWork* work ) +{ + GV_MSG *msg; + int count; + + count = GV_ReceiveMessage( work->name, &msg ); + + for (; count > 0; count--, msg++) + { + switch (msg->message[0]) + { + case HASH_SOUND_ON: + EnemyCommand.field_0x17A = 1; + break; + + case HASH_SOUND_OFF: + EnemyCommand.field_0x17A = 0; + break; + } + } +} + +void s00a_command_800D0218(void) +{ + if ( EnemyCommand.field_0x182 && GM_NoisePower == 0xFF ) + { + EnemyCommand.field_0x182 = 0; + EnemyCommand.field_0x180++; + } +} + +static void Act( CommanderWork* work ) +{ + if (GM_CheckMessage( &work->actor , work->name, HASH_KILL) != NULL) + { + GV_DestroyActor( &work->actor ); + return; + } + + s00a_command_800D018C( work ); + s00a_command_800D0344(); + s00a_command_800CFEA8(); + s00a_command_800D0218(); + s00a_command_800CFA94( work ); + s00a_command_800CEC90(); + s00a_command_800CECF4(); + EnemyCommand.field_0x0C++; +} + +static void Die( CommanderWork* work ) +{ + /* do nothing */ +} + +void s00a_command_800D02F4(void) +{ + if ( GM_PlayerBody->objs->bound_mode == 2 ) + { + GM_Camera.track = GV_NearExp8( GM_Camera.track, 4000 ); + } +} + +void s00a_command_800D0344(void) +{ + int flag; + if ( !( COM_GameStatus & 0x1000 ) ) + { + return; + } + + if ( s00a_dword_800E0D2C > 4 ) + { + if ( !( GM_Camera.flag & 3 ) ) + { + s00a_dword_800E0CB0 = GM_Camera; + flag = 0x40; + if ( !GM_event_camera_flag ) + { + flag = 0x80; + } + GM_Camera.flag |= ( 2 | flag ); + GM_SetCameraCallbackFunc( 1, s00a_command_800D02F4 ); + } + } + s00a_dword_800E0D2C++; +} + +/*---------------------------------------------------------------------------*/ + +void SetCameraActCall(void) +{ + if ( COM_GameStatus & 0x1000 ) return; + + COM_GameStatus |= 0x1000 ; + GM_GameStatus |= STATE_PADRELEASE ; + s00a_dword_800E0D2C = 0 ; + +} + +void UnsetCameraActCall(void) +{ + if ( !( COM_GameStatus & 0x1000 ) ) return; + + COM_GameStatus &= ~( 0x1000 ) ; + GM_GameStatus &= ~( STATE_PADRELEASE ) ; + + GM_SetCameraCallbackFunc( 1, NULL ) ; + s00a_dword_800E0D2C = 0 ; + GM_Camera.flag &= ~( 2 ) ; +} + +/*---------------------------------------------------------------------------*/ + +static void GetResources( CommanderWork *work, int name, int where ) +{ + int i; + char *ops; + + work->name = name; + + GM_GameFlag = 0; + EnemyCommand.field_0x17A = 1; + + COM_PlayerAddress = 0; + EnemyCommand.alert = 0; + EnemyCommand.field_0x170 = 0; + + EnemyCommand.field_0x180 = 0; + EnemyCommand.field_0x182 = 0; + EnemyCommand.field_0x40 = 0; + + COM_PlayerPosition = DG_ZeroVector; + COM_PlayerMap = where; + COM_VibTime = 0; + s00a_dword_800E0CA0 = 0; + s00a_dword_800E0D30 = 0; + s00a_dword_800E0D2C = 0; + + for ( i = 0 ; i < 8 ; i++ ) + { + EnemyCommand.field_0xC8[i].field_00 = 0; + EnemyCommand.field_0xC8[i].field_04 = 0; + EnemyCommand.field_0xC8[i].field_08 = 0; + EnemyCommand.field_0xC8[i].watcher = 0; + } + + for ( i = 0 ; i < 8 ; i++ ) + { + EnemyCommand.field_0x68[i] = i; + EnemyCommand.field_0x20[i] = 0; + EnemyCommand.field_0x148[i] = 0xFF; + } + //v_option( where ); + EnemyCommand.field_0x0C = 0; + EnemyCommand.mode = 0; + + TOPCOMMAND.mode = 0; + TOPCOMMAND.alert= 0; + EnemyCommand.map = GM_GetMap( where ); + + ops = GCL_GetOption( 'v' ); + if ( ops ) + { + EnemyCommand.field_0x54 = s00a_command_800CEDE8( ops, EnemyCommand.field_0x58 , where ); + } + else + { + EnemyCommand.field_0x54 = 1; + EnemyCommand.field_0x58[0] = 0; + } + + EnemyCommand.reset_enemy_max = 5; + + ops = GCL_GetOption( 'b'); + if ( ops ) + { + EnemyCommand.reset_enemy_max = GCL_StrToInt( ops ); + } + + EnemyCommand.field_0x88.vx = 0xFA0; + EnemyCommand.field_0x88.vy = 0xFA0; + EnemyCommand.field_0x88.vz = 0xFA0; + + ops = GCL_GetOption( 'l' ); + if ( ops ) + { + GCL_StrToSV( ops, (short *)&EnemyCommand.field_0x88 ); + } + + //loc_800D06A0: + COM_NOISEMODE_DIS = 0x1F40; + + ops = GCL_GetOption( 'y' ); + if ( ops ) + { + COM_NOISEMODE_DIS = GCL_StrToInt( ops ); + } + + printf( ( void* )&aCom_noisemode_disD_800E0940 , COM_NOISEMODE_DIS ); + EnemyCommand.field_0x88.pad = 0; + + ops = GCL_GetOption( 't' ); + if ( ops ) { + EnemyCommand.field_0x88.pad = GCL_StrToInt( ops ); + } + + switch ( EnemyCommand.field_0x88.pad ) + { + case 0: + COM_ALERT_DECREMENT = 4; + break; + case 1: + COM_ALERT_DECREMENT = 3; + break; + case 2: + COM_ALERT_DECREMENT = EnemyCommand.field_0x88.pad; + break; + default: + COM_ALERT_DECREMENT = 4; + } + + printf( ( void* )&aEeeDDDTD_800E095C, EnemyCommand.field_0x88.vx, EnemyCommand.field_0x88.vy, EnemyCommand.field_0x88.vz, COM_ALERT_DECREMENT ); + + EnemyCommand.field_0x1C = 0x1E; + EnemyCommand.c_reset_pos = 0; + EnemyCommand.field_0x16C = 0; + EnemyCommand.field_0x174 = 0; + + s00a_dword_800E0F28.mode = 0; + EnemyCommand.field_0x178 = 0; + COM_EYE_LENGTH = EnemyCommand.field_0x88.vx; + COM_SHOOTRANGE = EnemyCommand.field_0x88.vx + 0x1F4; + + ops = GCL_GetOption( 'w' ); + if ( ops ) + { + EnemyCommand.field_0x178 = GCL_StrToInt( ops ); + } + + ops = GCL_GetOption( 'z' ); + if ( ops ) + { + *(int*)(&EnemyCommand.field_0x17C) = GCL_StrToInt( GCL_NextStr() ); + } + else + { + *(int*)(&EnemyCommand.field_0x17C) = -1; + } + + COM_GameStatus = 0; + + ops = GCL_GetOption( 'a' ); + if ( ops ) + { + COM_GameStatus |= GCL_StrToInt( ops ); + } + + ops = GCL_GetOption( 'j' ); + if ( ops ) + { + EnemyCommand.field_0xA4.n_entry = s00a_command_800CED88( ops, &EnemyCommand.field_0xA4 ); + } + else + { + EnemyCommand.field_0xA4.n_entry = 1; + EnemyCommand.field_0xA4.field_04->field_00 = 0; + EnemyCommand.field_0xA4.field_04->field_02 = 0; + } + + EnemyCommand.field_0xA4.map_id = where; + EnemyCommand.field_0x08 = 0; + + ops = GCL_GetOption( 'n' ); + if ( ops ) + { + EnemyCommand.field_0x08 = s00a_command_800D0128( ops ); + } + + ops = GCL_GetOption( 'm' ); + if ( ops ) + { + EnemyCommand.field_0x08 += s00a_command_800D0128( ops ); + } + + EnemyCommand.field_0x00 = 0; + + ops = GCL_GetOption( 'c' ); + if ( ops ) + { + EnemyCommand.field_0x00 = s00a_command_800D0128( ops ); + } + + EnemyCommand.field_0x04 = 0; + + ops = GCL_GetOption( 's' ); + if ( ops ) + { + EnemyCommand.field_0x04 = s00a_command_800D0128( ops ); + } +} + +/*---------------------------------------------------------------------------*/ + +void *NewCommander(int name, int where, int argc, char **argv) +{ + CommanderWork *work ; + + work = GV_NewActor( EXEC_LEVEL, sizeof( CommanderWork ) ) ; + if ( work != NULL ) { + GV_SetNamedActor( &( work->actor ), Act, Die, "command.c" ); + GetResources( work, name, where ); + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/command.h b/components/mgs/mgs/source/enemy/command.h new file mode 100644 index 00000000..d3f09fca --- /dev/null +++ b/components/mgs/mgs/source/enemy/command.h @@ -0,0 +1,6 @@ +#ifndef __MGS_ENEMY_COMMAND_H__ +#define __MGS_ENEMY_COMMAND_H__ + +void *NewCommander(int name, int where, int argc, char **argv); + +#endif // __MGS_ENEMY_COMMAND_H__ diff --git a/components/mgs/mgs/source/enemy/demoasi.c b/components/mgs/mgs/source/enemy/demoasi.c new file mode 100644 index 00000000..4a846b34 --- /dev/null +++ b/components/mgs/mgs/source/enemy/demoasi.c @@ -0,0 +1,182 @@ +#include "demoasi.h" + +#include +#include +#include + +#include "common.h" +#include "libdg/libdg.h" +#include "libgv/libgv.h" +#include "game/game.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_PREV + +typedef struct _Work +{ + GV_ACT actor; + int map; + DG_PRIM *prim; + DG_TEX *tex; + MATRIX *world; + char pad2[0x10]; + SVECTOR pos[4]; + int f60; + int f64; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void ShadePacks(DG_PRIM *prim, DG_TEX *tex, int r, int g, int b) +{ + POLY_FT4 *poly; + + poly = prim->packs[0]; + setRGB0(poly, r, g, b); + + poly = prim->packs[1]; + setRGB0(poly, r, g, b); +} + +static void SetPos(SVECTOR *vec, int x, int y, int z) +{ + vec->vx = x; + vec->vy = y; + vec->vz = z; +} + +static void Act(Work *work) +{ + int t; + int shade; + + if (++work->f64 > (work->f60 - 300)) + { + GV_DestroyActor(&work->actor); + } + + t = work->f60 - work->f64; + if (t < 690) + { + shade = 48 - ((690 - t) * 48) / 690; + ShadePacks(work->prim, work->tex, shade, shade, shade); + } +} + +static void InitPacks(POLY_FT4 *poly, DG_TEX *tex, int abr, int r, int g, int b) +{ + setPolyFT4(poly); + setRGB0(poly, r, g, b); + + if (abr < 4) + { + setSemiTrans(poly, 1); + DG_SetPacketTexture4(poly, tex); + poly->tpage = (poly->tpage & ~0x60) | (abr << 5); + } + else + { + DG_SetPacketTexture4(poly, tex); + } +} + +static int GetResources(Work *work, int which, int height, int arg3) +{ + DG_PRIM *prim; + DG_TEX *tex; + + work->f60 = arg3; + + if (which == 1) + { + SetPos(&work->pos[1], -70, 0, -140); + SetPos(&work->pos[0], 70, 0, -140); + SetPos(&work->pos[3], -70, 0, 140); + SetPos(&work->pos[2], 70, 0, 140); + } + else + { + SetPos(&work->pos[0], -70, 0, -140); + SetPos(&work->pos[1], 70, 0, -140); + SetPos(&work->pos[2], -70, 0, 140); + SetPos(&work->pos[3], 70, 0, 140); + } + + prim = GM_MakePrim(DG_PRIM_POLY_FT4, 1, work->pos, NULL); + work->prim = prim; + if (prim == NULL) + { + return -1; + } + + work->f64 = 0; + work->map = GM_CurrentMap; + + work->world->t[1] = height; + + tex = DG_GetTexture(PCX_ASIATO); + work->tex = tex; + if (tex == NULL) + { + return -1; + } + + InitPacks(prim->packs[0], tex, 2, 48, 48, 48); + InitPacks(prim->packs[1], tex, 2, 48, 48, 48); + + DG_SetPos(work->world); + DG_PutPrim(work->prim); + + return 0; +} + +static void Die(Work *work) +{ + GM_FreePrim(work->prim); +} + +/*---------------------------------------------------------------------------*/ + +void *NewDemoAsiato(MATRIX *world, int which, int height) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "demoasi.c"); + + work->world = world; + + if (GetResources(work, which, height, 690) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + + return (void *)work; +} + +void *NewDemoAsiato2(MATRIX *world, int which, int height, int arg3) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "demoasi.c"); + + work->world = world; + + if (GetResources(work, which, height, arg3) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/demoasi.h b/components/mgs/mgs/source/enemy/demoasi.h new file mode 100644 index 00000000..d85f4057 --- /dev/null +++ b/components/mgs/mgs/source/enemy/demoasi.h @@ -0,0 +1,11 @@ +#ifndef __MGS_ENEMY_DEMOASI_H__ +#define __MGS_ENEMY_DEMOASI_H__ + +#include +#include +#include + +void *NewDemoAsiato(MATRIX *world, int which, int height); +void *NewDemoAsiato2(MATRIX *world, int which, int height, int arg3); + +#endif // __MGS_ENEMY_DEMOASI_H__ diff --git a/components/mgs/mgs/source/enemy/demokage.c b/components/mgs/mgs/source/enemy/demokage.c new file mode 100644 index 00000000..bf5fd762 --- /dev/null +++ b/components/mgs/mgs/source/enemy/demokage.c @@ -0,0 +1,242 @@ +#include "demokage.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_USER + +typedef struct _Work +{ + GV_ACT actor; + OBJECT *parent; + DG_PRIM *prim; + SVECTOR f28[4]; + SVECTOR f48; + int *f50; + int *f54; +} Work; + +/*---------------------------------------------------------------------------*/ + +static int Max3(int a, int b, int c) +{ + int max; + + max = MAX(a, b); + max = MAX(max, c); + + return max; +} + +static int Min3(int a, int b, int c) +{ + int min; + + min = MIN(a, b); + min = MIN(min, c); + + return min; +} + +static int d11c_800C42AC(SVECTOR *vec1, SVECTOR *vec2) +{ + SVECTOR diff; + + GV_SubVec3(vec1, vec2, &diff); + return GV_VecDir2(&diff); +} + +static void d11c_800C42D4(Work *work) +{ + SVECTOR sp10[4]; + SVECTOR *vec; + short *index; + int i; + DG_OBJ *obj; + int dist; + int yaw; + int tmp; + int x, z; + SVECTOR *vec2; + + vec = sp10; + index = (short *)&work->f48; + for (i = 0; i < 4; i++, vec++, index++) + { + obj = &work->parent->objs->objs[*index]; + vec->vx = obj->world.t[0]; + + obj = &work->parent->objs->objs[*index]; + vec->vy = obj->world.t[1]; + + obj = &work->parent->objs->objs[*index]; + vec->vz = obj->world.t[2]; + + vec->vy = 0; + } + + for (i = 1; i < 4; i++) + { + dist = GV_DiffVec3(&sp10[0], &sp10[i]); + yaw = d11c_800C42AC(&sp10[0], &sp10[i]); + GV_DirVec2(yaw - *work->f50, dist, &sp10[i]); + } + + tmp = Max3(sp10[1].vz, sp10[2].vz, sp10[3].vz); + z = (tmp - Min3(sp10[1].vz, sp10[2].vz, sp10[3].vz)) / 2; + + tmp = Max3(sp10[1].vx, sp10[2].vx, sp10[3].vx); + x = (tmp - Min3(sp10[1].vx, sp10[2].vx, sp10[3].vx)) / 2; + + vec2 = work->f28; + + if (z > 500) + { + x += 100; + + work->f28[2].vx = -x; + work->f28[0].vx = -x; + work->f28[3].vx = x; + vec2[1].vx = x; + + work->f28[1].vz = 10 - z; + vec2[0].vz = 10 - z; + work->f28[3].vz = z + 50; + vec2[2].vz = z + 50; + } + else + { + x += 200; + z += 200; + + work->f28[2].vx = -x; + work->f28[0].vx = -x; + work->f28[3].vx = x; + vec2[1].vx = x; + + work->f28[1].vz = -z; + vec2[0].vz = -z; + work->f28[3].vz = z; + vec2[2].vz = z; + } +} + +static void d11c_800C44F8(Work *work) +{ + SVECTOR vec; + + vec = DG_ZeroVector; + vec.vy = *work->f50; + + RotMatrixYXZ(&vec, &work->prim->world); + + work->prim->world.t[0] = work->parent->objs->world.t[0]; + work->prim->world.t[2] = work->parent->objs->world.t[2]; + work->prim->world.t[1] = *work->f54; +} + +/*---------------------------------------------------------------------------*/ + +static void Act(Work *work) +{ + if (work->parent->objs->flag & DG_FLAG_INVISIBLE) + { + DG_InvisiblePrim(work->prim); + } + else + { + DG_VisiblePrim(work->prim); + + d11c_800C42D4(work); + d11c_800C44F8(work); + } +} + +static void Die(Work *work) +{ + GM_FreePrim(work->prim); +} + +static int GetResources(Work *work, OBJECT *parent, SVECTOR arg2, int *arg3, int *arg4, char r, char g, char b, int unused) +{ + DG_PRIM *prim; + DG_TEX *tex; + int i; + POLY_FT4 *poly; + SVECTOR *vec; + + work->prim = GM_MakePrim(DG_PRIM_POLY_FT4, 1, work->f28, NULL); + prim = work->prim; + if (!prim) + { + return -1; + } + + prim->group_id = 0; + + tex = DG_GetTexture(GV_StrCode("shadow")); + if (!tex) + { + return -1; + } + + for (i = 0; i < 2; i++) + { + poly = prim->packs[i]; + + setPolyFT4(poly); + setSemiTrans(poly, 1); + setRGB0(poly, r, g, b); + DG_SetPacketTexture4(poly, tex); + } + + vec = work->f28; + for (i = 0; i < 4; vec++, i++) + { + *vec = DG_ZeroVector; + vec->vx = (i & 1) ? 400 : -400; + vec->vz = (i & 2) ? 400 : -400; + } + + work->prim->world.t[0] = 0; + work->prim->world.t[1] = -31000; + work->prim->world.t[2] = 0; + + work->parent = parent; + work->f48 = arg2; + work->f50 = arg3; + work->f54 = arg4; + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewDemoKage(OBJECT *parent, SVECTOR arg1, int *arg2, int *arg3, char r, char g, char b, int unused) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work == NULL) + { + return NULL; + } + + GV_SetNamedActor(&work->actor, Act, Die, "demokage.c"); + + if (GetResources(work, parent, arg1, arg2, arg3, r, g, b, unused) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/demokage.h b/components/mgs/mgs/source/enemy/demokage.h new file mode 100644 index 00000000..29da03f3 --- /dev/null +++ b/components/mgs/mgs/source/enemy/demokage.h @@ -0,0 +1,11 @@ +#ifndef __MGS_ENEMY_DEMOKAGE_H__ +#define __MGS_ENEMY_DEMOKAGE_H__ + +#include +#include +#include +#include "game/game.h" // for OBJECT + +void *NewDemoKage(OBJECT *parent, SVECTOR arg1, int *arg2, int *arg3, char r, char g, char b, int unused); + +#endif // __MGS_ENEMY_DEMOKAGE_H__ diff --git a/components/mgs/mgs/source/enemy/dymc_seg.c b/components/mgs/mgs/source/enemy/dymc_seg.c new file mode 100644 index 00000000..008b37da --- /dev/null +++ b/components/mgs/mgs/source/enemy/dymc_seg.c @@ -0,0 +1,104 @@ +#include "dymc_seg.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libhzd/libhzd.h" +#include "game/game.h" + +/*---------------------------------------------------------------------------*/ + +typedef struct _Work +{ + GV_ACT actor; + int map; + HZD_HDL *hzd; + HZD_SEG seg; + SVECTOR min; + SVECTOR max; + int enable; + int state; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void Act(Work *work) +{ + HZD_SEG *seg; + + seg = &work->seg; + if (work->state != 0) + { + if (work->enable == 0) + { + work->state = 0; + seg->p1.y = work->min.vy - 10000; + seg->p2.y = work->max.vy - 10000; + } + } + else + { + if (work->enable != 0) + { + work->state = 1; + seg->p1.y = work->min.vy; + seg->p2.y = work->max.vy; + } + } +} + +static void Die(Work *work) +{ + HZD_DequeueDynamicSegment(work->hzd, &work->seg); +} + +static int GetResources(Work *work, int map, SVECTOR *min, SVECTOR *max, int minh, int maxh, int flag) +{ + HZD_SEG *seg; + + work->map = map; + work->min = *min; + work->max = *max; + + seg = &work->seg; + seg->p1.x = min->vx; + seg->p1.y = min->vy; + seg->p1.z = min->vz; + seg->p1.h = minh; + + seg->p2.x = max->vx; + seg->p2.y = max->vy; + seg->p2.z = max->vz; + seg->p2.h = maxh; + + HZD_SetDynamicSegment(seg, seg); + + work->hzd = GM_FindMap(map)->hzd; + HZD_QueueDynamicSegment2(work->hzd, seg, flag); + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewDynamicSegment(int map, SVECTOR *min, SVECTOR *max, int minh, int maxh, int flag, int **enable) +{ + Work *work; + + work = GV_NewActor(GV_ACTOR_USER, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "dymc_seg.c"); + if (GetResources(work, map, min, max, minh, maxh, flag) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + if (enable != NULL) *enable = &work->enable; + work->enable = 1; + work->state = 1; + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/dymc_seg.h b/components/mgs/mgs/source/enemy/dymc_seg.h new file mode 100644 index 00000000..e6542fd5 --- /dev/null +++ b/components/mgs/mgs/source/enemy/dymc_seg.h @@ -0,0 +1,10 @@ +#ifndef __MGS_ENEMY_DYMC_SEG_H__ +#define __MGS_ENEMY_DYMC_SEG_H__ + +#include +#include +#include + +void *NewDynamicSegment(int where, SVECTOR *min, SVECTOR *max, int minh, int maxh, int flag, int **enable); + +#endif // __MGS_ENEMY_DYMC_SEG_H__ diff --git a/components/mgs/mgs/source/enemy/enemy.c b/components/mgs/mgs/source/enemy/enemy.c new file mode 100644 index 00000000..192f2934 --- /dev/null +++ b/components/mgs/mgs/source/enemy/enemy.c @@ -0,0 +1,418 @@ +#include "enemy.h" + +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "anime/animconv/anime.h" +#include "game/game.h" +#include "linkvar.h" +#include "strcode.h" + +#define SEGMENT_ATR ( HZD_SEG_NO_PLAYER ) + +extern CONTROL *GM_WhereList[94]; + +extern ENEMY_COMMAND EnemyCommand; + +extern int COM_NOISEMODE_DIS; +extern int COM_NoiseMinDisID; +extern int COM_ALERT_DECREMENT; +extern int COM_EYE_LENGTH; + +extern int sna_current_item_8004FB38(void); + +// in enemy/asiato.c +extern int AsiatoCheck( HZD_HDL *, SVECTOR * ); +extern int SearchNearAsiato( HZD_HDL *, SVECTOR *, short, short, short ); + +void s00a_command_800C9878( WatcherWork* work ) +{ + VISION *vision = &work->vision; + RADAR_SIGHT_PARAM *r_param = &work->control.radar_param; + + r_param->dir = vision->facedir; + r_param->dis = vision->length; + r_param->range = vision->angle * 2; + r_param->r = 0; +} + +void s00a_command_800C98A4( WatcherWork *work ) +{ + unsigned short flags; + if ( !( work->field_BA3 & 4 ) ) + { + return; + } + + flags = work->target->damaged; + if ( flags & 6 ) + { + work->field_BA1 |= 4; + return; + } + + flags = work->field_94C.damaged; + if ( flags & 0x80 ) + { + work->field_94C.damaged &= ~( 0x80 ); + if ( ( GM_PlayerStatus & ( PLAYER_MOVE | PLAYER_CB_BOX ) ) == PLAYER_CB_BOX ) + { + return; + } + work->field_BA1 |= 12; + } +} + +void s00a_command_800C9930( WatcherWork* work ) +{ + CONTROL *ctrl; + if ( !( work->field_BA3 & 1 ) ) + { + return; + } + + if ( work->act_status & 0x68 ) + { + return; + } + + ctrl = &work->control; + if ( !GM_NoisePower ) + { + return; + } + + + + if ( GM_NoisePower == 0xFF ) + { + + if ( !( ctrl->map->index & GM_ClaymoreMap ) && + ( !( ctrl->map->index & GM_PlayerMap ) || + !( GM_ClaymoreMap & GM_PlayerMap ) ) ) + { + return; + } + } + else + { + if ( !( ctrl->map->index & GM_PlayerMap ) ) + { + return; + } + } + + switch ( GM_NoisePower ) + { + case 5: + if ( GV_DiffVec3( &GM_NoisePosition, &ctrl->mov ) < 1500 ) + { + break; + } + return; + case 200: + if ( GV_DiffVec3( &GM_NoisePosition, &ctrl->mov ) < COM_NOISEMODE_DIS ) + { + break; + } + return; + case 255: + break; + case 100: + if ( work->field_B78 == COM_NoiseMinDisID ) + { + work->field_BA1 |= 1; + GM_NoiseLength = 0; + GM_NoisePower = 0; + return; + } + return; + default: + return; + } + work->field_BA1 |= 1; +} + +void s00a_command_800C9ACC( WatcherWork *work ) +{ + if ( !( work->field_BA3 & 2 ) ) + { + return; + } + + if ( EnemyCommand.mode == 2 && EnemyCommand.field_0x10 == 300 ) + { + return; + } + + if ( EnemyCommand.field_0x0C % EnemyCommand.field_0x08 == work->field_B78 ) + { + work->field_BA2 = 0; + } + + if ( work->vision.field_B92 == 2 && ( ( GM_PlayerStatus & ( PLAYER_MOVE | PLAYER_CB_BOX ) ) == PLAYER_CB_BOX ) ) + { + if ( ( GV_DiffVec3( &work->field_BA4, &GM_PlayerPosition ) > 50 ) || ( work->field_BAC != GM_WhereList[0]->rot.vy ) ) + { + if ( EnemyCommand.mode != TOP_COMM_ALERT ) + { + work->field_BA4 = GM_PlayerPosition; + work->field_BAC = GM_WhereList[0]->rot.vy; + work->field_BA1 |= 0x2; + } + else + { + work->field_BA1 |= work->field_BA2; + return; + } + } + else + { + if ( GV_DiffVec3( &work->control.mov, &GM_PlayerPosition ) < 1500 ) + { + work->field_BA2 |= 0x40; + } + } + work->vision.field_B92 = 0; + work->field_BA2 |= 0x80; + } + + work->field_BA1 |= work->field_BA2; +} + +void s00a_command_800C9C7C( WatcherWork* work ) +{ + HZD_HDL *hzd; + CONTROL *ctrl; + VISION *vision; + + if ( !( work->field_BA3 & 16 ) ) + { + return; + } + + ctrl = &work->control; + if ( work->think2 == 5 ) + { + return; + } + + hzd = work->control.map->hzd; + vision = &work->vision; + if ( !( AsiatoCheck( hzd, &ctrl->mov ) ) ) + { + return; + } + + if ( SearchNearAsiato( hzd, &ctrl->mov, vision->facedir, vision->angle, vision->length ) < 0 ) + { + return; + } + + work->field_BA1 |= 16; +} + +void s00a_command_800C9D28( WatcherWork* work ) +{ + work->field_BA1 = 0; + s00a_command_800C9930( work ); + + if ( EnemyCommand.mode != TOP_COMM_ALERT ) + { + s00a_command_800C98A4( work ); + s00a_command_800C9ACC( work ); + s00a_command_800C9C7C( work ); + } +} + +void s00a_command_800C9D7C( WatcherWork* work ) +{ + int val; + val = work->vision.field_B92; + switch ( val ) + { + case 0: + if ( work->field_B94 != 2 || GM_PlayerStatus & PLAYER_HIDDEN || !( GM_PlayerStatus & ( PLAYER_INTRUDE | PLAYER_NOHIDDEN ) ) || work->vision.length == 0 ) + { + work->field_B94 = 0; + work->alert_level -= COM_ALERT_DECREMENT; + } + break; + case 1: + work->field_B94 = val; + work->alert_level -= 1; + break; + case 2: + work->field_B94 = val; + work->alert_level += 1; + break; + } + + if ( work->alert_level < 0 ) + { + work->alert_level = 0; + } + else if ( work->alert_level > 255 ) + { + work->alert_level = 255; + } +} + +void s00a_command_800C9E68( WatcherWork* work ) +{ + int x; + int dir; + int dis; + int diff; + short *flag; + SVECTOR *pos; + SVECTOR svec; + CONTROL *ctrl; + MAP *map; + + flag = &work->vision.field_B92; + ctrl = &work->control; + pos = &GM_PlayerPosition; + GV_SubVec3( pos, &ctrl->mov, &svec ); + + dir = GV_VecDir2( &svec ); + work->sn_dir = dir; + dis = GV_VecLen3( &svec ); + + work->sn_dis = dis; + if ( dis == 0 ) + { + dis = 1; + } + + diff = pos->vy - work->control.mov.vy; + if ( diff < 0 ) + { + diff = work->control.mov.vy - pos->vy; + } + + if ( !( work->control.map->index & GM_PlayerMap ) || GM_PlayerStatus & ( PLAYER_INTRUDE | PLAYER_HIDDEN ) ) + { + work->vision.field_B92 = 0; + return; + } + + if ( EnemyCommand.field_0x0C % EnemyCommand.field_0x08 == work->field_B78 ) + { + x = 2000; + //ridiculous, but its the only way it matches + if ( COM_EYE_LENGTH + x >= dis ) + { + if ( x >= diff ) + { + if ( dis < 500 || GV_DiffDirAbs( work->vision.facedir, dir ) < work->vision.angle ) + { + map = work->control.map; + if ( !( HZD_OnlineHazardCheck( map->hzd, pos, &ctrl->mov, HZD_CHK_ALL, SEGMENT_ATR ) ) ) + { + if ( !( GM_OnlineTargetCheckAny( &ctrl->mov, pos, map->index, &svec ) ) ) + { + if ( work->vision.length < dis ) + { + work->vision.field_B92 = 1; + } + else + { + flag[0] = 2; + } + } + else + { + work->vision.field_B92 = 0; + } + } + else + { + work->vision.field_B92 = 0; + } + } + else + { + work->vision.field_B92 = 0; + } + } + else + { + work->vision.field_B92 = 0; + } + } + else + { + work->vision.field_B92 = 0; + } + } + + if ( sna_current_item_8004FB38() == IT_Stealth && work->param_blood != 83 ) + { + flag[0] = 0; + } +} + +void EnemyActionMain( WatcherWork *work ) +{ + if ( work->unknown.last_set < 0x31 ) + { + s00a_command_800C9878( work ); + s00a_command_800C9E68(work); + s00a_command_800C9D28(work); + s00a_command_800C9D7C(work); + Enemy_Think(work); + ENE_ExecPutChar(work); + } + + s00a_command_800C82B0(work); +} + +void EnemyPushMove( WatcherWork *work ) +{ + int s1; + CONTROL *ctrl; + TARGET *target; + + target = work->target; + s1 = 0; + if ( !( target->damaged & 0x8 ) ) + { + return; + } + + GV_AddVec3( &target->offset, &work->control.step, &work->control.step ); + target->damaged &= ~( 0x8 ); + + if ( work->unknown.last_set - 1 >= 2u ) + { + return; + } + + if ( target->offset.pad ) + { + if ( GV_Time & 256 ) + { + s1 = target->offset.pad * 1024; + if ( !( work->field_B78 & 1 ) ) + { + s1 = ( target->offset.pad + 2 ) * 1024; + } + } + else + { + if ( work->field_B78 & 1 ) + { + s1 = ( target->offset.pad + 2 ) * 1024; + } + else + { + s1 = target->offset.pad * 1024; + } + } + s1 &= 0xFFF; + } + + ctrl = &work->control; + ctrl->turn.vy = s1; + ctrl->r_sphere = GV_NearExp2( ctrl->r_sphere, work->unknown.field_1C ); +} diff --git a/components/mgs/mgs/source/enemy/enemy.h b/components/mgs/mgs/source/enemy/enemy.h new file mode 100644 index 00000000..091a94d9 --- /dev/null +++ b/components/mgs/mgs/source/enemy/enemy.h @@ -0,0 +1,795 @@ +#ifndef __MGS_ENEMY_H__ +#define __MGS_ENEMY_H__ + +#include "common.h" +#include "game/game.h" +#include "weapon/weapon.h" + +typedef struct _WatcherPad +{ + int press; //0x04 //0xB38 + int mode; //0x08 //0xB3C + int tmp; //0x0A //0xB40 + int time; //0x0C //0xB44 + short dir; //0x10 //0xB48 + short sound; //0x12 //0xB4A +} WatcherPad; + +typedef struct _WatcherUnk +{ + int field_00; //0x00 //0x8C8 + short field_04; //0x04 //0x8CC + short field_06; //0x06 //0x8CE + int field_08; //0x08 //0x8D0 + SVECTOR field_0C; //0x0C //0x8D4 + int field_14; //0x14 //0x8DC + short last_set; //0x18 //0x8E0 + short last_unset; //0x1A //0x8E2 + short field_1C; //0x1C //0x8E4 + short field_1E; //0x1E //0x8E6 + short field_20; //0x20 //0x8E8 + short field_22; //0x22 //0x8EA +} WatcherUnk; + +typedef struct _VISION +{ + short facedir; //0xB8C + short angle; //0xB8A + short length; //0xB90 + short field_B92; //0xB92 +} VISION; + +typedef struct _PARAM +{ + signed char index; //0xB78 + signed char blood; //0xB79 + signed char area; //0xB7A + signed char low_poly; //0xB7B + char field_B7C; //0xB7C + signed char root; //0xB7D + char c_root; //0xB7E + char field_B7F; //0xB7F + signed char item; //0xB80 + char field_B81; //0xB81 + short life; //0xB82 + short faint; //0xB84 +} PARAM; + +typedef struct _WatcherWork +{ + GV_ACT actor; + CONTROL control; //0x20 + OBJECT body; //0x9C + int has_kmd; //0x180 + DG_DEF *kmd; //0x184 + DG_DEF *def; //0x188 + MOTION_CONTROL m_ctrl; //0x18C + MOTION_SEGMENT m_segs1[17]; //0x1DC + MOTION_SEGMENT m_segs2[17]; //??? + SVECTOR rots[16]; //0x6A4 + SVECTOR adjust[16]; //0x724 + OBJECT weapon; //0x7A4 + MATRIX light[2]; //0x888 + WatcherUnk unknown; //0x8C8 + void *action; //0x8EC + void *action2; //0x8F0 + int time; //0x8F4 + int time2; //0x8F8 + int actend; //0x8FC + TARGET *target; //0x900 + TARGET field_904; //0x904 + TARGET field_94C; //0x94C + TARGET punch; //0x994 + HOMING *hom; //0x9DC + short scale; //0x9E0 + short field_9E2; //0x9E2 + short visible; //0x9E4 + short field_9E6; //0x9E6 + int n_nodes; //0x9E8 + SVECTOR nodes[32]; //0x9EC + int search_flag; //0xAEC + GV_ACT* field_AF0; //0xAF0 + int *field_AF4; //0xAF4 + GV_ACT* field_AF8; //0xAF8 + int *field_AFC; //0xAFC + void *field_B00[8]; //0xB00 + short think1; //0xB20 + short think2; //0xB22 + short think3; //0xB24 + short think4; //0xB26 + unsigned int count3; //0xB28 + int t_count; //0xB2C + int l_count; //0xB30 + int next_node; //0xB34 + WatcherPad pad; //0xB38 + short field_B4C; //0xB4C + short field_B4E; //0xB4E + u_long trigger; //0xB50 + GV_ACT *subweapon; //0xB54 + short field_B58; //0xB58 + short field_B5A; //0xB5A + short field_B5C; //0xB5C + short field_B5E; //0xB5E + GV_ACT *mark; //0xB60 + int field_B64; //0xB64 + GV_ACT *mosaic; //0xB68 + int mark_time; //0xB6C //could be wrong + int act_status; //0xB70 + int field_B74; //0xB74 + signed char field_B78; //0xB78 + signed char param_blood; //0xB79 //param.blood (should be struct) + signed char param_area; //0xB7A //param.area (should be struct) + signed char field_B7B; //0xB7B + char field_B7C; //0xB7C + signed char param_root; //0xB7D //used as node index + char param_c_root; //0xB7E + char field_B7F; //0xB7F + signed char param_item; //0xB80 //param.item (should be struct) + char field_B81; //0xB81 + short param_life; //0xB82 //param.life (should be struct) + short param_faint; //0xB84 //param.faint (should be struct) + char local_data; //0xB86 + char local_data2; //0xB87 + int field_B88; //0xB88 + VISION vision; //0xB8C + short field_B94; //0xB94 + short field_B96; //0xB96 + int alert_level; //0xB98 + signed char modetime[4]; //0xB9C + signed char field_BA0; //0xBA0 + char field_BA1; //0xBA1 + char field_BA2; //0xBA2 + char field_BA3; //0xBA3 + SVECTOR field_BA4; //0xBA4 + int field_BAC; //0xBAC + int field_BB0[8]; //0xBB0 //set_time + short field_BD0[4]; //0xBD0 //set_dir, might be svector + SVECTOR start_pos; //0xBD8 + SVECTOR target_pos; //0xBE0 + int start_addr; //0xBE8 + int start_map; //0xBEC + int field_BF0; //0xBF0 + int target_addr; //0xBF4 + int target_map; //0xBF8 + int field_BFC; //0xBFC + int field_C00; //0xC00 + int field_C04; //0xC04 + int field_C08; //0xC08 + int field_C0C; //0xC0C + int field_C10; //0xC10 + SVECTOR field_C14; //0xC14 + int field_C1C; //0xC1C + int field_C20; //0xC20 + int sn_dis; //0xC24 + int sn_dir; //0xC28 + short faseout; //0xC2C + short field_C2E; //0xC2E + int field_C30; //0xC30 + char field_C34; //0xC34 //num_set_time + char field_C35[7]; //0xC35 //set_time + int field_C3C; //0xC3C + int field_C40; //0xC40 + int field_C44; //0xC44 + short field_C48; //0xC48 + short field_C4A; //0xC4A +} WatcherWork; + +typedef struct _ZakoWork +{ + GV_ACT actor; + CONTROL control; //0x20 + OBJECT body; //0x9C + int has_kmd; //0x180 + DG_DEF *kmd; //0x184 + DG_DEF *def; //0x188 + MOTION_CONTROL m_ctrl; //0x18C + MOTION_SEGMENT m_segs1[17]; //0x1DC + MOTION_SEGMENT m_segs2[17]; //??? + SVECTOR rots[16]; //0x6A4 + SVECTOR adjust[16]; //0x724 + OBJECT weapon; //0x7A4 + MATRIX light[2]; //0x888 + WatcherUnk unknown; //0x8C8 + void *action; //0x8EC + void *action2; //0x8F0 + int time; //0x8F4 + int time2; //0x8F8 + int actend; //0x8FC + TARGET *target; //0x900 + TARGET field_904; //0x904 + TARGET field_94C; //0x94C + TARGET punch; //0x994 + HOMING *hom; //0x9DC + int scale; //0x9E0 + int visible; //0x9E4 + int n_nodes; //0x9E8 + SVECTOR nodes[32]; //0x9EC + int search_flag; //0xAEC + GV_ACT *shadow; //0xAF0 + int *shadow_enable; //0xAF4 + GV_ACT *glight; //0xAF8 + int *glight_enable; //0xAFC + void *field_B00[8]; //0xB00 + short think1; //0xB20 + short think2; //0xB22 + short think3; //0xB24 + short think4; //0xB26 + unsigned int count3; //0xB28 + int t_count; //0xB2C + int l_count; //0xB30 + int next_node; //0xB34 + WatcherPad pad; //0xB38 + short field_B4C; //0xB4C + short field_B4E; //0xB4E + u_long trigger; //0xB50 + GV_ACT *subweapon; //0xB54 + short field_B58; //0xB58 + short field_B5A; //0xB5A + short field_B5C; //0xB5C + short field_B5E; //0xB5E + GV_ACT *mark; //0xB60 + GV_ACT *field_B64; //0xB64 + int mark_time; //0xB68 //could be wrong + int act_status; //0xB6C + int field_B70; //0xB70 + signed char field_B74; //0xB74 + signed char param_blood; //0xB75 //param.blood (should be struct) + signed char param_area; //0xB76 //param.area (should be struct) + signed char param_low_poly; //0xB77 + char field_B78; //0xB78 + signed char param_root; //0xB79 //used as node index + char param_c_root; //0xB7A + char field_B7B; //0xB7B + signed char param_item; //0xB7C //param.item (should be struct) + char field_B7D; //0xB7D + short param_life; //0xB7E //param.life (should be struct) + short param_faint; //0xB80 //param.faint (should be struct) +//seems right up to here + short local_data; //0xB82 + short field_B84; //0xB84 //z_param + short field_B86; //0xB86 + int field_B88; //0xB88 + VISION vision; //0xB8C + short field_B94; //0xB94 + short field_B96; //0xB96 + int alert_level; //0xB98 + signed char modetime[4]; //0xB9C //change to 8 later + signed char field_BA0; //0xBA0 + char field_BA1; //0xBA1 + char field_BA2; //0xBA2 + char field_BA3; //0xBA3 + SVECTOR field_BA4; //0xBA4 + int field_BAC; //0xBAC + int field_BB0[8]; //0xBB0 //set_time + short field_BD0[4]; //0xBD0 //set_dir, might be svector + SVECTOR start_pos; //0xBD8 + SVECTOR target_pos; //0xBE0 + int start_addr; //0xBE8 + int start_map; //0xBEC + int field_BF0; //0xBF0 + int target_addr; //0xBF4 + int target_map; //0xBF8 + int field_BFC; //0xBFC + int field_C00; //0xC00 + SVECTOR field_C04; //0xC04 + int field_C0C; //0xC0C + int field_C10; //0xC10 + int field_C14; //0xC14 + int field_C18; //0xC18 + SVECTOR field_C1C; //0xC1C + int field_C24; //0xC24 + int field_C28; //0xC28 + int sn_dis; //0xC2C + int sn_dir; //0xC30 + int faseout; //0xC34 + int field_C38; //0xC38 + char field_C3C; //0xC3C //num_set_time + char field_C3D[3]; //0xC3D //set_time + GV_ACT *kogaku_body; //0xC40 + GV_ACT *kogaku_weapon; //0xC44 + int field_C48; //0xC48 + short field_C4C; //0xC4C + short field_C4E; //0xC4E +} ZakoWork; + +typedef struct _Zako11FWork +{ + GV_ACT actor; + CONTROL control; //0x20 + OBJECT body; //0x9C + int has_kmd; //0x180 + DG_DEF *kmd; //0x184 + DG_DEF *def; //0x188 + MOTION_CONTROL m_ctrl; //0x18C + MOTION_SEGMENT m_segs1[17]; //0x1DC + MOTION_SEGMENT m_segs2[17]; //??? + SVECTOR rots[16]; //0x6A4 + SVECTOR adjust[16]; //0x724 + OBJECT weapon; //0x7A4 + MATRIX light[2]; //0x888 + WatcherUnk unknown; //0x8C8 + void *action; //0x8EC + void *action2; //0x8F0 + int time; //0x8F4 + int time2; //0x8F8 + int actend; //0x8FC + TARGET *target; //0x900 + TARGET field_904; //0x904 + TARGET field_94C; //0x94C + TARGET punch; //0x994 + HOMING *hom; //0x9DC + int scale; //0x9E0 + int visible; //0x9E4 + int n_nodes; //0x9E8 + SVECTOR nodes[32]; //0x9EC + int search_flag; //0xAEC + GV_ACT *shadow; //0xAF0 + int *shadow_enable; //0xAF4 + GV_ACT *glight; //0xAF8 + int *glight_enable; //0xAFC + void *field_B00[8]; //0xB00 + short think1; //0xB20 + short think2; //0xB22 + short think3; //0xB24 + short think4; //0xB26 + unsigned int count3; //0xB28 + int t_count; //0xB2C + int l_count; //0xB30 + int next_node; //0xB34 + WatcherPad pad; //0xB38 + short field_B4C; //0xB4C + short field_B4E; //0xB4E + u_long trigger; //0xB50 + GV_ACT *subweapon; //0xB54 + short field_B58; //0xB58 + short field_B5A; //0xB5A + short field_B5C; //0xB5C + short field_B5E; //0xB5E + GV_ACT *mark; //0xB60 + GV_ACT *field_B64; //0xB64 + int mark_time; //0xB68 //could be wrong + int act_status; //0xB6C + int field_B70; //0xB70 + PARAM param; //0xB74 + short local_data; //0xB82 + short field_B84; //0xB84 //z_param + short field_B86; //0xB86 + int field_B88; //0xB88 + VISION vision; //0xB8C + short field_B94; //0xB94 + short field_B96; //0xB96 + int alert_level; //0xB98 + signed char modetime[8]; //0xB9C //change to 8 later + SVECTOR field_BA4; //0xBA4 + int field_BAC; //0xBAC + int field_BB0[8]; //0xBB0 //set_time + short field_BD0[4]; //0xBD0 //set_dir, might be svector + SVECTOR start_pos; //0xBD8 + SVECTOR target_pos; //0xBE0 + int start_addr; //0xBE8 + int start_map; //0xBEC + int field_BF0; //0xBF0 + int target_addr; //0xBF4 + int target_map; //0xBF8 + int field_BFC; //0xBFC + int field_C00; //0xC00 + SVECTOR field_C04; //0xC04 + int field_C0C; //0xC0C + int field_C10; //0xC10 + int field_C14; //0xC14 + int field_C18; //0xC18 + SVECTOR field_C1C; //0xC1C + int field_C24; //0xC24 + int field_C28; //0xC28 + int sn_dis; //0xC2C + int sn_dir; //0xC30 + int faseout; //0xC34 + int field_C38; //0xC38 + int field_C3C; //0xC3C + short field_C40; //0xC40 + short field_C42; //0xC42 +} Zako11FWork; + +typedef struct _CommanderWork +{ + GV_ACT actor; + unsigned short unk; //0x20 + unsigned short unk2; //0x22 + int name; //0x24 +} CommanderWork; + +typedef struct _ZakoCommanderWork +{ + GV_ACT actor; + int name; //0x24 +} ZakoCommanderWork; + +typedef struct _TOPCOMMAND_STRUCT { + int mode; + int alert; +} TOPCOMMAND_STRUCT; + +typedef struct _A4_INNER_STRUCT +{ + unsigned short field_00; //0x00 + unsigned short field_02; //0x02 + +} A4_INNER_STRUCT; + +typedef struct _A4_STRUCT +{ + unsigned short map_id; //0x00 + unsigned short n_entry; //0x02 + A4_INNER_STRUCT field_04[8]; //0x04 +} A4_STRUCT; + +typedef struct _WATCHER +{ + int field_00; //0x00 + int field_04; //0x04 + int field_08; //0x08 + void *watcher; //0x0C +} WATCHER; + +typedef struct _ENEMY_COMMAND +{ + int field_0x00; + int field_0x04; + int field_0x08; + int field_0x0C; + int field_0x10; + int alert; //0x14 + int mode; //0x18 + int field_0x1C; + int field_0x20[8]; + int field_0x40; + int com_addr; //0x44 + int com_map; //0x48 + SVECTOR com_pos; //0x4C + short field_0x54; + short c_reset_pos; //0x56 + short field_0x58[8]; + int field_0x68[8]; + SVECTOR field_0x88; + int reset_enemy_num; + int reset_enemy_max; + int field_0x98; + int field_0x9C; + MAP *map; //0xA0 + A4_STRUCT field_0xA4; + WATCHER field_0xC8[8]; + int field_0x148[8]; + int field_0x168; + int field_0x16C; + int field_0x170; + int field_0x174; + short field_0x178; + short field_0x17A; + int field_0x17C; + short field_0x180; + short field_0x182; +} ENEMY_COMMAND; + +typedef struct _ZAKO_COMMAND +{ + int field_0x00; + int field_0x04; + int field_0x08; + int field_0x0C; + int field_0x10; + int field_0x14; + int alert; + int mode; + int field_0x20; + int com_addr; + int com_map; + SVECTOR com_pos; + short field_0x34; + short field_0x36; + short field_0x38[4]; + int field_0x40[8]; + int field_0x60; + MAP* field_0x64; + A4_STRUCT field_0x68; + WATCHER field_0x8C[8]; + int field_0x10C; + int field_0x110; + int field_0x114; //reset_enemy_max + int field_0x118; + int field_0x11C; + int field_0x120; +} ZAKO_COMMAND; + +typedef struct _ZAKO11F_COMMAND +{ + int field_0x00; + int field_0x04; + int n_watchers; + int field_0x0C; + int field_0x10; + int field_0x14; + int alert; + int mode; + int field_0x20; + int com_addr; + int com_map; + SVECTOR com_pos; + short n_zones; + short reset_zone; + short zones[4]; + int field_0x40[8]; + int field_0x60; //enemy_reset_num + MAP *map; + A4_STRUCT field_0x68; + WATCHER watchers[8]; + int field_0x10C; + short field_0x110; + short field_0x112; + short field_0x114; //reset_enemy_max + short first_damage; + SVECTOR field_0x118; + int field_0x120; + int field_0x124[8]; +} ZAKO11F_COMMAND; + +//command.c +#define TOP_COMM_TRAVEL 0 +#define TOP_COMM_ALERT 1 + +void s00a_command_800CEC40( SVECTOR *mov , int n ); +int s00a_command_800CEA2C( WatcherWork *work ); +void s00a_command_800CEC90( void ) ; +void s00a_command_800CECF4( void ) ; +int s00a_command_800CEDE8( char *ops, short *val, int where ); +int s00a_command_800CED88( char *ops, A4_STRUCT * ); +void s00a_command_800CFA94( CommanderWork* work ) ; +void s00a_command_800CFEA8( void ) ; +int s00a_command_800D0128( char *ops ); +void s00a_command_800D018C( CommanderWork* work ) ; +void s00a_command_800D0218( void ) ; +void s00a_command_800D0344( void ) ; +void EnemyPushMove( WatcherWork* work ); +void EnemyActionMain( WatcherWork* work ); +void *AN_Unknown_800CA1EC( MATRIX* mat, int mark ); + +void ENE_SetTopCommAL( int alert ); +void ENE_SetTopCommMD( int mode ); +void ENE_SetGopointLast(void); + +void SetCameraActCall(void); +void UnsetCameraActCall(void); + +//watcher.c +#define EN_FASEOUT 0x10000000 + +// "low-poly heishi" ? +#define HASH_LOPRYHEI 0xD7E3 // GV_StrCode("lopryhei") + +#define TARGET_FLAG ( TARGET_POWER | TARGET_CAPTURE | TARGET_PUSH | TARGET_TOUCH | TARGET_SEEK | TARGET_AVAIL ) + +//check.c +int s00a_command_800C580C( int a, int b ); +int CheckDamage_800C5424( WatcherWork * work ) ; +int CheckPad_800C5A60( WatcherWork *work ) ; +void s00a_command_800C55B0( WatcherWork* work ) ; +void s00a_command_800C5860( WatcherWork* work ) ; +int AttackForce_800C58E8( WatcherWork * work ) ; +void s00a_command_800C59F8( WatcherWork *work ) ; +int s00a_command_800C513C( WatcherWork* work ) ; + +//put.c + +#define PUTBREATH 1 +#define GUNSHOOT 3 + +#define BW_MARK 4 + +typedef void( *PUTFUNC )( WatcherWork * ) ; +typedef void( *ZAKOPUTFUNC )( ZakoWork * ) ; +typedef void( *ZAKO11FPUTFUNC )( Zako11FWork * ) ; + +void ENE_PutMark( WatcherWork *work, int mark ) ; +void ENE_PutBlood( WatcherWork *work, int put, int i ) ; +void ENE_PutItem( WatcherWork *work ) ; +int ENE_SetPutChar( WatcherWork *work, int put ) ; + +void ENE_PutFog( WatcherWork *work ) ; +int ENE_ClearPutChar( WatcherWork *work, void *func ) ; +void ENE_ExecPutChar( WatcherWork *work ) ; + +//route.c +void s00a_command_800CB0E0( WatcherWork* work ); +void s00a_command_800CAFD4( WatcherWork* work ); +void s00a_command_800CAB74( WatcherWork* work ); +void s00a_command_800CAB04( WatcherWork* work ); +void s00a_command_800CAD84( WatcherWork *work ); +void s00a_command_800CAACC( WatcherWork* work ); +void s00a_command_800CB1C4( WatcherWork* work ); + +//action.c +#define COM_ST_DANBOWL 0x2000 + +// #define SP_GUNDOWN ? +// #define SP_SHOOT ? +// #define SP_READYGUN ? +#define SP_WEAPON 0x10000 +#define SP_WEAPON2 0x20000 +#define SP_DANBOWLKERI 0x400000 + +#define ACTINTERP 4 + +#define STANDSTILL 0 +#define ACTION1 1 //WALKING HOLDING GUN +#define ACTION2 2 //RUNNING HOLDING GUN +#define ACTION3 3 //STANDING AIMING GUN +#define ACTION4 4 //STANDING FIRING GUN +#define ACTION5 5 //SEMI-CROUCHING AIMING GUN +#define ACTION6 6 //SEMI_CROUCHING FIRING GUN +#define GRENADE 7 +#define ACTION8 8 //HITTING WITH GUN +#define ACTION9 9 //STANDING LOOKING TO THE RIGHT +#define ACTION10 10 //STANDING LOOKING TO THE LEFT +#define ACTION11 11 //STANDING LOOKING DOWN +#define ACTION12 12 //CROUCHING LOOKING FORWARD +#define ACTION13 13 //CROUCHING LOOKING A BIT TO THE RIGHT +#define ACTION14 14 //LOOKING UP +#define ACTION15 15 //GETTING HIT FROM THE FRONT +#define ACTION16 16 //GETTING HIT FROM BEHIND? +#define DANBOWLKERI 17 +#define DANBOWLPOSE 18 +#define ACTION19 19 //STRETCHING +#define ACTION20 20 //SLEEPING WHILST STANDING +#define ACTION21 21 //WAKING UP SUDDENLY? +#define ACTION22 22 //TALKING ON RADIO? +#define ACTION23 23 //RUBBING NECK +#define ACTION24 24 //SNEEZING +#define ACTION25 25 //I-POSE Maybe not used on this stage +#define ACTION26 26 //I-POSE Maybe not used on this stage +#define ACTION27 27 //GETTING HELD BY SNAKE +#define ACTION28 28 //PRE-NECK SNAP +#define ACTION29 29 //NECK SNAP AND FALLING TO FLOOR +#define ACTION30 30 //FALLING TO FLOOR +#define ACTION31 31 //GETTING FLIPPED OVER +#define ACTION32 32 //SHEILDING EYES +#define ACTION33 33 //STRUGGLING WHILST BEING HELD? +#define ACTION34 34 //FALLING TO THE FLOOR QUICKLY +#define ACTION35 35 //KNOCKED BACKWARDS +#define ACTION36 36 //KNOCKED FORWARDS +#define ACTION37 37 //NECK SNAP AND FALLING TO FLOOR FORWARDS +#define ACTION38 38 //FALLING TO THE FLOOR AND HAVING SOME SORT OF SEIZURE +#define ACTION39 39 //ON FLOOR ON STOMACH +#define ACTION40 40 //ON FLOOR ON BACK +#define ACTION41 41 //ON FLOOR ON STOMACH FACE DOWN +#define ACTION42 42 //FALLING FACE FIRST AND GETTING UP +#define ACTION43 43 //FALLING BACKWARDS AND GETTING UP +#define ACTION44 44 //BIKKURI GET UP FACE DOWN +#define ACTION45 45 //BIKKURI GET UP BACKWARDS +#define ACTION46 46 //JUST BEEN KNOCKED ONTO THE FLOOR ON FLOOR STOMACH +#define ACTION47 47 //JUST BEEN KNOCKED ONTO THE FLOOR ON BACK +#define ACTION48 48 //BEING DRAGGED BACKWARDS WHILST BEING HELD +#define ACTION49 49 //SAME ID AS 40 +#define ACTION50 50 //SAME ID AS 41 +#define ACTION51 51 //SAME ID AS 42 + +typedef void ( *ACTION )( WatcherWork *, int ) ; + +static inline void SetModeFields( WatcherWork *work, ACTION action ) +{ + work->action = action; + work->time = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; +} + +static inline void SetMode( WatcherWork *work, ACTION action ) +{ + work->action = action; + work->time = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; + GM_ConfigMotionAdjust( &( work->body ), 0 ); +} + +static inline void SetMode2( WatcherWork *work, void *func ) +{ + if ( work->action2 == NULL ) + { + work->action2 = func; + work->time2 = 0; + } + + work->control.turn.vz = 0; + work->control.turn.vx = 0; + GM_ConfigMotionAdjust( &( work->body ), 0 ); +} + +typedef void ( *ZAKOACTION )( ZakoWork *, int ) ; + +static inline void SetZakoModeFields( ZakoWork *work, ZAKOACTION action ) +{ + work->action = action; + work->time = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; +} + +static inline void SetZakoMode( ZakoWork *work, ZAKOACTION action ) +{ + work->action = action; + work->time = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; + GM_ConfigMotionAdjust( &( work->body ), 0 ); +} + +static inline void SetZakoMode2( ZakoWork *work, ZAKOACTION action ) +{ + if ( work->action2 == NULL ) + { + work->action2 = action; + work->time2 = 0; + } + + work->control.turn.vz = 0; + work->control.turn.vx = 0; + GM_ConfigMotionAdjust( &( work->body ), 0 ); +} + +typedef void ( *ZAKO11FACTION )( Zako11FWork *, int ) ; + +void s00a_command_800C82B0( WatcherWork *work ); + +void s00a_command_800C78E0( WatcherWork *work, int time ); +void s00a_command_800C77C8( WatcherWork *work, int time ); +void s00a_command_800C7354( WatcherWork *work, int time ); + +void s00a_command_800C841C( WatcherWork *work, int time ); +void s00a_command_800C84FC( WatcherWork *work, int time ); +void s00a_command_800C8688( WatcherWork *work, int time ); +void s00a_command_800C8734( WatcherWork *work, int time ); +void s00a_command_800C87FC( WatcherWork *work, int time ); + +void s00a_command_800C88D8( WatcherWork *work, int time ); +void s00a_command_800C8990( WatcherWork *work, int time ); +void s00a_command_800C8A6C( WatcherWork *work, int time ); +void s00a_command_800C8C98( WatcherWork *work, int time ); +void s00a_command_800C8DF8( WatcherWork *work, int time ); +void s00a_command_800C615C( WatcherWork *work, int time ); +void s00a_command_800C6164( WatcherWork *work, int time ); +void s00a_command_800C624C( WatcherWork *work, int time ); +void s00a_command_800C6320( WatcherWork *work, int time ); +void s00a_command_800C65A8( WatcherWork *work, int time ); +void s00a_command_800C5E48( WatcherWork *work, int time ); + +void s00a_command_800C6724( WatcherWork *work, int time ); +void s00a_command_800C67E4( WatcherWork *work, int time ); +void s00a_command_800C6A40( WatcherWork *work, int time ); +void s00a_command_800C8054( WatcherWork *work, int time ); +void s00a_command_800C7498( WatcherWork *work, int time ); + +void s00a_command_800C76BC( WatcherWork *work, int time ); +void s00a_command_800C76C4( WatcherWork *work, int time ); +void s00a_command_800C7E28( WatcherWork *work, int time ); +void s00a_command_800C818C( WatcherWork *work, int time ); + +void ActGrenade_800C67EC( WatcherWork *work, int time ); +void ActOverScoutD_800C85DC( WatcherWork *work, int time ); + +//think.c ? +#define T_NOISE 0 + +#define TH3_ATTACK_NEAR 0x19 +#define TH3_ATTACK_GRENADE 0x18 //? +#define TH3_ATTACK_HANDGUN 0x15 + +void Enemy_Think( WatcherWork * work ); +void AN_Piyopiyo( SVECTOR *svec ); +void AN_Sleep( SVECTOR *svec ); +void AN_Fog( SVECTOR *svec ); +void s00a_command_800CB13C( WatcherWork * work ); +void s00a_command_800CC210( WatcherWork * work ); +void s00a_command_800CEB54(); + +int DirectTrace_800CC154( WatcherWork *work, int val ); + +#endif // __MGS_ENEMY_H__ diff --git a/components/mgs/mgs/source/enemy/eyeflash.c b/components/mgs/mgs/source/enemy/eyeflash.c new file mode 100644 index 00000000..21fa0811 --- /dev/null +++ b/components/mgs/mgs/source/enemy/eyeflash.c @@ -0,0 +1,159 @@ +#include "eyeflash.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_USER + +typedef struct _Work +{ + GV_ACT actor; + SVECTOR field_20; + DG_PRIM *prim; + MATRIX *world; + RECT rect; + int field_38; + DG_TEX *tex; +} Work; + +/*---------------------------------------------------------------------------*/ + +SVECTOR eyeflash_svec = {0, 80, 50, 0}; +const RECT eyeflash_rect = {175, 100, 350, 200}; + +static void InitPacks(POLY_FT4 *poly, DG_TEX *tex) +{ + int u0, u1; + int v0, v1; + + setPolyFT4(poly); + setRGB0(poly, 100, 100, 100); + setSemiTrans(poly, 1); + + u0 = tex->off_x; + u1 = (tex->w + 1) / 3 + tex->off_x - 1; + + poly->u0 = poly->u2 = u0; + poly->u1 = poly->u3 = u1; + + v0 = tex->off_y; + v1 = tex->h + v0; + poly->v0 = poly->v1 = v0; + poly->v2 = poly->v3 = v1; + + poly->tpage = tex->tpage; + poly->clut = tex->clut; +} + +static void s00a_eyeflash_800D0A28(Work *work) +{ + DG_TEX *tex; + int field_38; + int u0, u1; + int v0, v1; + POLY_FT4 *poly; + short clut; + int tpage; + + field_38 = 5 - work->field_38; + if (field_38 >= 3) + { + field_38 -= 3; + } + + tex = work->tex; + poly = work->prim->packs[GV_Clock]; + + u0 = tex->off_x + (tex->w + 1) * field_38 / 3; + u1 = tex->off_x + (tex->w + 1) * (field_38 + 1) / 3 - 1; + + poly->u0 = poly->u2 = u0; + poly->u1 = poly->u3 = u1; + + v0 = tex->off_y; + v1 = tex->h + v0; + + poly->v0 = poly->v1 = v0; + poly->v2 = poly->v3 = v1; + + poly->tpage = tex->tpage; + + clut = tex->clut; + tpage = (poly->tpage & 0xFF9F) | 0x20; + + poly->tpage = tpage; + poly->clut = clut; + poly->tpage = tpage; +} + +static void Act(Work *work) +{ + if (--work->field_38 == 0) + { + GV_DestroyActor(&work->actor); + } + work->prim->world = *work->world; + s00a_eyeflash_800D0A28(work); +} + +static void Die(Work *work) +{ + GM_FreePrim(work->prim); +} + +static int GetResources(Work *work, const char *texture) +{ + DG_PRIM *prim; + RECT unused; + DG_TEX *tex; + + unused = eyeflash_rect; // let's waste cycles + + work->rect = eyeflash_rect; + work->prim = prim = GM_MakePrim(DG_PRIM_RECTANGLE | DG_PRIM_POLY_FT4, 1, &eyeflash_svec, &work->rect); + + if (prim) + { + prim->raise = 1000; + work->tex = tex = DG_GetTexture(GV_StrCode("kirari")); + if (tex) + { + InitPacks(prim->packs[0], tex); + InitPacks(prim->packs[1], tex); + return 0; + } + } + + return -1; +} + +/*---------------------------------------------------------------------------*/ + +void *NewEyeflash(MATRIX *world, SVECTOR *arg1, const char *texture, int arg3) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "eyeflash.c"); + work->field_20 = *arg1; + work->field_38 = 6; + work->world = world; + work->field_20.vy += arg3; + if (GetResources(work, texture) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/eyeflash.h b/components/mgs/mgs/source/enemy/eyeflash.h new file mode 100644 index 00000000..dc1fcd3b --- /dev/null +++ b/components/mgs/mgs/source/enemy/eyeflash.h @@ -0,0 +1,10 @@ +#ifndef __MGS_ENEMY_EYEFLASH_H__ +#define __MGS_ENEMY_EYEFLASH_H__ + +#include +#include +#include + +void *NewEyeflash(MATRIX *world, SVECTOR *arg1, const char *texture, int arg3); + +#endif // __MGS_ENEMY_EYEFLASH_H__ diff --git a/components/mgs/mgs/source/enemy/glight.c b/components/mgs/mgs/source/enemy/glight.c new file mode 100644 index 00000000..dd19c87e --- /dev/null +++ b/components/mgs/mgs/source/enemy/glight.c @@ -0,0 +1,134 @@ +#include "glight.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_USER + +typedef struct _Work +{ + GV_ACT actor; + DG_PRIM *prim; + MATRIX *world; + int visible; +} Work; + +/*---------------------------------------------------------------------------*/ + +static RECT glight_rect = {40, 40, 80, 80}; +static SVECTOR glight_pos = {0, 65136, 60, 0}; + +static void Act(Work *work) +{ + work->prim->world = *work->world; + + if (work->visible) + { + DG_VisiblePrim(work->prim); + } + else + { + DG_InvisiblePrim(work->prim); + } +} + +static void Die(Work *work) +{ + GM_FreePrim(work->prim); +} + +static void InitPacks(POLY_FT4 *poly, DG_TEX *tex, int shade) +{ + int tpage1, tpage2; + int u0, u1; + int v0, v1; + short clut; + + setPolyFT4(poly); + setRGB0(poly, shade, shade, shade); + setSemiTrans(poly, 1); + + u0 = tex->off_x; + u1 = u0 + tex->w; + v0 = tex->off_y; + v1 = v0 + tex->h; + + poly->u0 = u0; + poly->v0 = v0; + poly->v1 = v0; + poly->u1 = u1; + poly->u2 = u0; + poly->v2 = v1; + poly->u3 = u1; + poly->v3 = v1; + + tpage1 = (unsigned short)tex->tpage; + poly->tpage = tex->tpage; + clut = tex->clut; + tpage2 = (tpage1 & 0xFF9F) | 0x20; + poly->tpage = tpage2; + poly->clut = clut; +} + +static int GetResources(Work *work, MATRIX *world, int **pvisible) +{ + DG_TEX *tex; + + work->world = world; + + work->prim = GM_MakePrim(DG_PRIM_RECTANGLE | DG_PRIM_POLY_FT4, 1, &glight_pos, &glight_rect); + if (work->prim == NULL) + { + return -1; + } + + if (pvisible) + { + *pvisible = &work->visible; + } + + work->visible = TRUE; + + work->prim->group_id = 0; + work->prim->raise = 200; + + tex = DG_GetTexture(GV_StrCode("famas_l")); + if (tex == NULL) + { + return -1; + } + + InitPacks(work->prim->packs[0], tex, 250); + InitPacks(work->prim->packs[1], tex, 200); + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewGunLight(MATRIX *world, int **pvisible) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "glight.c"); + + if (GetResources(work, world, pvisible) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/glight.h b/components/mgs/mgs/source/enemy/glight.h new file mode 100644 index 00000000..5d9d2895 --- /dev/null +++ b/components/mgs/mgs/source/enemy/glight.h @@ -0,0 +1,10 @@ +#ifndef __MGS_ENEMY_GLIGHT_H__ +#define __MGS_ENEMY_GLIGHT_H__ + +#include +#include +#include + +void *NewGunLight(MATRIX *world, int **pvisible); + +#endif // __MGS_ENEMY_GLIGHT_H__ diff --git a/components/mgs/mgs/source/enemy/grnad_e.c b/components/mgs/mgs/source/enemy/grnad_e.c new file mode 100644 index 00000000..afdfee0b --- /dev/null +++ b/components/mgs/mgs/source/enemy/grnad_e.c @@ -0,0 +1,166 @@ +#include "grnad_e.h" + +#include +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "game/game.h" +#include "strcode.h" + +#include "bullet/tenage.h" // for NewTenage3 +#include "weapon/weapon.h" // for GRD_* + +extern void *NewBlast(MATRIX *world /*, BLAST_DATA *blast_data */); + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_USER + +#define GRENADE_MODEL 0x3b88 // GV_StrCode("grenade") + +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE \ + | DG_FLAG_GBOUND | DG_FLAG_ONEPIECE \ + | DG_FLAG_AMBIENT | DG_FLAG_IRTEXTURE ) + +/*---------------------------------------------------------------------------*/ + +typedef struct _Work +{ + GV_ACT actor; + CONTROL *control; + OBJECT *parent; + OBJECT object; + int num_parent; + u_long *flags; + SVECTOR pos; + int timer; + int type; +} Work; + +/*---------------------------------------------------------------------------*/ + +SVECTOR svec_800C360C[2] = {{0, 80, 80, 0}, {0, 20, 500, 0}}; + +static void Act(Work *work) +{ + SVECTOR pos; + SVECTOR step; + u_long flags; + MATRIX *world; + CONTROL *control; + SVECTOR *var_s2; + int dist; + + GM_CurrentMap = work->control->map->index; + + GM_ActObject2(&work->object); + + flags = *work->flags; + world = &work->parent->objs->objs[work->num_parent].world; + + if (flags & 0x1) + { + if (--work->timer <= 0) + { + // Missing second argument `pBlastData`. + NewBlast(world); + work->timer = 120; + } + + if (flags & 0x6) + { + control = work->control; + DG_SetPos2(&control->mov, &control->rot); + + var_s2 = (flags & 0x4) ? &svec_800C360C[1] : &svec_800C360C[0]; + + work->pos.vy = control->mov.vy; + + dist = GV_DiffVec3(&control->mov, &work->pos); + + if (flags & 0x2) + { + dist /= 2; + } + + var_s2->vz = dist / 8; + + DG_RotVector(var_s2, &step, 1); + + pos.vx = world->t[0]; + pos.vy = world->t[1]; + pos.vz = world->t[2]; + + NewTenage3(&pos, &step, work->timer, work->type, GRENADE_MODEL, 0, 0); + work->timer = 120; + } + + DG_VisibleObjs(work->object.objs); + } + else + { + DG_InvisibleObjs(work->object.objs); + work->timer = 120; + } +} + +static void Die(Work *work) +{ + GM_FreeObject(&work->object); +} + +static int GetResources(Work *work, OBJECT *parent, int num_parent, int unused) +{ + OBJECT *object; + + object = &work->object; + + printf("grenade model=%d \n", GRENADE_MODEL); + + GM_InitObject(object, GRENADE_MODEL, BODY_FLAG, 0); + GM_ConfigObjectRoot(object, parent, num_parent); + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +static void *InitGrenadeEnemy(CONTROL *control, OBJECT *parent, int num_parent, u_long *flags, int side, SVECTOR *pos, int type) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "grnad_e.c"); + + if (GetResources(work, parent, num_parent, type) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + work->control = control; + work->parent = parent; + work->num_parent = num_parent; + work->flags = flags; + work->timer = 120; + work->type = type; + work->pos = *pos; + } + + return (void *)work; +} + +void *NewGrenadeEnemy(CONTROL *control, OBJECT *parent, int num_parent, u_long *flags, SVECTOR *pos, int side) +{ + return InitGrenadeEnemy(control, parent, num_parent, flags, side, pos, GRD_GRENADE); +} + +void *NewGrenadeEnemy2(CONTROL *control, OBJECT *parent, int num_parent, u_long *flags, SVECTOR *pos, int side) +{ + return InitGrenadeEnemy(control, parent, num_parent, flags, side, pos, GRD_STUN); +} diff --git a/components/mgs/mgs/source/enemy/grnad_e.h b/components/mgs/mgs/source/enemy/grnad_e.h new file mode 100644 index 00000000..fef33687 --- /dev/null +++ b/components/mgs/mgs/source/enemy/grnad_e.h @@ -0,0 +1,12 @@ +#ifndef __MGS_ENEMY_GRNAD_E_H__ +#define __MGS_ENEMY_GRNAD_E_H__ + +#include +#include +#include +#include "game/game.h" // for CONTROL, OBJECT + +void *NewGrenadeEnemy(CONTROL *control, OBJECT *parent, int num_parent, u_long *flags, SVECTOR *pos, int side); +void *NewGrenadeEnemy2(CONTROL *control, OBJECT *parent, int num_parent, u_long *flags, SVECTOR *pos, int side); + +#endif // __MGS_ENEMY_GRNAD_E_H__ diff --git a/components/mgs/mgs/source/enemy/katana.c b/components/mgs/mgs/source/enemy/katana.c new file mode 100644 index 00000000..719bf816 --- /dev/null +++ b/components/mgs/mgs/source/enemy/katana.c @@ -0,0 +1,230 @@ +#include "katana.h" + +#include +#include "common.h" +#include "strcode.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" + +typedef struct _Work +{ + GV_ACT actor; + int map; + DG_PRIM *prim; + DG_TEX *tex; + MATRIX *world; + SVECTOR pos[2]; // start and end positions of the blade + int *enable; // 0 = off, 1 = short trail, 2 = long trail + int enable_flag; // used for demo katana, always set to 1 + SVECTOR verts[8][4]; // 0 & 1 = current position (blue), 2 & 3 = old position (transparent black) + int cur_index; + int unused; +} Work; + +static void UpdateVerts(Work *work) +{ + int prev, next; + + prev = work->cur_index++; + if (work->cur_index >= 8) + { + work->cur_index = 0; + } + + next = work->cur_index; + + DG_SetPos(work->world); + DG_PutVector(work->pos, work->verts[next], 2); + + work->verts[next][2] = work->verts[prev][0]; + work->verts[next][3] = work->verts[prev][1]; +} + +static void ShadePacks(Work *work) +{ + POLY_GT4 *packs; + int r, g, b; + int i; + int id; + POLY_GT4 *poly; + int r2, g2, b2; + + packs = work->prim->packs[GV_Clock]; + + if (*work->enable == 2) + { + r = 55; + g = 192; + b = 155; + } + else + { + r = 28; + g = 96; + b = 78; + } + + for (i = 0; i < 8; i++) + { + id = work->cur_index - i; + if (id < 0) id += 8; + poly = &packs[id]; + + r2 = MAX(r - i * 32, 0); + g2 = MAX(g - i * 32, 0); + b2 = MAX(b - i * 32, 0); + setRGB0(poly, r2, g2, b2); + setRGB1(poly, 0, 0, 0); + + r2 = MAX(r - (i + 1) * 32, 0); + g2 = MAX(g - (i + 1) * 32, 0); + b2 = MAX(b - (i + 1) * 32, 0); + setRGB2(poly, r2, g2, b2); + setRGB3(poly, 0, 0, 0); + } +} + +static void Act(Work *work) +{ + GM_CurrentMap = work->map; + + UpdateVerts(work); + ShadePacks(work); + + if (*work->enable > 0) + { + DG_VisiblePrim(work->prim); + } + else + { + DG_InvisiblePrim(work->prim); + } +} + +static void InitPacks(POLY_GT4 *packs, DG_TEX *tex, int abr, int r, int g, int b) +{ + int i; + + for (i = 0; i < 8; i++) + { + setPolyGT4(packs); + + setRGB0(packs, r + 100, g + 100, b + 100); + setRGB1(packs, r + 100, g + 100, b + 100); + setRGB2(packs, r - 100, g - 100, b - 100); + setRGB3(packs, r - 100, g - 100, b - 100); + + if (abr < 4) + { + int x, y, w, h; + + setSemiTrans(packs, 1); + x = tex->off_x; + w = tex->w; + y = tex->off_y; + h = tex->h; + setUVWH(packs, x, y, w, h); + packs->tpage = tex->tpage; + packs->clut = tex->clut; + packs->tpage = (packs->tpage & ~(3 << 5)) | (abr << 5); + } + else + { + int x, y, w, h; + + x = tex->off_x; + w = tex->w; + y = tex->off_y; + h = tex->h; + setUVWH(packs, x, y, w, h); + packs->tpage = tex->tpage; + packs->clut = tex->clut; + } + + packs++; + } +} + +static int GetResources(Work *work, MATRIX *world, SVECTOR *pos1, SVECTOR *pos2, int *enable) +{ + DG_PRIM *prim; + DG_TEX *tex; + + work->world = world; + work->pos[0] = *pos1; + work->pos[1] = *pos2; + work->enable = enable; + + work->prim = prim = GM_MakePrim(DG_PRIM_POLY_GT4, 8, (SVECTOR *)work->verts, NULL); + if (prim == NULL) + { + return -1; + } + + prim->world = DG_ZeroMatrix; + + work->tex = tex = DG_GetTexture(PCX_GOURAUD); + if (tex == NULL) + { + return -1; + } + + InitPacks(prim->packs[0], tex, 1, 128, 128, 128); + InitPacks(prim->packs[1], tex, 1, 128, 128, 128); + + work->unused = 0; + work->cur_index = 0; + + work->map = GM_CurrentMap; + return 0; +} + +static void Die(Work *work) +{ + GM_FreePrim(work->prim); +} + +void *NewKatana(MATRIX *world, SVECTOR *svec1, SVECTOR *svec2, int *enable) +{ + Work *work; + + work = GV_NewActor(GV_ACTOR_PREV, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(work, Act, Die, "katana.c"); + + if (GetResources(work, world, svec1, svec2, enable) < 0) + { + printf(" Katana Init Err !! \n"); + GV_DestroyActor(work); + return NULL; + } + } + return (void *)work; +} + +void *NewDemoKatana(MATRIX *world) +{ + SVECTOR vec1; + SVECTOR vec2; + Work *work; + + work = GV_NewActor(GV_ACTOR_PREV, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(work, Act, Die, "katana.c"); + + work->enable_flag = 1; + setVector(&vec1, 0, -65, -883); + setVector(&vec2, 0, -65, -45); + + if (GetResources(work, world, &vec1, &vec2, &work->enable_flag) < 0) + { + printf(" Katana Init Err !! \n"); + GV_DestroyActor(work); + return NULL; + } + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/katana.h b/components/mgs/mgs/source/enemy/katana.h new file mode 100644 index 00000000..f89c6390 --- /dev/null +++ b/components/mgs/mgs/source/enemy/katana.h @@ -0,0 +1,11 @@ +#ifndef __MGS_ENEMY_KATANA_H__ +#define __MGS_ENEMY_KATANA_H__ + +#include +#include +#include + +void *NewKatana(MATRIX *pos, SVECTOR *svec1, SVECTOR *svec2, int *field_44); +void *NewDemoKatana(MATRIX *pos); + +#endif // __MGS_ENEMY_KATANA_H__ diff --git a/components/mgs/mgs/source/enemy/kiken.c b/components/mgs/mgs/source/enemy/kiken.c new file mode 100644 index 00000000..53cce606 --- /dev/null +++ b/components/mgs/mgs/source/enemy/kiken.c @@ -0,0 +1,87 @@ +#include "kiken.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "enemy/enemy.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_PREV + +typedef struct _Work +{ + GV_ACT actor; + int active; + int name; +} Work; + +/*---------------------------------------------------------------------------*/ + +static int CheckMessage(Work *work) +{ + GV_MSG *msg; + int code; + + if (GV_ReceiveMessage(work->name, &msg) > 0) + { + code = msg->message[0]; + + if (code == HASH_LEAVE) + { + work->active = FALSE; + } + else if (code == HASH_ENTER) + { + work->active = TRUE; + } + + return code; + } + + return -1; +} + +static void Act(Work *work) +{ + CheckMessage(work); + + if (work->active) + { + ENE_SetTopCommAL(255); + ENE_SetGopointLast(); + } +} + +static void Die(Work *work) +{ + /* do nothing */ +} + +static int GetResources(Work *work, int name) +{ + work->name = name; + work->active = FALSE; + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewKikenKun(int name, int where, int argc, char **argv) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "kiken.c"); + + if (GetResources(work, name) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/kiken.h b/components/mgs/mgs/source/enemy/kiken.h new file mode 100644 index 00000000..ee93d74d --- /dev/null +++ b/components/mgs/mgs/source/enemy/kiken.h @@ -0,0 +1,6 @@ +#ifndef __MGS_ENEMY_KIKEN_H__ +#define __MGS_ENEMY_KIKEN_H__ + +void *NewKikenKun(int name, int where, int argc, char **argv); + +#endif // __MGS_ENEMY_KIKEN_H__ diff --git a/components/mgs/mgs/source/enemy/l_sight.c b/components/mgs/mgs/source/enemy/l_sight.c new file mode 100644 index 00000000..f40c4056 --- /dev/null +++ b/components/mgs/mgs/source/enemy/l_sight.c @@ -0,0 +1,115 @@ +#include "l_sight.h" + +#include "common.h" +#include "libdg/libdg.h" +#include "libgv/libgv.h" +#include "game/game.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_USER + +typedef struct _Work +{ + GV_ACT actor; + DG_PRIM *prim; + SVECTOR vecs[16]; + int timer; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void InitVecs(SVECTOR *from, SVECTOR *to, SVECTOR *out) +{ + SVECTOR last; + int i; + int x, y, z; + + last = *from; + + for (i = 1; i <= 8; i++) + { + out[0] = last; + + x = (to->vx - from->vx) * i; + out[1].vx = from->vx + x / 8; + + y = (to->vy - from->vy) * i; + out[1].vy = from->vy + y / 8; + + z = (to->vz - from->vz) * i; + out[1].vz = from->vz + z / 8; + + last = out[1]; + out += 2; + } +} + +static void ShadePacks(LINE_F2 *packs, int color) +{ + int i; + + for (i = 8; i > 0; i--) + { + LSTORE(color, &packs->r0); + setLineF2(packs); + setSemiTrans(packs, 1); + packs++; + } +} + +/*---------------------------------------------------------------------------*/ + +static void Act(Work *work) +{ + if (--work->timer == 0) + { + GV_DestroyActor(&work->actor); + } +} + +static void Die(Work *work) +{ + GM_FreePrim(work->prim); +} + +static int GetResources(Work *work, int color) +{ + DG_PRIM *prim; + + prim = GM_MakePrim(DG_PRIM_LINE_F2, 8, work->vecs, NULL); + work->prim = prim; + if (!prim) + { + return -1; + } + + ShadePacks(prim->packs[0], color); + ShadePacks(prim->packs[1], color); + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewLSight(SVECTOR *from, SVECTOR *to, int color) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "l_sight.c"); + + if (GetResources(work, color) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + InitVecs(from, to, work->vecs); + work->timer = 8; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/l_sight.h b/components/mgs/mgs/source/enemy/l_sight.h new file mode 100644 index 00000000..aba8c808 --- /dev/null +++ b/components/mgs/mgs/source/enemy/l_sight.h @@ -0,0 +1,10 @@ +#ifndef __MGS_ENEMY_L_SIGHT_H__ +#define __MGS_ENEMY_L_SIGHT_H__ + +#include +#include +#include + +void *NewLSight(SVECTOR *from, SVECTOR *to, int color); + +#endif // __MGS_ENEMY_L_SIGHT_H__ diff --git a/components/mgs/mgs/source/enemy/meryl.h b/components/mgs/mgs/source/enemy/meryl.h new file mode 100644 index 00000000..6d5e14c6 --- /dev/null +++ b/components/mgs/mgs/source/enemy/meryl.h @@ -0,0 +1,83 @@ +#ifndef __MGS_ENEMY_MERYL_H__ +#define __MGS_ENEMY_MERYL_H__ + +#include "enemy/enemy.h" + +#define COM_ST_DANBOWL 0x2000 +#define SP_DANBOWLKERI 0x400000 + +#define ACTINTERP 4 + +#define STANDSTILL 0 +#define ACTION1 1 //WALKING HOLDING GUN +#define ACTION2 2 //RUNNING HOLDING GUN +#define ACTION3 3 //STANDING AIMING GUN +#define ACTION4 4 //STANDING FIRING GUN +#define ACTION5 5 //SEMI-CROUCHING AIMING GUN +#define ACTION6 6 //SEMI_CROUCHING FIRING GUN +#define GRENADE 7 +#define ACTION8 8 //HITTING WITH GUN +#define ACTION9 9 //STANDING LOOKING TO THE RIGHT +#define ACTION10 10 //STANDING LOOKING TO THE LEFT +#define ACTION11 11 //STANDING LOOKING DOWN +#define ACTION12 12 //CROUCHING LOOKING FORWARD +#define ACTION13 13 //CROUCHING LOOKING A BIT TO THE RIGHT +#define ACTION14 14 //LOOKING UP +#define ACTION15 15 //GETTING HIT FROM THE FRONT +#define ACTION16 16 //GETTING HIT FROM BEHIND? +#define DANBOWLKERI 17 +#define DANBOWLPOSE 18 +#define ACTION19 19 //STRETCHING +#define ACTION20 20 //SLEEPING WHILST STANDING +#define ACTION21 21 //WAKING UP SUDDENLY? +#define ACTION22 22 //TALKING ON RADIO? +#define ACTION23 23 //RUBBING NECK +#define ACTION24 24 //SNEEZING +#define ACTION25 25 //I-POSE Maybe not used on this stage +#define ACTION26 26 //I-POSE Maybe not used on this stage +#define ACTION27 27 //GETTING HELD BY SNAKE +#define ACTION28 28 //PRE-NECK SNAP +#define ACTION29 29 //NECK SNAP AND FALLING TO FLOOR +#define ACTION30 30 //FALLING TO FLOOR +#define ACTION31 31 //GETTING FLIPPED OVER +#define ACTION32 32 //SHEILDING EYES +#define ACTION33 33 //STRUGGLING WHILST BEING HELD? +#define ACTION34 34 //FALLING TO THE FLOOR QUICKLY +#define ACTION35 35 //KNOCKED BACKWARDS +#define ACTION36 36 //KNOCKED FORWARDS +#define ACTION37 37 //NECK SNAP AND FALLING TO FLOOR FORWARDS +#define ACTION38 38 //FALLING TO THE FLOOR AND HAVING SOME SORT OF SEIZURE +#define ACTION39 39 //ON FLOOR ON STOMACH +#define ACTION40 40 //ON FLOOR ON BACK +#define ACTION41 41 //ON FLOOR ON STOMACH FACE DOWN +#define ACTION42 42 //FALLING FACE FIRST AND GETTING UP +#define ACTION43 43 //FALLING BACKWARDS AND GETTING UP +#define ACTION44 44 //BIKKURI GET UP FACE DOWN +#define ACTION45 45 //BIKKURI GET UP BACKWARDS +#define ACTION46 46 //JUST BEEN KNOCKED ONTO THE FLOOR ON FLOOR STOMACH +#define ACTION47 47 //JUST BEEN KNOCKED ONTO THE FLOOR ON BACK +#define ACTION48 48 //BEING DRAGGED BACKWARDS WHILST BEING HELD +#define ACTION49 49 //SAME ID AS 40 +#define ACTION50 50 //SAME ID AS 41 +#define ACTION51 51 //SAME ID AS 42 + +extern void s07a_meryl_unk_800D9E48( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800D9F14( WatcherWork *work, int time ); +extern void ActOverScoutD_800D9FE0( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA078( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA110( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA1C4( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA28C( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA330( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA3F8( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA610( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA75C( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800D7474( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800D7504( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800D75F8( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800D76CC( WatcherWork *work, int time ); +extern void ActReadyGun_800D7924( WatcherWork *work, int time ); + +#define SP_DANBOWLKERI 0x400000 + +#endif // __MGS_ENEMY_MERYL_H__ diff --git a/components/mgs/mgs/source/enemy/meryl7.c b/components/mgs/mgs/source/enemy/meryl7.c new file mode 100644 index 00000000..bf2050b7 --- /dev/null +++ b/components/mgs/mgs/source/enemy/meryl7.c @@ -0,0 +1,763 @@ +#include "enemy/enemy.h" + +#include +#include "common.h" +#include "mts/mts.h" // for fprintf +#include "libgv/libgv.h" +#include "libhzd/libhzd.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "strcode.h" + +#include "chara/snake/shadow.h" // for NewShadow2 +#include "enemy/dymc_seg.h" // for NewDynamicSegment +#include "enemy/glight.h" // for NewGunLight + +extern ENEMY_COMMAND EnemyCommand; +extern SVECTOR ENEMY_TARGET_SIZE; +extern SVECTOR ENEMY_TARGET_FORCE; +extern SVECTOR ENEMY_ATTACK_SIZE; +extern SVECTOR ENEMY_ATTACK_FORCE; +extern SVECTOR ENEMY_TOUCH_SIZE; +extern SVECTOR ENEMY_TOUCH_FORCE; +extern SVECTOR COM_NO_POINT; +extern unsigned short COM_EYE_LENGTH; + +extern GM_CameraSystemWork GM_Camera; + +/* Modified version of RootFlagCheck_800C3EE8 + (last two switch cases removed) + + ?????��??�???��???????��????��????? + ???�?????????��??�??????��?��???????��????��????? + ????????��????��????��????��??�???��????��???????��??????????? + �???��??????????��?��???????��?��????��????��?��?��?????????????��??�???��????? + ??��????��???????��?��?��?��??????????��?��???????��????��?��?��????????�??????? + */ +int s07a_meryl7_800D50F8( WatcherWork* work ) +{ + int count; + GV_MSG *msg; + SVECTOR svec; + CONTROL *ctrl; + + ctrl = &work->control; + ctrl->n_msg = GV_ReceiveMessage( ctrl->name, &work->control.msg ); + count = ctrl->n_msg; + msg = ctrl->msg; + + if ( count <= 0 ) return 0 ; + + for ( ; count > 0 ; --count, msg++ ) + { + switch ( msg->message[0] ) + { + case 0x430F: + work->param_c_root = msg->message[1]; + svec.vx = msg->message[2]; + svec.vy = msg->message[3]; + svec.vz = msg->message[4]; + + if ( svec.vy < 0x7530 ) + { + work->field_B7C = HZD_GetAddress( work->control.map->hzd, &svec, -1 ); + } + else + { + work->field_B7C = -1; + } + return 1; + case 0xF1BD: + work->hom->flag = FALSE ; + work->alert_level = 0 ; + GM_ConfigControlAttribute( &(work->control), 0 ) ; + work->visible = 0 ; + work->target->class = TARGET_AVAIL; + + work->faseout = 1 ; + work->act_status = EN_FASEOUT ; + break; + case 0x1DC4: + if ( EnemyCommand.field_0xC8[ work->field_B78 ].field_04 == 2 ) + { + work->visible = 1; + } + GM_ConfigControlAttribute( ctrl, 13 ); + work->faseout = 0; + work->act_status = 0; + break; + } + } + return 0; +} + +// Identical to s00a_watcher_800C409C +void s07a_meryl7_800D528C( WatcherWork* work ) +{ + if ( GM_GameStatus & STATE_ENEMY_OFF ) + { + if ( work->faseout == 0 ) + { + work->hom->flag = FALSE ; + work->alert_level = 0 ; + GM_ConfigControlAttribute( &(work->control), 0 ) ; + work->visible = 0 ; + work->target->class = TARGET_AVAIL; + + work->faseout = 1 ; + work->act_status = EN_FASEOUT ; + } + } +} + +// Identical to s00a_watcher_800C410C +void s07a_meryl7_800D52FC( WatcherWork* work ) +{ + s07a_meryl7_800D50F8( work ); + s07a_meryl7_800D528C( work ); +} + +// Identical to s00a_watcher_800C4138 +void s07a_meryl7_800D5328( DG_OBJS* objs, DG_DEF* def ) +{ + int i; + DG_OBJ *obj; + DG_FreeObjsPacket( objs, 0 ); + DG_FreeObjsPacket( objs, 1 ); + + objs->def = def; + obj = objs->objs; + for ( i = 0 ; i < 16 ; i++ ) + { + obj->model = &def->model[ i ]; + obj->n_packs = def->model[ i ].n_faces; + obj++; + } +} + +// Based on s00a_watcher_800C41B4, one "if" missing +void s07a_meryl7_800D53A4( WatcherWork *work ) +{ + if ( work->visible ) + { + if ( work->field_B7B == 1 ) + { + if ( GM_GameStatus & (GAME_FLAG_BIT_07 | STATE_BEHIND_CAMERA) || GM_Camera.first_person ) + { + if ( work->has_kmd != work->field_B7B ) + { + work->has_kmd = work->field_B7B; + s07a_meryl7_800D5328( work->body.objs, work->def ); + } + } + else if ( work->has_kmd ) + { + work->has_kmd = 0; + s07a_meryl7_800D5328( work->body.objs, work->kmd ); + } + } + DG_VisibleObjs( work->body.objs ); + DG_VisibleObjs( work->weapon.objs ); + work->field_AF4[0] = 1; + work->field_AFC[0] = 1; + } + else + { + DG_InvisibleObjs( work->body.objs ); + DG_InvisibleObjs( work->weapon.objs ); + work->field_AF4[0] = 0; + work->field_AFC[0] = 0; + } +} + +int s07a_meryl7_800D54DC( SVECTOR* svec ) +{ + if ( ( svec->vx < 4000 ) || ( svec->vx > 9500 ) || ( svec->vz < -20500 ) || ( svec->vz > -12000 ) ) + { + return 0; + } + + return 1; +} + +int s07a_meryl7_800D5520( SVECTOR* svec ) +{ + if ( ( svec->vx < 7000 ) || ( svec->vx > 8500 ) || ( svec->vz < -20500 ) || ( svec->vz > -19000 ) ) + { + return 0; + } + + return 1; +} + +int s07a_meryl7_800D5564( SVECTOR* svec ) +{ + if ( ( svec->vx < 4000 ) || ( svec->vx > 9500 ) || ( svec->vz < -20500 ) || ( svec->vz > -16500 ) ) + { + return 0; + } + + return 1; +} + +extern int *s07a_dword_800E3650; +extern int *s07a_dword_800E3654; + +void s07a_meryl7_800D55A8(WatcherWork *work ) +{ + if ( s07a_meryl7_800D54DC( &work->control.mov ) != 0 ) + { + *s07a_dword_800E3650 = 0; + } else + { + *s07a_dword_800E3650 = 1; + } + + if ( s07a_meryl7_800D5564( &GM_PlayerPosition ) != 0) + { + *s07a_dword_800E3654 = 0; + } + else + { + *s07a_dword_800E3654 = 1; + } +} + +int s07a_meryl7_800D5614( WatcherWork *work ) +{ + return s07a_meryl7_800D5520( &GM_PlayerPosition ); +} + +void MerylPushMove( WatcherWork *work ); // EnemyPushMove +void MerylActionMain( WatcherWork *work ); // EnemyActionMain + +// Modified version of WatcherAct_800C430C +void EnemyMerylAct_800D5638( WatcherWork *work ) +{ + VECTOR vec; + TARGET *trgt; + CONTROL *ctrl; + TARGET *trgt2; + + ctrl = &( work->control ) ; + if (GM_CheckMessage( &( work->actor ) , ctrl->name, HASH_KILL ) ) + { + GV_DestroyActor( &( work->actor ) ); + return; + } + + s07a_meryl7_800D52FC( work ); + s07a_meryl7_800D55A8( work ); + s07a_meryl7_800D5614( work ); + if ( !work->faseout ) + { + MerylPushMove( work ); + GM_ActControl( ctrl ); + GM_ActObject2( &( work->body ) ); + GM_ActObject2( &( work->weapon ) ); + + DG_GetLightMatrix2( &( ctrl->mov ), work->light ); + + MerylActionMain( work ); + trgt = work->target; + GM_MoveTarget( trgt, &( ctrl->mov ) ); + + GM_PushTarget( trgt ); + + if ( trgt->class & TARGET_TOUCH ) + { + trgt2 = &work->field_94C; + if ( trgt2->class & TARGET_TOUCH ) + { + if ( trgt2->damaged & TARGET_TOUCH ) + { + trgt2->damaged &= ~TARGET_TOUCH; + } + GM_MoveTarget( &( work->field_94C ), &( ctrl->mov ) ); + GM_TouchTarget( &( work->field_94C ) ); + } + } + vec.vx = vec.vy = vec.vz = work->scale; + ScaleMatrix( &( work->body.objs->world ), &vec ); + } + s07a_meryl7_800D53A4( work ); +} + +// Identical to InitTarget_800C444C +void s07a_meryl7_800D5780( WatcherWork *work ) +{ + TARGET *target; + TARGET *target2; + int life; + int faint; + + target = work->target; + life = work->param_life; + faint = work->param_faint; + + GM_SetTarget( target, TARGET_FLAG, ENEMY_SIDE, &ENEMY_TARGET_SIZE ); + GM_SetPowerTarget( target, POWER_DECREASE, -1, life, faint, &ENEMY_TARGET_FORCE ); + GM_SetCaptureTarget( target, -1, faint, NULL, NULL); + + GM_TargetBody( target, &( work->body.objs->objs[1].world ) ); + + target2 = &work->field_904; + GM_SetTarget( target2, TARGET_POWER, PLAYER_SIDE, &ENEMY_ATTACK_SIZE ); + GM_SetPowerTarget( target2, POWER_CONST | POWER_EXPLODE, 5, 0, 3, &ENEMY_ATTACK_FORCE ); + + target2 = &work->field_94C; + GM_SetTarget( target2, ( TARGET_TOUCH ), ENEMY_SIDE, &ENEMY_TOUCH_SIZE ); + GM_SetPowerTarget( target2, POWER_CONST | POWER_EXPLODE, 5, 0, 0, &ENEMY_TOUCH_FORCE ); +} + +// Identical to s00a_watcher_800C4578 +void s07a_meryl7_800D58AC( WatcherWork* work ) +{ + WatcherUnk *s; + s = &work->unknown; + + GV_ZeroMemory(s, 0x24); + s->field_00 = 0; + s->field_1C = 0x1C2; + s->field_1E = 1; + + work->action = 0; + work->action2 = 0; + work->time = 0; + work->time2 = 0; +} + +int MERYL_SetPutChar( WatcherWork *work, int put ); + +// Modified s00a_watcher_800C45D4 +int s07a_meryl7_800D5908( WatcherWork* work, int name, int where ) +{ + int i; + int has_kmd; + char *opt, *opt2; + CONTROL *ctrl; + OBJECT *body; + OBJECT *arm; //? + SVECTOR shadow; + + ctrl = &work->control; + if ( GM_InitControl( ctrl, name, where ) < 0 ) return -1; + + opt = GCL_GetOption( 'p' ); + + GM_ConfigControlString( ctrl, (char*)opt, (char*)GCL_GetOption( 'd' ) ) ; + GM_ConfigControlAttribute( ctrl, 13 ); + GM_ConfigControlInterp( ctrl, 4 ); + + ctrl->seg_flag = 2; + + GM_ConfigControlTrapCheck( ctrl ); + + //ctrl.r_sphere = -1; + + body = &work->body; + arm = &work->weapon; + + GM_InitObject( body, KMD_IPPANHEI, 0x32D, OAR_ENEMY ) ; + GM_ConfigObjectJoint( body ) ; + GM_ConfigMotionControl( body, &work->m_ctrl, OAR_ENEMY, work->m_segs1, work->m_segs2, ctrl, work->rots ); + GM_ConfigObjectLight( body, work->light ); + + work->field_B7B = 0; + + opt2 = GCL_GetOption( 'y' ); + if ( opt2 ) { + work->field_B7B = GCL_StrToInt( opt2 ); + } + + has_kmd = work->field_B7B; + if ( has_kmd == 1 ) + { + work->def = body->objs->def; + work->kmd = GV_GetCache( GV_CacheID( HASH_LOPRYHEI, 'k' ) ); + work->has_kmd = has_kmd; + } + + work->hom = GM_AllocHomingTarget( &body->objs->objs[6].world, ctrl ); + GM_InitObject( arm, KMD_FAMAS, 0x6D, 0 ); + GM_ConfigObjectLight( arm, work->light ) ; + GM_ConfigObjectRoot( arm, body, 4 ); + + for ( i = 0 ; i < 0 ; i++ ) + { + work->field_B00[i] = 0; + } + + shadow.vy = 6; + shadow.vz = 12; + shadow.pad = 15; + shadow.vx = 0; + + work->field_AF0 = (void*)NewShadow2( ctrl, body, shadow, &work->field_AF4 ) ; + work->field_AF8 = NewGunLight( &( body->objs->objs[4].world ), &work->field_AFC ) ; + + MERYL_SetPutChar( work, 0 ); + s07a_meryl7_800D58AC ( work ); + + return 0; +} + +// Identical to s00a_watcher_800C4814 +//FreeWatcher? +void s07a_meryl7_800D5B28( WatcherWork* work ) +{ + GM_FreeHomingTarget( work->hom ); + GM_FreeControl( &( work->control ) ); + GM_FreeObject( &( work->body ) ); + GM_FreeObject( &( work->weapon ) ); + GM_FreeTarget( work->target ); + GV_DestroyOtherActor( work->field_AF8 ); + GV_DestroyOtherActor( work->field_AF0 ); +} + +// Identical to WatcherDie_800C487C +void EnemyMerylDie_800D5B90( WatcherWork* work ) +{ + s07a_meryl7_800D5B28( work ); +} + +// Identical to ReadNodes_800C489C +int s07a_meryl7_800D5BB0( WatcherWork* work ) +{ + int i; + HZD_PAT *patrol; + HZD_PTP *points; + + patrol = work->control.map->hzd->def->routes; + patrol = &patrol[ work->param_root ]; + + work->n_nodes = patrol->n_points; + + if ( work->n_nodes <= 0 ) return -1; + + points = patrol->points; + for ( i = 0 ; i < work->n_nodes ; i++ ) + { + work->nodes[i].vx = points->x; + work->nodes[i].vy = points->y; + work->nodes[i].vz = points->z; + work->nodes[i].pad = points->command; + points++; + } + return 0; +} + +// Identical to s00a_watcher_800C4990 +int s07a_meryl7_800D5C4C( char *opt, short* s ) +{ + int i; + unsigned char *res; + + for ( i = 0 ; ( res = GCL_NextStr() ) ; i++ ) + { + *s = GCL_StrToInt( res ); + s++; + } + + return i; +} + +// Identical to s00a_watcher_800C49E8 +int s07a_meryl7_800D5CA4( char *opt, int* l ) +{ + int i; + unsigned char *res; + + for ( i = 0 ; ( res = GCL_NextStr() ) ; i++ ) + { + *l = GCL_StrToInt( res ); + l++; + } + + return i; +} + +extern unsigned short s07a_dword_800C3618[]; + +int s07a_meryl7_800D5CFC( WatcherWork* work ) +{ + int i; + char *opt; + int ret; + + for ( i = 0 ; i < 8 ; i++ ) + { + work->field_BB0[i] = s07a_dword_800C3618[i]; + } + + opt = GCL_GetOption( 't' ); + if ( opt ) + { + ret = s07a_meryl7_800D5CA4( opt, &work->field_BB0[1] ); + if ( ret > 4 ) + { + printf( "Err Err Err Set time Over\n" ) ; + return -1; + } + } + + for ( i = 0 ; i < 4 ; i++ ) + { + work->field_BD0[i] = i * 1024; + } + + opt = GCL_GetOption( 'i' ); + + if ( opt ) + { + ret = s07a_meryl7_800D5C4C( opt, work->field_BD0 ); + if ( ret > 4 ) + { + printf( "Err Err Err Set Dir Over\n" ) ; + return -1; + } + } + + return 0; +} + +int s07a_meryl7_800D5DD4( WatcherWork *work ) +{ + char *opt; + + opt = GCL_GetOption('v'); + if (opt != NULL && s07a_meryl7_800D5CA4(opt, &work->field_C40) > 2) + { + fprintf(0, "Err Err Err Sound Buff Over !!\n"); + return -1; + } + + return 0; +} + +void s07a_meryl7_800D5E34( WatcherWork *work ) +{ + int flag; + SVECTOR min, max; + + flag = 0xFE; + + min.vx = 5500; + min.vy = 0; + min.vz = -9500; + + max.vx = 7000; + max.vy = 0; + max.vz = -9500; + + NewDynamicSegment( GV_StrCode( "メイン" ), &min, &max, 3000, 3000, flag, &s07a_dword_800E3650 ); + + flag = 0xF7; + + min.vx = 4500; + min.vy = 0; + min.vz = -18500; + + max.vx = 7000; + max.vy = 0; + max.vz = -18500; + NewDynamicSegment( GV_StrCode( "女子トイレ" ), &min, &max, 3000, 3000, flag, &s07a_dword_800E3654 ); +} + +extern int s07a_dword_800C35F8[8]; +extern int s07a_dword_800E3658; + +void EnemyMerylGetResources_800D5F24( WatcherWork *work, int name, int where ) +{ + SVECTOR svec; + int addr; + char *opt; + int i; + + s07a_meryl7_800D5908( work, name, where ) ; + work->field_B78 = s00a_command_800CEA2C( work ) ; + + if ( work->field_B78 << 24 < 0 ) + { + printf( "Err not enough work !!\n" ) ; + } + + s07a_meryl7_800D5CFC( work ) ; + + work->param_root = 0; + opt = GCL_GetOption( 'r' ); + if ( opt ) + { + work->param_root = GCL_StrToInt( ( char* )opt ); + } + + work->param_life = 192; + opt = GCL_GetOption( 'l' ); + if ( opt ) + { + work->param_life = GCL_StrToInt( ( char* )opt ); + } + + work->param_faint = 10; + opt = GCL_GetOption( 'f' ); + if ( opt ) + { + work->param_faint = GCL_StrToInt( ( char* )opt ); + } + + work->param_blood = 65; + opt = GCL_GetOption( 'b' ); + if ( opt ) + { + work->param_blood = GCL_StrToInt( ( char* )opt ); + } + + work->field_B81 = 0xFF; + + opt = GCL_GetOption('g'); + if ( opt ) + { + work->field_B81 = GCL_StrToInt( ( char* )opt ); + } + if ( work->param_blood == 'Z' ) + { + work->field_B81 = 0; + } + + work->field_C34 = 0; + work->field_C35[0] = 0; + + opt = GCL_GetOption( 'e' ); + if ( opt ) + { + work->field_C3C = GCL_StrToInt( GCL_NextStr() ); + } + else + { + work->field_C3C = -1; + } + + s07a_dword_800E3658 = -1; + opt = GCL_GetOption( 'k' ); + if ( opt ) + { + s07a_dword_800E3658 = GCL_StrToInt( GCL_NextStr() ); + } + + s07a_meryl7_800D5DD4( work ); + work->field_BFC = s07a_dword_800C35F8[ work->field_B78 ]; + work->field_C00 = work->field_B78; + work->field_B7C = 0xFF; + + opt = GCL_GetOption( 'n' ); + if ( opt ) + { + GCL_StrToSV( ( char* )opt, (short *)&svec ); + if ( svec.vy < 0x7530 ) + { + work->field_B7C = HZD_GetAddress( work->control.map->hzd, &svec, -1 ); + } + else + { + work->field_B7C = 0xFF; + } + } + + work->param_area = 'A'; + opt = GCL_GetOption( 'a' ); + if (opt != 0) + { + work->param_area = GCL_StrToInt( ( char* )opt ); + } + + if ( work->param_area == 'S' ) MERYL_SetPutChar( work, PUTBREATH ) ; /* ??��??????????? */ + work->scale = 4096 ; /* ??��?��?��?? */ + + if ( ( opt = GCL_GetOption( 's' ) ) != NULL ) work->scale += GCL_StrToInt( ( char* )opt ); + work->param_item = 1; + + //fprintf(0,"Life=%d Faint=%d Blood=%c Area=%c \n", + // work->param.life, work->param.faint, work->param.blood,work->param.area); + + if( s07a_meryl7_800D5BB0( work ) < 0 ) printf( "meryl.c : action point Err\n" ); + + /* + �????????????��?��????????????????? + */ + if( ( work->target = GM_AllocTarget() ) != NULL ) s07a_meryl7_800D5780( work ) ; + + /* + ???????????��????��???????��?��????????????????? + */ + s07a_meryl7_800D5E34( work ); + work->think1 = 0 ; + work->think2 = 0 ; + work->think3 = 0 ; + work->think4 = 0 ; + work->count3 = 0 ; + work->l_count = 0 ; + work->t_count = 0 ; + work->mark_time = 0 ; + work->next_node = 0 ; + work->search_flag = 0 ; + work->act_status = 0 ; + + work->target_pos = work->nodes[ 0 ] ; + work->target_addr = HZD_GetAddress( work->control.map->hzd, &( work->target_pos ), -1 ) ; + work->target_map = GM_CurrentMap; + + work->alert_level = 0; + work->visible = 1; + work->vision.angle = 0x200; + work->vision.facedir = 0; + work->field_B94 = 0; + work->pad.sound = 0; + work->pad.time = 0; + work->vision.length = COM_EYE_LENGTH; + work->field_BA4 = COM_NO_POINT; + + work->subweapon = 0; + + work->control.mov = work->nodes[ 0 ] ; + work->param_c_root = work->param_root; + work->field_B7F = work->field_B7C; + + for ( i = 0 ; i <= 7 ; i++ ) + { + work->modetime[i] = 0; + } + + work->field_BA3 = 7; + work->field_BA0 = -1; + + opt = GCL_GetOption( 'c' ); + if ( opt ) + { + printf( "asiatoooo" ); + work->field_BA3 |= 0x10; + } + + GM_ConfigControlRadarparam( &work->control, 0, 0x200, COM_EYE_LENGTH, 0 ); + work->start_pos = work->nodes[ 0 ] ; + work->start_map = GM_CurrentMap; + addr = HZD_GetAddress( work->control.map->hzd, &( work->control.mov ), -1 ); + + work->start_addr = addr; + work->field_C08 = addr; + work->field_BF0 = addr; + + work->field_C14 = work->start_pos; +} + +void *NewEnemyMeryl_800D63A4( int name, int where, int argc, char **argv ) +{ + WatcherWork *work ; + + printf( " enemy work size = %d \n", sizeof( WatcherWork ) ); + work = GV_NewActor( GV_ACTOR_PREV, sizeof( WatcherWork ) ) ; + if ( work != NULL ) { + GV_SetNamedActor( &( work->actor ), EnemyMerylAct_800D5638, EnemyMerylDie_800D5B90, "meryl7.c" ); + EnemyMerylGetResources_800D5F24( work, name, where ); + } + return (void *)work ; +} diff --git a/components/mgs/mgs/source/enemy/merylaction.c b/components/mgs/mgs/source/enemy/merylaction.c new file mode 100644 index 00000000..46622d2f --- /dev/null +++ b/components/mgs/mgs/source/enemy/merylaction.c @@ -0,0 +1,1993 @@ +#include "enemy/enemy.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/item.h" +#include "linkvar.h" +#include "anime/animconv/anime.h" +#include "sound/g_sound.h" + +// from takabe/mosaic.h +extern void *NewMosaic( MATRIX *, int, int, int ); + +extern ENEMY_COMMAND EnemyCommand; +extern unsigned int COM_GameStatus; +extern int COM_EYE_LENGTH; +extern int COM_VibTime; + +extern SVECTOR s07a_dword_800C3694; + +extern int CheckPad_800D6DE4( WatcherWork *work ); +extern int CheckDamage_800D6B30( WatcherWork* work ); + +extern int s07a_meryl_unk_800D66F4( SVECTOR* svec ); +extern int s07a_meryl_unk_800D6808( WatcherWork* work ); +extern int s07a_meryl_unk_800D6B90( int dir, int dist ); +extern void ReviseReadyGun_800D6BE4( WatcherWork* work ); +extern void s07a_meryl_unk_800D6D7C( WatcherWork *work ); +extern int s07a_meryl_unk_800D6738( SVECTOR* svec ); +extern int s07a_meryl_unk_800D66B0( SVECTOR* svec ); + +extern void s07a_meryl_unk_800D71B0( WatcherWork* work, int time ); +extern void s07a_meryl_unk_800D7A90( WatcherWork* work, int time ); +extern void s07a_meryl_unk_800D7B48( WatcherWork* work, int time ); +extern void ActGrenade_800D7C98( WatcherWork* work, int time ); +extern void s07a_meryl_unk_800D7DF0( WatcherWork* work, int time ); +extern void ActOverScoutD_800D9FE0( WatcherWork* work, int time ); +extern void s07a_meryl_unk_800D8CB4( WatcherWork* work, int time ); +extern void s07a_meryl_unk_800D9410( WatcherWork* work, int time ); +extern void s07a_meryl_unk_800D8AA0( WatcherWork* work, int time ); +extern void s07a_meryl_unk_800D89D8( WatcherWork* work, int time ); + +extern void MERYL_PutItem( WatcherWork* work ); +extern int MERYL_SetPutChar( WatcherWork *work, int idx ); +extern void MERYL_PutBlood( WatcherWork *, int , int ); +extern void MERYL_PutFog( WatcherWork* work ); +extern int MERYL_ClearPutChar( WatcherWork *work, void *func ); + +extern int AttackForce_800D6C6C( WatcherWork *work ); + +extern void NewBlood( MATRIX *, int ); +extern void AN_Breath( MATRIX * ); +extern void *NewLSight(SVECTOR *from, SVECTOR *to, int color); + +extern SVECTOR s07a_dword_800C369C; +extern SVECTOR s07a_dword_800C36A4; +extern SVECTOR s07a_dword_800C36AC; +extern SVECTOR s07a_dword_800C36B4; +extern SVECTOR s07a_dword_800C36D8; +extern SVECTOR s07a_dword_800C36E0; + +extern void *s07a_dword_800C36C8[4]; + +extern const char *s07a_dword_800C36BC[3]; + +/****Inlines**********************************************************************************************/ +static inline void UnsetMode( WatcherWork *work ) +{ + extern short ActTable_800C3628[]; + work->unknown.last_unset = 0; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3628[ STANDSTILL ], 0, ACTINTERP, 0 ); + + work->action2 = 0; + work->time2 = 0; + work->unknown.last_unset = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; +} + +static inline void UnsetMode2( WatcherWork *work ) +{ + extern short ActTable_800C3628[]; + work->unknown.last_unset = 0; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3628[ STANDSTILL ], 0, ACTINTERP, 0 ); + + work->action2 = 0; + work->time2 = 0; + work->unknown.last_unset = 0; + work->control.turn.vz = 0; + work->control.turn.vx = 0; + + if ( work->mosaic ) + { + GV_DestroyOtherActor( work->mosaic ); + work->mosaic = 0; + } + +} + +static inline void SetAction( WatcherWork *work, int n_action, int interp ) +{ + extern short ActTable_800C3628[]; + work->unknown.last_set = n_action ; + GM_ConfigObjectAction( &( work->body ), ActTable_800C3628[ n_action ], 0, interp ); +} + +static inline void UnsetAction( WatcherWork *work, int n_action ) +{ + extern short ActTable_800C3628[]; + + work->unknown.last_unset = n_action; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3628[ n_action ], 0, ACTINTERP, 0x3FE ); +} + +static inline void UnsetActionManual( WatcherWork *work, int n_action, int a4 ) +{ + extern short ActTable_800C3628[]; + + work->unknown.last_unset = n_action; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3628[ n_action ], 0, ACTINTERP, a4 ); +} + +static inline void UnsetAction2( WatcherWork *work ) +{ + extern short ActTable_800C3628[]; + + work->unknown.last_unset = 0; + GM_ConfigObjectOverride( &( work->body ), ActTable_800C3628[ STANDSTILL ], 0, ACTINTERP, 0 ); + GV_DestroyOtherActor( work->subweapon ); +} +/*********************************************************************************************************/ + +void ActStandStill_800D7008( WatcherWork* work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + work->act_status |= 1; + + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->target->class |= TARGET_C4 ; + } + + work->vision.length = COM_EYE_LENGTH ; + + if ( time == 0 ) + { + SetAction( work, STANDSTILL, ACTINTERP ); + } + + if ( CheckDamage_800D6B30( work ) || CheckPad_800D6DE4( work ) ) + { + UnsetMode( work ) ; + return ; + } + + + if ( work->pad.dir >= 0 ) + { + if ( GV_DiffDirAbs( work->control.rot.vy, work->pad.dir ) < 0x100 ) + { + if ( work->pad.mode & 0x1 ) + { + SetAction( work, ACTION2, ACTINTERP ); + } + else + { + SetAction( work, ACTION1, ACTINTERP ); + } + SetMode( work, s07a_meryl_unk_800D71B0 ); + + } + else + { + work->control.turn.vy = work->pad.dir; + } + } + work->vision.facedir = work->control.rot.vy; +} + +void s07a_meryl_unk_800D71B0( WatcherWork* work, int time ) +{ + int x, z; + int s0; + int interval; + int tmp; + int dist; + CONTROL * ctrl; + int dir; + int field_8E0; + SVECTOR* svec; + + SetTargetClass( work->target, TARGET_FLAG ); + + work->act_status |= 0x1; + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->target->class |= TARGET_C4 ; + } + + work->vision.length = COM_EYE_LENGTH ; + + if ( CheckDamage_800D6B30( work ) || CheckPad_800D6DE4( work ) ) + { + UnsetMode( work ) ; + return ; + } + + dist = -1; + ctrl = &(work->control ); + dir = work->pad.dir; + field_8E0 = work->unknown.last_set; + svec = work->control.vecs; + + if ( (work->pad.mode & 0x1) && ( work->unknown.last_set != ACTION2 ) ) + { + SetAction( work, ACTION2, ACTINTERP ); + } + + if ( dir >= 0 ) + { + s0 = ctrl->n_touches; + if ( s0 > 0 ) + { + dist = GV_VecDir2( svec ); + + if ( s0 >= 2 ) + { + tmp = GV_VecDir2( &ctrl->vecs[1] ); + if ( GV_DiffDirAbs( dir, tmp ) < GV_DiffDirAbs( dir, dist ) ) + { + dist = tmp; + } + } + } + } + + if ( dir < 0 ) + { + SetMode( work, ActStandStill_800D7008); + UnsetMode( work ) ; + return; + } + + + switch ( s07a_meryl_unk_800D6B90( dir, dist ) ) + { + case 1: + dir = (dist - 1024) & 0xFFF; + break; + case 2: + dir = (dist + 1024) & 0xFFF; + break; + } + + + if ( !work->body.objs->bound_mode ) + { + ctrl->rot.vy = dir; + ctrl->turn.vy = dir; + work->vision.facedir = GV_NearExp4P( work->vision.facedir, work->control.rot.vy ); + } + else + { + ctrl->turn.vy = dir; + work->vision.facedir = work->control.rot.vy; + } + + interval = 0; + + if ( field_8E0 == 1 ) + { + interval = 50; + } + else if ( field_8E0 == 2 ) + { + interval = 120; + } + + x = interval * rsin( dir ); + + if ( x < 0 ) + { + x += 0xFFF; + } + + ctrl->step.vx = x >> 12; + + z = interval * rcos( dir ); + + if ( z < 0 ) + { + z += 0xFFF; + } + + ctrl->step.vz = z >> 12; + +} + +extern void s07a_meryl_unk_800D8798( WatcherWork* work, int time ) ; + +void s07a_meryl_unk_800D7474( WatcherWork* work, int time ) +{ + if ( time == 0 ) + { + work->unknown.field_14 = 3; + SetAction( work, ACTION35, 4 ); + } + + if ( !( CheckDamage_800D6B30( work ) ) && ( work->body.is_end ) ) + { + work->pad.tmp = 0; + SetMode( work, s07a_meryl_unk_800D8798 ); + } +} + +void s07a_meryl_unk_800D7504( WatcherWork* work, int time ) +{ + CONTROL *ctrl; + + ctrl = &( work->control ); + work->act_status |= 0x01; + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = COM_EYE_LENGTH ; + + if ( time == 0 ) + { + SetAction( work, ACTION16, ACTINTERP ); + } + + if ( CheckDamage_800D6B30( work ) ) + { + return ; + } + + if ( work->body.is_end || !( work->pad.press & 0x20 ) ) + { + SetMode( work, ActStandStill_800D7008 ); + return; + } + + ctrl->turn.vy = work->sn_dir; + ctrl->step.vx = 0; + ctrl->step.vz = 0; + work->vision.facedir = work->control.rot.vy; +} + +void s07a_meryl_unk_800D75F8( WatcherWork* work, int time ) +{ + CONTROL *ctrl; + + ctrl = &( work->control ); + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = COM_EYE_LENGTH ; + + if ( time == 0 ) + { + SetAction( work, ACTION13, ACTINTERP ); + } + + if ( CheckDamage_800D6B30( work ) ) + { + return ; + } + + if ( !( work->pad.press & 0x2000000 ) ) + { + SetMode( work, ActStandStill_800D7008 ); + return; + } + + ctrl->turn.vy = work->pad.dir; + ctrl->step.vx = 0; + ctrl->step.vz = 0; + work->vision.facedir = work->control.rot.vy; +} + +void s07a_meryl_unk_800D76CC( WatcherWork *work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ) ; + work->vision.length = COM_EYE_LENGTH; + + if ( CheckDamage_800D6B30( work ) ) + { + UnsetCameraActCall( ); + COM_GameStatus &= ~COM_ST_DANBOWL ; + return; + } + + if ( !(work->pad.press & SP_DANBOWLKERI) ) + { + if ( !(CheckPad_800D6DE4( work )) ) + { + SetMode( work, ActStandStill_800D7008 ) ; + } + UnsetCameraActCall( ); + COM_GameStatus &= ~COM_ST_DANBOWL ; + return ; + } + + if ( time == 0 ) { + SetMode2( work, ActOverScoutD_800D9FE0 ) ; + SetAction( work, STANDSTILL, ACTINTERP ) ; + SetCameraActCall( ); + } + + if ( time == 60 ) { + UnsetMode( work ) ; + SetAction( work, DANBOWLKERI, ACTINTERP ) ; + } + + if ( time == 78 ) { + extern void *NewBoxKeri( MATRIX *, SVECTOR * ) ; + NewBoxKeri( &( GM_PlayerBody->objs[ 0 ].world ), &( work->control.mov ) ) ; + } + + if ( time == 100 ) { + SetAction( work, DANBOWLPOSE, ACTINTERP ) ; + } + + if ( time == 130 ) { + GM_CurrentItemId = IT_None ; + } + + if ( time > 150 ) { + UnsetCameraActCall( ); + work->actend = 1 ; + SetMode( work, ActStandStill_800D7008 ) ; + COM_GameStatus &= ~COM_ST_DANBOWL ; + return ; + } + work->control.turn.vy = work->sn_dir; + work->vision.facedir = work->control.rot.vy; +} + +void ActReadyGun_800D7924( WatcherWork* work, int time ) +{ + int press; + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = COM_EYE_LENGTH ; + press = work->pad.press; + + if ( time == 0 ) + { + if ( press & 0x10000 ) + { + SetAction( work, ACTION3, ACTINTERP ) ; + } + else + { + SetAction( work, ACTION5, ACTINTERP ); + } + GM_ConfigMotionAdjust( &( work->body ), work->adjust ) ; + } + + ReviseReadyGun_800D6BE4( work ); + if ( CheckDamage_800D6B30( work ) ) + { + return ; + } + + if ( !(press & 0x30000) ) + { + SetMode( work, ActStandStill_800D7008 ) ; + return ; + } + + if ( press & 0x40000 ) + { + SetModeFields( work, s07a_meryl_unk_800D7A90 ) ; + return; + } + + if ( press & 0x80000 ) + { + SetModeFields( work, s07a_meryl_unk_800D7B48 ) ; + return; + } + + if ( press & 0x100000 ) + { + SetModeFields( work, ActGrenade_800D7C98 ) ; + return; + } + + if ( press & 0x200000 ) + { + SetModeFields( work, s07a_meryl_unk_800D7DF0 ) ; + return; + } + + work->control.turn.vy = work->sn_dir; + work->vision.facedir = work->control.rot.vy; +} + +void s07a_meryl_unk_800D7A90( WatcherWork* work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = COM_EYE_LENGTH ; + + if ( time == 0 ) + { + SetAction( work, ACTION4, 0 ) ; + MERYL_SetPutChar( work, 3 ) ; + GM_ConfigMotionAdjust( &( work->body ), work->adjust ) ; + } + + ReviseReadyGun_800D6BE4( work ); + work->control.turn.vy = work->sn_dir; + + if ( CheckDamage_800D6B30( work ) ) + { + return ; + } + + SetModeFields( work, ActReadyGun_800D7924 ); +} + +void s07a_meryl_unk_800D7B48( WatcherWork* work, int time ) +{ + SVECTOR *rot; + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = COM_EYE_LENGTH ; + + if ( time == 0 ) + { + GM_ConfigMotionAdjust( &( work->body ), work->adjust ); + } + + rot = &work->control.rot; + if ( time == 1 || time == 7 ) + { + rot->vy = ( rot->vy - 0x100 ) & 0xFFF; + } + + if ( time == 3 || time == 5 ) + { + rot->vy = ( rot->vy + 0x100 ) & 0xFFF; + } + + if ( time == 2 || time == 4 || time == 6 ) + { + SetAction( work, ACTION6, 0 ); + MERYL_SetPutChar( work, GUNSHOOT ); + } + + ReviseReadyGun_800D6BE4( work ); + if ( CheckDamage_800D6B30( work ) ) + { + return ; + } + + if ( work->body.is_end ) + { + SetMode( work, ActReadyGun_800D7924 ); + } + + work->control.rot.vy = rot->vy; +} + +void ActGrenade_800D7C98( WatcherWork* work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ) ; + work->vision.length = COM_EYE_LENGTH ; /* ???? */ + + if ( time == 0 ) + { + extern void *NewGrenadeEnemy( CONTROL *, OBJECT *, int, u_long *, SVECTOR *, int ) ; + + SetAction( work, GRENADE, ACTINTERP ) ; + work->subweapon = NewGrenadeEnemy( &(work->control), &(work->body), 9, + &(work->trigger), &GM_PlayerPosition, ENEMY_SIDE ) ; + } + + if ( time > ACTINTERP ) + { + work->trigger |= WEAPON_TAKE ; + } + if ( time == 17 ) + { + GM_SeSet( &( work->control.mov ), SE_PINNUKI ) ; + } + if ( time == 45 ) + { + work->trigger |= WEAPON_TRIG2 ; + } + + if ( CheckDamage_800D6B30( work ) ) + { + GV_DestroyOtherActor( work->subweapon ) ; + return ; + } + + + if ( work->body.is_end ) + { + GV_DestroyOtherActor( work->subweapon ) ; + SetMode( work, ActReadyGun_800D7924 ); + } +} + +void s07a_meryl_unk_800D7DF0( WatcherWork* work, int time ) +{ + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = COM_EYE_LENGTH ; + + if ( time == 0 ) + { + SetAction( work, ACTION8, 0 ); + } + + if ( time == 3 ) + { + if ( AttackForce_800D6C6C( work ) ) + { + GM_SeSet( &( work->control.mov ), SE_HIT ); + } + } + + work->control.turn.vy = work->sn_dir; + + if ( CheckDamage_800D6B30( work ) ) + { + return ; + } + + if ( work->body.is_end ) + { + SetMode( work, ActReadyGun_800D7924 ); + } +} + +void s07a_meryl_unk_800D7EC8( WatcherWork* work, int time ) +{ + work->vision.length = 0; + + if ( time == 0 ) + { + SetAction( work, ACTION21, ACTINTERP ) ; + } + + if ( CheckDamage_800D6B30( work ) ) + { + return ; + } + + SetTargetClass( work->target, TARGET_FLAG ); + + if ( work->body.is_end ) + { + work->actend = 1; + SetMode( work, ActStandStill_800D7008 ); + } +} + +void s07a_meryl_unk_800D7F70( WatcherWork* work, int time ) +{ + CONTROL *ctrl; + TARGET *target; + + target = work->target; + ctrl = &work->control; + work->unknown.field_1E = 0; + work->act_status |= 0x08; + + if ( time == 0 ) + { + work->unknown.field_14 = 6; + SetAction( work, ACTION31, ACTINTERP ); + } + + if ( time - 8 < 12u ) + { + s07a_meryl_unk_800D6D7C( work ); + } + + if ( work->unknown.last_set == 31 ) + { + if ( time == 22 ) + { + + if ( ctrl->mov.vy - ctrl->levels[0] < 2000 ) + { + GM_SeSet( &ctrl->mov, 0xC3 ) ; + GM_SeSet( &ctrl->mov, SE_HIT_FLOOR ) ; + MERYL_PutBlood( work, 6, 0 ) ; + GM_SetNoise( 0x64, 4, &work->control.mov ) ; + } + } + if ( work->body.is_end ) + { + if ( !ctrl->grounded ) + { + SetAction( work, ACTION40, ACTINTERP ); + } + else + { + SetMode( work, s07a_meryl_unk_800D8798 ); + } + } + } + else + { + if ( ctrl->grounded ) + { + GM_SeSet( &ctrl->mov, 0xC3 ) ; + GM_SeSet( &ctrl->mov, SE_HIT_FLOOR ) ; + GM_SetNoise( 0x64, 4, &work->control.mov ) ; + MERYL_PutBlood( work, 6, 1 ) ; + SetMode( work, s07a_meryl_unk_800D8798 ); + } + } + if ( time < 24 ) + { + ctrl->turn = *target->field_1C; + } + + if ( time < 22 ) + { + work->control.hzd_height = -32767; + } +} + +void s07a_meryl_unk_800D8210( WatcherWork* work ) +{ + SVECTOR svec; + + work->control.turn = GM_PlayerControl->rot; + DG_SetPos2(&GM_PlayerPosition, &GM_PlayerControl->rot); + DG_PutVector(&s07a_dword_800C3694, &svec, 1); + GV_SubVec3(&svec, &work->control.mov, &work->control.step); +} + +void s07a_meryl_unk_800D8290( WatcherWork *work, int time ) +{ + int s2; + TARGET *target; + + target = work->target; + s2 = work->unknown.last_set; + + work->unknown.field_1E = 0; + work->act_status |= 0x0C; + + if ( time == 0 ) + { + work->field_B5A = target->faint; + GM_SeSet( &work->control.mov, 0xBE ); + } + + if ( work->field_B5A != target->faint ) + { + GM_SeSet( &work->control.mov, 0xBE ); + work->field_B5A = target->faint; + } + + if ( target->damaged & TARGET_POWER ) + { + MERYL_PutBlood( work, 5, 0 ); + GM_SeSet( &work->control.mov, 0xBE ); + target->force = DG_ZeroVector; + target->damage = 0; + target->damaged = TARGET_STALE; + + if ( target->vital <= 0 ) + { + work->unknown.field_14 = 5; + target->side = ENEMY_SIDE; + SetMode( work, s07a_meryl_unk_800D8CB4 ); + target->captured = 0; + } + } + switch ( GM_PlayerAction ) + { + case 0x7: + case 0xD: + if( s2 != 0x1B && s2 != 0x30 ) + { + target->side = PLAYER_SIDE; + SetAction( work, ACTION27, ACTINTERP ); + work->field_B5C = 0; + } + + if ( work->field_B5C == 0x32 && target->faint > 0 ) + { + SetAction( work, ACTION48, ACTINTERP ); + } + + if ( s2 == 0x30 && !( work->field_B5C & 7 ) ) + { + COM_VibTime = 4; + } + work->control.turn = GM_PlayerControl->rot; + s07a_meryl_unk_800D8210( work ); + work->field_B5C++; + break; + case 0x26: + if ( s2 != 0x1C ) + { + target->side = PLAYER_SIDE; + SetAction( work, ACTION28, ACTINTERP ); + } + work->control.turn = GM_PlayerControl->rot; + s07a_meryl_unk_800D8210( work ); + break; + case 0x27: + work->unknown.field_14 = 4; + target->side = ENEMY_SIDE; + target->vital = 0; + SetMode( work, s07a_meryl_unk_800D8CB4 ); + return; + default: + work->unknown.field_14 = 5; + target->side = ENEMY_SIDE; + SetMode( work, s07a_meryl_unk_800D8CB4 ); + return; + } + + if ( work->sn_dis > 800 ) + { + target->captured = 0; + } + + work->target->class |= ( TARGET_SEEK | TARGET_POWER) ; + work->vision.facedir = work->control.rot.vy; +} + +void s07a_meryl_unk_800D85E0( WatcherWork *work, int time ) +{ + if ( time == 0 ) + { + SetAction( work, ACTION38, ACTINTERP ); + } + + if (work->body.is_end ) + { + SetMode( work, s07a_meryl_unk_800D9410 ); + } +} + +void s07a_meryl_unk_800D8654( WatcherWork *work, int time ) +{ + CONTROL *ctrl; + int time_offset; + + ctrl = &work->control; + work->unknown.field_1E = 0; + work->vision.length = 0; + work->act_status |= 0x8; + + if ( time == 0 ) + { + SetAction( work, ACTION32, ACTINTERP ) ; + } + + if ( CheckDamage_800D6B30( work ) ) + { + return ; + } + + time_offset = 10; + + if ( time == time_offset ) + { + work->unknown.field_14 = 1; + SetAction( work, ACTION37, ACTINTERP ) ; + } + else + { + SetTargetClass( work->target, TARGET_FLAG ); + } + + if ( time == time_offset + 24 ) + { + GM_SeSet( &ctrl->mov, SE_ENEMY_COLLAPSE ); + } + + if ( time == time_offset + 46 ) + { + GM_SeSet( &ctrl->mov, SE_HIT_FLOOR ); + } + + if ( time >= time_offset + 50 && work->body.is_end ) + { + SetMode( work, s07a_meryl_unk_800D8798 ) ; + } +} + +void s07a_meryl_unk_800D8798( WatcherWork *work, int time ) +{ + int x; + SVECTOR svec; + + work->act_status |= 0x20; + + if ( time == 0 ) + { + + if ( work->unknown.field_14 < 3 ) + { + if ( work->unknown.field_14 == 1 ) + { + SetAction( work, ACTION41, ACTINTERP ); + } + else + { + SetAction( work, ACTION39, ACTINTERP ); + } + } + else + { + SetAction( work, ACTION40, ACTINTERP ); + } + + if ( work->target->faint <= 0 ) + { + x = work->control.mov.vx % 90; + work->field_B5A = abs(x) + 90; + + svec.vx = work->body.objs->objs[6].world.t[0]; + svec.vy = work->body.objs->objs[6].world.t[1]; + svec.vz = work->body.objs->objs[6].world.t[2]; + if ( !( s07a_meryl_unk_800D66F4( &work->control.mov ) ) ) + { + AN_Piyopiyo( &svec ); + } + } + else + { + x = work->control.mov.vx % 30; + work->field_B5A = abs(x) + 10; + } + } + + if (s07a_meryl_unk_800D6808( work ) ) + { + SetMode( work, s07a_meryl_unk_800D89D8 ); + } + else + { + if ( work->field_B5A < time ) + { + if ( work->target->faint <= 0 ) + { + work->target->faint = work->param_faint; + } + SetMode( work, s07a_meryl_unk_800D8AA0 ); + } + } +} + +void s07a_meryl_unk_800D89D8( WatcherWork *work, int time ) +{ + if ( time == 0 ) + { + if ( work->unknown.field_14 < 3 ) + { + SetAction( work, 0x2E, ACTINTERP ); + } + else + { + SetAction( work, 0x2F, ACTINTERP ); + } + GM_SeSet( &work->control.mov, SE_ITEM_CURSOR ); + } + + if ( time == 4 ) + { + MERYL_PutItem( work ); + } + + if ( work->body.is_end ) + { + SetMode( work, s07a_meryl_unk_800D9410 ); + } +} + +void s07a_meryl_unk_800D8AA0( WatcherWork* work, int time ) +{ + work->unknown.field_1E = 0; + work->act_status |= 0x08; + + if ( time == 0 ) + { + if ( work->unknown.field_14 < 3 ) + { + if ( work->field_B5A >= 0x5A ) + { + SetAction( work, ACTION44, ACTINTERP ); + } + else + { + SetAction( work, ACTION42, ACTINTERP ); + } + } + else + { + if ( work->field_B5A >= 0x5A ) + { + SetAction( work, ACTION45, ACTINTERP ); + } + else + { + SetAction( work, ACTION43, ACTINTERP ); + } + } + } + + if ( work->body.is_end ) + { + SetMode( work, ActStandStill_800D7008 ); + } +} + +void s07a_meryl_unk_800D8BA4( WatcherWork* work, int time ) +{ + TARGET* target; + + work->unknown.field_1E = 0; + SetTargetClass( work->target, TARGET_FLAG ); + work->vision.length = COM_EYE_LENGTH ; + work->act_status |= 0x08; + + if ( CheckDamage_800D6B30( work ) ) + { + return ; + } + + target = work->target; + if (time == 0 ) + { + SetAction( work, ACTION15, ACTINTERP ) ; + + GM_SeSet( &work->control.mov, 0xC3 ); + if ( target->a_mode == 3 ) + { + GM_SeSet( &work->control.mov, SE_PUNCH_HIT ); + MERYL_PutFog( work ); + } + else + { + MERYL_PutBlood( work, 5, 0 ); + + } + } + + if ( work->body.is_end) + { + SetMode( work, ActStandStill_800D7008 ) ; + } +} + +void s07a_meryl_unk_800D8CB4( WatcherWork *work, int time ) +{ + CONTROL* ctrl; + WatcherUnk *unk; + + unk = &work->unknown; + work->unknown.field_1E = 0; + work->act_status |= 0x8; + work->control.step = work->target->force; + + + ctrl = &work->control; + if ( time == 0 ) + { + switch( unk->field_14 ) + { + case 0: + GM_SeSet( &ctrl->mov, SE_PUNCH_HIT ); + SetAction( work, ACTION34, ACTINTERP ); + GM_SeSet( &ctrl->mov, 0xC3 ); + MERYL_PutBlood( work, 5, 0 ); + work->field_B5A = 17; + break; + case 1: + SetAction( work, ACTION37, ACTINTERP ); + if ( work->target->vital <= 0 ) + { + if ( GM_CurrentWeaponId == WP_Rifle ) + { + MERYL_PutBlood( work, 6, 2 ); + } + else + { + MERYL_PutBlood( work, 6, 1 ); + } + } + else + { + MERYL_PutBlood( work, 5, 0 ); + + } + GM_SeSet( &ctrl->mov, 0xC3 ); + work->field_B5A = 46; + break; + case 3: + GM_SeSet( &ctrl->mov, 0xC3 ); + SetAction( work, ACTION35, ACTINTERP ); + MERYL_PutBlood( work, 5, 0 ); + work->field_B5A = 17; + break; + case 2: + GM_SeSet( &ctrl->mov, 0xC4 ); + SetAction( work, ACTION36, ACTINTERP ); + MERYL_PutBlood( work, 5, 0 ); + work->field_B5A = 22; + break; + case 4: + GM_SeSet( &ctrl->mov, 0x90 ); + GM_SeSet( &ctrl->mov, 0xBF ); + SetAction( work, ACTION29, ACTINTERP ); + work->field_B5A = 67; + break; + case 5: + SetAction( work, ACTION30, ACTINTERP ); + work->field_B5A = 15; + break; + } + } + + if ( time == 2 && work->target->vital <= 0 && GM_GameOverTimer == 0 && GM_SnakeCurrentHealth > 0 ) + { + if ( work->field_C3C >= 0 ) + { + GCL_ExecProc( work->field_C3C, 0 ); + } + + GM_GameOver(); + GM_GameOverTimer = -2; + GM_GameStatus |= STATE_PADRELEASE; + + if ( GM_StreamStatus() == -1 ) + { + GM_VoxStream( work->field_C40, 0 ); + } + } + + switch( unk->field_14 ) + { + case 0: + if ( time < 12 ) + { + ctrl->turn.vy += 170; + } + + if ( time - 7 < 23u ) + { + s07a_meryl_unk_800D6D7C( work ); + } + + if ( time < 20 ) + { + work->control.hzd_height = -32767; + } + break; + case 1: + if ( time == 24 ) + { + GM_SeSet( &ctrl->mov, SE_ENEMY_COLLAPSE ); + } + break; + case 3: + if ( time - 7 < 23u ) + { + s07a_meryl_unk_800D6D7C( work ); + } + if ( time < 15 ) + { + ctrl->hzd_height = -32767; + } + break; + case 2: + if ( time - 7 < 23u ) + { + s07a_meryl_unk_800D6D7C( work ); + } + if ( time < 20 ) + { + ctrl->hzd_height = -32767; + } + break; + case 4: + case 5: + work->act_status |= 0x04; + break; + } + + if ( time > 16 && ctrl->grounded ) + { + ctrl->step = DG_ZeroVector; + } + + if ( time == work->field_B5A ) + { + if (ctrl->mov.vy - ctrl->levels[0] < 2000) + { + GM_SeSet( &ctrl->mov, SE_HIT_FLOOR ) ; + GM_SetNoise( 0x64, 4, &ctrl->mov ) ; + MERYL_PutBlood( work, 6, 0 ) ; + } + } + + if ( work->body.is_end ) + { + work->unknown.field_1E = 1; + work->target->force = DG_ZeroVector; + if ( work->target->vital <= 0 ) + { + SetMode( work, s07a_meryl_unk_800D9410 ); + } + else + { + SetMode( work, s07a_meryl_unk_800D8798 ); + } + } +} + +void s07a_meryl_unk_800D9230( WatcherWork* work, int time ) +{ + CONTROL *ctrl; + + ctrl = &work->control; + work->unknown.field_1E = 0; + work->act_status |= 0x8; + + ctrl->step = work->target->force; + + if ( time == 0 && work->unknown.field_14 != 2 ) + { + GM_SeSet( &ctrl->mov, 0xC4 ); + } + + if ( time > 16 && ctrl->grounded ) + { + ctrl->step = DG_ZeroVector; + } + + if ( work->unknown.last_set < 39 ) + { + + if ( work->body.is_end ) + { + if ( work->unknown.field_14 < 3 ) + { + if ( work->unknown.field_14 == 1 ) + { + SetAction( work, ACTION41, ACTINTERP ); + } + else + { + SetAction( work, ACTION39, ACTINTERP ); + } + } + else + { + SetAction( work, ACTION40, ACTINTERP ); + } + } + } + else if ( ctrl->grounded ) + { + work->unknown.field_1E = 1; + work->target->force = DG_ZeroVector; + GM_SeSet( &ctrl->mov, SE_HIT_FLOOR ); + MERYL_PutBlood( work, 6, 1 ); + SetMode( work, s07a_meryl_unk_800D9410 ); + } +} + +//meryl dead +void s07a_meryl_unk_800D9410( WatcherWork *work, int time ) +{ + work->act_status |= 0x40; + if ( time == 0 ) + { + if ( work->unknown.field_14 < 3 ) + { + if ( work->unknown.field_14 == 1 ) + { + SetAction( work, ACTION51, ACTINTERP ); + } + else + { + SetAction( work, ACTION49, ACTINTERP ); + } + } + else + { + SetAction( work, ACTION50, ACTINTERP ); + } + GM_ConfigControlAttribute( &work->control, 0 ); + work->alert_level = 0; + + if ( GM_GameOverTimer == -2 ) + { + GM_GameOverTimer = 1; + } + } + + if ( time > 450 && GM_StreamStatus() == -1 ) + { + GM_GameOver(); + } +} + +void enemy_meryl_unk_800D9508( WatcherWork *work, int time ) +{ + work->act_status |= 0x40; + + if ( time == 0 ) + { + work->visible = 1; + } +} + +void s07a_meryl_unk_800D952C( WatcherWork *work ) +{ + short v0; + int time_prev; + ACTION action; + CONTROL *ctrl; + WatcherUnk *unk; + + work->trigger = 0; + work->vision.length = 0; + work->target->class = TARGET_AVAIL; + + unk = &work->unknown; + + work->actend = 0; + work->act_status = 0; + + action = work->action; + ctrl = &work->control; + + work->field_C30 = 0; + + ctrl->height = ( work->body.height * work->scale ) / 4096 ; + ctrl->hzd_height = ctrl->levels[0] + 750; + + time_prev = work->time; + work->time++; + + if ( !action ) + { + action = ActStandStill_800D7008; + work->action = ActStandStill_800D7008; + } + + action( work, time_prev ); + action = work->action2; + + if ( action ) + { + time_prev = work->time2; + work->time2++; + action( work, time_prev ); + } + + if ( !unk->field_1E ) + { + ctrl->r_sphere = GV_NearExp2( ctrl->r_sphere, unk->field_1C ); + } + else + { + ctrl->r_sphere = -1; + } + + if ( work->target->class & TARGET_POWER ) + { + work->hom->flag = TRUE; + } + else + { + work->hom->flag = FALSE; + } + + if ( unk->field_04 < 0 && ctrl->grounded ) + { + unk->field_04 = 0; + } + + v0 = unk->field_04 - 16; + unk->field_04 = v0; + ctrl->step.vy = v0; + + if ( work->mark_time > 0 ) + { + work->mark_time--; + } + + if ( s07a_meryl_unk_800D66F4( &ctrl->mov ) || s07a_meryl_unk_800D6738( &ctrl->mov ) ) + { + work->target->class = TARGET_AVAIL; + } + + if ( s07a_meryl_unk_800D66F4( &ctrl->mov ) ) + { + if ( s07a_meryl_unk_800D66F4( &GM_PlayerPosition ) || s07a_meryl_unk_800D66B0( &ctrl->mov ) ) + { + work->visible = 0; + GM_TargetBody( work->target, NULL ); + } + else + { + work->visible = 1; + GM_TargetBody( work->target, &( work->body.objs->objs[1].world ) ); + } + } +} + + +//put funcs +void MERYL_PutBlood( WatcherWork* work, int obj_idx, int count ) +{ + MATRIX mat; + DG_SetPos( &work->body.objs->objs[ obj_idx ].world ); + DG_MovePos( &s07a_dword_800C369C ); + DG_RotatePos( &s07a_dword_800C36A4 ); + ReadRotMatrix( &mat ); + NewBlood( &mat, count ); +} + +void MERYL_PutFog(WatcherWork *work ) +{ + MATRIX mat; + SVECTOR svec; + + DG_SetPos( &work->body.objs->objs[1].world ); + DG_MovePos( &s07a_dword_800C36AC ); + DG_RotatePos( &s07a_dword_800C36B4 ); + ReadRotMatrix( &mat ); + + svec.vx = mat.t[ 0 ]; + svec.vy = mat.t[ 1 ]; + svec.vz = mat.t[ 2 ]; + + AN_Fog( &svec ); +} + +void MERYL_PutItem( WatcherWork* work ) +{ + int rand; + SVECTOR svec; + CONTROL *ctrl; + Item_Info item; + + svec = work->unknown.field_0C; + rand = 10; + ctrl = &work->control; + svec.vx += GV_RandU( rand ); + svec.vy += 100; + rand = GV_RandU( rand ); + svec.vz += rand; + rand = GV_RandU( 4 ); + + switch ( rand ) + { + case 0: + case 1: + item.type = 4; + item.id = IT_Ration; + item.num = 1; + item.message = s07a_dword_800C36BC[0]; + break; + case 2: + //socom + if ( GM_SocomFlag < 0 ) + { + item.type = 4; + item.id = IT_Ration; + item.num = 1; + item.message = s07a_dword_800C36BC[0]; + } + else + { + //Socom bullets + item.type = 2; + item.id = WP_Socom; + item.num = 12; + item.message = s07a_dword_800C36BC[1]; + } + break; + case 3: + //famas + if ( GM_FamasFlag < 0 ) + { + item.type = 4; + item.id = IT_Ration; + item.num = 1; + item.message = s07a_dword_800C36BC[0]; + } + else + { + item.id = WP_Famas; + item.type = 2; + item.num = 25; + item.message = s07a_dword_800C36BC[2]; + } + break; + } + item.time = 900; + NewItemPut( &ctrl->mov, &svec, &item ); +} + +void MERYL_PutMark( WatcherWork *work, int mark ) +{ + MATRIX *mat; + if ( !( work->control.map->index & GM_PlayerMap ) ) + { + return; + } + + mat = &work->body.objs->objs[6].world; + if( mark == 0 ) + { + s00a_command_800CEC40( &work->control.mov , 0x10 ); + } + + if ( work->mark_time ) + { + GV_DestroyOtherActor( work->mark ); + } + + work->mark = AN_Unknown_800CA1EC( mat , mark ) ; + work->mark_time = 30; +} + +int s07a_meryl_unk_800D9A28( SVECTOR* svec ) +{ + if ( ( (unsigned short)svec->vx - 0xFA0 >= 0x157Du ) || ( svec->vz < -0x5014 ) || (svec->vz >= -0x251B ) ) + { + return 0; + } + + return 1; +} + +void MERYL_PutSound( WatcherWork *work, int mark ) +{ + int a3; + int a2; + + if ( s07a_meryl_unk_800D9A28( &work->control.mov ) && s07a_meryl_unk_800D9A28( &GM_PlayerPosition ) ) + { + return; + } + + a3 = work->unknown.last_set; + a2 = work->m_ctrl.info1.frame; + + if( a3 == 1 ) + { + if ( a2 == 22 ) + { + GM_SeSetMode( &work->control.mov, 0xB8, GM_SEMODE_REAL ); + } + else if ( a2 == 11 ) + { + GM_SeSetMode( &work->control.mov, 0xB7, GM_SEMODE_REAL ); + } + } + else if ( a3 == 2 ) + { + if ( a2 == 16 ) + { + GM_SeSetMode( &work->control.mov, 0xB8, GM_SEMODE_REAL ); + } + else if ( a2 == 8 ) + { + GM_SeSetMode( &work->control.mov, 0xB7, GM_SEMODE_REAL ); + } + } +} + +void MERYL_PutBreath( WatcherWork *work, int arg1 ) +{ + int frame; + if ( EnemyCommand.mode == TOP_COMM_ALERT ) + { + return; + } + + if ( !( work->control.map->index & GM_PlayerMap ) ) + { + return; + } + + if ( work->unknown.last_unset == 20 ) + { + frame = work->m_ctrl.info2.frame; + if ( frame == 31 ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } + else if ( work->unknown.last_unset == 22 ) + { + frame = work->m_ctrl.info2.frame; + if ( ( frame == 15 ) || ( frame == 35 ) || ( frame == 50 ) || ( frame == 60 ) || + ( frame == 70 ) || ( frame == 74 ) || ( frame == 78 ) ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } + else if ( work->unknown.last_unset == 19 ) + { + frame = work->m_ctrl.info2.frame; + if ( ( frame == 30 ) || ( frame == 40 ) || ( frame == 50 ) || ( frame == 60 ) || + ( frame == 70 ) || ( frame == 80 ) || ( frame == 90 ) || ( frame == 95 ) || + ( frame == 100 ) || ( frame == 105 ) ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } + else + { + if ( ( GV_Time % 64 ) == ( work->field_B78 * 16 ) ) + { + AN_Breath( &work->body.objs->objs[6].world ); + } + } +} + +void MERYL_PutLSight( WatcherWork* work ) +{ + if ( work->vision.field_B92 == 2 ) + { + NewLSight( &GM_PlayerPosition, &work->control.mov, 0x00008F ); + } +} + +void MERYL_PutBullet( WatcherWork *work ) +{ + MATRIX* mat; + SVECTOR svec; + MATRIX local_mat; + + svec = DG_ZeroVector; + svec.vx = GV_RandS( 16 ); + svec.vz = GV_RandU( 128 ); + + mat = &work->body.objs->objs[4].world; + DG_SetPos( mat ); + DG_MovePos( &s07a_dword_800C36D8 ); + DG_RotatePos( &svec ); + ReadRotMatrix( &local_mat ); + + GM_SeSet( &work->control.mov, SE_ENEMY_SHOT ); + AN_BulletSmoke(mat, 0); + AN_FamasFlash(&local_mat); + + MERYL_ClearPutChar( work, MERYL_PutBullet ); +} + +int MERYL_SetPutChar( WatcherWork *work, int idx ) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( work->field_B00[ i ] == NULL ) + { + work->field_B00[ i ] = s07a_dword_800C36C8[ idx ]; + return 1; + } + } + return 0; +} + +int MERYL_ClearPutChar( WatcherWork *work, void *func ) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( work->field_B00[ i ] == func ) + { + work->field_B00[ i ] = 0; + return 1; + } + } + return 0; +} + +void MERYL_ExecPutChar( WatcherWork* work ) +{ + int i; + + for ( i = 0 ; i < 8 ; i++ ) + { + if ( work->field_B00[i] ) + { + PUTFUNC func = work->field_B00[i]; + func( work ); + } + } +} + +//main action funcs +void s07a_meryl_unk_800D9E48( WatcherWork* work, int time ) +{ + if ( time == 0 ) + { + UnsetAction( work, ACTION9 ); + } + + if ( !( work->pad.press & 1 ) ) + { + UnsetMode( work ); + } + else + { + if ( time < 4 ) + { + work->vision.facedir = ( work->control.rot.vy - ( time * 256 ) ) & 0xFFF; + } + else + { + work->vision.facedir = ( work->control.rot.vy - 1024 ) & 0xFFF; + } + } +} + +void s07a_meryl_unk_800D9F14( WatcherWork* work, int time) +{ + if ( time == 0 ) + { + UnsetAction( work, ACTION10 ); + } + + if ( !( work->pad.press & 2 ) ) + { + UnsetMode( work ); + } + else + { + if ( time < 4 ) + { + work->vision.facedir = ( work->control.rot.vy + ( time * 256 ) ) & 0xFFF; + } + else + { + work->vision.facedir = ( work->control.rot.vy + 1024 ) & 0xFFF; + } + } +} + +void ActOverScoutD_800D9FE0( WatcherWork* work, int time ) +{ + work->vision.length = 3000; + + if ( time == 0 ) + { + UnsetAction( work, ACTION11 ); + } + + if ( !( work->pad.press & 4 ) ) + { + UnsetMode( work ); + } +} + +void s07a_meryl_unk_800DA078( WatcherWork* work, int time ) +{ + work->vision.length = 3000; + + if ( time == 0 ) + { + UnsetAction( work, ACTION14 ); + } + + if ( !( work->pad.press & 0x2000 ) ) + { + UnsetMode( work ); + } +} + +void s07a_meryl_unk_800DA110( WatcherWork* work, int time ) +{ + work->vision.length = 0; + + if ( time == 0 ) + { + UnsetAction( work, ACTION24 ); + GM_SeSet( &work->control.mov, 0x94 ); + } + + if ( work->body.time2 || !( work->pad.press & 0x40 ) ) + { + work->pad.time = 0; + UnsetMode( work ); + } +} + +void s07a_meryl_unk_800DA1C4( WatcherWork* work, int time ) +{ + work->vision.length = 0; + + if ( time == 0 ) + { + UnsetAction( work, ACTION22 ); + } + + if ( time == 60 ) + { + GM_SeSet( &work->control.mov, 0x92 ); + } + + if ( work->body.time2 || !( work->pad.press & 0x80 ) ) + { + work->pad.time = 0; + UnsetMode( work ); + } +} + +void s07a_meryl_unk_800DA28C( WatcherWork* work, int time ) +{ + if ( time == 0 ) + { + UnsetAction( work, ACTION23 ); + } + + if ( work->body.time2 || !( work->pad.press & 0x400 ) ) + { + work->pad.time = 0; + UnsetMode( work ); + } +} + +void s07a_meryl_unk_800DA330( WatcherWork* work, int time ) +{ + work->vision.length = 0; + + if ( time == 0 ) + { + UnsetAction( work, ACTION19 ); + } + + if ( time == 90 ) + { + GM_SeSet( &work->control.mov, 0x92 ); + } + + if ( work->body.time2 || !( work->pad.press & 0x800 ) ) + { + work->pad.time = 0; + UnsetMode( work ); + } +} + +void s07a_meryl_unk_800DA3F8( WatcherWork *work, int time ) +{ + SVECTOR mov; + work->vision.length = 0; + + if ( time == 0 ) + { + UnsetAction( work, ACTION19 ); + } + + if ( time == 90 ) + { + GM_SeSet( &work->control.mov, 0x92 ); + } + + if ( time == 110 ) + { + UnsetAction( work, ACTION20 ); + } + + + if ( time > 110 ) + { + work->act_status |= 0x10; + if ( work->m_ctrl.info2.frame == 30 ) + { + mov = work->control.mov; + mov.vy += 500; + AN_Sleep( &mov ); + GM_SeSet( &work->control.mov, 0x93 ); + + if ( work->sn_dis < 1000 && ( GM_PlayerStatus & PLAYER_WATCH ) ) + { + int res = GV_RandU( 12 ); + if ( res > 10 ) + { + work->pad.sound = 240; + } + } + } + if ( work->pad.press & 0x1000 ) + { + SetMode( work, s07a_meryl_unk_800D7EC8 ); + UnsetMode( work ); + return; + } + } + + if ( !( work->pad.press & 0x200 ) ) + { + UnsetMode( work ); + } +} + +extern void AN_Unknown_800C3B7C( MATRIX *matrix ); + +void s07a_meryl_unk_800DA5D0( WatcherWork* work ) +{ + MATRIX mat; + + DG_SetPos( &work->body.objs->objs[6].world ); + DG_MovePos( &s07a_dword_800C36E0 ); + ReadRotMatrix( &mat ); + AN_Unknown_800C3B7C( &mat ); +} + +void s07a_meryl_unk_800DA610( WatcherWork *work, int time ) +{ + work->vision.length = 3000; + work->act_status |= 0x80; + + if ( time == 0 ) + { + UnsetAction( work, ACTION25 ); + if ( work->mosaic == NULL ) + { + work->mosaic = NewMosaic(&work->body.objs->objs[0].world, 300, 4, -250 ); + } + GM_SeSet( &work->control.mov, 0xB9 ); + } + + if ( time > 30 ) + { + GV_RandU( 4 ); + if ( time == ( ( time / 5 ) * 5 ) ) + { + GM_SeSet( &work->control.mov, 0xB1 ); + } + if ( (time & 3) && time > 45 ) + { + s07a_meryl_unk_800DA5D0( work ); + } + } + + if ( !( work->pad.press & 0x800000 ) ) + { + UnsetMode( work ); + } +} + +void s07a_meryl_unk_800DA75C( WatcherWork *work, int time ) +{ + work->vision.length = 3000; + + if ( time == 0 ) + { + UnsetAction( work, ACTION25 ); + if ( work->mosaic == NULL ) + { + work->mosaic = NewMosaic(&work->body.objs->objs[0].world, 300, 4, -250 ); + } + } + + if ( time == 186 ) + { + UnsetActionManual( work, ACTION26, 0xFFFF ); + } + + if ( time == 320 ) + { + GM_SeSet( &work->control.mov, 0xB9 ); + } + + //? + if ( time == 0 || time == 4 || time == 8 || time == 12 || + time == 20 || time == 28 || time == 36 || time == 48 || + time == 60 || time == 120 || time == 122 || time == 124 || + time == 126 || time == 180 || time == 182 || time == 184 || + time == 186 ) + { + GM_SeSet( &work->control.mov, 0xB1 ); + s07a_meryl_unk_800DA5D0( work ); + } + + if ( ( time > 186 && work->body.time2 ) || !( work->pad.press & 0x1000000 ) ) + { + GM_SeSet( &work->control.mov, 0xB4 ); + work->pad.time = 0; + UnsetMode( work ); + } +} diff --git a/components/mgs/mgs/source/enemy/merylcheck.c b/components/mgs/mgs/source/enemy/merylcheck.c new file mode 100644 index 00000000..50712ab5 --- /dev/null +++ b/components/mgs/mgs/source/enemy/merylcheck.c @@ -0,0 +1,379 @@ +#include "enemy/enemy.h" + +#include "common.h" +#include "libdg/libdg.h" + +extern TOPCOMMAND_STRUCT TOPCOMMAND; +extern SVECTOR ENEMY_ATTACK_FORCE; + +extern void s07a_meryl_unk_800D8CB4( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800D8BA4( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800D8654( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800D85E0( WatcherWork *work, int time ); + +extern int s07a_meryl_unk_800D6824( SVECTOR* svec ); + +int s07a_meryl_unk_800D66B0( SVECTOR* svec ) +{ + if ( ( (unsigned short)svec->vx - 0x1B58 >= 0x5DDU ) || ( svec->vz < -0x5014 ) || (svec->vz >= -0x4A37 ) ) + { + return 0; + } + + return 1; +} + +int s07a_meryl_unk_800D66F4( SVECTOR* svec ) +{ + if ( ( (unsigned short)svec->vx - 0xFA0 >= 0x157Du ) || ( svec->vz < -0x5014 ) || (svec->vz >= -0x251B ) ) + { + return 0; + } + + return 1; +} + +int s07a_meryl_unk_800D6738( SVECTOR* svec ) +{ + if ( svec->vx > 5500 && svec->vx < 7000 && svec->vz > -9500 && svec->vz < -8500 ) + { + return 1; + } + + return 0; +} + +extern void s07a_meryl_unk_800D7F70( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800D8290( WatcherWork *work, int time ); + +int s07a_meryl_unk_800D677C( WatcherWork* work ) +{ + TARGET *target; + + target= work->target; + if ( target->damaged & 2 ) + { + if ( target->a_mode == 0x24 ) + { + SetMode( work, s07a_meryl_unk_800D7F70 ) ; + } + else + { + SetMode( work, s07a_meryl_unk_800D8290 ) ; + } + target->damaged = 0; + return 1; + } + return 0; +} + +// Identical to s00a_command_800C513C +int s07a_meryl_unk_800D6808( WatcherWork* work ) { + int check; + check = work->target->damaged & 0x20; + return check > 0; +} + +// Identical to s00a_command_800C5158 +int s07a_meryl_unk_800D6824( SVECTOR* svec ) +{ + return abs( svec->vx ) + abs( svec->vy ) + abs( svec->vz ) ; +} + +int s07a_meryl_unk_800D6860( WatcherWork *work ) +{ + short val; + TARGET *target; + + target = work->target; + if ( !( target->damaged & TARGET_POWER ) ) return 0; + + val = target->a_mode - 1; + switch ( val ) + { + case 2: + target->force = DG_ZeroVector; + if ( target->faint <= 0 ) + { + work->unknown.field_14 = 1; + SetMode( work, s07a_meryl_unk_800D8CB4 ) ; + } + else + { + SetMode( work, s07a_meryl_unk_800D8BA4 ) ; + } + break; + case 3: + work->unknown.field_14 = 0; + SetMode( work, s07a_meryl_unk_800D8CB4 ) ; + break; + case 0: + target->force = DG_ZeroVector; + if ( target->vital <= 0 ) + { + work->unknown.field_14 = 1; + SetMode( work, s07a_meryl_unk_800D8CB4 ) ; + } + else + { + SetMode( work, s07a_meryl_unk_800D8BA4 ) ; + } + break; + case 1: + if ( s07a_meryl_unk_800D6824( &target->force ) < 100 ) + { + work->unknown.field_14 = 3; + } + else + { + work->unknown.field_14 = 2; + } + SetMode( work, s07a_meryl_unk_800D8CB4 ) ; + break; + case 4: + work->unknown.field_14 = 3; + SetMode( work, s07a_meryl_unk_800D8CB4 ) ; + break; + case 7: + SetMode( work, s07a_meryl_unk_800D85E0 ) ; + break; + case 8: + SetMode( work, s07a_meryl_unk_800D8654 ) ; + break; + } + + target->damage = 0; + target->damaged = 0; + return 1; +} + +int s07a_meryl_unk_800D6A50( WatcherWork * work ) +{ + CONTROL *ctrl; + + ctrl = &work->control; + + if ( s07a_meryl_unk_800D66F4( &ctrl->mov ) || s07a_meryl_unk_800D6738( &ctrl->mov ) ) + { + return 0; + } + + if ( work->target->class & TARGET_POWER && GM_GameStatus & STATE_STUN ) + { + SetMode( work, s07a_meryl_unk_800D8654 ) ; + + if ( TOPCOMMAND.mode == TOP_COMM_TRAVEL ) + { + work->target->faint = 0; + } + else + { + work->target->faint -= 5; + } + + return 1; + } + + return 0; +} + +int CheckDamage_800D6B30( WatcherWork* work ) +{ + if ( s07a_meryl_unk_800D6860( work ) || s07a_meryl_unk_800D677C( work ) || s07a_meryl_unk_800D6A50( work ) ) + { + work->target->class = TARGET_AVAIL ; + return 1; + } + return 0; +} + +// Identical to s00a_command_800C580C +int s07a_meryl_unk_800D6B90( int dir, int dist ) +{ + int diff ; + if ( dist < 0 || dir < 0 ) return 0 ; + + diff = GV_DiffDirS( dir, dist ) ; + + if ( diff + 0x380 > 0x700u ) return 0 ; + if ( diff >= 0 ) return 1 ; + + return 2 ; +} + +void ReviseReadyGun_800D6BE4( WatcherWork* work ) +{ + + int trans; + int near; + + trans = ( ( GM_PlayerBody->objs->objs[6].world.t[1] - work->body.objs->objs[6].world.t[1] ) ); + trans = ratan2( work->sn_dis, trans ) & 0xFFF; + + near = GV_NearExp8( work->adjust[2].vx, trans - 0x400 ); + work->adjust[2].vx = near; + work->adjust[6].vx = near; + + if ( near < 0 ) + { + work->adjust[7].vx = near * 3 ; + } + else + { + work->adjust[7].vx = ( near * 3 ) / 2 ; + } +} + +extern SVECTOR size_800E2F50; +extern SVECTOR force_800E2F48; +extern SVECTOR rp_shift_800E2F40; + +int AttackForce_800D6C6C( WatcherWork *work ) +{ + TARGET *target; + SVECTOR svec; + SVECTOR rp_shift = rp_shift_800E2F40; /* 右パンチ */ + SVECTOR force = force_800E2F48; + SVECTOR size = size_800E2F50; + + target = &work->punch; + GM_SetTarget( target, 4, ENEMY_SIDE, &size ); + DG_SetPos2( &work->control.mov, &work->control.rot ); + DG_RotVector( &force, &svec, 1 ); + GM_SetPowerTarget( target, POWER_ONCE, 2, 32, 1, &svec ); + DG_PutVector( &rp_shift, &work->punch.center, 1 ); + return GM_PowerTarget( target ); +} + +void s07a_meryl_unk_800D6D7C( WatcherWork *work) +{ + TARGET* target; + + target = &work->field_904; + GM_SetPowerTarget(target, POWER_CONST | POWER_EXPLODE, 5, 0, 3, &ENEMY_ATTACK_FORCE); + GM_MoveTarget( target, &work->control.mov ); + GM_PowerTarget( target ); +} + +extern void s07a_meryl_unk_800D9E48( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800D9F14( WatcherWork *work, int time ); +extern void ActOverScoutD_800D9FE0( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA078( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA110( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA1C4( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA28C( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA330( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA3F8( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA610( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800DA75C( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800D7474( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800D7504( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800D75F8( WatcherWork *work, int time ); +extern void s07a_meryl_unk_800D76CC( WatcherWork *work, int time ); +extern void ActReadyGun_800D7924( WatcherWork *work, int time ); + +#define SP_DANBOWLKERI 0x400000 + +int CheckPad_800D6DE4( WatcherWork *work ) +{ + int press = work->pad.press; + + if ( press & 0x01 ) + { + SetMode2( work, s07a_meryl_unk_800D9E48 ); + return 0; + } + + if ( press & 0x02 ) + { + SetMode2( work, s07a_meryl_unk_800D9F14 ); + return 0; + } + + if ( press & 0x04 ) + { + SetMode2( work, ActOverScoutD_800D9FE0 ); // //ActOverScoutD_800C85DC + return 0; + } + + if ( press & 0x2000 ) + { + SetMode2( work, s07a_meryl_unk_800DA078 ); + return 0; + } + + if ( press & 0x40 ) + { + SetMode2( work, s07a_meryl_unk_800DA110 ); + return 0; + } + + if ( press & 0x80 ) + { + SetMode2( work, s07a_meryl_unk_800DA1C4 ); + return 0; + } + + if ( press & 0x400 ) + { + SetMode2( work, s07a_meryl_unk_800DA28C ); + return 0; + } + + if ( press & 0x800 ) + { + SetMode2( work, s07a_meryl_unk_800DA330 ); + return 0; + } + + if ( press & 0x200 ) + { + SetMode2( work, s07a_meryl_unk_800DA3F8 ); + return 0; + } + + if ( press & 0x800000 ) + { + SetMode2( work, s07a_meryl_unk_800DA610 ); + return 0; + } + + if ( press & 0x1000000 ) + { + SetMode2( work, s07a_meryl_unk_800DA75C ); + return 0; + } + + if ( press & 0x100 ) + { + SetMode( work, s07a_meryl_unk_800D7474 ); + return 1; + } + + if ( press & 0x20 ) + { + SetMode( work, s07a_meryl_unk_800D7504 ); + return 1; + } + + if ( press & 0x2000000 ) + { + SetMode( work, s07a_meryl_unk_800D75F8 ); + return 1; + } + + if ( press & SP_DANBOWLKERI ) + { + SetMode( work, s07a_meryl_unk_800D76CC ); + return 1; + } + + if ( press & 0x30000 ) + { + SetMode( work, ActReadyGun_800D7924 ); + work->adjust[2].vx = 0; + return 1; + } + + return 0; +} diff --git a/components/mgs/mgs/source/enemy/merylenemy.c b/components/mgs/mgs/source/enemy/merylenemy.c new file mode 100644 index 00000000..ba4ca53a --- /dev/null +++ b/components/mgs/mgs/source/enemy/merylenemy.c @@ -0,0 +1,436 @@ +#include "enemy/enemy.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libhzd/libhzd.h" +#include "game/game.h" +#include "linkvar.h" + +#define SEGMENT_ATR ( HZD_SEG_NO_PLAYER ) + +extern CONTROL *GM_WhereList[94]; + +extern int COM_EYE_LENGTH; +extern int COM_NOISEMODE_DIS; +extern int COM_ALERT_DECREMENT; + +extern ENEMY_COMMAND EnemyCommand; + +extern int sna_current_item_8004FB38(void); + +// in enemy/asiato.c +extern int AsiatoCheck( HZD_HDL *, SVECTOR * ); +extern int SearchNearAsiato( HZD_HDL *, SVECTOR *, short, short, short ); + +// Identical to s00a_command_800C9878 +void s07a_meryl_unk_800DA948( WatcherWork* work ) +{ + VISION *vision = &work->vision; + RADAR_SIGHT_PARAM *r_param = &work->control.radar_param; + + r_param->dir = vision->facedir; + r_param->dis = vision->length; + r_param->range = vision->angle * 2; + r_param->r = 0; +} + +// Identical to s00a_command_800C98A4 +void s07a_meryl_unk_800DA974( WatcherWork *work ) +{ + unsigned short flags; + if ( !( work->field_BA3 & 4 ) ) + { + return; + } + + flags = work->target->damaged; + if ( flags & 6 ) + { + work->field_BA1 |= 4; + return; + } + + flags = work->field_94C.damaged; + if ( flags & 0x80 ) + { + work->field_94C.damaged &= ~( 0x80 ); + if ( ( GM_PlayerStatus & ( PLAYER_MOVE | PLAYER_CB_BOX ) ) == PLAYER_CB_BOX ) + { + return; + } + work->field_BA1 |= 12; + } +} + +int s07a_meryl_unk_800DAA00( HZD_HDL *hzd, SVECTOR *pos, SVECTOR *pos2 ) +{ + int from; + int to; + + from = HZD_GetAddress( hzd, pos, -1 ); + + //TODO: fix + do + { + to = HZD_GetAddress( hzd, pos2, -1 ); + do { + return HZD_ZoneDistance( hzd, from & 0xFF, to & 0xFF ); + } while (0); + } while (0); +} + +void s07a_meryl_unk_800DAA60( WatcherWork* work ) +{ + CONTROL *ctrl; + if ( !( work->field_BA3 & 1 ) ) + { + return; + } + + if ( work->act_status & 0x68 ) + { + return; + } + + ctrl = &work->control; + if ( !GM_NoisePower ) + { + return; + } + + + + if ( GM_NoisePower == 0xFF ) + { + + if ( !( ctrl->map->index & GM_ClaymoreMap ) && + ( !( ctrl->map->index & GM_PlayerMap ) || + !( GM_ClaymoreMap & GM_PlayerMap ) ) ) + { + return; + } + } + else + { + if ( !( ctrl->map->index & GM_PlayerMap ) ) + { + return; + } + } + + switch ( GM_NoisePower ) + { + case 5: + if ( GV_DiffVec3( &GM_NoisePosition, &ctrl->mov ) < 1500 ) + { + break; + } + return; + case 200: + if ( GV_DiffVec3( &GM_NoisePosition, &ctrl->mov ) < COM_NOISEMODE_DIS ) + { + break; + } + return; + case 255: + break; + case 100: + if ( GV_DiffVec3( &GM_NoisePosition, &ctrl->mov ) < COM_NOISEMODE_DIS && ( s07a_meryl_unk_800DAA00( ctrl->map->hzd, &ctrl->mov, &GM_NoisePosition ) < 300 ) ) + { + work->field_BA1 |= 1; + GM_NoiseLength = 0; + GM_NoisePower = 0; + return; + } + return; + default: + return; + } + work->field_BA1 |= 1; +} + +// Based on s00a_command_800C9ACC, with one modification: +// GM_PlayerStatus & PLAYER_MOVE | PLAYER_CB_BOX -> GM_PlayerStatus & PLAYER_CB_BOX +void s07a_meryl_unk_800DAC50( WatcherWork *work ) +{ + if ( !( work->field_BA3 & 2 ) ) + { + return; + } + + if ( EnemyCommand.mode == 2 && EnemyCommand.field_0x10 == 300 ) + { + return; + } + + if ( EnemyCommand.field_0x0C % EnemyCommand.field_0x08 == work->field_B78 ) + { + work->field_BA2 = 0; + } + + if ( work->vision.field_B92 == 2 && ( ( GM_PlayerStatus & PLAYER_CB_BOX ) == PLAYER_CB_BOX ) ) + { + if ( ( GV_DiffVec3( &work->field_BA4, &GM_PlayerPosition ) > 50 ) || ( work->field_BAC != GM_WhereList[0]->rot.vy ) ) + { + if ( EnemyCommand.mode != TOP_COMM_ALERT ) + { + work->field_BA4 = GM_PlayerPosition; + work->field_BAC = GM_WhereList[0]->rot.vy; + work->field_BA1 |= 0x2; + } + else + { + work->field_BA1 |= work->field_BA2; + return; + } + } + else + { + if ( GV_DiffVec3( &work->control.mov, &GM_PlayerPosition ) < 1500 ) + { + work->field_BA2 |= 0x40; + } + } + work->vision.field_B92 = 0; + work->field_BA2 |= 0x80; + } + + work->field_BA1 |= work->field_BA2; +} + +// Identical to s00a_command_800C9C7C +void s07a_meryl_unk_800DAE00( WatcherWork* work ) +{ + HZD_HDL *hzd; + CONTROL *ctrl; + VISION *vision; + + if ( !( work->field_BA3 & 16 ) ) + { + return; + } + + ctrl = &work->control; + if ( work->think2 == 5 ) + { + return; + } + + hzd = work->control.map->hzd; + vision = &work->vision; + if ( !( AsiatoCheck( hzd, &ctrl->mov ) ) ) + { + return; + } + + if ( SearchNearAsiato( hzd, &ctrl->mov, vision->facedir, vision->angle, vision->length ) < 0 ) + { + return; + } + + work->field_BA1 |= 16; +} + +void s07a_meryl_unk_800DAEAC( WatcherWork* work ) +{ + work->field_BA1 = 0; + s07a_meryl_unk_800DAA60( work ); + + if ( EnemyCommand.mode != TOP_COMM_ALERT ) + { + s07a_meryl_unk_800DA974( work ); + s07a_meryl_unk_800DAC50( work ); + s07a_meryl_unk_800DAE00( work ); + } +} + +void s07a_meryl_unk_800DAF00( WatcherWork* work ) +{ + int val; + val = work->vision.field_B92; + switch ( val ) + { + case 0: + if ( work->field_B94 != 2 || !( GM_PlayerStatus & PLAYER_INTRUDE ) || work->vision.length == 0 ) + { + work->field_B94 = 0; + work->alert_level -= COM_ALERT_DECREMENT; + } + break; + case 1: + work->field_B94 = val; + work->alert_level -= 1; + break; + case 2: + work->field_B94 = val; + work->alert_level += 1; + break; + } + + if ( work->alert_level < 0 ) + { + work->alert_level = 0; + } + else if ( work->alert_level > 255 ) + { + work->alert_level = 255; + } +} + +void s07a_meryl_unk_800DAFD8( WatcherWork* work ) +{ + int dir; + int dis; + int diff; + short *flag; + SVECTOR *pos; + SVECTOR svec; + CONTROL *ctrl; + MAP *map; + + flag = &work->vision.field_B92; + ctrl = &work->control; + pos = &GM_PlayerPosition; + GV_SubVec3( pos, &ctrl->mov, &svec ); + + dir = GV_VecDir2( &svec ); + work->sn_dir = dir; + dis = GV_VecLen3( &svec ); + + work->sn_dis = dis; + + diff = pos->vy - work->control.mov.vy; + if ( diff < 0 ) + { + diff = work->control.mov.vy - pos->vy; + } + + if ( !( work->control.map->index & GM_PlayerMap ) || GM_PlayerStatus & PLAYER_INTRUDE ) + { + work->vision.field_B92 = 0; + return; + } + + if ( EnemyCommand.field_0x0C % EnemyCommand.field_0x08 == work->field_B78 ) + { + //ridiculous, but its the only way it matches + if ( COM_EYE_LENGTH + 2000 >= dis ) + { + if ( 2000 >= diff ) + { + if ( dis < 500 || GV_DiffDirAbs( work->vision.facedir, dir ) < work->vision.angle ) + { + map = work->control.map; + if ( !( HZD_OnlineHazardCheck( map->hzd, pos, &ctrl->mov, HZD_CHK_ALL, SEGMENT_ATR ) ) ) + { + if ( !( GM_OnlineTargetCheckAny( &ctrl->mov, pos, map->index, &svec ) ) ) + { + if ( work->vision.length < dis ) + { + work->vision.field_B92 = 1; + } + else + { + work->vision.field_B92 = 2; + } + } + else + { + work->vision.field_B92 = 0; + } + } + else + { + work->vision.field_B92 = 0; + } + } + else + { + work->vision.field_B92 = 0; + } + } + else + { + work->vision.field_B92 = 0; + } + } + else + { + work->vision.field_B92 = 0; + } + } + + if ( GM_CurrentItemId == IT_Stealth ) + { + flag[0] = 0; + } +} + +extern void MerylThink_800DF3A0( WatcherWork * ); +extern void MERYL_ExecPutChar( WatcherWork* work ); +extern void s07a_meryl_unk_800D952C( WatcherWork *work ); + +void MerylActionMain( WatcherWork *work ) +{ + if ( work->unknown.last_set < 0x31 ) + { + s07a_meryl_unk_800DA948( work ); + s07a_meryl_unk_800DAFD8(work); + s07a_meryl_unk_800DAEAC(work); + s07a_meryl_unk_800DAF00(work); + MerylThink_800DF3A0(work); + MERYL_ExecPutChar(work); + } + + s07a_meryl_unk_800D952C(work); +} + +void MerylPushMove( WatcherWork *work ) +{ + int s1; + CONTROL *ctrl; + TARGET *target; + + target = work->target; + s1 = 0; + if ( !( target->damaged & 0x8 ) ) + { + return; + } + + GV_AddVec3( &target->offset, &work->control.step, &work->control.step ); + target->damaged &= ~( 0x8 ); + + if ( work->unknown.last_set - 1 >= 2u ) + { + return; + } + + if ( target->offset.pad ) + { + if ( GV_Time & 256 ) + { + s1 = target->offset.pad * 1024; + if ( !( work->field_B78 & 1 ) ) + { + s1 = ( target->offset.pad + 2 ) * 1024; + } + } + else + { + if ( work->field_B78 & 1 ) + { + s1 = ( target->offset.pad + 2 ) * 1024; + } + else + { + s1 = target->offset.pad * 1024; + } + } + s1 &= 0xFFF; + } + + ctrl = &work->control; + ctrl->turn.vy = s1; + ctrl->r_sphere = GV_NearExp2( ctrl->r_sphere, work->unknown.field_1C ); +} \ No newline at end of file diff --git a/components/mgs/mgs/source/enemy/merylthink.c b/components/mgs/mgs/source/enemy/merylthink.c new file mode 100644 index 00000000..6fc06826 --- /dev/null +++ b/components/mgs/mgs/source/enemy/merylthink.c @@ -0,0 +1,2900 @@ +#include "enemy/enemy.h" +#include "enemy/eyeflash.h" // for NewEyeflash + +#include +#include +#include +#include + +#include "common.h" +#include "mts/mts.h" // for fprintf +#include "libgv/libgv.h" +#include "libhzd/libhzd.h" +#include "game/game.h" + +#define SEGMENT_ATR ( HZD_SEG_NO_NAVIGATE ) + +extern ENEMY_COMMAND EnemyCommand; +extern unsigned int COM_GameStatus; +extern SVECTOR COM_PlayerPosition; +extern SVECTOR COM_PlayerPositionOne[8]; +extern int COM_PlayerMapOne[8]; +extern int COM_PlayerAddressOne[8]; +extern int COM_VibTime; +extern int COM_SHOOTRANGE; +extern int COM_PlayerAddress; +extern int COM_PlayerMap; + + +void MERYL_PutMark( WatcherWork *work, int mark ); + +#define T_NOISE 0 + +extern CONTROL *GM_WhereList[94]; + +// in enemy/asiato.c +extern void AsiatoPos( signed char, SVECTOR * ); +extern int NextAsiato( HZD_HDL *, signed char, SVECTOR * ); +extern void CleanAsiato(void); +extern char NearAsiato(void); + +// Identical to s00a_command_800CAACC +void s07a_meryl_unk_800DB340( WatcherWork* work ) +{ + work->target_addr = work->start_addr; + work->target_pos = work->start_pos; + work->target_map = work->start_map; +} + +void s07a_meryl_unk_800DB378( WatcherWork* work ) +{ + work->target_addr = COM_PlayerAddress; + work->target_pos = COM_PlayerPosition; + work->target_map = COM_PlayerMap; +} + +void s07a_meryl_unk_800DB3C0( WatcherWork* work ) +{ + int addr; + HZD_ZON *zone; + + addr = HZD_GetAddress( GM_WhereList[0]->map->hzd, &GM_NoisePosition, -1 ) & 0xFF; + work->target_addr = ( addr << 8 ) | addr; + zone = &GM_WhereList[0]->map->hzd->def->zones[ addr ]; + + work->target_pos.vx = zone->x; + work->target_pos.vy = zone->y; + work->target_pos.vz = zone->z; + work->target_map = COM_PlayerMap; +} + +int s07a_meryl_unk_800DB470( WatcherWork *work, HZD_ZON *zone ) +{ + return ( (unsigned short)zone->padding - 2 ) < 2u; +} + +int s07a_meryl_unk_800DB484( WatcherWork *work, int addr, int addr2 ) +{ + SVECTOR svec; + SVECTOR svec2; + HZD_HDL *hzd; + HZD_ZON *zone; + HZD_ZON *zone2; + + hzd = work->control.map->hzd; + + zone = &hzd->def->zones[ addr ]; + zone2 = &hzd->def->zones[ addr2 ]; + + if ( zone->y || zone2->y ) + { + return 0; + } + + svec.vx = zone->x; + svec.vy = zone->y + 500; + svec.vz = zone->z; + + svec2.vx = zone2->x; + svec2.vy = zone2->y + 500; + svec2.vz = zone2->z; + + if ( HZD_OnlineHazardCheck( hzd, &svec, &svec2, HZD_CHK_D_SEGMENT, SEGMENT_ATR ) != 0 ) + { + printf(" okokoko %d -> %d \n", addr, addr2); + return 1; + } + + printf(" hazure %d -> %d \n", addr, addr2); + return 0; +} + +int s00a_command_800CEA9C( int addr ); +void s00a_command_800CEA84( int a, int addr ); + +void s07a_meryl_unk_800DB590( WatcherWork *work ) +{ + int i; + int vx; + int res; + int addr; + CONTROL *ctrl; + int addr_copy; + HZD_HDL *hzd; + HZD_ZON *zone; + HZD_ZON *zone2; + HZD_ZON *zone3; + int unk[5]; //? + + ctrl = &work->control; + hzd = work->control.map->hzd; + + addr = HZD_GetAddress( hzd, &ctrl->mov, -1 ) & 0xFF; + zone = &hzd->def->zones[ addr ]; + addr_copy = addr; + + res = HZD_NearZones( hzd, addr, unk ); + work->search_flag = 1; + + if ( res > 0 ) + { + vx = work->control.mov.vx % res; + + if ( vx < 0 ) + { + vx = -vx; + } + + for ( i = 0 ; i < res ; i++ ) + { + addr = unk[vx]; + zone2 = &work->control.map->hzd->def->zones[ addr ]; + + if ( !s07a_meryl_unk_800DB470( work , zone2 ) ) + { + if ( !s07a_meryl_unk_800DB484( work, addr_copy, addr ) ) + { + if ( s00a_command_800CEA9C( addr ) ) + { + goto loop; + } + else + { + goto end; + } + } + else + { + goto loop; + } + + if ( s00a_command_800CEA9C( addr ) ) + { + goto loop; + } + else + { +end: + work->search_flag = 0; + s00a_command_800CEA84( work->field_B78, addr ); + break; + } + } + addr = addr_copy; +loop: + vx++; + if ( vx >= res ) + { + vx = 0; + } + } + + } + else + { + work->search_flag = 0; + } + + zone3 = &hzd->def->zones[ addr ]; + work->target_addr = addr << 8 | addr; + + work->target_pos.vx = zone3->x; + work->target_pos.vy = zone3->y; + work->target_pos.vz = zone3->z; + work->target_map = COM_PlayerMap; +} + +void s07a_meryl_unk_800DB768( WatcherWork* work ) +{ + work->target_addr = EnemyCommand.com_addr; + work->target_pos = EnemyCommand.com_pos; + work->target_map = EnemyCommand.com_map; +} + +// Identical to s00a_command_800CB0E0 +void s07a_meryl_unk_800DB7A8( WatcherWork* work ) +{ + int v0, v1, a2; + HZD_HDL *hzd; + void *a1; + MAP *map; + HZD_DEF *hdr; + + v0 = work->field_B7C; + do {} while (0); + map = work->control.map; + v1 = v0 << 1; + v1 = v1 + v0; + hzd = map->hzd; + a2 = v0 << 8; + hdr = hzd->def; + v0 = v0 | a2; + a1 = hdr->zones; + v1 = v1 << 3; + work->target_addr = v0; + a1 = a1 + v1; + + work->target_pos.vx = ((HZD_ZON*)a1)->x; + work->target_pos.vy = ((HZD_ZON*)a1)->y; + work->target_pos.vz = ((HZD_ZON*)a1)->z; + work->target_map = work->start_map; + + //should just be this + /* + addr = work->field_B7C; + work->target_addr = addr | (addr << 8); + zone = &work->control.map->hzd->def->zones[ addr ]; + + work->target_pos.vx = zone->x; + work->target_pos.vy = zone->y; + work->target_pos.vz = zone->z; + work->target_map = work->start_map; + */ +} + +// Identical to s00a_command_800CB13C +void s07a_meryl_unk_800DB804( WatcherWork* work ) +{ + int x; + + x = work->next_node + 1; + if ( x >= work->n_nodes ) + { + x = 0; + } + work->next_node = x; + work->target_pos = work->nodes[ x ]; + work->target_addr = HZD_GetAddress( work->control.map->hzd, &work->target_pos, -1 ); + work->target_map = work->start_map; +} + +// Identical to s00a_command_800CB1C4 +void s07a_meryl_unk_800DB88C( WatcherWork* work ) +{ + AsiatoPos( work->field_BA0, &work->target_pos ); + work->target_addr = HZD_GetAddress( work->control.map->hzd, &work->target_pos, -1 ); + work->target_map = work->control.map->index; +} + +//everything before this probably not in think.c + +// Identical to EnemyResetThink_800CB224 +void s07a_meryl_unk_800DB8EC( WatcherWork* work ) +{ + work->think1 = 0; + work->think2 = 0; + work->think3 = 0; + work->count3 = 0; + work->field_B4C = 0; + work->pad.mode = 0; +} + +extern SVECTOR s07a_dword_800C3770; + +void s07a_meryl_unk_800DB908( WatcherWork* work ) +{ + int addr; + HZD_HDL *hzd; + HZD_ZON *zone; + + hzd = work->control.map->hzd; + addr = HZD_GetAddress( hzd, &s07a_dword_800C3770, -1 ) & 0xFF; + printf(" toillet zone = %d \n", addr); + zone = &hzd->def->zones[ addr ]; + work->target_addr = addr | ( addr << 8 ); + + work->target_pos.vx = zone->x; + work->target_pos.vy = zone->y; + work->target_pos.vz = zone->z; + work->target_map = COM_PlayerMap; +} + +extern SVECTOR s07a_dword_800C3778; + +void s07a_meryl_unk_800DB9B8( WatcherWork* work ) +{ + int addr; + HZD_HDL *hzd; + HZD_ZON *zone; + + hzd = work->control.map->hzd; + addr = HZD_GetAddress( hzd, &s07a_dword_800C3778, -1 ) & 0xFF; + printf(" toillet zone = %d \n", addr); + zone = &hzd->def->zones[ addr ]; + work->target_addr = addr | ( addr << 8 ); + + work->target_pos.vx = zone->x; + work->target_pos.vy = zone->y; + work->target_pos.vz = zone->z; + work->target_map = COM_PlayerMap; +} + + +void s07a_meryl_unk_800DBA68( WatcherWork* work ) +{ + s07a_meryl_unk_800DB908( work ); + work->think1 = 3; + work->think2 = 0xE; + work->think3 = 0x25; + work->count3 = 0; + work->field_B4C = 0; + work->pad.mode = 1; +} + +// Identical to s00a_command_800CB240 +void s07a_meryl_unk_800DBAB4( WatcherWork* work ) +{ + work->think2 = 7; + work->think3 = 14; + work->count3 = 0; +} + +// Identical to s00a_command_800CB258 +void s07a_meryl_unk_800DBACC( WatcherWork *work ) +{ + if ( work->field_B7C == 0xFF ) + { + work->think1 = 1; + work->think2 = 8; + work->think3 = 19; + } + else + { + work->think1 = 1; + work->think2 = 10; + work->think3 = 28; + s07a_meryl_unk_800DB7A8( work ); + } + work->count3 = 0; + work->pad.mode = 1; +} + +// Identical to s00a_command_800CB2C8 +void s07a_meryl_unk_800DBB3C( WatcherWork *work ) +{ + + work->think1 = 1; + work->think2 = 9; + work->think3 = 27; + work->count3 = 32; + work->pad.mode = 1; +} + + +void s07a_meryl_unk_800DBB68( WatcherWork *work ) +{ + if ( work->field_B7C == 0xFF ) + { + s07a_meryl_unk_800DB768( work ); + work->think1 = 1; + + if ( EnemyCommand.field_0x40 == 1 ) + { + work->think2 = 8; + work->think3 = 18; + } + else + { + work->think2 = 10; + work->think3 = 29; + } + } + else + { + s07a_meryl_unk_800DB7A8( work ); + work->think1 = 1; + work->think2 = 10; + work->think3 = 28; + } + work->count3 = 0; + work->pad.mode = 1; +} + + +void s07a_meryl_unk_800DBC08( WatcherWork *work ) +{ + if ( work->field_B7C == 0xFF ) + { + work->think1 = 1; + work->think2 = 10; + work->think3 = 29; + } + else + { + s07a_meryl_unk_800DB7A8( work ); + work->think1 = 1; + work->think2 = 10; + work->think3 = 28; + } + work->count3 = 0; + work->pad.mode = 1; +} + +extern void s07a_meryl_unk_800DBD54( WatcherWork *work ); + +void s07a_meryl_unk_800DBC78( WatcherWork* work ) +{ + EnemyCommand.field_0x40 = 0; + if ( work->act_status & 4 ) + { + work->think1 = 2; + s07a_meryl_unk_800DBD54( work ); + work->pad.mode = 0; + return; + } + + if ( work->field_B7C != 0xFF ) + { + if ( !( HZD_InsideZone( work->control.map->hzd, &work->control.mov, work->field_B7C ) ) ) + { + s07a_meryl_unk_800DB7A8( work ); + work->think1 = 2; + work->think2 = 11; + work->count3 = 0; + work->pad.mode = 1; + } + else + { + work->think1 = 2; + work->think2 = 13; + work->think3 = 34; + work->think4 = 3; + work->count3 = 0; + work->pad.mode = 0; + } + } + else + { + if ( work->field_BFC >= 5800 ) + { + work->think1 = 2; + work->think2 = 13; + work->think3 = 34; + work->think4 = 3; + work->count3 = 0; + work->pad.mode = 0; + + } + else + { + work->think1 = 2; + work->think2 = 11; + work->count3 = 0; + work->pad.mode = 1; + } + } +} + +// Identical to s00a_command_800CB3F0 +void s07a_meryl_unk_800DBD54( WatcherWork *work ) +{ + + work->think2 = 4; + + if ( (work->act_status & 0x10) && (work->field_BA1 & 8) ) + { + work->think3 = 0xE; + } + else + { + work->think3 = 4; + } + + work->count3 = 0; + work->modetime[2] = 0; +} + +void s07a_meryl_unk_800DBD90( WatcherWork* work ) +{ + switch( GM_NoisePower ) + { + case 5: + work->think2 = 1; + work->think3 = 8; + break; + case 200: + s07a_meryl_unk_800DB3C0( work ); + work->think2 = 1; + + if (work->act_status & 0x10 ) + { + work->think3 = 14; + } + else + { + work->think3 = 5; + } + work->modetime[( T_NOISE )] = 0; + break; + case 255: + s07a_meryl_unk_800DB3C0( work ); + s00a_command_800CEB54(); + work->think2 = 7; + work->think3 = 16; + break; + default: + case 100: + s07a_meryl_unk_800DB3C0( work ); + work->think2 = 1; + + if ( work->act_status & 0x10 ) + { + work->think3 = 14; + } + else + { + work->think3 = 5; + } + work->modetime[( T_NOISE )] = 0; + break; + } + work->count3 = 0; +} + +// Identical to s00a_command_800CB610 +void s07a_meryl_unk_800DBE84( WatcherWork* work ) +{ + work->think2 = 2; + work->think3 = 5; + work->count3 = 0; +} + +// Identical to s00a_command_800CB628 +void s07a_meryl_unk_800DBE9C( WatcherWork *work ) +{ + work->think2 = 5; + work->think3 = 5; + work->field_BA0 = NearAsiato(); + work->count3 = 0; +} + + +void s07a_meryl_unk_800DBED4(WatcherWork* work) { + + HZD_HDL* temp_s0; + int temp_v0; + + temp_s0 = work->control.map->hzd; + temp_v0 = HZD_GetAddress(temp_s0, &work->control.mov, -1); + work->field_C04 = temp_v0; + + if (HZD_ZoneDistance(temp_s0, temp_v0 & 0xFF, (char)work->target_addr) < 0xC8) { + + work->pad.mode = 0; + return; + } + + work->pad.mode = 1; +} + +int ToiletArea_800DBF40(SVECTOR* vec) { + + if (((unsigned short)vec->vx - 4000 >= 5501U) || + ((vec->vz < -20500) != 0) || (vec->vz >= -9499)) { + + return 0; + } + + return 1; +} + +int s07a_meryl_unk_800DBF84(SVECTOR* vec) { + + if (((unsigned short)vec->vx - 5500 >= 1501U) || + ((vec->vz < -9500) != 0) || (vec->vz >= -7999)) { + + return 0; + } + + return 1; +} + +int s07a_meryl_unk_800DBFC8(SVECTOR* vec) { + + if (((unsigned short)vec->vx - 4500 >= 4001U) || + ((vec->vz < -20500) != 0) || (vec->vz >= -15499)) { + + return 0; + } + + return 1; +} + +int s07a_meryl_unk_800DC00C( WatcherWork *work ) +{ + int count3 = work->count3; + + if ( ToiletArea_800DBF40( &GM_PlayerPosition ) || s07a_meryl_unk_800DBF84( &GM_PlayerPosition ) ) + { + work->count3 = 0; + } + + if ( EnemyCommand.mode != TOP_COMM_TRAVEL ) + { + work->count3 = 0; + } + + if ( ToiletArea_800DBF40( &work->start_pos ) ) + { + work->count3 = 0; + } + + if ( count3 > 180 ) + { + return 1; + } + + if ( s07a_meryl_unk_800DBFC8( &work->control.mov ) == 0 ) + { + printf( " kroekroekrokeork %d %d\n", work->control.mov.vx , work->control.mov.vz ); + return 1; + } + + work->count3++; + return 0; +} + +extern int s07a_meryl_unk_800DCD58(WatcherWork* work); + +void s07a_meryl_unk_800DC0DC( WatcherWork* work ) +{ + switch ( work->think3 ) + { + case 37: + if ( s07a_meryl_unk_800DCD58( work ) ) + { + work->think2 = 15; + work->pad.mode = 0; + work->think3 = 39; + work->count3 = 0; + } + work->alert_level = 0; + return; + case 38: + if ( s07a_meryl_unk_800DCD58( work ) ) + { + work->think2 = 15; + work->pad.mode = 0; + work->think3 = 39; + work->count3 = 0; + } + if ( work->alert_level > 1 ) + { + work->count3 = 0; + s07a_meryl_unk_800DBAB4( work ); + } + } +} + +extern int s07a_meryl_unk_800DCF78( WatcherWork* work ) ; + +void s07a_meryl_unk_800DC18C( WatcherWork* work ) +{ + if ( ( work->think3 == 39 ) && ( s07a_meryl_unk_800DC00C( work ) ) ) + { + s07a_meryl_unk_800DB9B8( work ); + work->think2 = 0x10; + work->think3 = 0x28; + work->count3 = 0; + work->pad.mode = 0; + } + + if ( s07a_meryl_unk_800DCF78( work ) ) + { + s07a_meryl_unk_800DB340( work ); + work->next_node = work->n_nodes; + } + + work->alert_level = 0; +} + +void s07a_meryl_unk_800DC214( WatcherWork *work ) +{ + int mode; + if ( ( work->think3 == 40 ) && ( s07a_meryl_unk_800DCD58( work ) ) ) + { + s07a_meryl_unk_800DB340( work ); + printf( " out toillet go addr=%d \n", work->target_addr ); + work->next_node = work->n_nodes; + s07a_meryl_unk_800DB8EC( work ); + work->count3 = 0; + + } + + if ( ToiletArea_800DBF40( &GM_PlayerPosition ) || s07a_meryl_unk_800DBF84( &GM_PlayerPosition ) || work->alert_level > 1 ) + { + work->count3 = 0; + s07a_meryl_unk_800DBAB4( work ); + return; + } + + mode = EnemyCommand.mode; + if ( mode == TOP_COMM_ALERT ) + { + s07a_meryl_unk_800DB908( work ); + work->think2 = 14; + work->think3 = 37; + work->count3 = 0; + work->pad.mode = mode; + return; + + } + + work->alert_level = 0; +} + +extern void s07a_meryl_unk_800DE810( WatcherWork *work ); + +void s07a_meryl_unk_800DC310( WatcherWork *work ) +{ + + switch ( work->think2 ) { + case 14: + printf( "0+" ); + s07a_meryl_unk_800DC0DC( work ); + return; + case 15: + printf( "1+" ); + s07a_meryl_unk_800DC18C( work ); + return; + case 16: + printf( "2+" ); + s07a_meryl_unk_800DC214( work ); + return; + case 7: + printf( "3+" ); + work->control.radar_atr |= 0x1000; + s07a_meryl_unk_800DE810( work ); + return; + } +} + +// Identical to s00a_command_800CB6CC +int s07a_meryl_unk_800DC3E0( WatcherWork* work ) +{ + int count; + int v1; + + count = work->l_count; + v1 = count / 30; + v1 = v1 + work->field_B78; + + if ( v1 == 1 || v1 == 4 ) + { + work->pad.press |= 0x2; + } + else if ( v1 == 2 || v1 == 5 ) + { + work->pad.press |= 0x1; + } + + if ( count > 90 ) + { + work->l_count = 0; + return 1; + } + else + { + work->l_count++; + return 0; + } +} + +// Identical to s00a_command_800CB770 +int s07a_meryl_unk_800DC484( WatcherWork* work ) +{ + int count; + + count = work->l_count; + + if ( count == 0 ) + { + work->control.turn.vy = ( work->control.turn.vy + 0x800 ) & 0xFFF; + } + + if ( count == 24 ) + { + work->control.turn.vy = ( work->control.turn.vy + 0x800 ) & 0xFFF; + } + + if ( count > 30 ) + { + work->l_count = 0; + return 1; + } + else + { + work->l_count++; + return 0; + } +} + +int s07a_meryl_unk_800DC4F4( WatcherWork *work ) +{ + int count3 = work->count3; + if ( count3 & 31 ) + { + if ( count3 > 64 ) + { + return 1; + } + } + else + { + work->field_B58 = GV_RandU( 8 ) << 9; + } + + work->pad.dir = work->field_B58; + work->count3++; + return 0; +} + +// Identical to s00a_command_800CB7E0 +int s07a_meryl_unk_800DC560( WatcherWork *work ) +{ + work->count3++; + work->pad.dir = work->sn_dir; + return 0; +} + +int s07a_meryl_unk_800DC57C( WatcherWork* work ) +{ + int count = work->count3; + if ( count != 0 && (int)work->count3 > 0x20 ) + { + return 1; + } + + work->count3++; + return 0; +} + + +int s07a_meryl_unk_800DC5B0( WatcherWork *work ) +{ + int temp; + SVECTOR svec; + CONTROL *ctrl; + HZD_HDL *hzd; + HZD_ZON *zone; + int addr, addr2, addr3; + int reach; + + ctrl = &work->control; + hzd = work->control.map->hzd; + addr = work->target_addr; + + addr2 = HZD_GetAddress( hzd, &ctrl->mov, work->field_C04 ); + work->field_C04 = addr2; + + reach = HZD_ReachTo( hzd, addr2, work->field_C08 ); + + if ( addr != work->field_BF0 || reach <= 0 ) + { + work->field_BF0 = addr; + if ( HZD_ReachTo( hzd, addr2, work->field_BF0 ) < 2 ) + { + work->field_C14 = work->target_pos; + work->field_C08 = addr; + + GV_SubVec3( &work->field_C14, &ctrl->mov, &svec ); + + work->pad.dir = GV_VecDir2( &svec ); + return -1; + } + + if ( !( work->field_C00 & 1 ) ) + { + addr3 = HZD_Navigate2( hzd, addr2, addr, &ctrl->mov ); + zone = &hzd->def->zones[ addr3 ]; + + if ( GM_PlayerPosition.vx & 1 ) + { + work->field_C14.vx = zone->x + 250; + } + else + { + work->field_C14.vx = zone->x - 250; + } + + if ( GM_PlayerPosition.vz & 1 ) + { + work->field_C14.vz = zone->z + 250; + } + else + { + work->field_C14.vz = zone->z - 250; + } + work->field_C14.vy = zone->y; + } + else + { + addr3 = HZD_Navigate( hzd, addr2, addr, &ctrl->mov ); + zone = &hzd->def->zones[ addr3 ]; + work->field_C14.vx = zone->x; + work->field_C14.vy = zone->y; + work->field_C14.vz = zone->z; + } + temp = addr3 & 0xFF; + work->field_C08 = temp | temp << 8; + } + + GV_SubVec3( &work->field_C14, &work->control.mov, &svec ); + return GV_VecDir2( &svec ); +} + +int s07a_meryl_unk_800DC7CC( WatcherWork *work ) +{ + int i; + MAP *map; + PARAM *param; + HZD_PAT *patrol; + HZD_PTP *points; + + map = GM_GetMap( work->start_map ); + param = ( PARAM * )&work->field_B78; + patrol = map->hzd->def->routes; + patrol = &patrol[ param->root ]; + + fprintf( 1, "c_root= %d root %d pat %d n_points = %d \n", param->c_root, param->root, (int)patrol, patrol->n_points ); + work->n_nodes = patrol->n_points; + + if ( work->n_nodes <= 0 ) return -1; + + points = patrol->points; + for ( i = 0 ; i < work->n_nodes ; i++ ) + { + work->nodes[i].vx = points->x; + work->nodes[i].vy = points->y; + work->nodes[i].vz = points->z; + work->nodes[i].pad = points->command; + points++; + } + + work->start_pos = work->nodes[0]; + work->start_addr = HZD_GetAddress( map->hzd, &work->start_pos, -1 ); + return 0; +} + +extern unsigned short s07a_dword_800C36E8[4]; +extern int s07a_dword_800C36F0[32]; + +extern int DirectTrace_800DCE48( WatcherWork *, int ); + +int s07a_meryl_unk_800DC8F0( WatcherWork *work ) +{ + int a2; + int a0, con; + int act, time, dir; + int command; + +start: + // starting from lo + // |con |dir|tim| act // + //000|0 00|00 |000|0 0000// + + command = work->target_pos.pad; + act = ( command & 0x1F ); //5 bits + time = ( command & 0xE0 ) >> 5; //3 bits + dir = ( command & 0x300 ) >> 8; //2 bits + con = ( command & 0x1C00 ) >> 10; //3 bits + + if ( work->field_B78 == 2 ) + { + printf( "act=%d, time=%d dir=%d con=%d\n", act, time, dir, con ); + } + + if ( s07a_dword_800C36F0[ act ] == 0x1F ) + { + work->param_c_root = con + ( dir * 8 ); + return 0; + } + + if ( time != 6 ) + { + + a2 = GV_Time % 100; + a0 = con & 3; + con = con & 4; + + if ( a2 >= s07a_dword_800C36E8[ a0 ] || work->field_B4C == 1 ) + { + work->field_B4C = 0; + s07a_meryl_unk_800DB804( work ); + if ( !DirectTrace_800DCE48( work , 350 ) ) + { + return 0; + } + goto start; + } + + if ( con != 0 ) + { + work->field_B4C = 1; + } + } + + work->pad.time = work->field_BB0[ time ]; + work->pad.tmp = s07a_dword_800C36F0[ act ]; + + if ( COM_GameStatus & 1 ) + { + if ( work->pad.tmp == 0x80 || work->pad.tmp == 0x200 || work->pad.tmp == 0x400 || work->pad.tmp == 0x800 ) + { + work->pad.tmp = 0; + if ( work->pad.time == 450 ) + { + work->pad.time = 90; + } + } + } + + if ( act == 0 && time == 0 ) + { + work->pad.time = 0; + work->pad.dir = work->control.rot.vy; + s07a_meryl_unk_800DB804( work ); + return 0; + } + + if ( act & 0x10 && act != 0x1F ) + { + s07a_meryl_unk_800DB804( work ); + return 0; + } + + work->pad.dir = work->field_BD0[ dir ]; + return 1; +} + +// Identical to s00a_command_800CBD2C +int s07a_meryl_unk_800DCB24( WatcherWork* work ) +{ + int len; + CONTROL *ctrl; + GV_MSG *msg; + + ctrl = &work->control; + len = ctrl->n_msg; + msg = ctrl->msg; + + for ( ; len > 0 ; len--, msg++ ) + { + if ( msg->message[0] == work->field_B4C ) + { + return 1; + } + } + return 0; +} + +int s07a_meryl_unk_800DCB64( WatcherWork* work ) +{ + if ( work->pad.time == 32001 ) + { + if ( s07a_meryl_unk_800DCB24( work ) ) + { + work->field_B4C = 0; + work->pad.time = 0; + return 1; + } + work->pad.press |= work->pad.tmp; + return 0; + } + + if ( !work->pad.time ) + { + return 1; + } + + work->pad.press |= work->pad.tmp; + work->pad.time--; + return 0; +} + +int s07a_meryl_unk_800DCBF4( WatcherWork* work ) +{ + int count; + + count = work->count3; + + if ( count == 0 ) + { + MERYL_PutMark( work, 0 ); + NewEyeflash( &work->body.objs->objs[6].world, &work->control.mov, "kirari01", 0 ); + COM_VibTime = 10; + } + + if ( count < 20 ) + { + work->pad.press |= 0x20; + work->count3++; + return 0; + } + + return 1; + +} + +int s07a_meryl_unk_800DCC88(SVECTOR* arg0, SVECTOR* arg1 ) +{ + SVECTOR svec; + + GV_SubVec3(arg1, arg0, &svec); + return GV_VecDir2(&svec); +} + +void s07a_meryl_unk_800DCCBC( WatcherWork* work ) +{ + work->pad.dir = s07a_meryl_unk_800DCC88( &work->control.mov, &work->target_pos ); +} + +int s07a_meryl_unk_800DCCEC( WatcherWork* work ) +{ + if ( work->count3 == 0 ) + { + MERYL_PutMark( work, 0 ); + s07a_meryl_unk_800DCCBC( work ); + } + + if ( work->count3 > 20 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s07a_meryl_unk_800DCD50( void ) +{ + return 0; +} + +int s07a_meryl_unk_800DCD58(WatcherWork *work) { + + short temp_v0; + + if (!(work->count3 & 0x1F)) { + + work->field_C04 = -1; + work->field_BF0 = -1; + s07a_meryl_unk_800DC5B0(work); + } + + temp_v0 = s07a_meryl_unk_800DC5B0(work); + work->pad.dir = temp_v0; + + if (work->pad.dir < 0) { + + return 1; + } + + work->count3 += 1; + return 0; +} + +int s07a_meryl_unk_800DCDC8(WatcherWork *work) { + + short temp_v0; + + if (s07a_meryl_unk_800DCD50() == 0) { + + if (!(work->count3 & 0x1F)) { + + work->field_C04 = -1; + work->field_BF0 = -1; + s07a_meryl_unk_800DC5B0(work); + } + + temp_v0 = s07a_meryl_unk_800DC5B0(work); + work->pad.dir = temp_v0; + + if (work->pad.dir >= 0) { + + work->count3 += 1; + return 0; + } + } + + return 1; +} + +int DirectTrace_800DCE48( WatcherWork* work, int a1 ) +{ + int x, z; + SVECTOR svec; + + svec.vx = x = work->target_pos.vx - work->control.mov.vx; + svec.vz = z = work->target_pos.vz - work->control.mov.vz; + + if ( -a1 >= x || x >= a1 || -a1 >= z || z >= a1 ) + { + work->pad.dir = GV_VecDir2( &svec ); + work->count3++; + return 0; + } + + return 1; +} + +// Identical to s00a_command_800CC240 +int s07a_meryl_unk_800DCED0(SVECTOR* svec, SVECTOR* svec2, int a1) { + int x, z; + + x = svec->vx - svec2->vx; + z = svec->vz - svec2->vz; + + if ( -a1 < x || x < a1 || -a1 < z || z < a1 ) + { + return 0; + } + + return 1; +} + +int s07a_meryl_unk_800DCF24( WatcherWork *work ) +{ + int x; + + x = NextAsiato( work->control.map->hzd, work->field_BA0, &work->control.mov ); + + if ( x >= 0 ) + { + work->field_BA0 = x; + return 1; + } + + work->field_BA0 = -1; + return 0; +} + +int s07a_meryl_unk_800DCF78( WatcherWork* work ) +{ + + if ( work->param_c_root != work->param_root ) + { + fprintf( 1, " Root Change !!\n" ); + work->param_root = work->param_c_root; + s07a_meryl_unk_800DC7CC( work ); + return 1; + } + + return 0; +} + +int s07a_meryl_unk_800DCFD4( WatcherWork *work ) +{ + unsigned int count; + if ( work->count3 == 0 ) + { + if ( COM_GameStatus & 0x2000 ) + { + work->think3 = 13; + return 0; + } + + COM_GameStatus |= 0x2000; + work->pad.dir = work->sn_dir; + } + + count = work->count3; + + asm(""); //TODO fix + if ( work->actend != 0 || count > 300 ) + { + return 1; + } + + work->pad.press |= SP_DANBOWLKERI; + work->count3++; + + return 0; +} + +int s07a_meryl_unk_800DD05C( WatcherWork *work ) +{ + if ( !( COM_GameStatus & 0x2000 ) ) + { + return 1; + } + + work->pad.press |= 0x4; + work->count3++; + return 0; +} + +// Identical to s00a_command_800CB6CC +int s07a_meryl_unk_800DD09C( WatcherWork* work ) +{ + int count; + int v1; + + count = work->l_count; + v1 = count / 30; + v1 = v1 + work->field_B78; + + if ( v1 == 1 || v1 == 4 ) + { + work->pad.press |= 0x2; + } + else if ( v1 == 2 || v1 == 5 ) + { + work->pad.press |= 0x1; + } + + if ( count > 90 ) + { + work->l_count = 0; + return 1; + } + else + { + work->l_count++; + return 0; + } +} + +int Think3_GoNext_800DD140( WatcherWork* work ) { + if ( work->pad.time ) { + (work->pad.press |= ( work->pad.tmp )) ; + work->pad.time -- ; + } + + if ( DirectTrace_800DCE48( work, 350 ) ) { + return 1 ; + } + + return 0 ; +} + +int Think3_BikkuriGetUp_800DD194( WatcherWork* work ) +{ + if( work->count3 == 0){ + work->pad.press |= 0x00001000 ; + } else if( work->count3 > 90 || work->actend ){ + + return 1 ; + } + work->count3 ++ ; + return 0 ; +} + +int Think3_NoiseModeWatch_800DD1EC( WatcherWork *work ) +{ + + if( work->count3 == 0) + { + if( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + if( work->modetime[( T_NOISE )] <= 1 ) + { + GM_SeSet( &work->control.mov, 0xC0 ); + } + if( work->modetime[( T_NOISE )] <= 3 ) + { + MERYL_PutMark( work ,BW_MARK ); + } + work->pad.dir = work->sn_dir; + } + else + { + if( work->modetime[( T_NOISE )] ) return 1; + MERYL_PutMark( work ,BW_MARK ); + work->pad.dir = work->sn_dir; + } + } + else + { + work->pad.dir = -1; + } + + switch( work->modetime[( T_NOISE )] ) + { + case 0: + if( work->count3 >= 16) + { + return 1; + } + break; + case 1: + case 2: + if ( work->count3 >= 48 ) + { + return 1; + } + break; + case 3: + return 1; + } + work->count3++; + return 0; +} + +// Identical to s00a_command_800CC760 +int s07a_meryl_unk_800DD310( WatcherWork *work ) +{ + if ( work->count3 == 0 ) + { + work->pad.dir = work->sn_dir; + } + + if ( work->count3 > 14 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s07a_meryl_unk_800DD354( WatcherWork *work ) +{ + SVECTOR svec; + GV_SubVec3( &GM_NoisePosition, &work->control.mov, &svec ); + work->pad.dir = GV_VecDir2( &svec ); + work->pad.press |= 0x02000000; + + if ( work->count3 == 0 ) + { + MERYL_PutMark( work, BW_MARK ); + } + + if ( work->count3 > 30 ) + { + return 1; + } + + if ( work->field_BA1 & 1 ) + { + work->count3 = 1; + } + else + { + work->count3++; + } + + return 0; +} + +int s07a_meryl_unk_800DD3EC( WatcherWork* work ) +{ + if ( work->count3 == 0 ) + { + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + GM_SeSet( &work->control.mov, 0xC0 ); + } + + MERYL_PutMark( work, 5 ); + work->pad.dir = work->sn_dir; + } + else + { + work->pad.dir = -1; + } + + if ( work->count3 > 15 ) + { + return 1; + } + + work->count3++; + return 0; +} + +// Identical to s00a_command_800CC8C8 +int s07a_meryl_unk_800DD47C( WatcherWork *work ) +{ + if ( work->count3 > 2 && work->act_status & 1 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s07a_meryl_unk_800DD4C0( WatcherWork *work ) +{ + if ( work->count3 == 0 ) + { + work->pad.dir = work->sn_dir; + } + + if ( work->count3 == 30 ) + { + GM_SeSet( &work->control.mov, 0xC0 ); + MERYL_PutMark( work, 5 ); + } + + if ( work->count3 > 29 && s07a_meryl_unk_800DC3E0( work ) ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s07a_meryl_unk_800DD554( WatcherWork *work ) +{ + if ( work->count3 == 0 ) + { + GM_SeSet( &work->control.mov, 0xC0 ); + MERYL_PutMark( work, BW_MARK ); + work->pad.dir = work->sn_dir; + } + else + { + work->pad.dir = -1; + } + + if ( work->count3 >= 16 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s07a_meryl_unk_800DD5D0( WatcherWork *work ) +{ + if ( work->count3 == 20 ) + { + MERYL_PutMark( work, 5 ); + } + + if ( s07a_meryl_unk_800DC3E0( work ) ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s07a_meryl_unk_800DD628( WatcherWork *work ) +{ + if ( work->count3 == 20 ) + { + MERYL_PutMark( work, 5 ); + } + + if ( s07a_meryl_unk_800DC3E0( work ) ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s07a_meryl_unk_800DD680( WatcherWork *work ) +{ + work->pad.press |= 0x4; + if ( work->count3 == 20 ) + { + MERYL_PutMark( work, 5 ); + } + + if ( work->count3 > 120 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s07a_meryl_unk_800DD6E8( WatcherWork *work ) +{ + MAP *map; + s07a_meryl_unk_800DB378( work ); + + map = work->control.map; + + if ( !( map->index & work->target_map ) ) + { + return -1; + } + + if ( work->count3 & 16 ) + { + work->field_C04 = HZD_GetAddress( map->hzd, &work->control.mov, -1 ); + if ( HZD_ReachTo( work->control.map->hzd, work->field_C04, work->target_addr ) > 1 ) + { + return -1; + } + } + + return DirectTrace_800DCE48( work, 1500 ); +} + +// Identical to s00a_command_800CCC14 +int s07a_meryl_unk_800DD780( WatcherWork *work ) +{ + int count; + + count = work->count3; + + work->pad.press |= 0x10000; + + if ( count == 0 ) + { + work->count3 = GV_RandU( 8 ); + } + + if ( count < 9 ) + { + if ( !( count & 1 ) ) + { + work->pad.press |= 0x40000; + } + } + + if ( count > 11 ) + { + return 1; + } + + work->pad.dir = work->sn_dir; + work->count3++; + return 0; + +} + +int s07a_meryl_unk_800DD818( WatcherWork *work ) +{ + + work->pad.press |= 0x20000; + + if ( work->count3 == 0 ) + { + work->pad.press |= 0x80000; + } + else + { + if ( work->count3 == 8 ) + { + return 1; + } + } + + work->pad.dir = work->sn_dir; + work->count3++; + return 0; +} + +int s07a_meryl_unk_800DD870( WatcherWork *work ) +{ + work->pad.press |= 0x10000; + + if ( work->count3 != 30) + { + if (work->body.is_end != 0) + { + return 1; + } + } + else + { + work->pad.press |= 0x100000; + } + + work->pad.dir = work->sn_dir; + work->count3++; + return 0; +} + +int s07a_meryl_unk_800DD8D0( WatcherWork *work ) +{ + work->pad.press |= 0x10000; + + if ( work->count3 != 0) + { + if (work->body.is_end != 0) + { + return 1; + } + } + else + { + work->pad.press |= 0x200000; + } + + work->pad.dir = work->sn_dir; + work->count3++; + return 0; +} + +// Identical to s00a_command_800CCDA0 +int s07a_meryl_unk_800DD92C( WatcherWork *work ) +{ + int count; + work->pad.press |= 0x10000; + + count = work->count3; + if ( count < 10 ) + { + if ( !( work->count3 & 1 ) ) + { + work->pad.press |= 0x40000; + } + } + else if ( count >= 20 ) + { + return 1; + } + + work->pad.dir = work->sn_dir; + work->count3++; + return 0; +} + +int s07a_meryl_unk_800DD994( WatcherWork *work ) +{ + work->pad.press |= 0x10000; + work->pad.dir = work->sn_dir; + work->count3++; + return 0; +} + +int s07a_meryl_unk_800DD9C0( WatcherWork* work ) +{ + switch ( work->think4 ) + { + case 3: + if ( s07a_meryl_unk_800DC3E0( work ) ) + { + work->think4 = 4; + work->count3 = 0; + } + work->count3++; + break; + case 4: + if (s07a_meryl_unk_800DC4F4( work ) ) + { + work->think4 = 3; + work->count3 = 0; + } + break; + } + return 0; +} + +int s07a_meryl_unk_800DDA50( WatcherWork* work ) +{ + switch ( work->think4 ) + { + case 3: + if ( s07a_meryl_unk_800DC3E0( work ) ) + { + work->think4 = 1; + work->count3 = 0; + s07a_meryl_unk_800DB378( work ); + } + work->count3++; + break; + case 1: + if (s07a_meryl_unk_800DCD58( work ) ) + { + work->think4 = 3; + work->count3 = 0; + } + break; + } + return 0; +} + +int s07a_meryl_unk_800DDADC( WatcherWork *work ) +{ + switch( work->think4 ) + { + case 0: + if ( s07a_meryl_unk_800DD09C(work) ) + { + work->think4 = 2; + s07a_meryl_unk_800DB590( work ); + work->count3 = 0; + } + break; + case 3: + if ( s07a_meryl_unk_800DC3E0(work) ) + { + work->think4 = 2; + s07a_meryl_unk_800DB590( work ); + work->count3 = 0; + } + work->count3++; + break; + case 2: + if ( DirectTrace_800DCE48( work, 250 ) ) + { + s07a_meryl_unk_800DB590( work ); + work->count3 = 0; + } + break; + } + + return 0; +} + +void s07a_meryl_unk_800DDBA8( WatcherWork *work ) +{ + if ( work->think3 == 3 && s07a_meryl_unk_800DC57C( work ) ) + { + s07a_meryl_unk_800DB340( work ); + work->next_node = work->n_nodes; + s07a_meryl_unk_800DB8EC( work ); + work->pad.mode = 1; + } + + if ( work->field_BA1 & 4 ) + { + s07a_meryl_unk_800DBD54( work ); + } + else if ( work->field_BA1 & 1 ) + { + s07a_meryl_unk_800DBD90( work ); + } + else if ( work->alert_level > 1 ) + { + s07a_meryl_unk_800DBAB4( work ); + } +} + +static inline void set_dir( WatcherWork *work ) +{ + if ( GV_DiffDirAbs( work->pad.dir, work->control.rot.vy ) < 128 ) + { + work->pad.dir = -1; + } +} + +static inline void think_reset( WatcherWork *work ) +{ + s07a_meryl_unk_800DB340( work ); + work->next_node = work->n_nodes; + s07a_meryl_unk_800DB8EC( work ); +} + +static inline void think_reset2( WatcherWork *work ) +{ + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->next_node--; + s07a_meryl_unk_800DB804( work ); + s07a_meryl_unk_800DB8EC( work ); + s07a_meryl_unk_800DBED4( work ); + } + else + { + s07a_meryl_unk_800DBC78( work ); + } +} + +static inline void think_noise_inline( WatcherWork *work ) +{ + s07a_meryl_unk_800DB3C0( work ); + s00a_command_800CEB54(); + work->think2 = 7; + work->think3 = 16; + work->count3 = 0; +} + +void s07a_meryl_unk_800DDC60( WatcherWork* work ) +{ + switch ( work->think3 ) + { + case 0: + if ( s07a_meryl_unk_800DCD58( work ) ) + { + if (ToiletArea_800DBF40( &work->control.mov ) != 0) + { + work->think1 = 3; + work->think2 = 0xF; + work->think3 = 0x27; + work->count3 = 0; + work->pad.time = 0; + + } else + { + work->pad.mode = 0; + work->think3 = 1; + work->next_node--; + s07a_meryl_unk_800DB804( work ); + work->count3 = 0; + work->pad.time = 0; + } + } + break; + case 1: + if ( Think3_GoNext_800DD140( work ) ) + { + if ( s07a_meryl_unk_800DCF78( work ) ) + { + think_reset( work ); + return; + } + else + { + if (s07a_meryl_unk_800DC8F0( work ) ) + { + work->think3 = 2; + } + work->count3 = 0; + } + } + else + { + if ( work->field_BA1 & 64 ) + { + s07a_meryl_unk_800DBE84( work ); + work->think3 = 12; + return; + } + } + break; + case 2: + if ( s07a_meryl_unk_800DCB64( work ) ) + { + if ( s07a_meryl_unk_800DCF78( work ) ) + { + think_reset( work ); + } + else + { + s07a_meryl_unk_800DB804( work ); + if ( DirectTrace_800DCE48( work, 350 ) ) + { + if ( !s07a_meryl_unk_800DC8F0( work ) ) + { + work->think3 = 1; + } + else + { + set_dir( work ); + } + } + else + { + work->think3 = 1; + } + work->count3 = 0; + } + } + break; + } + + if ( s07a_meryl_unk_800DCF78( work ) ) + { + think_reset( work ); + } + + if ( work->field_BA1 & 4 ) + { + s07a_meryl_unk_800DBD54( work ); + } + else if ( work->field_BA1 & 2) + { + s07a_meryl_unk_800DBE84( work ); + } + else if ( work->field_BA1 & 1) + { + s07a_meryl_unk_800DBD90( work ); + } + else if ( work->field_BA1 & 16 ) + { + s07a_meryl_unk_800DBE9C( work ); + } + else if ( work->alert_level > 2 ) + { + s07a_meryl_unk_800DBAB4( work ); + } + else if ( ToiletArea_800DBF40( &work->control.mov ) ) + { + s07a_meryl_unk_800DBA68( work ); + work->pad.mode = TOP_COMM_TRAVEL; + } +} + +void s07a_meryl_unk_800DDF0C( WatcherWork *work ) +{ +} + +void s07a_meryl_unk_800DDF14( WatcherWork *work ) +{ + switch ( work->think3 ) + { + case 14: + if ( Think3_BikkuriGetUp_800DD194( work ) ) + { + work->think3 = 5; + work->count3 = 0; + } + break; + case 4: + if ( s07a_meryl_unk_800DD47C( work ) ) + { + if (ToiletArea_800DBF40( &work->control.mov ) != 0) + { + s07a_meryl_unk_800DBA68( work ); + return; + } + work->think3 = 5; + work->count3 = 0; + } + break; + case 5: + if ( s07a_meryl_unk_800DD4C0( work ) ) + { + think_reset2( work ); + } + + if ( work->field_BA1 & 1 ) + { + if ( GM_NoisePower == 200 || GM_NoisePower == 255 ) + { + think_noise_inline( work ); + } + return; + } + else if ( work->field_BA1 & 2 ) + { + s07a_meryl_unk_800DBE84( work ); + return; + } + break; + } + + if ( ( *( int * )&work->field_BA0 & 0xC00 ) == 0x400 ) + { + s07a_meryl_unk_800DBD54( work ); + } + else if ( work->alert_level > 1 ) + { + s07a_meryl_unk_800DBAB4( work ); + } +} + + +void s07a_meryl_unk_800DE0C8( WatcherWork *work ) +{ + short x; + x = work->think3 - 5; + switch ( x ) + { + case 0: + if ( s07a_meryl_unk_800DD3EC( work ) ) + { + s07a_meryl_unk_800DB378( work ); + work->think3 = 9; + work->count3 = 0; + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->pad.mode = 0; + } + else + { + work->pad.mode = 1; + } + } + break; + case 4: + if ( s07a_meryl_unk_800DCD58( work ) ) + { + work->think3 = 10; + work->count3 = 0; + } + if ( s07a_meryl_unk_800DCED0( &work->target_pos, &work->control.mov, 1500 ) ) + { + work->think3 = 11; + work->count3 = 0; + } + if ( work->sn_dis < 1500 ) + { + if ( ( signed char )work->field_BA1 & 128 ) + { + if ( ( work->field_BA1 & 16 ) || work->think1 == 2 ) + { + work->think3 = 12; + work->count3 = 0; + } + else + { + work->think3 = 11; + work->count3 = 0; + } + } + } + break; + case 5: + if ( DirectTrace_800DCE48( work, 1500 ) ) + { + work->pad.dir = work->sn_dir; + + if ( ( work->field_BA1 & 16 ) || work->think1 == 2 ) + { + work->think3 = 12; + work->count3 = 0; + } + else + { + work->think3 = 11; + work->count3 = 0; + } + + } + break; + case 6: + if ( s07a_meryl_unk_800DD680( work ) ) + { + think_reset2( work ); + } + break; + case 7: + if ( s07a_meryl_unk_800DCFD4( work ) ) + { + think_reset2( work ); + } + break; + case 8: + if ( s07a_meryl_unk_800DD05C( work ) ) + { + think_reset2( work ); + } + break; + } + + if ( work->alert_level > 1 || work->field_BA1 & 2 ) + { + s07a_meryl_unk_800DBAB4( work ); + return; + } + + if ( work->field_BA1 & 4 ) + { + s07a_meryl_unk_800DBD54( work ); + return; + } + + if ( work->field_BA1 & 1 ) + { + if ( (GM_NoisePower != 100) ) + { + if ( GM_NoisePower == 255 ) + { + think_noise_inline( work ); + return; + } + } + s07a_meryl_unk_800DBAB4( work ); + work->count3 = 0; + return ; + } +} + +void s07a_meryl_unk_800DE360( WatcherWork *work ) +{ + short x; + x = work->think3 - 5; + switch ( x ) + { + case 10: + if ( Think3_BikkuriGetUp_800DD194( work ) ) + { + work->think3 = 6; + work->count3 = 0; + } + break; + case 1: + if ( s07a_meryl_unk_800DD310( work ) ) + { + //noise_inline without setting count; + s07a_meryl_unk_800DB3C0( work ); + s00a_command_800CEB54(); + work->think2 = 7; + work->think3 = 16; + break; + } + break; + case 9: + if ( Think3_BikkuriGetUp_800DD194( work ) ) + { + work->think3 = 5; + work->count3 = 0; + } + break; + case 0: + if ( Think3_NoiseModeWatch_800DD1EC( work ) ) + { + work->modetime[( T_NOISE )]++; + work->think3 = 9; + if ( EnemyCommand.mode == TOP_COMM_TRAVEL && work->modetime[( T_NOISE )] < 2 ) + { + work->pad.mode = 0; + } + else + { + work->pad.mode = 1; + } + work->count3 = 0; + } + break; + case 4: + if ( s07a_meryl_unk_800DCD58( work ) ) + { + work->think3 = 11; + work->count3 = 0; + } + + if ( work-> count3 > 32 ) + { + if ( work->field_BA1 & 1 && work->modetime[( T_NOISE )] < 3 ) + { + s07a_meryl_unk_800DB3C0( work ); + work->think3 = 5; + work->count3 = 0; + } + } + break; + case 6: + if ( s07a_meryl_unk_800DD5D0( work ) ) + { + think_reset2( work ); + } + break; + case 3: + if ( s07a_meryl_unk_800DD354( work ) ) + { + think_reset2( work ); + } + break; + } + + if ( work->alert_level > 2 ) + { + s07a_meryl_unk_800DBAB4( work ); + } + else if ( work->field_BA1 & 2 ) + { + s07a_meryl_unk_800DBE84( work ); + } + else if ( work->field_BA1 & 16 ) + { + s07a_meryl_unk_800DBE9C( work ); + } + else if ( work->field_BA1 & 4 ) + { + s07a_meryl_unk_800DBD54( work ); + } + else if ( work->field_BA1 & 1 ) + { + if ( GM_NoisePower == 200 || GM_NoisePower == 255 ) + { + think_noise_inline( work ); + } + } +} + +void s07a_meryl_unk_800DE61C( WatcherWork *work ) +{ + switch ( work->think3 ) + { + case 5: + if ( s07a_meryl_unk_800DD554( work ) ) + { + s07a_meryl_unk_800DB88C( work ); + work->think3 = 10; + work->t_count = 0; + work->pad.mode = 0; + work->count3 = 0; + } + break; + case 10: + work->pad.press |= 0x4; + if ( DirectTrace_800DCE48( work , 500 ) ) + { + if ( s07a_meryl_unk_800DCF24( work ) != 0 ) + { + s07a_meryl_unk_800DB88C( work ); + work->t_count++; + if ( work->t_count == 12 ) + { + work->think3 = 32; + } + else + { + work->pad.dir = work->control.turn.vy; + } + work->count3 = 0; + break; + } + else + { + work->think3 = 11; + CleanAsiato(); + work->count3 = 0; + } + } + break; + case 32: + if ( s07a_meryl_unk_800DC484( work ) ) + { + work->think3 = 10; + work->t_count = 0; + work->count3 = 0; + } + break; + case 11: + if ( s07a_meryl_unk_800DD628 ( work ) ) + { + think_reset2( work ); + } + break; + } + + if ( work->alert_level > 1 ) + { + s07a_meryl_unk_800DBAB4( work ); + CleanAsiato(); + } + else if ( work->field_BA1 & 2 ) + { + s07a_meryl_unk_800DBE84( work ); + } + else if ( work->field_BA1 & 4 ) + { + s07a_meryl_unk_800DBD54( work ); + CleanAsiato(); + } + + else if ( work->field_BA1 & 1 ) + { + s07a_meryl_unk_800DBD90( work ); + CleanAsiato(); + } +} + +void s07a_meryl_unk_800DE810( WatcherWork *work ) +{ + switch( work->think3 ) + { + case 14: + if ( s07a_meryl_unk_800DCBF4( work ) ) + { + if ( work->act_status & 0x01 ) + { + if ( GM_PlayerStatus & PLAYER_CB_BOX ) + { + GM_SeSet( &work->control.mov, 0xC1 ); + } + else + { + GM_SeSet( &work->control.mov, 0xB5 ); + } + } + s07a_meryl_unk_800DBA68( work ); + } + break; + case 16: + if ( s07a_meryl_unk_800DCCEC( work ) ) + { + s07a_meryl_unk_800DBC08( work ); + } + break; + case 17: + if (s07a_meryl_unk_800DCBF4( work ) != 0) + { + s07a_meryl_unk_800DBACC( work ); + ENE_SetGopointLast(); + } + } +} + +void s07a_meryl_unk_800DE908( WatcherWork *work ) +{ + int res; + short x; + x = work->think3 - 21; + switch ( x ) + { + case 0: + if ( s07a_meryl_unk_800DD780( work ) ) + { + work->think3 = 27; + work->count3 = 0; + } + break; + case 1: + if ( s07a_meryl_unk_800DD818( work ) ) + { + work->think3 = 27; + work->count3 = 0; + } + break; + case 3: + if ( s07a_meryl_unk_800DD870( work ) ) + { + work->think3 = 27; + work->count3 = 0; + } + break; + case 4: + if ( s07a_meryl_unk_800DD8D0( work ) ) + { + work->think3 = 27; + work->count3 = 0; + } + break; + case 5: + if ( s07a_meryl_unk_800DD92C( work ) ) + { + work->think3 = 27; + work->count3 = 0; + } + break; + case 6: + res = s07a_meryl_unk_800DD994( work ); + if ( res ) + { + work->think3 = res; + work->count3 = 0; + } + break; + } + + if ( ( ( GV_Time / 4 ) % 4 ) != work->field_B78 ) + { + return; + } + + if ( work->field_B7C == 0xFF ) + { + if ( work->field_C04 != COM_PlayerAddress ) + { + if ( work->field_BFC < 0xDAD ) + { + if ( (work->field_BFC + 0x3E8) < work->sn_dis || work->vision.field_B92 != 2 ) + { + s07a_meryl_unk_800DBACC( work ); + } + } + else + { + if ( work->field_BFC < work->sn_dis ) + { + s07a_meryl_unk_800DBACC( work ); + } + } + } + } + else + { + if ( work->vision.field_B92 != 2 ) + { + if (!(HZD_InsideZone( work->control.map->hzd, &work->control.mov, work->field_B7C ))) + { + s07a_meryl_unk_800DBACC(work); + } + } + } +} + +void s07a_meryl_unk_800DEAEC( WatcherWork *work ) +{ + int res; + switch ( work->think3 ) + { + case 18: + s07a_meryl_unk_800DB378( work ); + if ( s07a_meryl_unk_800DCDC8(work) ) + { + work->think3 = 19; + work->count3 = 0; + } + break; + case 19: + s07a_meryl_unk_800DB378( work ); + if ( s07a_meryl_unk_800DCD58( work ) ) + { + work->think3 = 20; + work->count3 = 0; + } + break; + case 20: + res = s07a_meryl_unk_800DD6E8( work ); + if ( res < 0 ) + { + work->think3 = 19; + work->count3 = 0; + } + else if ( res > 0 ) + { + s07a_meryl_unk_800DBB3C( work ); + } + break; + } + + if ( work->sn_dis < work->field_BFC ) + { + if ( ( work->field_BFC >= 0xFA0 ) || ( work->vision.field_B92 == 2 ) ) + { + s07a_meryl_unk_800DBB3C( work ); + } + } +} + +void s07a_meryl_unk_800DEBF8( WatcherWork* work ) +{ + switch ( work->think3 ) + { + case 29: + if ( s07a_meryl_unk_800DCDC8( work ) ) + { + work->think3 = 28; + work->count3 = 0; + } + break; + case 28: + if ( ( work->field_B7C != 0xFF ) && (char)work->target_addr != (short)work->field_B7C ) + { + s07a_meryl_unk_800DB7A8( work ); + } + if ( s07a_meryl_unk_800DCD58( work ) ) + { + if ( EnemyCommand.field_0x40 == 1 || ( work->field_B7C != 0xFF) ) + { + s07a_meryl_unk_800DBB3C( work ); + } + else + { + work->think3 = 30; + work->count3 = 0; + } + } + break; + case 30: + break; + } + + if ( ( work->field_B7C == 0xFF) || EnemyCommand.field_0x40 != 1 ) + { + if ( work->vision.field_B92 == 2 ) + { + if ( EnemyCommand.field_0x40 != 1 ) + { + work->think2 = 7; + work->think3 = 17; + work->count3 = 0; + return; + } + work->pad.dir = work->sn_dir; + s07a_meryl_unk_800DBB3C( work ); + return; + } + } + else + { + if ( work->vision.field_B92 == 2 ) + { + work->pad.dir = work->sn_dir; + s07a_meryl_unk_800DBB3C( work ); + return; + } + } +} + +void s07a_meryl_unk_800DED40( WatcherWork* work ) +{ + if ( s07a_meryl_unk_800DCD58( work ) ) + { + work->think2 = 13; + work->think3 = 34; + work->think4 = 3; + work->pad.mode = 0; + work->count3 = 0; + } + + if ( work->alert_level > 1 ) + { + s07a_meryl_unk_800DBAB4( work ); + } + + if ( work->field_BA1 & 2 ) + { + s07a_meryl_unk_800DBE84( work ); + } + + if ( work->field_BA1 & 1 ) + { + s07a_meryl_unk_800DBD90( work ); + } + + if ( work->field_BA1 & 16 ) + { + s07a_meryl_unk_800DBE9C( work ); + work->pad.mode = 0; + } + else if ( work->field_BA1 & 4 ) + { + s07a_meryl_unk_800DBD54( work ); + work->pad.mode = 0; + } +} + +void s07a_meryl_unk_800DEE14( WatcherWork* work ) +{ + s07a_meryl_unk_800DB378( work ); + + if ( s07a_meryl_unk_800DCD58( work ) || ( work->sn_dis < ( work->field_BFC + 500 ) ) || GM_AlertLevel < 150 ) + { + work->think2 = 13; + work->think3 = 34; + work->think4 = 0; + work->pad.mode = 0; + work->count3 = 0; + } + + if ( work->alert_level > 1 ) + { + s07a_meryl_unk_800DBAB4( work ); + } + + if ( work->field_BA1 & 2 ) + { + s07a_meryl_unk_800DBE84( work ); + } + + if ( work->field_BA1 & 1 ) + { + s07a_meryl_unk_800DBD90( work ); + } + + if ( work->field_BA1 & 16 ) + { + s07a_meryl_unk_800DBE9C( work ); + work->pad.mode = 0; + } + else if ( work->field_BA1 & 4 ) + { + s07a_meryl_unk_800DBD54( work ); + work->pad.mode = 0; + } +} + +void s07a_meryl_unk_800DEF1C( WatcherWork* work ) +{ + switch ( work->think3 ) + { + case 31: + s07a_meryl_unk_800DD9C0( work ); + break; + case 32: + s07a_meryl_unk_800DC560( work ); + break; + case 33: + s07a_meryl_unk_800DDA50( work ); + break; + case 34: + s07a_meryl_unk_800DDADC( work ); + break; + } + + if ( work->alert_level > 1 ) + { + s07a_meryl_unk_800DBAB4(work); + } + if (work->field_BA1 & 2) + { + s07a_meryl_unk_800DBE84(work); + } + else if (work->field_BA1 & 1) + { + s07a_meryl_unk_800DBD90(work); + } + else if (work->field_BA1 & 16 ) + { + s07a_meryl_unk_800DBE9C(work); + } + else if (work->field_BA1 & 4) + { + s07a_meryl_unk_800DBD54(work); + } +} + +void s07a_meryl_unk_800DF038( WatcherWork *work ) +{ + switch( work->think2 ) + { + case 6: + s07a_meryl_unk_800DDBA8( work ); + break; + case 0: + s07a_meryl_unk_800DDC60( work ); + break; + case 1: + work->control.radar_atr |= 0x1000; + s07a_meryl_unk_800DE360( work ); + break; + case 2: + work->control.radar_atr |= 0x1000; + s07a_meryl_unk_800DE0C8( work ); + break; + case 3: + work->control.radar_atr |= 0x1000; + s07a_meryl_unk_800DDF0C( work ); + break; + case 4: + work->control.radar_atr |= 0x1000; + s07a_meryl_unk_800DDF14( work ); + break; + case 5: + work->control.radar_atr |= 0x1000; + s07a_meryl_unk_800DE61C( work ); + break; + case 7: + work->control.radar_atr |= 0x1000; + s07a_meryl_unk_800DE810( work ); + break; + } + + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + return; + } + + if ( work->think2 == 7 ) + { + work->think1 = 1; + } + else + { + s07a_meryl_unk_800DBB68( work ); + } +} + +void s07a_meryl_unk_800DF17C( WatcherWork* work ) +{ + switch ( work->think2 ) + { + case 8: + s07a_meryl_unk_800DEAEC( work ); + break; + case 9: + s07a_meryl_unk_800DE908( work ); + break; + case 10: + s07a_meryl_unk_800DEBF8( work ); + break; + case 7: + s07a_meryl_unk_800DE810( work ); + break; + } + + if ( EnemyCommand.mode != TOP_COMM_ALERT ) + { + s07a_meryl_unk_800DBC78( work ); + } +} + +void s07a_meryl_unk_800DF234( WatcherWork *work ) +{ + short x; + x = work->think2 - 1; + switch( x ) + { + case 10: + s07a_meryl_unk_800DED40( work ); + break; + case 11: + s07a_meryl_unk_800DEE14( work ); + break; + case 12: + s07a_meryl_unk_800DEF1C( work ); + break; + case 4: + s07a_meryl_unk_800DE61C( work ); + break; + case 0: + s07a_meryl_unk_800DE360( work ); + break; + case 1: + s07a_meryl_unk_800DE0C8( work ); + break; + case 3: + s07a_meryl_unk_800DDF14( work ); + break; + case 6: + s07a_meryl_unk_800DE810( work ); + break; + } + + if ( EnemyCommand.mode == TOP_COMM_ALERT ) + { + s07a_meryl_unk_800DBB68( work ); + } + + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->think1 = 0; + if ( work->think2 != 7 && work->think2 != 4 ) + { + if ( work->think2 == 2 ) + { + unsigned short th3 = work->think3; + if ( ( th3 - 12 ) < 2u ) + { + return; + } + } + + if ( work->think2 == 5 ) + { + CleanAsiato(); + } + + work->think2 = 6; + work->think3 = 3; + work->count3 = 0; + } + } +} + + +void MerylThink_800DF3A0( WatcherWork* work ) +{ + work->pad.dir = -1; + work->pad.press = 0; + + if ( ToiletArea_800DBF40( &GM_PlayerPosition ) ) + { + work->control.radar_atr = RADAR_SIGHT | RADAR_ALL_MAP ; + } + else + { + work->control.radar_atr = RADAR_VISIBLE | RADAR_SIGHT | RADAR_ALL_MAP ; + } + + switch ( work->think1 ) + { + case 0: + work->unknown.field_1E = 1; + s07a_meryl_unk_800DF038( work ); + break; + case 1: + work->unknown.field_1E = 1; + s07a_meryl_unk_800DF17C( work ); + break; + case 2: + work->unknown.field_1E = 1; + s07a_meryl_unk_800DF234( work ); + break; + case 3: + work->unknown.field_1E = 1; + s07a_meryl_unk_800DC310( work ); + break; + } +} diff --git a/components/mgs/mgs/source/enemy/object.c b/components/mgs/mgs/source/enemy/object.c new file mode 100644 index 00000000..d85d6ba0 --- /dev/null +++ b/components/mgs/mgs/source/enemy/object.c @@ -0,0 +1,582 @@ +#include "object.h" + +#include +#include "common.h" +#include "mts/mts.h" // for fprintf +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "anime/animconv/anime.h" + +extern int GM_GameFlag; + +/*---------------------------------------------------------------------------*/ +// NOTE: This is apparently the scrapped forklift actor that would roam around +// the Heliport in early versions of the game, as seen in the E3 & TGS 1997 +// trailers ( https://www.youtube.com/watch?v=x3Y5TS6_NxQ&t=247 ). + +#define EXEC_LEVEL GV_ACTOR_PREV + +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE \ + | DG_FLAG_GBOUND | DG_FLAG_AMBIENT | DG_FLAG_IRTEXTURE ) + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + OBJECT field_9C; + TARGET *field_180; + MATRIX light; + int field_1A4; + int field_1A8; + int field_1AC; + int field_1B0; + int field_1B4; + int field_1B8; + int field_1BC; + int field_1C0; + int field_1C4; + int field_1C8; + int field_1CC; + int field_1D0; + int field_1D4; + int field_1D8; + int field_1DC; + int field_1E0; + int field_1E4; + DVECTOR field_1E8[16]; + short field_228[2]; + int field_22C; + int field_230; + int field_234; + int field_238; + int field_23C; + int field_240; + int field_244; + int field_248; + int field_24C; + int field_250; + int field_254; + int field_258; + int field_25C; + int field_260; + int field_264; + int field_268; + int field_26C; + int field_270; + DVECTOR field_274; + int field_278; + short field_27C; + short field_27E; + int field_280; + short field_284; + short field_286; + int field_288; + int field_28C; +} Work; + +/*---------------------------------------------------------------------------*/ + +SVECTOR object_svec1_800C3CAC = {1000, 2000, 1500, 0}; +SVECTOR object_svec2_800C3CB4 = {1500, 2000, 1500, 0}; + +static int s01a_object_800D98B0(Work *work, int threshold) +{ + SVECTOR svec; + int vx, vz; + + vx = work->field_274.vx - work->control.mov.vx; + vz = work->field_274.vy - work->control.mov.vz; + if (vx <= -threshold || vx >= threshold || vz <= -threshold || vz >= threshold) + { + svec.vx = vx; + svec.vz = vz; + return GV_VecDir2(&svec); + } + return -1; +} + +static int s01a_object_800D991C(char *opt, DVECTOR *out) +{ + int count; + char *param; + + count = 0; + + while ((param = GCL_NextStr()) != NULL) + { + out->vx = GCL_StrToInt(param); + out->vy = GCL_StrToInt(GCL_NextStr()); + out++; + + count++; + } + + return count; +} + +static int s01a_object_800D9984(char *opt, short *params) +{ + int count; + unsigned char *param; + + for (count = 0; (param = GCL_NextStr()) != NULL; params++, count++) + { + *params = GCL_StrToInt(param); + } + return count; +} + +static void s01a_object_800D99DC(Work *work) +{ + SVECTOR svec; + + if (work->field_264 >= 0) + { + work->control.turn.vy = work->field_264; + } + if (work->field_284 >= 0) + { + GV_DirVec2(work->field_284, work->field_268, &svec); + work->control.step.vx = svec.vx; + work->control.step.vz = svec.vz; + } + else + { + work->control.step.vx = 0; + work->control.step.vz = 0; + } + + work->control.height = 20; + work->control.step.vy -= 16; + if (work->control.step.vy < 0 && work->control.grounded != 0) + { + work->control.step.vy = 0; + } +} + +static void s01a_object_800D9A88(Work *work) +{ + SVECTOR diff; + int index; + int yaw; + + index = work->field_26C + 1; + if (index >= work->field_1E4) + { + index = 0; + } + work->field_26C = index; + + work->field_274 = work->field_1E8[index]; + + if (work->field_228[index] == 1) + { + diff.vx = work->control.mov.vx - work->field_274.vx; + diff.vy = 0; + diff.vz = work->control.mov.vz - work->field_274.vy; + + yaw = GV_VecDir2(&diff); + GV_DirVec2(yaw, 500, &diff); + + work->field_274.vx += diff.vx; + work->field_274.vy += diff.vz; + } + else if (work->field_228[index] == 2) + { + diff.vx = work->field_274.vx - work->control.mov.vx; + diff.vy = 0; + diff.vz = work->field_274.vy - work->control.mov.vz; + + yaw = GV_VecDir2(&diff); + GV_DirVec2(yaw, 1250, &diff); + + work->field_274.vx += diff.vx; + work->field_274.vy += diff.vz; + } +} + +static int s01a_object_800D9BA0(Work *work) +{ + int field_284; + + if (work->field_260 == 0) + { + s01a_object_800D9A88(work); + } + + field_284 = s01a_object_800D98B0(work, 32); + if (field_284 < 0) + { + return 1; + } + + work->field_284 = field_284; + work->field_268 = 32; + work->field_260++; + return 0; +} + +static int s01a_object_800D9C10(Work *work) +{ + int field_284; + + field_284 = -1; + work->field_268 = 0; + if (work->field_260 < 4) + { + if (work->field_260 & 1) + { + field_284 = work->field_264; + work->field_268 = 32; + } + else + { + field_284 = work->field_264 + 2048; + if (field_284 > 4096) + { + field_284 = work->field_264 - 2048; + } + work->field_268 = 32; + } + } + else if (work->field_260 >= 32) + { + return 1; + } + work->field_284 = field_284; + work->field_260++; + return 0; +} + +static int s01a_object_800D9C8C(Work *work) +{ + SVECTOR diff; + int yaw; + int index; + + yaw = -1; + + work->field_284 = -1; + work->field_268 = 0; + + index = work->field_26C + 1; + if (index >= work->field_1E4) + { + index = 0; + } + + diff.vx = work->field_1E8[index].vx - work->control.mov.vx; + diff.vy = 0; + diff.vz = work->field_1E8[index].vy - work->control.mov.vz; + + yaw = GV_VecDir2(&diff); + if (GV_DiffDirAbs(work->field_264, yaw) < 16) + { + return 1; + } + + if (GV_DiffDirS(work->field_264, yaw) > 0) + { + work->field_284 = work->field_264 + 16; + if (work->field_284 >= 4096) + { + work->field_284 -= 4096; + } + } + else + { + work->field_284 = work->field_264 - 16; + if (work->field_284 < 0) + { + work->field_284 += 4096; + } + } + + work->field_268 = 32; + work->field_264 = work->field_284; + work->field_260++; + + return 0; +} + +static int s01a_object_800D9DAC(Work *work) +{ + return --work->field_260 < 1; +} + +static int s01a_object_800D9DC4(Work *work) +{ + switch (work->field_25C) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + if (s01a_object_800D9BA0(work)) + { + return 1; + } + default: + work->field_260++; + return 0; + } +} + +static int s01a_object_800D9E20(Work *work) +{ + switch (work->field_25C) + { + case 0: + if (s01a_object_800D9C10(work)) + { + work->field_25C = 5; + } + break; + + case 1: + if (s01a_object_800D9C8C(work)) + { + return 1; + } + break; + + case 2: + if (s01a_object_800D9C10(work)) + { + work->field_25C = 1; + work->field_260 = 0; + } + break; + + case 3: + work->field_25C = 6; + work->field_260 = 12; + break; + + case 4: + work->field_25C = 6; + work->field_260 = 32; + break; + + case 5: + work->field_25C = 6; + work->field_260 = 120; + break; + + case 6: + if (s01a_object_800D9DAC(work)) + { + return 1; + } + break; + } + + return 0; +} + +static void s01a_object_800D9F10(Work *work) +{ + switch (work->field_258) + { + case 0: + if (s01a_object_800D9DC4(work)) + { + work->field_258 = 1; + work->field_25C = work->field_228[work->field_26C]; + work->field_260 = 0; + } + break; + + case 1: + if (s01a_object_800D9E20(work)) + { + work->field_258 = 0; + work->field_25C = work->field_228[work->field_26C]; + work->field_260 = 0; + } + break; + } +} + +static void s01a_object_800D9F9C(Work *work) +{ + int field_254; + + field_254 = work->field_254; + work->field_284 = -1; + work->field_27C = 0; + work->field_27E = 0; + if (field_254 == 0) + { + s01a_object_800D9F10(work); + } + else if (field_254 == 1) + { + s01a_object_800D9F10(work); + } +} + +static void Act(Work *work) +{ + TARGET *target; + + s01a_object_800D9F9C(work); + s01a_object_800D99DC(work); + if (work->field_28C == 1 && GM_GameFlag & 1) + { + work->control.mov.vy = 10000; + work->control.step.vy = 0; + } + GM_ActControl(&work->control); + GM_ActObject2(&work->field_9C); + DG_GetLightMatrix2(&work->control.mov, &work->light); + + target = work->field_180; + if (target->damaged & TARGET_PUSH) + { + target->damaged &= ~TARGET_PUSH; + } + GM_MoveTarget(target, &work->control.mov); +} + +static void s01a_object_800DA08C(Work *work) +{ + TARGET *target; + + work->field_180 = target = GM_AllocTarget(); + if (work->field_28C == 1) + { + GM_SetTarget(target, 0x18, 0, &object_svec2_800C3CB4); + } + else + { + GM_SetTarget(target, 0x18, 0, &object_svec1_800C3CAC); + } + + target->flag = 1; +} + +static int s01a_object_800DA108(Work *work, int unused, int unused2) +{ + char *opt; + + s01a_object_800DA08C(work); + + opt = GCL_GetOption('r'); + if (opt == NULL) + { + printf("OBJECT:no ROOT\n"); + } + + work->field_1E4 = s01a_object_800D991C(opt, work->field_1E8); + + work->control.mov.vx = work->field_1E8[0].vx; + work->control.mov.vy = 4000; + work->control.mov.vz = work->field_1E8[0].vy; + + opt = GCL_GetOption('x'); + if (opt == NULL) + { + printf("OBJECT:no ACTION\n"); + } + + if (work->field_1E4 != s01a_object_800D9984(opt, work->field_228)) + { + printf("node action set err!!\n"); + } + + work->field_254 = 0; + work->field_258 = 0; + work->field_260 = 0; + work->field_268 = 0; + work->field_26C = 0; + work->field_270 = -1; + work->field_264 = work->control.rot.vy; + work->field_25C = work->field_228[0]; + + return 0; +} + +static int GetResources(Work *work, int arg1) +{ + CONTROL *ctrl; + OBJECT *obj; + int type; + char *name; + + ctrl = &work->control; + if (GM_InitControl(ctrl, 0, arg1) < 0) + { + return -1; + } + + GM_ConfigControlString(ctrl, GCL_GetOption('p'), GCL_GetOption('d')); + GM_ConfigControlHazard(ctrl, -1, -1, -1); + work->control.step = DG_ZeroVector; + + fprintf(0, "rot.vx=%d, rot.vy=%d, rot.vz=%d \n", ctrl->rot.vx, ctrl->rot.vy, ctrl->rot.vz); + + type = 0x41; + + obj = &work->field_9C; + name = GCL_GetOption('n'); + + if (name != NULL) + { + type = GCL_StrToInt(name); + } + + if ((type & 0xFF) == 0x41) + { + name = "rift"; + work->field_28C = 0; + } + else if ((type & 0xFF) == 0x4B) + { + name = "m13_crane"; + work->field_28C = 2; + } + else + { + name = "rift"; + work->field_28C = 1; + } + + GM_InitObject(obj, GV_StrCode(name), BODY_FLAG, 0); + GM_ConfigObjectJoint(obj); + GM_ConfigObjectLight(obj, &work->light); + GM_ConfigObjectStep(obj, &work->control.step); + + return 0; +} + +static void Die(Work *work) +{ + GM_FreeControl(&work->control); + GM_FreeObject(&work->field_9C); + GM_FreeTarget(work->field_180); +} + +/*---------------------------------------------------------------------------*/ + +void *NewMovingObject(int name, int where, int argc, char **argv) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "object.c"); + if (GetResources(work, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + s01a_object_800DA108(work, name, where); + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/object.h b/components/mgs/mgs/source/enemy/object.h new file mode 100644 index 00000000..493766db --- /dev/null +++ b/components/mgs/mgs/source/enemy/object.h @@ -0,0 +1,6 @@ +#ifndef __MGS_ENEMY_OBJECT_H__ +#define __MGS_ENEMY_OBJECT_H__ + +void *NewMovingObject(int name, int where, int argc, char **argv); + +#endif // __MGS_ENEMY_OBJECT_H__ diff --git a/components/mgs/mgs/source/enemy/searchli.c b/components/mgs/mgs/source/enemy/searchli.c new file mode 100644 index 00000000..52d64a53 --- /dev/null +++ b/components/mgs/mgs/source/enemy/searchli.c @@ -0,0 +1,1154 @@ +#include "searchli.h" + +#include +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "anime/animconv/anime.h" +#include "game/game.h" +#include "linkvar.h" +#include "strcode.h" + +#include "enemy/enemy.h" +#include "takabe/lit_mdl.h" // for s01a_lit_mdl_800E2C88 + +// Strange +typedef struct _SearchlightSub +{ + SVECTOR fFC; + SVECTOR f104; + SVECTOR f10C; + SVECTOR f114; + SVECTOR f11C; + SVECTOR pad2[36]; // 124 + SVECTOR f244; + char pad3[0x8]; // 24C + DG_PRIM *prim; // 254 + DG_TEX *tex; +} SearchlightSub; + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + char pad[0x60]; // 9C + SearchlightSub fFC; + char pad4[0x4]; + SVECTOR f260; + short f268; + short f26A; + short height; + short f26E; + short angle; + short f272; + short f274; + short f276; + short f278; + short f27A; + short f27C; + short f27E; + int debug; + SVECTOR f284; + int f28C; + int f290; + int f294; + char pad6[0x4]; + int f29C; + int f2A0; + int f2A4; + int f2A8; + int f2AC; + int f2B0; + int f2B4; + char pad8[0x8]; + GV_ACT *lit_mdl; + MATRIX lit_mtx; +} Work; + +extern CONTROL *GM_WhereList[96]; +extern int COM_VibTime; + +extern ENEMY_COMMAND EnemyCommand; +extern TOPCOMMAND_STRUCT TOPCOMMAND; + +const SVECTOR s01a_svec_800E4660 = {0, 0, 65024}; + +const char aSearchliUDRotXLRRotY[] = "UD:ROT.X LR:ROT.Y\n"; +const char aSearchliUDPosXLRPosZ[] = "UD:POS.X LR:POS.Z\n"; +const char aSearchliUDPosYLRAngle[] = "UD:POS.Y LR:ANGLE\n"; +const char aSearchliRot7D7D7D[] = "ROT:%7d %7d %7d\n"; +const char aSearchliPos7D7D7D[] = "POS:%7d %7d %7d\n"; +const char aSearchliAngle4D[] = "ANGLE:%4d \n"; + +const SVECTOR SearchliCenter_800E46D8 = {0, 0, 3000, 0}; + +void s00a_command_800CEC40(SVECTOR *, int); +void s01a_object_800D9424(CONTROL *, int); +void s01a_800E2364(MATRIX *, SVECTOR *, VECTOR *); +void s01a_lit_mdl_800E2D3C(GV_ACT *, int angle); + +#define EXEC_LEVEL GV_ACTOR_PREV + +void s01a_searchli_800D7320(DG_PRIM *prim, DG_TEX *tex, int r, int g, int b) +{ + POLY_FT4 *poly; + + poly = prim->packs[0]; + setRGB0(poly, r, g, b); + + poly = prim->packs[1]; + setRGB0(poly, r, g, b); +} + +void s01a_searchli_800D734C(SVECTOR *from, SVECTOR *to, SVECTOR *out) +{ + SVECTOR diff; + int dy; + + GV_SubVec3(to, from, &diff); + out->vy = ratan2(diff.vx, diff.vz) & 0xFFF; + + dy = diff.vy; + diff.vy = 0; + + out->vx = (ratan2(GV_VecLen3(&diff), dy) & 0xFFF) - 1024; + if (out->vx < 0) + { + out->vx += 4096; + } +} + +int s01a_searchli_800D73D8(Work *work) +{ + if (!(work->control.map->index & GM_PlayerMap) || (work->f2A4 == 0)) + { + work->f290 = 0; + return 0; + } + + if ((TOPCOMMAND.mode != 1) && (GM_PlayerStatus & PLAYER_CB_BOX)) + { + if (work->f290 == 0) + { + work->f284 = GM_PlayerPosition; + work->f28C = GM_WhereList[0]->rot.vy; + work->f290 = 1; + return 0; + } + + if ((GV_DiffVec3(&work->f284, &GM_PlayerPosition) < 50) && + (work->f28C == GM_WhereList[0]->rot.vy)) + { + work->f290 = 1; + return 0; + } + } + + work->f290 = 1; + return 1; +} + +void s01a_searchli_800D7500(SVECTOR *in, SVECTOR *out, Work *work) +{ + int diff; + + if (out->vx > work->f276) + { + out->vx = work->f276; + } + else if (out->vx < work->f274) + { + out->vx = work->f274; + } + + diff = GV_DiffDirS(in->vy, out->vy); + if (diff > work->f278) + { + out->vy = (in->vy + work->f278) & 0xFFF; + } + else if (diff < -work->f278) + { + out->vy = (in->vy - work->f278) & 0xFFF; + } +} + +void s01a_searchli_800D75C0(Work *work) +{ + SVECTOR player; + + player = GM_PlayerPosition; + player.vy = work->height; + + s01a_searchli_800D734C(&work->control.mov, &player, &work->control.turn); + s01a_searchli_800D7500(&work->f260, &work->control.turn, work); +} + +int s01a_searchli_800D763C(Work *work) +{ + SVECTOR player; + SVECTOR *pos; + SVECTOR *rot; + + pos = &work->control.mov; + rot = &work->control.turn; + + player = GM_PlayerPosition; + player.vy = work->height; + + s01a_searchli_800D734C(pos, &player, rot); + s01a_searchli_800D7500(&work->f260, rot, work); + + if (work->f2A0 == 0) + { + s00a_command_800CEC40(pos, 64); + COM_VibTime = 10; + } + + if (work->f2A0 < 0 || work->f2A0 > 14) + { + return 1; + } + + work->f2A0++; + return 0; +} + +int s01a_searchli_800D770C(Work *work) +{ + SVECTOR *rot; + + rot = &work->control.turn; + if (work->f2A0 < 0 || work->f2A0 > 60) + { + rot->vy = (rot->vy + work->f27A) & 0xFFF; + if (GV_DiffDirAbs(work->f260.vy, rot->vy) > work->f26A) + { + rot->vy = work->f260.vy + work->f26A; + return 1; + } + } + + work->f2A0++; + return 0; +} + +int s01a_searchli_800D77A4(Work *work) +{ + SVECTOR *rot; + + rot = &work->control.turn; + if (work->f2A0 < 0 || work->f2A0 > 60) + { + rot->vy = (rot->vy - work->f27A) & 0xFFF; + if (GV_DiffDirAbs(work->f260.vy, rot->vy) > work->f26A) + { + rot->vy = work->f260.vy - work->f26A; + return 1; + } + } + + work->f2A0++; + return 0; +} + +void Searchli_800D783C(Work *work) +{ + SVECTOR *rot; + int rnd, vx, vy; + + if (!(work->f2A0 & 0x3F)) + { + work->f2A0 += GV_RandU(16); + + work->control.interp = work->f27C; + + rot = &work->control.turn; + + rnd = rand(); + vy = work->f260.vy - work->f278 + (rnd * work->f278 * 2 >> 15); + rot->vy = vy & 0xFFF; + + rot->vx = vx = rand() * (vx = work->f276) >> 15; + s01a_searchli_800D7500(&work->f260, rot, work); + } + + work->f2A0++; +} + +int Searchli_800D7908(Work *work) +{ + SVECTOR *from; + SVECTOR *to; + int diff; + + from = &work->f260; + to = &work->control.turn; + + diff = GV_DiffDirS(from->vy, to->vy); + if (diff < -10) + { + to->vy += 8; + } + else if (diff < 10) + { + to->vy = from->vy; + } + else + { + to->vy -= 8; + } + to->vy &= 0xFFF; + + diff = GV_DiffDirS(from->vx, to->vx); + if (diff < -10) + { + to->vx += 8; + } + else if (diff < 10) + { + to->vx = from->vx; + } + else + { + to->vx -= 8; + } + to->vx &= 0xFFF; + + if (to->vx == from->vx && to->vy == from->vy && (work->f2A0 < 0 || work->f2A0 > 96)) + { + return 1; + } + + work->f2A0++; + return 0; +} + +void Searchli_800D7A4C(Work *work) +{ + switch (work->f29C) + { + case 2: + if (s01a_searchli_800D770C(work)) + { + work->f29C = 3; + work->f2A0 = 0; + } + + s01a_searchli_800D7500(&work->control.turn, &work->control.turn, work); + + if (s01a_searchli_800D73D8(work)) + { + work->f29C = 1; + work->f2A0 = 0; + } + break; + + case 3: + if (s01a_searchli_800D77A4(work)) + { + work->f29C = 2; + work->f2A0 = 0; + } + + s01a_searchli_800D7500(&work->control.turn, &work->control.turn, work); + + if (s01a_searchli_800D73D8(work)) + { + work->f29C = 1; + work->f2A0 = 0; + } + break; + + case 4: + s01a_searchli_800D75C0(work); + break; + + case 1: + if (s01a_searchli_800D763C(work)) + { + ENE_SetTopCommAL(255); + ENE_SetGopointLast(); + work->f294 = 1; + work->f29C = 4; + work->f2A0 = 0; + } + break; + } + + if (TOPCOMMAND.mode == 1) + { + work->f294 = TOPCOMMAND.mode; + + do {} while (0); + + if (work->f294 == EnemyCommand.field_0x40) + { + work->f29C = 4; + } + else + { + work->f29C = 0; + } + + work->f2A0 = 0; + } +} + +void Searchli_800D7BB8(Work *work) +{ + switch (work->f29C) + { + case 4: + s01a_searchli_800D75C0(work); + + if (s01a_searchli_800D73D8(work)) + { + ENE_SetTopCommAL(255); + ENE_SetGopointLast(); + } + break; + + case 0: + if (s01a_searchli_800D73D8(work)) + { + work->f29C = 4; + } + break; + } + + if (TOPCOMMAND.mode != 1) + { + work->f294 = 2; + work->f29C = 5; + work->f2A0 = 0; + } +} + +void Searchli_800D7C58(Work *work) +{ + switch (work->f29C) + { + case 5: + Searchli_800D783C(work); + + if (TOPCOMMAND.mode == 0) + { + work->f29C = 6; + work->f2A0 = 0; + } + break; + + case 6: + if (Searchli_800D7908(work)) + { + work->f294 = 0; + work->f29C = 2; + work->f2A0 = 0; + } + break; + } + + if (s01a_searchli_800D73D8(work)) + { + ENE_SetTopCommAL(255); + ENE_SetGopointLast(); + s00a_command_800CEC40(&work->control.mov, 64); + COM_VibTime = 10; + work->f294 = 1; + work->f29C = 4; + work->f2A0 = 0; + } + else if (TOPCOMMAND.mode == 1) + { + work->f294 = 1; + work->f29C = 4; + work->f2A0 = 0; + } +} + +void Searchli_800D7D40(Work *work) +{ + switch (work->f294) + { + case 0: + Searchli_800D7A4C(work); + break; + + case 1: + Searchli_800D7BB8(work); + break; + + case 2: + Searchli_800D7C58(work); + break; + } +} + +// clang-format off +// $13 replaced with $14 in gte_ld_intpol_sv0_xz +#ifndef __psyz +#define gte_ld_intpol_sv0_xz( r0 ) __asm__ volatile ( \ + "lh $12, 0( %0 );" \ + "lh $14, 4( %0 );" \ + "ctc2 $12, $21;" \ + "ctc2 $14, $23" \ + : \ + : "r"( r0 ) \ + : "$12", "$14" ) +#endif /* __psyz: psyz provides a portable form */ + + +#ifndef __psyz +#define gte_ld_intpol_sv1_xz( r0 ) __asm__ volatile ( \ + "lhu $12, 0( %0 );" \ + "lhu $13, 4( %0 );" \ + "mtc2 $12, $9;" \ + "mtc2 $13, $11" \ + : \ + : "r"( r0 ) \ + : "$12", "$13" ) +#endif /* __psyz: psyz provides a portable form */ + + +#ifndef __psyz +#define gte_stsv_xz( r0 ) __asm__ volatile ( \ + "mfc2 $12, $9;" \ + "mfc2 $14, $11;" \ + "sh $12, 0( %0 );" \ + "sh $0, 2( %0 );" \ + "sh $14, 4( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14", "memory" ) +#endif /* __psyz: psyz provides a portable form */ + +// clang-format on + +void Searchli_800D7DBC(SVECTOR *in, SVECTOR *out, int count) +{ + SVECTOR *scratch; + SVECTOR *var_a0; + SVECTOR *var_t0; + int temp_lo; + int temp_t7; + int i; + int j; + int total; + int num; + int temp; + int one; + + temp_t7 = count + 1; + temp_lo = temp_t7 * temp_t7; + scratch = (SVECTOR *)SCRPAD_ADDR; + + total = 4096 / count; + + scratch[0] = in[0]; + scratch[count] = in[1]; + scratch[temp_lo - temp_t7] = in[2]; + one = 1; + scratch[temp_lo - one] = in[3]; + + var_t0 = scratch; + + for (i = 2; i > 0; i--) + { + var_a0 = var_t0 + (temp_t7 - 1); + gte_ld_intpol_sv0_xz(var_a0); + + var_a0 = var_t0; + num = total; + for (j = count - 1; j > 0; j--) + { + gte_ld_intpol_sv1_xz(var_t0); + gte_lddp(num); + + num += total; + var_a0++; + + gte_intpl_b(); + gte_stsv_xz(var_a0); + } + + var_t0 = scratch + (temp_lo - temp_t7); + } + + var_t0 = scratch; + for (i = temp_t7; i > 0; i--) + { + var_a0 = var_t0 + (temp_lo - temp_t7); + gte_ld_intpol_sv0_xz(var_a0); + + var_a0 = var_t0; + + num = total; + for (j = count - 1; j > 0; j--) + { + gte_ld_intpol_sv1_xz(var_t0); + gte_lddp(num); + + num += total; + var_a0 += temp_t7; + + gte_intpl_b(); + gte_stsv_xz(var_a0); + } + + var_t0++; + } + + temp = temp_t7 + 1; + var_t0 = scratch; + for (i = count; i > 0; i--) + { + for (j = count; j > 0; j--) + { + out[0] = var_t0[0]; + out[1] = var_t0[1]; + out[2] = var_t0[temp_t7]; + out[3] = var_t0[temp]; + out += 4; + var_t0++; + } + + var_t0++; + } +} + +void Searchli_800D80AC(SVECTOR *vec, int x, int y, int z) +{ + vec->vx = x; + vec->vy = y; + vec->vz = z; +} + +void Searchli_800D80BC(Work *work) +{ + SVECTOR sp18; + VECTOR sp20; + VECTOR sp30; + SVECTOR pos; + SVECTOR rot; + + SearchlightSub *sp50; + + SVECTOR *temp_fp; + SVECTOR *temp_s0_8; + SVECTOR *temp_t0; + int var_s4; + int temp_a1; + int dx, dy, dz; + int dx2, dy2, dz2; // probably inline related + int temp_s0_2; + int temp_s0_7; + int temp_s1_2; + int temp_s2_2; + int temp_s6; + int shade; + int var_s5; + int target; + + sp50 = &work->fFC; + temp_fp = &work->fFC.f244; + temp_t0 = &sp50->fFC; + + if (GM_WhereList[0]->name != SNAKE_NAME) + { + return; + } + var_s4 = GM_PlayerBody->objs->objs[6].world.t[1] - work->height; + + if (var_s4 < 300) + { + var_s4 = 10; + } + else if (var_s4 > 1000) + { + var_s4 += 300; + } + + if ((work->f2B4 != 0) || (var_s4 < 0)) + { + if (work->f2A8 < (work->f272 / 4)) + { + shade = 78; + } + else + { + shade = ((work->f2A8 - (work->f272 / 4)) * 78) / (work->f272 / 4); + } + + s01a_searchli_800D7320(work->fFC.prim, work->fFC.tex, shade, shade, shade); + + DG_VisiblePrim(work->fFC.prim); + dword_800ABA1C |= 1 << (work->f26E + 1); + } + else + { + DG_InvisiblePrim(work->fFC.prim); + dword_800ABA1C &= ~(1 << (work->f26E + 1)); + return; + } + + dx = GM_PlayerPosition.vx - work->control.mov.vx; + dy = GM_PlayerBody->objs->objs[6].world.t[1] - work->control.mov.vy; + dz = GM_PlayerPosition.vz - work->control.mov.vz; + + target = 1024; + + temp_s0_2 = ratan2(dy, SquareRoot0(dx * dx + dz * dz)) + target; + + if ((temp_s0_2 & 0x7FF) != target) + { + var_s5 = (var_s4 * rsin(temp_s0_2)) / rcos(temp_s0_2); + } + else + { + var_s5 = var_s4 * rsin(temp_s0_2); + } + + temp_t0->vy = ratan2(dx, dz); + + sp18 = GM_PlayerPosition; + sp18.vy = GM_PlayerBody->objs->objs[6].world.t[1]; + + s01a_800E2364(&work->lit_mtx, &sp18, &sp20); + + sp20.vx >>= 2; + sp20.vy >>= 2; + sp20.vz >>= 2; + + Square0(&sp20, &sp30); + + temp_s2_2 = ratan2(SquareRoot0(sp30.vx + sp30.vy), sp20.vz) & 0xFFF; + if (temp_s2_2 > (work->f272 / 2)) + { + temp_s6 = (rsin(work->f272 / 2) * 4096) / rcos(work->f272 / 2); + temp_s1_2 = (rsin(work->f2A8) * 4096) / rcos(work->f2A8); + temp_s0_7 = (rsin(temp_s2_2) * 4096) / rcos(temp_s2_2) - temp_s1_2; + + if (temp_s0_7 != 0) + { + var_s5 = (var_s5 * (temp_s6 - temp_s1_2)) / temp_s0_7; + } + else + { + var_s5 = 0; + } + } + + temp_a1 = -200 - var_s5; + temp_s0_8 = &sp50->f104; + + Searchli_800D80AC(temp_s0_8, temp_a1, 0, temp_a1); + + *temp_fp = GM_PlayerPosition; + temp_fp->vy = var_s4; + + temp_t0->vy = (temp_t0->vy + 1536) & 0xFFF; + temp_fp->vy = work->height + 10; + + Searchli_800D7DBC(temp_s0_8, sp50->pad2, 3); + + DG_SetPos2(temp_fp, temp_t0); + DG_PutPrim(sp50->prim); + + pos = s01a_svec_800E4660; + + dx2 = GM_PlayerPosition.vx - work->control.mov.vx; + dy2 = GM_PlayerPosition.vy - work->control.mov.vy; + dz2 = GM_PlayerPosition.vz - work->control.mov.vz; + + rot.vx = ratan2(dy2, SquareRoot0(dx2 * dx2 + dz2 * dz2)) + 1024; + rot.vy = work->control.rot.vy; + rot.vz = 0; + + DG_SetPos2(&GM_PlayerControl->mov, &rot); + DG_PutVector(&pos, &pos, 1); + DG_SetTmpLight(&pos, 1024, 2000); +} + +void SearchlightAct_800D86F0(Work *work) +{ + SVECTOR pos; + VECTOR sp18; + VECTOR sp28; + SVECTOR center; + int f2A8; + int status; + + GM_ActControl(&work->control); + + DG_SetPos2(&work->control.mov, &work->control.rot); + ReadRotMatrix(&work->lit_mtx); + + if (GM_CurrentItemId == IT_Stealth) + { + work->f2A4 = 0; + work->f2B4 = 0; + } + else + { + // Snake detected by searchlight logic + pos.vx = GM_PlayerPosition.vx; + pos.vz = GM_PlayerPosition.vz; + pos.vy = work->height; + + s01a_800E2364(&work->lit_mtx, &pos, &sp18); + + sp18.vx >>= 2; + sp18.vy >>= 2; + sp18.vz >>= 2; + + Square0(&sp18, &sp28); + + f2A8 = ratan2(SquareRoot0(sp28.vx + sp28.vy), sp18.vz) & 0xFFF; + work->f2A8 = f2A8; + work->f2AC = SquareRoot0(sp28.vx + sp28.vy + sp28.vz); + + if ((work->angle / 2) < f2A8) + { + work->f2A4 = 0; + } + else + { + work->f2A4 = 1; + } + + if (f2A8 < (work->f272 / 2)) + { + work->f2B4 = 1; + } + else + { + work->f2B4 = 0; + } + } + + if ((work->debug != 0) && (GV_PadData[1].status & PAD_SQUARE)) + { + status = GV_PadData[1].status; + + if ((GV_PadData[1].press & PAD_TRIANGLE) && (++work->debug >= 4)) + { + work->debug = 1; + } + + MENU_Locate(8, 180, 0); + + switch (work->debug - 1) + { + case 0: + MENU_Printf(aSearchliUDRotXLRRotY); + + if (status & PAD_UP) + { + work->control.turn.vx -= 5; + } + + if (status & PAD_DOWN) + { + work->control.turn.vx += 5; + } + + if (status & PAD_LEFT) + { + work->control.turn.vy += 5; + } + + if (status & PAD_RIGHT) + { + work->control.turn.vy -= 5; + } + break; + + case 1: + MENU_Printf(aSearchliUDPosXLRPosZ); + + if (status & PAD_UP) + { + work->control.mov.vz -= 20; + } + + if (status & PAD_DOWN) + { + work->control.mov.vz += 20; + } + + if (status & PAD_LEFT) + { + work->control.mov.vx -= 20; + } + + if (status & PAD_RIGHT) + { + work->control.mov.vx += 20; + } + break; + + case 2: + MENU_Printf(aSearchliUDPosYLRAngle); + + if (status & PAD_UP) + { + work->control.mov.vy += 20; + } + + if (status & PAD_DOWN) + { + work->control.mov.vy -= 20; + } + + if (status & PAD_LEFT) + { + work->angle = GV_NearSpeed(work->angle, 0, 5); + } + + if (status & PAD_RIGHT) + { + work->angle = GV_NearSpeed(work->angle, 512, 5); + } + break; + } + + MENU_Printf(aSearchliRot7D7D7D, work->control.turn.vx, work->control.turn.vy, work->control.turn.vz); + MENU_Printf(aSearchliPos7D7D7D, work->control.mov.vx, work->control.mov.vy, work->control.mov.vz); + MENU_Printf(aSearchliAngle4D, work->angle); + + s01a_lit_mdl_800E2D3C(work->lit_mdl, work->angle); + + if (status & PAD_L2) + { + center = SearchliCenter_800E46D8; + DG_SetPos2(&work->control.mov, &work->control.turn); + DG_PutVector(¢er, ¢er, 1); + DG_LookAt(DG_Chanl(0), &work->control.mov, ¢er, 320); + } + else + { + s01a_object_800D9424(&work->control, 2); + } + + Searchli_800D80BC(work); + } + else + { + Searchli_800D80BC(work); + + if (work->debug == 0) + { + Searchli_800D7D40(work); + } + } +} + +int Searchlight_800D8B84(Work *work, int name, int map) +{ + int opt; + + opt = (int)GCL_GetOption('i'); + work->f26E = GCL_StrToInt(opt ? (char *)opt : "0"); + + opt = (int)GCL_GetOption('w'); + work->f268 = GCL_StrToInt(opt ? (char *)opt : "0"); + if (work->f268 > 4000) + { + work->f268 = 4000; + } + + opt = (int)GCL_GetOption('x'); + work->f26A = GCL_StrToInt(opt ? (char *)opt : "0"); + if (work->f26A > 1024) + { + work->f26A = 1024; + } + + opt = (int)GCL_GetOption('h'); + work->height = GCL_StrToInt(opt ? (char *)opt : "0"); + if (work->height > 30000) + { + work->height = 30000; + } + + work->f260 = work->control.rot; + if (work->f260.vx > 1000) + { + work->f260.vx = 1000; + } + + work->angle = 300; + if (GCL_GetOption('a')) + { + work->angle = GCL_StrToInt(GCL_NextStr()); + } + + if (GCL_GetOption('l')) + { + work->f272 = GCL_StrToInt(GCL_NextStr()); + } + + work->f272 += work->angle; + + if (GCL_GetOption('t')) + { + work->f274 = GCL_StrToInt(GCL_NextStr()); + work->f276 = GCL_StrToInt(GCL_NextStr()); + work->f26A = work->f278 = GCL_StrToInt(GCL_NextStr()); + work->f27A = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->f274 = 280; + work->f276 = 330; + work->f278 = 512; + work->f27A = 4; + } + + work->f27C = 48; + + if (GCL_GetOption('z')) + { + work->debug = GCL_StrToInt(GCL_NextStr()); + } + + work->f294 = 0; + work->f29C = 2; + work->f2A0 = 0; + work->f2B0 = 100000; + + return 0; +} + +void Searchli_800D8DDC(POLY_FT4 *packs, DG_TEX *tex, int abr, int r, int g, int b, int size) +{ + int xs, ys; + int x, y; + int u0, u1, v0, v1; + + xs = size; + ys = size; + + for (y = 0; y < ys; y++) + { + for (x = 0; x < xs; x++) + { + setPolyFT4(packs); + setRGB0(packs, r, g, b); + + if (abr < 4) + { + setSemiTrans(packs, 1); + + u0 = tex->off_x + ((tex->w + 1) * x) / xs; + u1 = tex->off_x + ((tex->w + 1) * (x + 1)) / xs - 1; + packs->u0 = packs->u2 = u0; + packs->u1 = packs->u3 = u1; + + v0 = tex->off_y + ((tex->h + 1) * y) / ys; + v1 = tex->off_y + ((tex->h + 1) * (y + 1)) / ys - 1; + packs->v0 = packs->v1 = v0; + packs->v2 = packs->v3 = v1; + + packs->tpage = tex->tpage; + packs->clut = tex->clut; + + packs->tpage = (packs->tpage & ~0x60) | (abr << 5); + } + else + { + DG_SetPacketTexture4(packs, tex); + } + + packs++; + } + } +} + +int Searchli_800D9040(Work *work) +{ + DG_PRIM *prim; + DG_TEX *tex; + + Searchli_800D80AC(&work->fFC.f244, 0, 0, 0); + Searchli_800D80AC(&work->fFC.fFC, 0, 0, 0); + Searchli_800D80AC(&work->fFC.f104, -200, 0, -200); + Searchli_800D80AC(&work->fFC.f10C, 200, 0, -200); + Searchli_800D80AC(&work->fFC.f114, -200, 0, 200); + Searchli_800D80AC(&work->fFC.f11C, 200, 0, 200); + + prim = GM_MakePrim(DG_PRIM_POLY_FT4, 9, work->fFC.pad2, NULL); + work->fFC.prim = prim; + if (prim == NULL) + { + return -1; + } + + prim->raise = 600; + + tex = DG_GetTexture(GV_StrCode("shadow")); + work->fFC.tex = tex; + if (tex == NULL) + { + return -1; + } + + Searchli_800D8DDC(prim->packs[0], tex, 2, 78, 78, 78, 3); + Searchli_800D8DDC(prim->packs[1], tex, 2, 78, 78, 78, 3); + return 0; +} + +int SearchlightGetResources_800D91B0(Work *work, int name, int map) +{ + CONTROL *control; + char *pos, *dir; + + control = &work->control; + + if (GM_InitControl(control, name, map) < 0) + { + return -1; + } + + pos = GCL_GetOption('p'); + dir = GCL_GetOption('d'); + + GM_ConfigControlString(control, pos, dir); + GM_ConfigControlAttribute(control, 0); + GM_ConfigControlHazard(control, -1, -2, -1); + work->control.step = DG_ZeroVector; + + Searchli_800D9040(work); + return 0; +} + +void SearchlightDie_800D9274(Work *work) +{ + GM_FreeControl(&work->control); + GM_FreePrim(work->fFC.prim); +} + +void *NewSearchlight(int name, int where, int argc, char **argv) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, SearchlightAct_800D86F0, SearchlightDie_800D9274, "searchli.c"); + + if (SearchlightGetResources_800D91B0(work, name, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + Searchlight_800D8B84(work, name, where); + + work->lit_mtx = DG_ZeroMatrix; + work->lit_mdl = s01a_lit_mdl_800E2C88(&work->lit_mtx, work->height, work->f272, 500); + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/searchli.h b/components/mgs/mgs/source/enemy/searchli.h new file mode 100644 index 00000000..81ec54c5 --- /dev/null +++ b/components/mgs/mgs/source/enemy/searchli.h @@ -0,0 +1,6 @@ +#ifndef __MGS_ENEMY_SEARCHLI_H__ +#define __MGS_ENEMY_SEARCHLI_H__ + +void *NewSearchlight(int name, int where, int argc, char **argv); + +#endif // __MGS_ENEMY_SEARCHLI_H__ diff --git a/components/mgs/mgs/source/enemy/smoke.c b/components/mgs/mgs/source/enemy/smoke.c new file mode 100644 index 00000000..a4507815 --- /dev/null +++ b/components/mgs/mgs/source/enemy/smoke.c @@ -0,0 +1,231 @@ +#include "smoke.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" + +typedef struct _Work +{ + GV_ACT actor; + DG_PRIM *field_20; + SVECTOR field_24; + DG_TEX *field_2C; + int field_30; + int field_34; + int field_38; + int field_3C; +} Work; + +#define EXEC_LEVEL GV_ACTOR_USER + +const char aSmoke[] = "smoke"; +const char aSmokeC[] = "smoke.c"; + +RECT smoke_rect = {500, 1000, 1000, 2000}; + +int s00a_smoke_800D2694(Work *work) +{ + int color; + POLY_FT4 *poly; + + if (work->field_3C > 128) + { + return 1; + } + + poly = work->field_20->packs[GV_Clock]; + color = work->field_3C / 2; + setRGB0(poly, color, color, color); + work->field_3C++; + return 0; +} + +int s00a_smoke_800D26F0(Work *work) +{ + int color; + POLY_FT4 *poly; + + if (work->field_3C > 128) + { + return 1; + } + + poly = work->field_20->packs[GV_Clock]; + color = 64 - work->field_3C / 2; + setRGB0(poly, color, color, color); + work->field_3C++; + return 0; +} + +void s00a_smoke_800D2758(POLY_FT4 *poly, DG_TEX *text, Work *work) +{ + short clut; + int x0, y0; + int width, height; + int field_30; + char offx, offy; + + if (++work->field_30 >= 59) + { + work->field_30 = 0; + } + + field_30 = work->field_30 / 2; + y0 = field_30 & 0xF; + x0 = field_30 / 8; + x0 = x0 & 0x3; + + width = text->w + 1; + offx = text->off_x; + + poly->u0 = poly->u2 = offx + width * y0 / 8; + poly->u1 = poly->u3 = offx + width * (y0 + 1) / 8 - 1; + + height = text->h + 1; + offy = text->off_y; + + poly->v0 = poly->v1 = offy + height * x0 / 4; + poly->v2 = poly->v3 = offy + height * (x0 + 1) / 4 - 1; + + poly->tpage = text->tpage; + clut = text->clut; + + poly->y1 += 100; + + poly->clut = clut; + poly->tpage = (poly->tpage & 0xFF9F) | 0x20; +} + +void s00a_smoke_800D2888(POLY_FT4 *prim, DG_TEX *unused, short color) +{ + DG_TEX *tex1, *tex2; + unsigned short tpage; + int width, height; + unsigned char offx, offy; + short clut; + + // We already have this exact texture in variable called "unused", but let's waste + // cycles and get it again just to be sure: + tex1 = DG_GetTexture(GV_StrCode(aSmoke)); + tex2 = tex1; // ...and make an additional copy to waste even more cycles + + setPolyFT4(prim); + setSemiTrans(prim, 1); + setRGB0(prim, color, color, color); + + width = tex1->w; + offx = tex1->off_x; + prim->u0 = prim->u2 = offx; + prim->u1 = prim->u3 = offx + (width + 1) / 8 - 1; + + height = tex1->h; + offy = tex1->off_y; + prim->v0 = prim->v1 = offy; + prim->v2 = prim->v3 = offy + (height + 1) / 4 - 1; + + tpage = tex2->tpage; + prim->tpage = tpage; + + clut = tex2->clut; + + prim->tpage = (tpage & 0xFF9F) | 0x20; + prim->clut = clut; +} + +void s00a_smoke_800D296C(Work *work) +{ + switch (work->field_34) + { + case 0: + if (work->field_3C > 100) + { + work->field_3C = 0; + work->field_34 = 1; + } + work->field_3C++; + break; + case 1: + if (s00a_smoke_800D26F0(work)) + { + work->field_3C = 0x1E; + work->field_38 = 2; + work->field_34 = 5; + } + break; + case 2: + if (s00a_smoke_800D2694(work)) + { + work->field_3C = 0; + work->field_34 = 0; + } + break; + case 5: + if (--work->field_3C <= 0) + { + work->field_3C = 0; + work->field_34 = work->field_38; + } + break; + } + s00a_smoke_800D2758(work->field_20->packs[GV_Clock], work->field_2C, work); +} + +void SmokeAct_800D2A80(Work *work) +{ + s00a_smoke_800D296C(work); +} + +void SmokeDie_800D2AA0(Work *work) +{ + GM_FreePrim(work->field_20); +} + +void s00a_smoke_800D2ADC(SVECTOR *svec) +{ + GCL_StrToSV(GCL_NextStr(), (short *)svec); +} + +int SmokeGetResources_800D2B0C(Work *work, int where) +{ + DG_PRIM *prim; + DG_TEX *tex; + + GM_CurrentMap = where; + prim = GM_MakePrim(DG_PRIM_RECTANGLE | DG_PRIM_POLY_FT4, 1, &work->field_24, &smoke_rect); + work->field_20 = prim; + if (prim != NULL) + { + tex = DG_GetTexture(GV_StrCode(aSmoke)); + work->field_2C = tex; + if (tex != 0) + { + s00a_smoke_800D2888(prim->packs[0], tex, 0x80); + s00a_smoke_800D2888(prim->packs[1], tex, 0x80); + work->field_30 = GV_RandU(0x1FU); + work->field_3C = 0; + work->field_34 = 0; + return 0; + } + } + return -1; +} + +void *NewSmoke(int name, int where, int argc, char **argv) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, SmokeAct_800D2A80, SmokeDie_800D2AA0, aSmokeC); + s00a_smoke_800D2ADC(&work->field_24); + if (SmokeGetResources_800D2B0C(work, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/smoke.h b/components/mgs/mgs/source/enemy/smoke.h new file mode 100644 index 00000000..029f2cf1 --- /dev/null +++ b/components/mgs/mgs/source/enemy/smoke.h @@ -0,0 +1,6 @@ +#ifndef __MGS_ENEMY_SMOKE_H__ +#define __MGS_ENEMY_SMOKE_H__ + +void *NewSmoke(int name, int where, int argc, char **argv); + +#endif // __MGS_ENEMY_SMOKE_H__ diff --git a/components/mgs/mgs/source/enemy/think.c b/components/mgs/mgs/source/enemy/think.c new file mode 100644 index 00000000..4ba48292 --- /dev/null +++ b/components/mgs/mgs/source/enemy/think.c @@ -0,0 +1,2877 @@ +#include "enemy.h" +#include "enemy/eyeflash.h" // for NewEyeflash + +#include +#include +#include +#include + +#include "common.h" +#include "mts/mts.h" // for fprintf +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libhzd/libhzd.h" +#include "game/game.h" + +#define SEGMENT_ATR ( HZD_SEG_NO_NAVIGATE ) + +extern ENEMY_COMMAND EnemyCommand; +extern unsigned int COM_GameStatus; +extern SVECTOR COM_PlayerPosition; +extern SVECTOR COM_PlayerPositionOne[8]; +extern int COM_PlayerMapOne[8]; +extern int COM_PlayerAddressOne[8]; +extern int COM_VibTime; +extern int COM_PlayerMap; +extern int COM_SHOOTRANGE; + +const char aErrerrerrnotlinkroutedtod_800E0690[] = " Err Err Err Not Link Route [%d] to [%d] !!!!\n"; +const char aErrnozoneidingcl_800E06C0[] = "!!!!!!Err No Zone ID In Gcl !!!!!!!!\n"; +const char aKirari_800E06E8[] = "kirari01"; +const char aBefmapnamed_800E06F4[] = "bef map name =%d \n"; +const char aAftmapnamed_800E0708[] = "aft map name =%d \n"; +const char aRootchange_800E071C[] = " Root Change !!\n"; + +unsigned short s00a_dword_800C351C[4] = { 100, 50, 25, 10 }; + +int s00a_dword_800C3524[32] = +{ + 0x00000000, 0x00000001, 0x00000002, 0x00000004, + 0x00002000, 0x00000040, 0x00000080, 0x00000200, + 0x00000400, 0x00000800, 0x00000100, 0x00800000, + 0x01000000, 0x04000000, 0x08000000, 0x00000000, + 0x00000000, 0x00000001, 0x00000002, 0x00000004, + 0x00002000, 0x00000040, 0x00000080, 0x00000200, + 0x00000400, 0x00000800, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x0000001F +}; + +// in enemy/asiato.c +extern void AsiatoPos( signed char, SVECTOR * ); +extern int NextAsiato( HZD_HDL *, signed char, SVECTOR * ); +extern void CleanAsiato(void); +extern char NearAsiato(void); + +int s00a_command_800CA898( WatcherWork* work, HZD_ZON* zone ) +{ + int i; + for ( i = 0 ; i < work->field_C34 ; i++ ) + { + if ( zone->padding == work->field_C35[i] ) return 1; + } + return 0; +} + +int s00a_command_800CA8E0( WatcherWork *work, int addr_in ) +{ + int temp, temp2; + int res; + int addr; + HZD_HDL *hzd; + SVECTOR svec; + HZD_ZON *zone; + CONTROL *ctrl; + int local_addr; + + ctrl = &work->control; + hzd = ctrl->map->hzd; + addr = HZD_GetAddress( hzd, &ctrl->mov, -1 ) & 0xFF; + zone = &hzd->def->zones[ addr ]; + svec.vx = zone->x; + svec.vy = zone->y; + svec.vz = zone->z; + + temp2 = addr_in & 0xFF; + local_addr = temp2 | (temp2 << 8); + + for ( ;; ) + { + temp = addr & 0xFF; + res = HZD_Navigate( hzd, temp | ( temp << 8 ), local_addr, &svec ); + if ( res == addr ) + { + printf( (char *)aErrerrerrnotlinkroutedtod_800E0690, addr, addr_in ); + return addr; + } + + zone = &hzd->def->zones[ res ]; + if ( s00a_command_800CA898( work, zone ) ) + { + return addr; + } + + svec.vx = zone->x; + svec.vy = zone->y; + svec.vz = zone->z; + addr = res; + } +} + +void s00a_command_800CAA2C( WatcherWork *work, int addr ) +{ + MAP *map; + HZD_ZON *zone; + + work->target_addr = (addr | addr << 8); + zone = &work->control.map->hzd->def->zones[ addr ]; + + work->target_pos.vx = zone->x; + work->target_pos.vy = zone->y; + work->target_pos.vz = zone->z; + + map = GM_FindMapZone( 1 << zone->padding ); + if ( map ) + { + work->target_map = map->index; + } + else + { + printf( (char *)aErrnozoneidingcl_800E06C0 ); + } +} + +void s00a_command_800CAACC( WatcherWork* work ) +{ + work->target_addr = work->start_addr; + work->target_pos = work->start_pos; + work->target_map = work->start_map; +} + +void s00a_command_800CAB04( WatcherWork* work ) +{ + work->target_addr = COM_PlayerAddressOne[ work->field_B78 ]; + work->target_pos = COM_PlayerPositionOne[ work->field_B78 ]; + work->target_map = COM_PlayerMapOne[ work->field_B78 ]; +} + +extern CONTROL *GM_WhereList[94]; + +void s00a_command_800CAB74( WatcherWork* work ) +{ + int addr; + MAP *map; + HZD_ZON *zone; + + addr = HZD_GetAddress( GM_WhereList[0]->map->hzd, &GM_NoisePosition, -1 ) & 0xFF; + zone = &GM_WhereList[0]->map->hzd->def->zones[ addr ]; + + if ( work->field_C34 && s00a_command_800CA898( work , zone ) ) + { + s00a_command_800CAA2C( work, s00a_command_800CA8E0( work, addr ) ); + return; + } + + addr &= 0xFF; + work->target_addr = addr | ( addr << 8 ); + work->target_pos.vx = zone->x; + work->target_pos.vy = zone->y; + work->target_pos.vz = zone->z; + + map = GM_FindMapZone( 1 << zone->padding ); + if ( map == NULL ) + { + printf( (char *)aErrnozoneidingcl_800E06C0 ); + work->target_map = COM_PlayerMapOne[ work->field_B78 ]; + } + else + { + work->target_map = map->index; + } +} + +int s00a_command_800CACA0( WatcherWork *work, int addr, int addr2 ) +{ + SVECTOR svec; + SVECTOR svec2; + HZD_HDL *hzd; + HZD_ZON *zone; + HZD_ZON *zone2; + + if ( work->param_blood != 0x51 ) + { + return 0; + } + + hzd = work->control.map->hzd; + + zone = &hzd->def->zones[ addr ]; + zone2 = &hzd->def->zones[ addr2 ]; + + if ( zone->y || zone2->y ) + { + return 0; + } + + svec.vx = zone->x; + svec.vy = zone->y + 500; + svec.vz = zone->z; + + svec2.vx = zone2->x; + svec2.vy = zone2->y + 500; + svec2.vz = zone2->z; + + return HZD_OnlineHazardCheck( hzd, &svec, &svec2, HZD_CHK_D_SEGMENT, SEGMENT_ATR ) != 0; +} + +int s00a_command_800CEA9C( int addr ); +void s00a_command_800CEA84( int a, int addr ); + +void s00a_command_800CAD84( WatcherWork *work ) +{ + int i; + int vx; + int res; + int addr; + MAP *map; + CONTROL *ctrl; + int addr_copy; + HZD_HDL *hzd; + HZD_ZON *zone; + HZD_ZON *zone2; + HZD_ZON *zone3; + int unk[5]; //? + + ctrl = &work->control; + hzd = work->control.map->hzd; + + addr = HZD_GetAddress( hzd, &ctrl->mov, -1 ) & 0xFF; + i = 0; + zone = &hzd->def->zones[ addr ]; + addr_copy = addr; + + res = HZD_NearZones( hzd, addr, unk ); + work->search_flag = 1; + + if ( res > 0 ) + { + vx = work->control.mov.vx % res; + + if ( vx < 0 ) + { + vx = -vx; + } + + for ( i = 0 ; i < res ; i++ ) + { + addr = unk[vx]; + zone2 = &work->control.map->hzd->def->zones[ addr ]; + + if ( !s00a_command_800CA898( work , zone2 ) ) + { + map = GM_FindMapZone( 1 << zone2->padding ); + if ( map ) + { + if ( map->used || zone->padding == zone2->padding ) + { + if ( !s00a_command_800CACA0( work, addr_copy, addr ) ) + { + if ( s00a_command_800CEA9C( addr ) ) + { + goto loop; + } + else + { + goto end; + } + } + } + } + else + { + printf( (char *)aErrnozoneidingcl_800E06C0 ); + if ( s00a_command_800CEA9C( addr ) ) + { + goto loop; + } + else + { +end: + work->search_flag = 0; + s00a_command_800CEA84( work->field_B78, addr ); + break; + } + } + } +loop: + vx++; + addr = addr_copy; + if ( vx >= res ) + { + vx = 0; + } + } + + } + else + { + work->search_flag = 0; + } + + zone3 = &hzd->def->zones[ addr ]; + work->target_addr = addr << 8 | addr; + + work->target_pos.vx = zone3->x; + work->target_pos.vy = zone3->y; + work->target_pos.vz = zone3->z; + work->target_map = COM_PlayerMap; +} + +void s00a_command_800CAFD4( WatcherWork* work ) +{ + unsigned char addr; + + if ( !work->field_C34 ) + { + work->target_addr = EnemyCommand.com_addr; + work->target_pos = EnemyCommand.com_pos; + work->target_map = EnemyCommand.com_map; + return; + } + + addr = EnemyCommand.com_addr; + if ( s00a_command_800CA898( work, &work->control.map->hzd->def->zones[ addr ] ) ) + { + s00a_command_800CAA2C( work, s00a_command_800CA8E0( work, addr ) ); + return; + } + + work->target_addr = EnemyCommand.com_addr; + work->target_pos = EnemyCommand.com_pos; + work->target_map = EnemyCommand.com_map; +} + +void s00a_command_800CB0E0( WatcherWork* work ) +{ + int v0, v1, a2; + HZD_HDL *hzd; + void *a1; + MAP *map; + HZD_DEF *hdr; + + v0 = work->field_B7C; + do {} while (0); + map = work->control.map; + v1 = v0 << 1; + v1 = v1 + v0; + hzd = map->hzd; + a2 = v0 << 8; + hdr = hzd->def; + v0 = v0 | a2; + a1 = hdr->zones; + v1 = v1 << 3; + work->target_addr = v0; + a1 = a1 + v1; + + work->target_pos.vx = ((HZD_ZON*)a1)->x; + work->target_pos.vy = ((HZD_ZON*)a1)->y; + work->target_pos.vz = ((HZD_ZON*)a1)->z; + work->target_map = work->start_map; + + //should just be this + /* + addr = work->field_B7C; + work->target_addr = addr | (addr << 8); + zone = &work->control.map->hzd->def->zones[ addr ]; + + work->target_pos.vx = zone->x; + work->target_pos.vy = zone->y; + work->target_pos.vz = zone->z; + work->target_map = work->start_map; + */ +} + +void s00a_command_800CB13C( WatcherWork* work ) +{ + int x; + + x = work->next_node + 1; + if ( x >= work->n_nodes ) + { + x = 0; + } + work->next_node = x; + work->target_pos = work->nodes[ x ]; + work->target_addr = HZD_GetAddress( work->control.map->hzd, &work->target_pos, -1 ); + work->target_map = work->start_map; +} + +void s00a_command_800CB1C4( WatcherWork* work ) +{ + AsiatoPos( work->field_BA0, &work->target_pos ); + work->target_addr = HZD_GetAddress( work->control.map->hzd, &work->target_pos, -1 ); + work->target_map = work->control.map->index; +} + +void EnemyResetThink_800CB224( WatcherWork* work ) +{ + work->think1 = 0; + work->think2 = 0; + work->think3 = 0; + work->count3 = 0; + work->field_B4C = 0; + work->pad.mode = 0; +} + +void s00a_command_800CB240( WatcherWork* work ) +{ + work->think2 = 7; + work->think3 = 14; + work->count3 = 0; +} + +void s00a_command_800CB258( WatcherWork *work ) +{ + if ( work->field_B7C == 0xFF ) + { + work->think1 = 1; + work->think2 = 8; + work->think3 = 19; + } + else + { + work->think1 = 1; + work->think2 = 10; + work->think3 = 28; + s00a_command_800CB0E0( work ); + } + work->count3 = 0; + work->pad.mode = 1; +} + +void s00a_command_800CB2C8( WatcherWork *work ) +{ + + work->think1 = 1; + work->think2 = 9; + work->think3 = 27; + work->count3 = 32; + work->pad.mode = 1; +} + +void s00a_command_800CB2F4( WatcherWork *work ) +{ + if ( work->field_B7C == 0xFF ) + { + s00a_command_800CAFD4( work ); + work->think1 = 1; + + if ( EnemyCommand.field_0x40 == 1 ) + { + work->think2 = 8; + work->think3 = 18; + } + else + { + work->think2 = 10; + work->think3 = 29; + } + } + else + { + s00a_command_800CB0E0( work ); + work->think1 = 1; + work->think2 = 10; + work->think3 = 28; + } + work->count3 = 0; + work->pad.mode = 1; +} + +void s00a_command_800CB394( WatcherWork *work ) +{ + work->think1 = 1; + work->think2 = 10; + + if ( work->field_B7C == 0xFF ) + { + work->think3 = 29; + } + else + { + s00a_command_800CB0E0( work ); + work->think3 = 28; + } + + work->count3 = 0; + work->pad.mode = 1; +} + +void s00a_command_800CB3F0( WatcherWork *work ) +{ + + work->think2 = 4; + + if ( (work->act_status & 0x10) && (work->field_BA1 & 8) ) + { + work->think3 = 0xE; + } + else + { + work->think3 = 4; + } + + work->count3 = 0; + work->modetime[2] = 0; +} + +void s00a_command_800CB42C( WatcherWork* work ) +{ + EnemyCommand.field_0x40 = 0; + if ( work->act_status & 4 ) + { + work->think1 = 2; + s00a_command_800CB3F0( work ); + work->pad.mode = 0; + return; + } + + if ( work->field_B7C != 0xFF ) + { + if ( !( HZD_InsideZone( work->control.map->hzd, &work->control.mov, work->field_B7C ) ) ) + { + s00a_command_800CB0E0( work ); + work->think1 = 2; + work->think2 = 11; + work->pad.mode = 1; + } + else + { + work->think1 = 2; + work->think2 = 13; + work->think3 = 34; + work->think4 = 3; + work->pad.mode = 0; + } + } + else + { + if ( work->field_BFC >= 5800 ) + { + work->think1 = 2; + work->think2 = 13; + work->think3 = 34; + work->think4 = 3; + work->pad.mode = 0; + } + else + { + work->think1 = 2; + work->think2 = 11; + work->pad.mode = 1; + } + } + work->count3 = 0; +} + +void s00a_command_800CB504( WatcherWork* work ) +{ + switch( GM_NoisePower ) + { + case 5: + work->think2 = 1; + work->think3 = 8; + break; + case 200: + s00a_command_800CAB74( work ); + s00a_command_800CEB54(); + work->think2 = 1; + + if (work->act_status & 0x10 ) + { + work->think3 = 15; + } + else + { + work->think3 = 6; + } + break; + case 255: + s00a_command_800CAB74( work ); + s00a_command_800CEB54(); + work->think2 = 7; + work->think3 = 16; + break; + default: + case 100: + s00a_command_800CAB74( work ); + work->think2 = 1; + + if ( work->act_status & 0x10 ) + { + work->think3 = 14; + } + else + { + work->think3 = 5; + } + work->modetime[( T_NOISE )] = 0; + break; + } + work->count3 = 0; +} + +void s00a_command_800CB610( WatcherWork* work ) +{ + work->think2 = 2; + work->think3 = 5; + work->count3 = 0; +} + +void s00a_command_800CB628( WatcherWork *work ) +{ + work->think2 = 5; + work->think3 = 5; + work->field_BA0 = NearAsiato(); + work->count3 = 0; +} + +void s00a_command_800CB660( WatcherWork *work ) +{ + int addr; + HZD_HDL *hzd; + + hzd = work->control.map->hzd; + addr = HZD_GetAddress( hzd, &work->control.mov, -1 ); + + work->field_C04 = addr; + if ( HZD_ZoneDistance( hzd, addr & 0xFF, work->target_addr & 0xFF ) < 200 ) + { + work->pad.mode = 0; + } + else + { + work->pad.mode = 1; + } +} + +int s00a_command_800CB6CC( WatcherWork* work ) +{ + int count; + int v1; + + count = work->l_count; + v1 = count / 30; + v1 = v1 + work->field_B78; + + if ( v1 == 1 || v1 == 4 ) + { + work->pad.press |= 0x2; + } + else if ( v1 == 2 || v1 == 5 ) + { + work->pad.press |= 0x1; + } + + if ( count > 90 ) + { + work->l_count = 0; + return 1; + } + else + { + work->l_count++; + return 0; + } +} + +int s00a_command_800CB770( WatcherWork* work ) +{ + int count; + + count = work->l_count; + + if ( count == 0 ) + { + work->control.turn.vy = ( work->control.turn.vy + 0x800 ) & 0xFFF; + } + + if ( count == 24 ) + { + work->control.turn.vy = ( work->control.turn.vy + 0x800 ) & 0xFFF; + } + + if ( count > 30 ) + { + work->l_count = 0; + return 1; + } + else + { + work->l_count++; + return 0; + } +} + +int s00a_command_800CB7E0( WatcherWork *work ) +{ + work->count3++; + work->pad.dir = work->sn_dir; + return 0; +} + +int s00a_command_800CB7FC( WatcherWork *work ) +{ + int count = work->count3; + + if ( count == 0 ) + { + work->pad.sound = 245; + } + else + { + if ( count > 32 ) + { + return 1; + } + } + work->count3++; + return 0; +} + +int s00a_command_800CB838( WatcherWork *work ) +{ + int temp; + SVECTOR svec; + CONTROL *ctrl; + HZD_HDL *hzd; + HZD_ZON *zone; + int addr, addr2, addr3; + int reach; + + ctrl = &work->control; + hzd = work->control.map->hzd; + addr = work->target_addr; + + addr2 = HZD_GetAddress( hzd, &ctrl->mov, work->field_C04 ); + work->field_C04 = addr2; + + reach = HZD_ReachTo( hzd, addr2, work->field_C08 ); + + if ( addr != work->field_BF0 || reach <= 0 ) + { + work->field_BF0 = addr; + if ( HZD_InsideZone( hzd, &work->control.mov, work->target_addr & 0xFF ) ) + { + work->field_C14 = work->target_pos; + work->field_C08 = addr; + + GV_SubVec3( &work->field_C14, &ctrl->mov, &svec ); + + work->pad.dir = GV_VecDir2( &svec ); + return -1; + } + + if ( !( work->field_C00 & 1 ) ) + { + addr3 = HZD_Navigate2( hzd, addr2, addr, &ctrl->mov ); + zone = &hzd->def->zones[ addr3 ]; + + if ( GM_PlayerPosition.vx & 1 ) + { + work->field_C14.vx = zone->x + 230; + } + else + { + work->field_C14.vx = zone->x - 230; + } + + if ( GM_PlayerPosition.vz & 1 ) + { + work->field_C14.vz = zone->z + 230; + } + else + { + work->field_C14.vz = zone->z - 230; + } + work->field_C14.vy = zone->y; + } + else + { + addr3 = HZD_Navigate( hzd, addr2, addr, &ctrl->mov ); + zone = &hzd->def->zones[ addr3 ]; + work->field_C14.vx = zone->x; + work->field_C14.vy = zone->y; + work->field_C14.vz = zone->z; + } + temp = addr3 & 0xFF; + work->field_C08 = temp | temp << 8; + } + + GV_SubVec3( &work->field_C14, &work->control.mov, &svec ); + return GV_VecDir2( &svec ); +} + +int s00a_command_800CBA50( WatcherWork *work ) +{ + int i; + MAP *map; + HZD_PAT *patrol; + HZD_PTP *points; + + map = GM_GetMap( work->start_map ); + patrol = map->hzd->def->routes; + patrol = &patrol[ work->param_root ]; + + work->n_nodes = patrol->n_points; + + if ( work->n_nodes <= 0 ) return -1; + + points = patrol->points; + for ( i = 0 ; i < work->n_nodes ; i++ ) + { + work->nodes[i].vx = points->x; + work->nodes[i].vy = points->y; + work->nodes[i].vz = points->z; + work->nodes[i].pad = points->command; + points++; + } + + work->start_pos = work->nodes[0]; + work->start_addr = HZD_GetAddress( map->hzd, &work->start_pos, -1 ); + return 0; +} + +int s00a_command_800CBB44( WatcherWork *work ) +{ + int a2; + int a0, con; + int act, time, dir; + int command; + +start: + // starting from lo + // |con |dir|tim| act // + //000|0 00|00 |000|0 0000// + + command = work->target_pos.pad; + act = ( command & 0x1F ); //5 bits + time = ( command & 0xE0 ) >> 5; //3 bits + dir = ( command & 0x300 ) >> 8; //2 bits + con = ( command & 0x1C00 ) >> 10; //3 bits + + if ( s00a_dword_800C3524[ act ] == 0x1F ) + { + work->param_c_root = con + ( dir * 8 ); + return 0; + } + + if ( time != 6 ) + { + + a2 = GV_Time % 100; + a0 = con & 3; + con = con & 4; + + if ( a2 >= s00a_dword_800C351C[ a0 ] || work->field_B4C == 1 ) + { + work->field_B4C = 0; + s00a_command_800CB13C( work ); + if ( !DirectTrace_800CC154( work , 350 ) ) + { + return 0; + } + goto start; + } + + if ( con != 0 ) + { + work->field_B4C = 1; + } + } + + work->pad.time = work->field_BB0[ time ]; + work->pad.tmp = s00a_dword_800C3524[ act ]; + + if ( COM_GameStatus & 1 ) + { + if ( work->pad.tmp == 0x80 || work->pad.tmp == 0x200 || work->pad.tmp == 0x400 || work->pad.tmp == 0x800 ) + { + work->pad.tmp = 0; + if ( work->pad.time == 450 ) + { + work->pad.time = 90; + } + } + } + + if ( act == 0 && time == 0 ) + { + work->pad.time = 0; + work->pad.dir = work->control.rot.vy; + s00a_command_800CB13C( work ); + return 0; + } + + if ( act & 0x10 && act != 0x1F ) + { + s00a_command_800CB13C( work ); + return 0; + } + + work->pad.dir = work->field_BD0[ dir ]; + return 1; +} + +int s00a_command_800CBD2C( WatcherWork* work ) +{ + int len; + CONTROL *ctrl; + GV_MSG *msg; + + ctrl = &work->control; + len = ctrl->n_msg; + msg = ctrl->msg; + + for ( ; len > 0 ; len--, msg++ ) + { + if ( msg->message[0] == work->field_B4C ) + { + return 1; + } + } + return 0; +} + +int s00a_command_800CBD6C( WatcherWork* work ) +{ + if ( work->pad.time == 32001 ) + { + if ( s00a_command_800CBD2C( work ) ) + { + work->field_B4C = 0; + work->pad.time = 0; + return 1; + } + work->pad.press |= work->pad.tmp; + return 0; + } + + if ( !work->pad.time ) + { + return 1; + } + + work->pad.press |= work->pad.tmp; + work->pad.time--; + return 0; +} + +int s00a_command_800CBDFC( WatcherWork* work ) +{ + int count; + + count = work->count3; + + if ( count == 0 ) + { + ENE_PutMark( work, 0 ); + NewEyeflash( &work->body.objs->objs[6].world, &work->control.mov, aKirari_800E06E8, 0 ); + COM_VibTime = 10; + } + + if ( count < 20 ) + { + work->pad.press |= 0x20; + work->count3++; + return 0; + } + + return 1; + +} + +int s00a_command_800CBE90( WatcherWork* work ) +{ + if ( work->count3 == 0 ) + { + ENE_PutMark( work, 0 ); + s00a_command_800CC210( work ); + COM_VibTime = 10; + } + + if ( work->count3 > 20 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s00a_command_800CBF00( WatcherWork *work ) +{ + int addr2; + SVECTOR svec; + int addr; + CONTROL *ctrl; + DG_OBJS* objs; + HZD_ZON *zone; + MAP *map; + + objs = work->body.objs; + if ( objs->bound_mode ) + { + return 0; + } + + if (!( work->act_status & 0x100 ) ) + { + return 0; + } + + ctrl = &work->control; + addr2 = work->field_C04 & 0xFF; + addr = HZD_BoundOutZone( ctrl->map->hzd, work->target_addr & 0xFF, addr2, 0xC8 ); + + if ( addr == addr2 ) + { + return 0; + } + + zone = &ctrl->map->hzd->def->zones[ addr ]; + svec.vx = zone->x; + svec.vy = zone->y; + svec.vz = zone->z; + DG_PointCheck( &svec, 1 ); + + if ( svec.pad == 1 ) + { + return 0; + } + + ctrl->mov.vx = zone->x; + ctrl->mov.vy = zone->y + 1000; + ctrl->mov.vz = zone->z; + + printf( (char *)aBefmapnamed_800E06F4, ctrl->map->index ); + + map = GM_FindMapZone( 1 << zone->padding ); + if ( map ) + { + ctrl->map = map; + } + else + { + printf( (char *)aErrnozoneidingcl_800E06C0 ); + } + + printf( (char *)aAftmapnamed_800E0708, ctrl->map->index ); + return 1; +} + +int s00a_command_800CC064( WatcherWork* work ) +{ + if ( !(work->count3 & 31 ) ) + { + work->field_C04 = -1; + work->field_BF0 = -1; + s00a_command_800CB838( work ); + } + + work->pad.dir = s00a_command_800CB838( work ); + if ( work->pad.dir < 0 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s00a_command_800CC0D4( WatcherWork* work ) +{ + if ( s00a_command_800CBF00( work ) ) + { + return 1; + } + + if ( !(work->count3 & 31 ) ) + { + work->field_C04 = -1; + work->field_BF0 = -1; + s00a_command_800CB838( work ); + } + + work->pad.dir = s00a_command_800CB838( work ); + if ( work->pad.dir < 0 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int DirectTrace_800CC154( WatcherWork* work, int a1 ) +{ + int x, z; + SVECTOR svec; + + svec.vx = x = work->target_pos.vx - work->control.mov.vx; + svec.vz = z = work->target_pos.vz - work->control.mov.vz; + + if ( -a1 >= x || x >= a1 || -a1 >= z || z >= a1 ) + { + work->pad.dir = GV_VecDir2( &svec ); + work->count3++; + return 0; + } + + return 1; +} + +int s00a_command_800CC1DC(SVECTOR* arg0, SVECTOR* arg1 ) +{ + SVECTOR svec; + + GV_SubVec3(arg1, arg0, &svec); + return GV_VecDir2(&svec); +} + +void s00a_command_800CC210( WatcherWork* work ) +{ + work->pad.dir = s00a_command_800CC1DC( &work->control.mov, &work->target_pos ); +} + +int s00a_command_800CC240(SVECTOR* svec, SVECTOR* svec2, int a1) { + int x, z; + + x = svec->vx - svec2->vx; + z = svec->vz - svec2->vz; + + if ( -a1 < x || x < a1 || -a1 < z || z < a1 ) + { + return 0; + } + + return 1; +} + +int s00a_command_800CC294( WatcherWork *work ) +{ + int x; + + x = NextAsiato( work->control.map->hzd, work->field_BA0, &work->control.mov ); + + if ( x >= 0 ) + { + work->field_BA0 = x; + return 1; + } + + work->field_BA0 = -1; + return 0; +} + +int s00a_command_800CC2E8( WatcherWork* work ) +{ + + if ( work->param_c_root != work->param_root ) + { + fprintf( 1, (char *)aRootchange_800E071C ); + work->param_root = work->param_c_root; + s00a_command_800CBA50( work ); + return 1; + } + + return 0; +} + +int s00a_command_800CC344( WatcherWork *work ) +{ + if ( work->count3 == 0 ) + { + if ( COM_GameStatus & 0x2000 ) + { + work->think3 = 13; + return 0; + } + + COM_GameStatus |= 0x2000; + work->pad.dir = work->sn_dir; + } + + if ( work->count3 == 63 && !( work->field_BA1 & 16 ) && EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->pad.sound = VO_ENEMY_INMYWAY; + } + + if ( work->actend || work->count3 >= 301 ) + { + return 1; + } + + work->pad.press |= SP_DANBOWLKERI; + work->count3++; + + return 0; +} + +int s00a_command_800CC40C( WatcherWork *work ) +{ + if ( !( COM_GameStatus & 0x2000 ) ) + { + return 1; + } + + work->pad.press |= 0x4; + work->count3++; + return 0; +} + +int s00a_command_800CC44C( WatcherWork *work ) +{ + int count; + int v1; + + count = work->l_count; + + if ( count == 0 && work->field_C00 == 0 ) + { + work->pad.sound = VO_ENEMY_WHEREDHEGO; + } + + v1 = count / 30; + v1 = v1 + work->field_B78; + + if ( v1 == 1 || v1 == 4 ) + { + work->pad.press |= 0x2; + } + else if ( v1 == 2 || v1 == 5 ) + { + work->pad.press |= 0x1; + } + + if ( count > 90 ) + { + work->l_count = 0; + return 1; + } + + work->l_count++; + return 0; +} + +int Think3_GoNext_800CC514( WatcherWork* work ) { + if ( work->pad.time ) { + (work->pad.press |= ( work->pad.tmp )) ; + work->pad.time -- ; + } + + if ( DirectTrace_800CC154( work, 350 ) ) { + return 1 ; + } + + return 0 ; +} + +int Think3_BikkuriGetUp_800CC568( WatcherWork* work ) +{ + if( work->count3 == 0){ + work->pad.press |= 0x00001000 ; + } else if( work->count3 > 90 || work->actend ){ + + return 1 ; + } + work->count3 ++ ; + return 0 ; +} + +int Think3_NoiseModeWatch_800CC5C0( WatcherWork *work ) +{ + if ( work->act_status & 0x00000080 ) + { + work->pad.press |= 0x00800000 ; + } + + if( work->count3 == 0) + { + if( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + if( work->modetime[( T_NOISE )] <= 1 ) + { + work->pad.sound = ( 0x88 ) ; // VO_ENEMY_HUH + } + if( work->modetime[( T_NOISE )] <= 3 ) + { + ENE_PutMark( work ,BW_MARK ); + } + if ( !( work->act_status & 0x00000080 ) ) + { + work->pad.dir = work->sn_dir; + } + } + else + { + if( work->modetime[( T_NOISE )] ) return 1; + work->pad.sound = VO_ENEMY_THATSOUND; + ENE_PutMark( work ,BW_MARK ); + work->pad.dir = work->sn_dir; + } + } + else + { + work->pad.dir = -1; + } + + switch( work->modetime[( T_NOISE )] ) + { + case 0: + if( work->count3 >= 16) + { + if( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + if ( !(work->act_status & 0x00000080 ) ) + { + work->pad.sound = 241; + } + } + return 1; + } + break; + case 1: + case 2: + if ( work->count3 >= 48 ) + { + return 1; + } + break; + case 3: + return 1; + } + work->count3++; + return 0; +} + +int s00a_command_800CC760( WatcherWork *work ) +{ + if ( work->count3 == 0 ) + { + work->pad.dir = work->sn_dir; + } + + if ( work->count3 > 14 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s00a_command_800CC7A4( WatcherWork *work ) +{ + SVECTOR svec; + GV_SubVec3( &GM_NoisePosition, &work->control.mov, &svec ); + work->pad.dir = GV_VecDir2( &svec ); + work->pad.press |= 0x02000000; + + if ( work->count3 == 0 ) + { + ENE_PutMark( work, BW_MARK ); + } + + if ( work->count3 > 30 ) + { + return 1; + } + + if ( work->field_BA1 & 1 ) + { + work->count3 = 1; + } + else + { + work->count3++; + } + + return 0; +} + +int s00a_command_800CC83C( WatcherWork* work ) +{ + if ( work->count3 == 0 ) + { + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->pad.sound = VO_ENEMY_HUH; + } + else + { + work->pad.sound = VO_ENEMY_WHATSTHAT; + } + ENE_PutMark( work, 5 ); + work->pad.dir = work->sn_dir; + } + else + { + work->pad.dir = -1; + } + + if ( work->count3 > 15 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s00a_command_800CC8C8( WatcherWork *work ) +{ + if ( work->count3 > 2 && work->act_status & 1 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s00a_command_800CC90C( WatcherWork *work ) +{ + if ( work->count3 == 0 ) + { + work->pad.dir = work->sn_dir; + } + + if ( work->count3 == 30 ) + { + work->pad.sound = VO_ENEMY_HUH; + ENE_PutMark( work, 5 ); + } + + if ( work->count3 > 29 && s00a_command_800CB6CC( work ) ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s00a_command_800CC99C( WatcherWork *work ) +{ + if ( work->count3 == 0 ) + { + work->pad.sound = VO_ENEMY_HUH; + ENE_PutMark( work, BW_MARK ); + work->pad.dir = work->sn_dir; + } + else + { + work->pad.dir = -1; + } + + if ( work->count3 >= 16 ) + { + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->pad.sound = 243; + } + return 1; + } + + work->count3++; + return 0; +} + +int s00a_command_800CCA28( WatcherWork *work ) +{ + if ( work->count3 == 20 ) + { + ENE_PutMark( work, 5 ); + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->pad.sound = 244; + } + else + { + work->pad.sound = VO_ENEMY_WHEREDHEGO; + } + } + + if ( s00a_command_800CB6CC( work ) ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s00a_command_800CCA9C( WatcherWork *work ) +{ + if ( work->count3 == 20 ) + { + ENE_PutMark( work, 5 ); + work->pad.sound = 244; + } + + if ( s00a_command_800CB6CC( work ) ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s00a_command_800CCAFC( WatcherWork *work ) +{ + work->pad.press |= 0x4; + if ( work->count3 == 20 ) + { + ENE_PutMark( work, 5 ); + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->pad.sound = 242; + } + } + + if ( work->count3 > 120 ) + { + return 1; + } + + work->count3++; + return 0; +} + +int s00a_command_800CCB7C( WatcherWork *work ) +{ + MAP *map; + s00a_command_800CAB04( work ); + + map = work->control.map; + + if ( !( map->index & work->target_map ) ) + { + return -1; + } + + if ( work->count3 & 16 ) + { + work->field_C04 = HZD_GetAddress( map->hzd, &work->control.mov, -1 ); + if ( HZD_ReachTo( work->control.map->hzd, work->field_C04, work->target_addr ) > 1 ) + { + return -1; + } + } + + return DirectTrace_800CC154( work, 1500 ); +} + +int s00a_command_800CCC14( WatcherWork *work ) +{ + int count; + + count = work->count3; + + work->pad.press |= 0x10000; + + if ( count == 0 ) + { + work->count3 = GV_RandU( 8 ); + } + + if ( count < 9 ) + { + if ( !( count & 1 ) ) + { + work->pad.press |= 0x40000; + } + } + + if ( count > 11 ) + { + return 1; + } + + work->pad.dir = work->sn_dir; + work->count3++; + return 0; + +} + +int s00a_command_800CCCAC( WatcherWork *work ) +{ + work->pad.press |= 0x10000; + + if ( work->param_blood == 71 ) + { + return 1; + } + + if ( work->count3 == 30 ) + { + work->pad.press |= 0x100000; + if ( work->act_status & 1 ) + { + work->pad.sound = VO_ENEMY_EATTHIS; + } + + } + else + { + if ( work->actend || work->count3 > 150 ) + { + return 1; + } + } + + work->pad.dir = work->sn_dir; + work->count3++; + return 0; +} + +int s00a_command_800CCD38( WatcherWork *work ) +{ + + work->pad.press |= 0x10000; + + if ( work->count3 == 0 ) + { + work->pad.press |= 0x200000; + } + else + { + if ( work->body.is_end || work->count3 > 30 ) + { + return 1; + } + } + + work->pad.dir = work->sn_dir; + work->count3++; + return 0; +} + +int s00a_command_800CCDA0( WatcherWork *work ) +{ + int count; + work->pad.press |= 0x10000; + + count = work->count3; + if ( count < 10 ) + { + if ( !( work->count3 & 1 ) ) + { + work->pad.press |= 0x40000; + } + } + else if ( count >= 20 ) + { + return 1; + } + + work->pad.dir = work->sn_dir; + work->count3++; + return 0; +} + +int Think3_AttackSetup_800CCE08( WatcherWork *work ) +{ + /* + if ( ID == 2 ) { + work->pad.press |= SP_WEAPON2 ; + } else { + work->pad.press |= SP_WEAPON ; + } + */ + work->pad.press |= 0x10000; //SP_WEAPON? + work->pad.dir = work->sn_dir; + + /* + switch( ZAKO_GameFlag ) { + case FLAG_FIGHT1_DEMO : + case FLAG_FIGHT1_DEMO1 : + case FLAG_FIGHT1_DEMO2 : + return 0 ; + break ; + } + */ + + if ( work->field_B7C == 0xFF ) + { + if ( work->count3 == 16 ) + { + work->count3 += GV_RandU( 14 ); + } + + if ( work->count3 > 32 ) + { + /* + if( work->chase_dis <= CHASE_DIS1 + && GM_PlayerStatus & PLAYER_INTRUDE ){ + return TH3_ATTACK_GRENADE ; + } + */ + if ( work->field_C00 == 0 && + work->alert_level == 255 && + GM_PlayerStatus & ( PLAYER_NOHIDDEN | PLAYER_INTRUDE ) && + work->vision.field_B92 == 0 ) + { + return 0x18; + } + + if ( work->sn_dis < COM_SHOOTRANGE && work->vision.field_B92 == 2 ) + { + if ( work->sn_dis < 1000 && /* 距離が近かなかったら */ + !(GM_PlayerStatus & PLAYER_SQUAT) && /* しゃがんでなかったら */ + !(GM_PlayerStatus & PLAYER_GROUND) && /* 匍匐じゃなかったら */ + !(GM_PlayerStatus & PLAYER_CB_BOX) ) { /* 段ボールじゃなかったら */ + return TH3_ATTACK_NEAR; + } + else + { + return TH3_ATTACK_HANDGUN; + } + } + } + } + else + { + if ( work->sn_dis < COM_SHOOTRANGE && work->vision.field_B92 == 2 && (work->count3 & 1) ) + { + if ( work->sn_dis < 1000 && /* 距離が近かなかったら */ + !(GM_PlayerStatus & PLAYER_SQUAT) && /* しゃがんでなかったら */ + !(GM_PlayerStatus & PLAYER_GROUND) && /* 匍匐じゃなかったら */ + !(GM_PlayerStatus & PLAYER_CB_BOX) ) { /* 段ボールじゃなかったら */ + return TH3_ATTACK_NEAR; + } + else + { + return 0x1A; + } + } + + if ( work->count3 > 32 ) + { + if ( work->field_C00 == 0 && + work->alert_level == 255 && + (GM_PlayerStatus & ( PLAYER_NOHIDDEN | PLAYER_INTRUDE )) && + work->vision.field_B92 == 0 ) + { + return 0x18; + } + } + } + + work->count3++; + return 0; +} + +int s00a_command_800CD000( WatcherWork* work ) +{ + switch ( work->think4 ) + { + case 3: + if ( s00a_command_800CB6CC( work ) ) + { + work->think4 = 1; + work->count3 = 0; + s00a_command_800CAB04( work ); + } + work->count3++; + break; + case 1: + if (s00a_command_800CC064( work ) ) + { + work->think4 = 3; + work->count3 = 0; + } + break; + } + return 0; +} + +int s00a_command_800CD08C( WatcherWork *work ) +{ + switch( work->think4 ) + { + case 0: + if ( s00a_command_800CC44C(work) ) + { + work->think4 = 2; + s00a_command_800CAD84( work ); + work->count3 = 0; + } + break; + case 3: + if ( s00a_command_800CB6CC(work) ) + { + work->think4 = 2; + s00a_command_800CAD84( work ); + work->count3 = 0; + } + work->count3++; + break; + case 2: + if ( DirectTrace_800CC154( work, 250 ) ) + { + s00a_command_800CAD84( work ); + work->count3 = 0; + } + break; + } + + return 0; +} + +void s00a_command_800CD158( WatcherWork *work ) +{ + if ( work->think3 == 3 && s00a_command_800CB7FC( work ) ) + { + s00a_command_800CAACC( work ); + work->next_node = work->n_nodes; + EnemyResetThink_800CB224( work ); + work->pad.mode = 1; + } + + if ( work->field_BA1 & 4 ) + { + s00a_command_800CB3F0( work ); + } + else if ( work->field_BA1 & 1 ) + { + s00a_command_800CB504( work ); + } + else if ( work->alert_level > 1 ) + { + s00a_command_800CB240( work ); + } +} + +static inline void set_dir( WatcherWork *work ) +{ + if ( GV_DiffDirAbs( work->pad.dir, work->control.rot.vy ) < 128 ) + { + work->pad.dir = -1; + } +} + +static inline void think_reset( WatcherWork *work ) +{ + s00a_command_800CAACC( work ); + work->next_node = work->n_nodes; + EnemyResetThink_800CB224( work ); +} + +static inline void think_reset2( WatcherWork *work ) +{ + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->next_node--; + s00a_command_800CB13C( work ); + EnemyResetThink_800CB224( work ); + s00a_command_800CB660( work ); + } + else + { + s00a_command_800CB42C( work ); + } +} + +static inline void think_noise_inline( WatcherWork *work ) +{ + s00a_command_800CAB74( work ); + s00a_command_800CEB54(); + work->think2 = 7; + work->think3 = 16; + work->count3 = 0; +} + +void s00a_command_800CD210( WatcherWork* work ) +{ + switch ( work->think3 ) + { + case 0: + if ( s00a_command_800CC064( work ) ) + { + work->pad.mode = 0; + work->think3 = 1; + work->count3 = 0; + work->next_node--; + s00a_command_800CB13C( work ); + work->pad.time = 0; + } + break; + case 1: + if ( Think3_GoNext_800CC514( work ) ) + { + if (!s00a_command_800CC2E8( work ) ) + { + if ( s00a_command_800CBB44( work ) ) + { + work->think3 = 2; + set_dir( work ); + } + work->count3 = 0; + } + else + { + think_reset( work ); + } + } + else + { + if ( work->field_BA1 & 64 ) + { + s00a_command_800CB610( work ); + work->think3 = 12; + return; + } + } + break; + case 2: + if ( s00a_command_800CBD6C( work ) ) + { + if ( s00a_command_800CC2E8( work ) ) + { + think_reset( work ); + } + else + { + s00a_command_800CB13C( work ); + if ( DirectTrace_800CC154( work, 350 ) ) + { + if ( !s00a_command_800CBB44( work ) ) + { + if ( s00a_command_800CC2E8( work ) ) + { + think_reset( work ); + break; + } + work->think3 = 1; + } + else + { + set_dir( work ); + } + } + else + { + work->think3 = 1; + } + work->count3 = 0; + } + } + break; + } + + if ( s00a_command_800CC2E8( work ) ) + { + think_reset( work ); + } + + if ( work->field_BA1 & 4 ) + { + s00a_command_800CB3F0( work ); + } + else if ( work->field_BA1 & 2) + { + s00a_command_800CB610( work ); + } + else if ( work->field_BA1 & 1) + { + s00a_command_800CB504( work ); + } + else if ( work->field_BA1 & 16 ) + { + s00a_command_800CB628( work ); + } + else if ( work->alert_level > 2 ) + { + ENE_SetGopointLast(); + s00a_command_800CB240( work ); + } +} + +void s00a_command_800CD470( WatcherWork *work ) +{ + +} + +void s00a_command_800CD478( WatcherWork *work ) +{ + switch ( work->think3 ) + { + case 14: + if ( Think3_BikkuriGetUp_800CC568( work ) ) + { + work->think3 = 5; + work->count3 = 0; + } + break; + case 4: + if ( s00a_command_800CC8C8( work ) ) + { + work->think3 = 5; + work->count3 = 0; + } + break; + case 5: + if ( s00a_command_800CC90C( work ) ) + { + think_reset2( work ); + } + if ( ( work->field_BA1 & 1 ) && ( GM_NoisePower == 200 || GM_NoisePower == 255 ) ) + { + think_noise_inline( work ); + } + if ( work->field_BA1 & 2 ) + { + s00a_command_800CB610( work ); + return; + } + break; + } + + if ( ( *( int * )&work->field_BA0 & 0xC00 ) == 0x400 ) + { + s00a_command_800CB3F0( work ); + } + else if ( work->alert_level > 1 ) + { + s00a_command_800CB240( work ); + } +} + + +void s00a_command_800CD608( WatcherWork *work ) +{ + short x; + x = work->think3 - 5; + switch ( x ) + { + case 0: + if ( s00a_command_800CC83C( work ) ) + { + s00a_command_800CAB04( work ); + work->think3 = 9; + work->count3 = 0; + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->pad.mode = 0; + } + else + { + work->pad.mode = 1; + } + } + break; + case 4: + if ( s00a_command_800CC064( work ) ) + { + work->think3 = 10; + work->count3 = 0; + } + if ( s00a_command_800CC240( &work->target_pos, &work->control.mov, 1500 ) ) + { + work->think3 = 11; + work->count3 = 0; + } + if ( work->sn_dis < 1500 ) + { + if ( ( signed char )work->field_BA1 & 128 ) + { + if ( ( ( work->field_BA1 & 16 ) || work->think1 == 2 ) ) + { + work->think3 = 12; + work->count3 = 0; + break; + } + work->think3 = 11; + work->count3 = 0; + } + } + break; + case 5: + if ( DirectTrace_800CC154( work, 1500 ) ) + { + int a; + a = work->field_BA1; + work->pad.dir = work->sn_dir; + + if ( !( a & 16 ) ) + { + if ( work->think1 != 2 ) + { + work->think3 = 11; + work->count3 = 0; + break; + } + } + + if ( a & 128 ) + { + work->think3 = 12; + work->count3 = 0; + break; + } + + work->think3 = 11; + work->count3 = 0; + } + break; + case 6: + if ( s00a_command_800CCAFC( work ) ) + { + think_reset2( work ); + } + break; + case 7: + if ( s00a_command_800CC344( work ) ) + { + think_reset2( work ); + } + break; + case 8: + if ( s00a_command_800CC40C( work ) ) + { + think_reset2( work ); + } + break; + } + + if ( work->field_BA1 & 4 ) + { + s00a_command_800CB3F0( work ); + return; + } + + if ( work->alert_level > 1 || work->field_BA1 & 2 ) + { + s00a_command_800CB240( work ); + return; + } + + if ( work->field_BA1 & 1 ) + { + if ( (GM_NoisePower != 100) ) + { + if ( GM_NoisePower == 255 ) + { + think_noise_inline( work ); + return; + } + } + s00a_command_800CB240( work ); + work->count3 = 0; + return ; + } +} + +void s00a_command_800CD8B0( WatcherWork *work ) +{ + short x; + x = work->think3 - 5; + switch ( x ) + { + case 10: + if ( Think3_BikkuriGetUp_800CC568( work ) ) + { + work->think3 = 6; + work->count3 = 0; + } + break; + case 1: + if ( s00a_command_800CC760( work ) ) + { + think_noise_inline( work ); + break; + } + break; + case 9: + if ( Think3_BikkuriGetUp_800CC568( work ) ) + { + work->think3 = 5; + work->count3 = 0; + } + break; + case 0: + if ( Think3_NoiseModeWatch_800CC5C0( work ) ) + { + if ( ( work->act_status & 0x80 ) ) + { + work->think2 = 0; + work->think3 = 2; + } + else + { + work->modetime[( T_NOISE )]++; + work->think3 = 9; + if ( EnemyCommand.mode == TOP_COMM_TRAVEL && work->modetime[( T_NOISE )] < 2 ) + { + work->pad.mode = 0; + } + else + { + work->pad.mode = 1; + } + + } + work->count3 = 0; + } + break; + case 4: + if ( s00a_command_800CC064( work ) ) + { + work->think3 = 11; + work->count3 = 0; + } + + if ( work-> count3 > 32 ) + { + if ( work->field_BA1 & 1 && work->modetime[( T_NOISE )] < 3 ) + { + s00a_command_800CAB74( work ); + work->think3 = 5; + work->count3 = 0; + } + } + break; + case 6: + if ( s00a_command_800CCA28( work ) ) + { + think_reset2( work ); + } + break; + case 3: + if ( s00a_command_800CC7A4( work ) ) + { + think_reset2( work ); + } + break; + } + + if ( work->field_BA1 & 4 ) + { + s00a_command_800CB3F0( work ); + } + else if ( work->alert_level > 2 ) + { + s00a_command_800CB240( work ); + } + else if ( work->field_BA1 & 2 ) + { + s00a_command_800CB610( work ); + } + else if ( work->field_BA1 & 16 ) + { + s00a_command_800CB628( work ); + } + else if ( work->field_BA1 & 1 ) + { + if ( GM_NoisePower == 200 || GM_NoisePower == 255 ) + { + think_noise_inline( work ); + } + } +} + +void s00a_command_800CDB88( WatcherWork *work ) +{ + switch ( work->think3 ) + { + case 5: + if ( s00a_command_800CC99C( work ) ) + { + s00a_command_800CB1C4( work ); + work->think3 = 10; + work->t_count = 0; + work->pad.mode = 0; + work->count3 = 0; + } + break; + case 10: + work->pad.press |= 0x4; + if ( DirectTrace_800CC154( work , 500 ) ) + { + if ( s00a_command_800CC294( work ) != 0 ) + { + s00a_command_800CB1C4( work ); + work->t_count++; + if ( work->t_count == 12 ) + { + work->think3 = 32; + } + else + { + work->unknown.field_1E = 0; + work->pad.dir = work->control.turn.vy; + } + work->count3 = 0; + break; + } + else + { + work->think3 = 11; + CleanAsiato(); + work->count3 = 0; + } + } + break; + case 32: + if ( s00a_command_800CB770( work ) ) + { + work->think3 = 10; + work->t_count = 0; + work->count3 = 0; + } + break; + case 11: + if ( s00a_command_800CCA9C ( work ) ) + { + think_reset2( work ); + } + break; + } + + if ( work->field_BA1 & 4 ) + { + s00a_command_800CB3F0( work ); + CleanAsiato(); + } + else if ( work->alert_level > 1 ) + { + s00a_command_800CB240( work ); + CleanAsiato(); + } + else if ( work->field_BA1 & 2 ) + { + s00a_command_800CB610( work ); + } + else if ( work->field_BA1 & 1 ) + { + s00a_command_800CB504( work ); + CleanAsiato(); + } +} + +void s00a_command_800CDD80( WatcherWork *work ) +{ + switch( work->think3 ) + { + case 14: + if ( s00a_command_800CBDFC( work ) ) + { + s00a_command_800CB2C8( work ); + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->pad.sound = 240; + } + else + { + work->pad.sound = 246; + } + work->alert_level = 255; + ENE_SetGopointLast(); + } + break; + case 16: + if ( s00a_command_800CBE90( work ) ) + { + if ( EnemyCommand.field_0x180 < 2 ) + { + s00a_command_800CB394( work ); + } + else + { + ENE_SetGopointLast(); + s00a_command_800CB2F4( work ); + } + work->alert_level = 255; + } + break; + case 17: + ENE_SetGopointLast(); + s00a_command_800CB258( work ); + work->pad.sound = 246; + work->alert_level = 255; + } +} + +void s00a_command_800CDE90( WatcherWork *work ) { + int res; + short x; + x = work->think3 - 21; + switch ( x ) + { + case 0: + if ( s00a_command_800CCC14( work ) ) + { + work->think3 = 27; + work->count3 = 0; + } + break; + case 3: + if ( s00a_command_800CCCAC( work ) ) + { + work->think3 = 27; + work->count3 = 0; + } + break; + case 4: + if ( s00a_command_800CCD38( work ) ) + { + work->think3 = 27; + work->count3 = 0; + } + break; + case 5: + if ( s00a_command_800CCDA0( work ) ) + { + work->think3 = 27; + work->count3 = 0; + } + break; + case 6: + res = Think3_AttackSetup_800CCE08( work ); + if ( res ) + { + work->think3 = res; + work->count3 = 0; + } + break; + } + + if ( ( ( GV_Time / 4 ) % 4 ) == work->field_B78 % 4 ) + { + if ( work->field_B7C == 0xFF ) + { + if ( work->field_C04 != COM_PlayerAddressOne[ work->field_B78 ] ) + { + if ( work->field_BFC < 0xDAD ) + { + if ( (work->field_BFC + 0x3E8) < work->sn_dis || work->vision.field_B92 != 2 ) + { + s00a_command_800CB258( work ); + } + } + else + { + if ( work->field_BFC < work->sn_dis ) + { + s00a_command_800CB258( work ); + } + } + } + } + else + { + if ( work->vision.field_B92 != 2 ) + { + if ( work->alert_level != 0xFF ) + { + if (!(HZD_InsideZone( work->control.map->hzd, &work->control.mov, work->field_B7C ))) + { + s00a_command_800CB258(work); + } + } + } + } + } + + if ( work->vision.field_B92 == 2 ) + { + ENE_SetGopointLast(); + work->alert_level = 0xFF; + return; + } +} + +void s00a_command_800CE0B8( WatcherWork *work ) +{ + int res; + switch ( work->think3 ) + { + case 18: + s00a_command_800CAB04( work ); + if ( s00a_command_800CC0D4(work) ) + { + work->think3 = 19; + work->count3 = 0; + } + break; + case 19: + s00a_command_800CAB04( work ); + if ( s00a_command_800CC064( work ) ) + { + work->think3 = 20; + work->count3 = 0; + } + break; + case 20: + res = s00a_command_800CCB7C( work ); + if ( res < 0 ) + { + work->think3 = 19; + work->count3 = 0; + } + else if ( res > 0 ) + { + s00a_command_800CB2C8( work ); + } + break; + } + + if ( work->sn_dis < work->field_BFC ) + { + if ( ( work->field_BFC >= 0xFA0 ) || ( work->vision.field_B92 == 2 ) ) + { + s00a_command_800CB2C8( work ); + } + } + + if ( work->vision.field_B92 == 2 ) + { + work->alert_level = 255; + } + + ENE_SetGopointLast(); +} + +void s00a_command_800CE1E0( WatcherWork* work ) +{ + switch ( work->think3 ) + { + case 29: + if ( s00a_command_800CC0D4( work ) ) + { + work->think3 = 28; + work->count3 = 0; + } + break; + case 28: + if ( ( work->field_B7C != 0xFF ) && (char)work->target_addr != (short)work->field_B7C ) + { + s00a_command_800CB0E0( work ); + } + if ( s00a_command_800CC064( work ) ) + { + if ( EnemyCommand.field_0x40 == 1 || ( work->field_B7C != 0xFF) ) + { + s00a_command_800CB2C8( work ); + } + else + { + work->think3 = 30; + work->count3 = 0; + } + } + break; + case 30: + if ( EnemyCommand.field_0x40 == 1 ) + { + s00a_command_800CB2C8( work ); + return; + } + break; + + } + + if ( ( work->field_B7C == 0xFF) || EnemyCommand.field_0x40 != 1 ) + { + if ( work->vision.field_B92 == 2 ) + { + if ( EnemyCommand.field_0x40 != 1 ) + { + work->think2 = 7; + work->think3 = 17; + work->count3 = 0; + return; + } + work->pad.dir = work->sn_dir; + s00a_command_800CB2C8( work ); + return; + } + } + else + { + if ( work->vision.field_B92 == 2 ) + { + work->pad.dir = work->sn_dir; + s00a_command_800CB2C8( work ); + return; + } + } +} + +void s00a_command_800CE354( WatcherWork* work ) +{ + if ( s00a_command_800CC064( work ) ) + { + work->think2 = 13; + work->think3 = 34; + work->think4 = 3; + work->pad.mode = 0; + work->count3 = 0; + } + + if ( work->alert_level > 1 ) + { + s00a_command_800CB240( work ); + } + + if ( work->field_BA1 & 2 ) + { + s00a_command_800CB610( work ); + } + + if ( work->field_BA1 & 1 ) + { + s00a_command_800CB504( work ); + } + + if ( work->field_BA1 & 16 ) + { + s00a_command_800CB628( work ); + work->pad.mode = 0; + } + else if ( work->field_BA1 & 4 ) + { + s00a_command_800CB3F0( work ); + work->pad.mode = 0; + } +} + +void s00a_command_800CE428( WatcherWork* work ) +{ + s00a_command_800CAB04( work ); + + if ( s00a_command_800CC064( work ) || ( work->sn_dis < ( work->field_BFC + 500 ) ) || GM_AlertLevel < 150 ) + { + work->think2 = 13; + work->think3 = 34; + work->think4 = 0; + work->pad.mode = 0; + work->count3 = 0; + } + + if ( work->alert_level > 1 ) + { + s00a_command_800CB240( work ); + } + + if ( work->field_BA1 & 2 ) + { + s00a_command_800CB610( work ); + } + + if ( work->field_BA1 & 1 ) + { + s00a_command_800CB504( work ); + } + + if ( work->field_BA1 & 16 ) + { + s00a_command_800CB628( work ); + work->pad.mode = 0; + } + else if ( work->field_BA1 & 4 ) + { + s00a_command_800CB3F0( work ); + work->pad.mode = 0; + } +} + +void s00a_command_800CE530( WatcherWork* work ) +{ + switch ( work->think3 ) + { + case 32: + s00a_command_800CB7E0( work ); + break; + case 33: + s00a_command_800CD000( work ); + break; + case 34: + s00a_command_800CD08C( work ); + break; + } + + if ( work->alert_level > 1 ) + { + s00a_command_800CB240(work); + } + if (work->field_BA1 & 2) + { + s00a_command_800CB610(work); + } + else if (work->field_BA1 & 1) + { + s00a_command_800CB504(work); + } + else if (work->field_BA1 & 16 ) + { + s00a_command_800CB628(work); + } + else if (work->field_BA1 & 4) + { + s00a_command_800CB3F0(work); + } +} + +void s00a_command_800CE634( WatcherWork *work ) +{ + switch( work->think2 ) + { + case 6: + s00a_command_800CD158( work ); + break; + case 0: + s00a_command_800CD210( work ); + break; + case 1: + work->control.radar_atr |= 0x1000; + s00a_command_800CD8B0( work ); + break; + case 2: + work->control.radar_atr |= 0x1000; + s00a_command_800CD608( work ); + break; + case 3: + work->control.radar_atr |= 0x1000; + s00a_command_800CD470( work ); + break; + case 4: + work->control.radar_atr |= 0x1000; + s00a_command_800CD478( work ); + break; + case 5: + work->control.radar_atr |= 0x1000; + s00a_command_800CDB88( work ); + break; + case 7: + work->control.radar_atr |= 0x1000; + s00a_command_800CDD80( work ); + break; + } + + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + return; + } + + if ( work->think2 == 7 ) + { + work->think1 = 1; + } + else + { + s00a_command_800CB2F4( work ); + } +} + +void s00a_command_800CE778( WatcherWork* work ) +{ + switch ( work->think2 ) + { + case 8: + s00a_command_800CE0B8( work ); + break; + case 9: + s00a_command_800CDE90( work ); + break; + case 10: + s00a_command_800CE1E0( work ); + break; + case 7: + s00a_command_800CDD80( work ); + break; + } + + if ( EnemyCommand.mode != TOP_COMM_ALERT ) + { + s00a_command_800CB42C( work ); + } +} + +void s00a_command_800CE830( WatcherWork *work ) +{ + short x; + x = work->think2 - 1; + switch( x ) + { + case 10: + s00a_command_800CE354( work ); + break; + case 11: + s00a_command_800CE428( work ); + break; + case 12: + s00a_command_800CE530( work ); + break; + case 4: + s00a_command_800CDB88( work ); + break; + case 0: + s00a_command_800CD8B0( work ); + break; + case 1: + s00a_command_800CD608( work ); + break; + case 3: + s00a_command_800CD478( work ); + break; + case 6: + s00a_command_800CDD80( work ); + break; + } + + if ( EnemyCommand.mode == TOP_COMM_ALERT ) + { + s00a_command_800CB2F4( work ); + } + + if ( EnemyCommand.mode == TOP_COMM_TRAVEL ) + { + work->think1 = 0; + if ( work->think2 != 7 && work->think2 != 4 ) + { + if ( work->think2 == 2 ) + { + unsigned short th3 = work->think3; + if ( ( th3 - 12 ) < 2u ) + { + return; + } + } + + if ( work->think2 == 5 ) + { + CleanAsiato(); + } + + work->think2 = 6; + work->think3 = 3; + work->count3 = 0; + } + } +} + +void Enemy_Think( WatcherWork* work ) +{ + work->pad.dir = -1; + work->pad.press = 0; + work->control.radar_atr = RADAR_VISIBLE | RADAR_SIGHT | RADAR_ALL_MAP; + + switch ( work->think1 ) + { + case 0: + work->unknown.field_1E = 1; + s00a_command_800CE634( work ); + break; + case 1: + work->unknown.field_1E = 1; + s00a_command_800CE778( work ); + break; + case 2: + work->unknown.field_1E = 1; + s00a_command_800CE830( work ); + break; + } +} diff --git a/components/mgs/mgs/source/enemy/wall.c b/components/mgs/mgs/source/enemy/wall.c new file mode 100644 index 00000000..ec7151e6 --- /dev/null +++ b/components/mgs/mgs/source/enemy/wall.c @@ -0,0 +1,301 @@ +#include "wall.h" + +#include +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_USER + +#define FLAG_UNLIT ( DG_FLAG_TEXT | DG_FLAG_PAINT | DG_FLAG_TRANS \ + | DG_FLAG_BOUND | DG_FLAG_ONEPIECE ) + +#define FLAG_UNLIT_IR ( DG_FLAG_TEXT | DG_FLAG_PAINT | DG_FLAG_TRANS \ + | DG_FLAG_BOUND | DG_FLAG_ONEPIECE | DG_FLAG_IRTEXTURE ) + +#define FLAG_AMBIENT ( DG_FLAG_TEXT | DG_FLAG_SHADE | DG_FLAG_TRANS \ + | DG_FLAG_BOUND | DG_FLAG_ONEPIECE \ + | DG_FLAG_AMBIENT | DG_FLAG_IRTEXTURE ) + +/*---------------------------------------------------------------------------*/ + +typedef struct _Work +{ + GV_ACT actor; + OBJECT object; + MATRIX light[2]; + SVECTOR position; + int name; + int visible; + short g_flag; + short ir_flag; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void CheckMessage(Work *work) +{ + GV_MSG *msg; + + if (GV_ReceiveMessage(work->name, &msg)) + { + if (msg->message[0] == HASH_LEAVE) + { + work->visible = FALSE; + } + else if (msg->message[0] == HASH_ENTER) + { + work->visible = TRUE; + } + } +} + +static void SetVisibility(Work *work) +{ + if (work->visible) + { + DG_VisibleObjs(work->object.objs); + } + else + { + DG_InvisibleObjs(work->object.objs); + } +} + +static void WriteObjsPacket(DG_OBJS *objs) +{ + DG_OBJ *obj; + int i; + + obj = objs->objs; + for (i = objs->n_models; i > 0; obj++, i--) + { + if (obj->packs[0]) + { + DG_WriteObjPacketRGB(obj, 0); + } + + if (obj->packs[1]) + { + DG_WriteObjPacketRGB(obj, 1); + } + } +} + +static void SetLighting(Work *work) +{ + if (work->g_flag) + { + if (GM_GameStatus & STATE_THERMG) + { + if (!work->ir_flag) + { + work->object.flag = FLAG_AMBIENT; + work->object.objs->flag = FLAG_AMBIENT; + DG_GetLightMatrix2(&work->position, work->light); + GM_ConfigObjectLight(&work->object, work->light); + work->ir_flag = TRUE; + } + } + else if (work->ir_flag) + { + work->object.flag = FLAG_UNLIT_IR; + work->object.objs->flag = FLAG_UNLIT_IR; + WriteObjsPacket(work->object.objs); + work->ir_flag = FALSE; + } + } +} + +static void Act(Work *work) +{ + CheckMessage(work); + SetVisibility(work); + SetLighting(work); + + if (GM_CheckMessage(&work->actor, work->name, HASH_KILL)) + { + GV_DestroyActor(&work->actor); + } +} + +static void Die(Work *work) +{ + printf("destroy\n"); + + work->object.objs->flag = FLAG_UNLIT; + GM_FreeObject(&work->object); +} + +static int GetResources(work, pos, dir, def_model, map) + Work *work; + SVECTOR *pos; + SVECTOR *dir; + int def_model; + int map; +{ + SVECTOR position; + VECTOR scale; + int model; + OBJECT *object; + + position = *pos; + + switch (def_model) + { + case 0: + model = GV_StrCode("box_01"); + scale.vx = 8192; + scale.vy = 16384; + scale.vz = 8192; + position.vy += 200; + break; + + case 1: + model = GV_StrCode("dbx1"); + scale.vx = 4096; + scale.vy = 4096; + scale.vz = 4096; + break; + + case 2: + model = GV_StrCode("dbx2"); + scale.vx = 4096; + scale.vy = 4096; + scale.vz = 4096; + break; + + default: + model = def_model; + scale.vx = 4096; + scale.vy = 4096; + scale.vz = 4096; + break; + } + + GM_CurrentMap = map; + object = &work->object; + + if (!work->g_flag) + { + GM_InitObject(object, model, FLAG_UNLIT, map); + } + else + { + GM_InitObject(object, model, FLAG_AMBIENT, map); + } + + GM_ConfigObjectJoint(object); + + work->position = position; + + DG_SetPos2(&position, dir); + DG_PutObjs(work->object.objs); + ScaleMatrix(&work->object.objs->world, &scale); + GM_ReshadeObjs(object->objs); + + work->visible = TRUE; + work->ir_flag = TRUE; + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewWall(SVECTOR *pos, SVECTOR *dir) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, NULL, Die, "wall.c"); + + // Why? GetResources is missing two last arguments, leading to nasty UB + if (GetResources(work, pos, dir) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + + return (void *)work; +} + +void *NewWallGcl(int name, int where, int argc, char **argv) +{ + SVECTOR pos; + SVECTOR dir; + Work *work; + int model; + char *param; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "wall.c"); + + param = GCL_GetOption('t'); + if (param != 0) + { + model = GCL_StrToInt(param); + } + else + { + model = 0; + } + + param = GCL_GetOption('m'); + if (param != 0) + { + model = GCL_StrToInt(param); + } + + param = GCL_GetOption('p'); + if (param != 0) + { + GCL_StrToSV(param, (short *)&pos); + } + else + { + pos = DG_ZeroVector; + } + + param = GCL_GetOption('d'); + if (param != 0) + { + GCL_StrToSV(param, (short *)&dir); + } else + { + dir = DG_ZeroVector; + } + + param = GCL_GetOption('g'); + if (param != 0) + { + work->g_flag = GCL_StrToInt(param); + } + else + { + work->g_flag = FALSE; + } + + if (GetResources(work, &pos, &dir, model, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + work->name = name; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/wall.h b/components/mgs/mgs/source/enemy/wall.h new file mode 100644 index 00000000..88bb1d81 --- /dev/null +++ b/components/mgs/mgs/source/enemy/wall.h @@ -0,0 +1,11 @@ +#ifndef __MGS_ENEMY_WALL_H__ +#define __MGS_ENEMY_WALL_H__ + +#include +#include +#include + +void *NewWall(SVECTOR *pos, SVECTOR *dir); +void *NewWallGcl(int name, int where, int argc, char **argv); + +#endif // __MGS_ENEMY_WALL_H__ diff --git a/components/mgs/mgs/source/enemy/watcher.c b/components/mgs/mgs/source/enemy/watcher.c new file mode 100644 index 00000000..04d99a0d --- /dev/null +++ b/components/mgs/mgs/source/enemy/watcher.c @@ -0,0 +1,713 @@ +#include "watcher.h" +#include "enemy.h" + +#include +#include +#include +#include + +#include "common.h" +#include "mts/mts.h" // for fprintf +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "libhzd/libhzd.h" +#include "game/game.h" +#include "strcode.h" + +#include "chara/snake/shadow.h" // for NewShadow2 +#include "enemy/glight.h" // for NewGunLight + +int s00a_dword_800C3328[8] = { 2500, 3500, 4000, 5000, 5500, 5600, 5700, 5800 }; +unsigned short s00a_dword_800C3348[8] = { 450, 15, 30, 60, 90, 0, 32001, 30000 }; + +extern ENEMY_COMMAND EnemyCommand; +extern SVECTOR ENEMY_TARGET_SIZE; +extern SVECTOR ENEMY_TARGET_FORCE; +extern SVECTOR ENEMY_ATTACK_SIZE; +extern SVECTOR ENEMY_ATTACK_FORCE; +extern SVECTOR ENEMY_TOUCH_SIZE; +extern SVECTOR ENEMY_TOUCH_FORCE; +extern SVECTOR COM_NO_POINT; +extern unsigned short COM_EYE_LENGTH; + +const char aErrerrerrsettimeover_800DFC7C[] = "Err Err Err Set time Over\n"; +const char aErrerrerrsetdirover_800DFC98[] = "Err Err Err Set Dir Over\n"; +const char aErrnotenoughwork_800DFCB4[] = "Err not enough work !!\n"; +const char aWatcharcactionpointerr_800DFCCC[] = "watchar.c : action point Err\n"; +const char aWatcherc_800DFCEC[] = "watcher.c"; + +extern GM_CameraSystemWork GM_Camera; + +/* ?????��??�???��???????��????��????? + ???�?????????��??�??????��?��???????��????��????? + ????????��????��????��????��??�???��????��???????��??????????? + �???��??????????��?��???????��?��????��????��?��?��?????????????��??�???��????? + ??��????��???????��?��?��?��??????????��?��???????��????��?��?��????????�??????? + */ +int RootFlagCheck_800C3EE8( WatcherWork* work ) +{ + int count; + GV_MSG *msg; + SVECTOR svec; + CONTROL *ctrl; + + ctrl = &work->control; + ctrl->n_msg = GV_ReceiveMessage( ctrl->name, &work->control.msg ); + count = ctrl->n_msg; + msg = ctrl->msg; + + if ( count <= 0 ) return 0 ; + + for ( ; count > 0 ; --count, msg++ ) + { + switch ( msg->message[0] ) + { + case 0x430F: + work->param_c_root = msg->message[1]; + svec.vx = msg->message[2]; + svec.vy = msg->message[3]; + svec.vz = msg->message[4]; + + if ( svec.vy < 0x7530 ) + { + work->field_B7C = HZD_GetAddress( work->control.map->hzd, &svec, -1 ); + } + else + { + work->field_B7C = -1; + } + return 1; + case 0xF1BD: + work->hom->flag = FALSE ; + work->alert_level = 0 ; + GM_ConfigControlAttribute( &(work->control), 0 ) ; + work->visible = 0 ; + work->target->class = TARGET_AVAIL; + + work->faseout = 1 ; + work->act_status = EN_FASEOUT ; + break; + case 0x1DC4: + if ( EnemyCommand.field_0xC8[ work->field_B78 ].field_04 == 2 ) + { + work->visible = 1; + } + GM_ConfigControlAttribute( ctrl, 13 ); + work->faseout = 0; + work->act_status = 0; + break; + case 0xEF15: + work->param_item = 0; + break; + case 0x566D: + work->param_item = 1; + break; + } + } + return 0; +} + +void s00a_watcher_800C409C( WatcherWork* work ) +{ + if ( GM_GameStatus & STATE_ENEMY_OFF ) + { + if ( work->faseout == 0 ) + { + work->hom->flag = FALSE ; + work->alert_level = 0 ; + GM_ConfigControlAttribute( &(work->control), 0 ) ; + work->visible = 0 ; + work->target->class = TARGET_AVAIL; + + work->faseout = 1 ; + work->act_status = EN_FASEOUT ; + } + } +} + +void s00a_watcher_800C410C( WatcherWork* work ) +{ + RootFlagCheck_800C3EE8( work ); + s00a_watcher_800C409C( work ); +} + +void s00a_watcher_800C4138( DG_OBJS* objs, DG_DEF* def ) +{ + int i; + DG_OBJ *obj; + DG_FreeObjsPacket( objs, 0 ); + DG_FreeObjsPacket( objs, 1 ); + + objs->def = def; + obj = objs->objs; + for ( i = 0 ; i < 16 ; i++ ) + { + obj->model = &def->model[ i ]; + obj->n_packs = def->model[ i ].n_faces; + obj++; + } +} + +void s00a_watcher_800C41B4( WatcherWork *work ) +{ + if ( work->visible ) + { + if ( work->field_B7B == 1 ) + { + if ( GM_GameStatus & (GAME_FLAG_BIT_07 | STATE_BEHIND_CAMERA) || GM_Camera.first_person ) + { + if ( work->has_kmd != work->field_B7B ) + { + work->has_kmd = work->field_B7B; + s00a_watcher_800C4138( work->body.objs, work->def ); + } + } + else if ( work->has_kmd ) + { + work->has_kmd = 0; + s00a_watcher_800C4138( work->body.objs, work->kmd ); + } + } + DG_VisibleObjs( work->body.objs ); + DG_VisibleObjs( work->weapon.objs ); + work->field_AF4[0] = 1; + + if ( work->control.map->index & GM_PlayerMap ) + { + work->field_AFC[0] = 1; + } + else + { + work->field_AFC[0] = 0; + } + } + else + { + DG_InvisibleObjs( work->body.objs ); + DG_InvisibleObjs( work->weapon.objs ); + work->field_AF4[0] = 0; + work->field_AFC[0] = 0; + } +} + +void WatcherAct_800C430C( WatcherWork *work ) +{ + VECTOR vec; + TARGET *trgt; + CONTROL *ctrl; + TARGET *trgt2; + + ctrl = &( work->control ) ; + if (GM_CheckMessage( &( work->actor ) , ctrl->name, HASH_KILL ) ) + { + GV_DestroyActor( &( work->actor ) ); + return; + } + + s00a_watcher_800C410C( work ); + if ( !work->faseout ) + { + EnemyPushMove( work ); + GM_ActControl( ctrl ); + GM_ActObject2( &( work->body ) ); + GM_ActObject2( &( work->weapon ) ); + + DG_GetLightMatrix2( &( ctrl->mov ), work->light ); + + EnemyActionMain( work ); + trgt = work->target; + GM_MoveTarget( trgt, &( ctrl->mov ) ); + + GM_PushTarget( trgt ); + + if ( trgt->class & TARGET_TOUCH ) + { + trgt2 = &work->field_94C; + if ( trgt2->class & TARGET_TOUCH ) + { + if ( trgt2->damaged & TARGET_TOUCH ) + { + trgt2->damaged &= ~TARGET_TOUCH; + } + GM_MoveTarget( &( work->field_94C ), &( ctrl->mov ) ); + GM_TouchTarget( &( work->field_94C ) ); + } + } + } + s00a_watcher_800C41B4( work ); + vec.vx = vec.vz = work->scale; + vec.vy = work->field_9E2; + ScaleMatrix( &( work->body.objs->world ), &vec ); +} + +void InitTarget_800C444C( WatcherWork *work ) +{ + TARGET *target; + TARGET *target2; + int life; + int faint; + + target = work->target; + life = work->param_life; + faint = work->param_faint; + + GM_SetTarget( target, TARGET_FLAG, ENEMY_SIDE, &ENEMY_TARGET_SIZE ); + GM_SetPowerTarget( target, POWER_DECREASE, -1, life, faint, &ENEMY_TARGET_FORCE ); + GM_SetCaptureTarget( target, -1, faint, NULL, NULL); + + GM_TargetBody( target, &( work->body.objs->objs[1].world ) ); + + target2 = &work->field_904; + GM_SetTarget( target2, TARGET_POWER, PLAYER_SIDE, &ENEMY_ATTACK_SIZE ); + GM_SetPowerTarget( target2, POWER_CONST | POWER_EXPLODE, 5, 0, 3, &ENEMY_ATTACK_FORCE ); + + target2 = &work->field_94C; + GM_SetTarget( target2, ( TARGET_TOUCH ), ENEMY_SIDE, &ENEMY_TOUCH_SIZE ); + GM_SetPowerTarget( target2, POWER_CONST | POWER_EXPLODE, 5, 0, 0, &ENEMY_TOUCH_FORCE ); +} + +void s00a_watcher_800C4578( WatcherWork* work ) +{ + WatcherUnk *s; + s = &work->unknown; + + GV_ZeroMemory(s, 0x24); + s->field_00 = 0; + s->field_1C = 0x1C2; + s->field_1E = 1; + + work->action = 0; + work->action2 = 0; + work->time = 0; + work->time2 = 0; +} + +int s00a_watcher_800C45D4( WatcherWork* work, int name, int where ) +{ + int i; + int has_kmd; + char *opt, *opt2; + CONTROL *ctrl; + OBJECT *body; + OBJECT *arm; //? + SVECTOR shadow; + + ctrl = &work->control; + if ( GM_InitControl( ctrl, name, where ) < 0 ) return -1; + + opt = GCL_GetOption( 'p' ); + + GM_ConfigControlString( ctrl, opt, GCL_GetOption( 'd' ) ) ; + GM_ConfigControlAttribute( ctrl, 13 ); + GM_ConfigControlInterp( ctrl, 4 ); + + ctrl->seg_flag = 2; + + GM_ConfigControlTrapCheck( ctrl ); + + ctrl->r_sphere = -1; + + body = &work->body; + arm = &work->weapon; + + GM_InitObject( body, KMD_IPPANHEI, 0x32D, OAR_ENEMY ) ; + GM_ConfigObjectJoint( body ) ; + GM_ConfigMotionControl( body, &work->m_ctrl, OAR_ENEMY, work->m_segs1, work->m_segs2, ctrl, work->rots ); + GM_ConfigObjectLight( body, work->light ); + + work->field_B7B = 0; + + opt2 = GCL_GetOption( 'y' ); + if ( opt2 ) { + work->field_B7B = GCL_StrToInt( opt2 ); + } + + has_kmd = work->field_B7B; + if ( has_kmd == 1 ) + { + work->def = body->objs->def; + work->kmd = GV_GetCache( GV_CacheID( HASH_LOPRYHEI, 'k' ) ); + work->has_kmd = has_kmd; + } + + work->hom = GM_AllocHomingTarget( &body->objs->objs[6].world, ctrl ); + GM_InitObject( arm, KMD_FAMAS, 0x6D, 0 ); + GM_ConfigObjectLight( arm, work->light ) ; + GM_ConfigObjectRoot( arm, body, 4 ); + + for ( i = 0 ; i < 8 ; i++ ) + { + work->field_B00[i] = 0; + } + + shadow.vy = 6; + shadow.vz = 12; + shadow.pad = 15; + shadow.vx = 0; + + work->field_AF0 = (void*)NewShadow2( ctrl, body, shadow, &work->field_AF4 ) ; + work->field_AF8 = NewGunLight( &( body->objs->objs[4].world ), &work->field_AFC ) ; + + ENE_SetPutChar( work, 0 ); + s00a_watcher_800C4578 ( work ); + + return 0; +} + +//FreeWatcher? +void s00a_watcher_800C4814( WatcherWork* work ) +{ + GM_FreeHomingTarget( work->hom ); + GM_FreeControl( &( work->control ) ); + GM_FreeObject( &( work->body ) ); + GM_FreeObject( &( work->weapon ) ); + GM_FreeTarget( work->target ); + GV_DestroyOtherActor( work->field_AF8 ); + GV_DestroyOtherActor( work->field_AF0 ); +} + +void WatcherDie_800C487C( WatcherWork* work ) +{ + s00a_watcher_800C4814( work ); +} + +int ReadNodes_800C489C( WatcherWork* work ) +{ + int i; + HZD_PAT *patrol; + HZD_PTP *points; + + patrol = work->control.map->hzd->def->routes; + patrol = &patrol[ work->param_root ]; + + work->n_nodes = patrol->n_points; + + if ( work->n_nodes <= 0 ) return -1; + + points = patrol->points; + for ( i = 0 ; i < work->n_nodes ; i++ ) + { + work->nodes[i].vx = points->x; + work->nodes[i].vy = points->y; + work->nodes[i].vz = points->z; + work->nodes[i].pad = points->command; + points++; + } + return 0; +} + +int s00a_watcher_800C4938( char *opt, char* c ) +{ + int i; + unsigned char *res; + + for ( i = 0 ; ( res = GCL_NextStr() ) ; i++ ) + { + *c = GCL_StrToInt( res ); + c++; + } + + return i; +} + +int s00a_watcher_800C4990( char *opt, short* s ) +{ + int i; + unsigned char *res; + + for ( i = 0 ; ( res = GCL_NextStr() ) ; i++ ) + { + *s = GCL_StrToInt( res ); + s++; + } + + return i; +} + +int s00a_watcher_800C49E8( char *opt, int* l ) +{ + int i; + unsigned char *res; + + for ( i = 0 ; ( res = GCL_NextStr() ) ; i++ ) + { + *l = GCL_StrToInt( res ); + l++; + } + + return i; +} + +int s00a_watcher_800C4A40( WatcherWork *work ) +{ + int i; + char *opt; + int ret; + + for ( i = 0 ; i < 8 ; i++ ) + { + work->field_BB0[i] = s00a_dword_800C3348[i]; + } + + opt = GCL_GetOption( 't' ); + if ( opt ) + { + ret = s00a_watcher_800C49E8( opt, &work->field_BB0[1] ); + if ( ret > 4 ) + { + printf( (char *)aErrerrerrsettimeover_800DFC7C ) ; + return -1; + } + } + + for ( i = 0 ; i < 4 ; i++ ) + { + work->field_BD0[i] = i * 1024; + } + + opt = GCL_GetOption( 'i' ); + + if ( opt ) + { + ret = s00a_watcher_800C4990( opt, work->field_BD0 ); + if ( ret > 4 ) + { + printf( (char *)aErrerrerrsetdirover_800DFC98 ) ; + return -1; + } + } + + return 0; +} + +int s00a_watcher_800C4B18( WatcherWork* work ) +{ + int res; + char *opt; + work->field_C34 = 0; + + opt = GCL_GetOption( 'j' ); + if ( opt ) + { + res = s00a_watcher_800C4938( opt, work->field_C35 ); + if ( res > 7 ) + { + printf( (char *)aErrerrerrsettimeover_800DFC7C ); + return -1; + } + work->field_C34 = res; + } + return 0; +} + +void WatcherGetResources_800C4B7C( WatcherWork *work, int name, int where ) +{ + SVECTOR svec; + int addr; + char *opt; + int i; + + s00a_watcher_800C45D4( work, name, where ) ; + work->field_B78 = s00a_command_800CEA2C( work ) ; + + if ( work->field_B78 << 24 < 0 ) + { + printf( (char *)aErrnotenoughwork_800DFCB4 ) ; + } + + s00a_watcher_800C4A40( work ) ; + + work->param_root = 0; + opt = GCL_GetOption( 'r' ); + if ( opt ) + { + work->param_root = GCL_StrToInt( ( char* )opt ); + } + + work->param_life = 192; + opt = GCL_GetOption( 'l' ); + if ( opt ) + { + work->param_life = GCL_StrToInt( ( char* )opt ); + } + + work->param_faint = 10; + opt = GCL_GetOption( 'f' ); + if ( opt ) + { + work->param_faint = GCL_StrToInt( ( char* )opt ); + } + + work->param_blood = 65; + opt = GCL_GetOption( 'b' ); + if ( opt ) + { + work->param_blood = GCL_StrToInt( ( char* )opt ); + } + + work->local_data = 0; + work->local_data2 = 0; + //work->local_data = work->local_data2 = 0 ; + opt = GCL_GetOption( 'h' ); + if ( opt ) + { + work->local_data = GCL_StrToInt( ( char* )opt ); + } + + if ( work->local_data == 3 ) + { + opt = GCL_NextStr(); + if ( opt ) + { + work->local_data2 = GCL_StrToInt( ( char* )opt ); + } + } + + work->field_B81 = 0xFF; + opt = GCL_GetOption('g'); + if ( opt ) + { + work->field_B81 = GCL_StrToInt( ( char* )opt ); + } + if ( work->param_blood == 'Z' ) + { + work->field_B81 = 0; + } + + s00a_watcher_800C4B18( work ); + opt = GCL_GetOption( 'e' ); + if ( opt ) + { + work->field_C3C = GCL_StrToInt( GCL_NextStr() ); + } + else + { + work->field_C3C = -1; + } + + work->field_BFC = s00a_dword_800C3328[ work->field_B78 ]; + work->field_C00 = work->field_B78; + work->field_B7C = 0xFF; + + opt = GCL_GetOption( 'n' ); + if ( opt ) + { + GCL_StrToSV( ( char* )opt, (short *)&svec ); + if ( svec.vy < 0x7530 ) + { + work->field_B7C = HZD_GetAddress( work->control.map->hzd, &svec, -1 ); + } + else + { + work->field_B7C = 0xFF; + } + } + + work->param_area = 'A'; + opt = GCL_GetOption( 'a' ); + if (opt != 0) + { + work->param_area = GCL_StrToInt( ( char* )opt ); + } + + if ( work->param_area == 'S' ) ENE_SetPutChar( work, PUTBREATH ) ; /* ??��??????????? */ + work->scale = 4096 ; /* ??��?��?��?? */ + + if ( ( opt = GCL_GetOption( 's' ) ) != NULL ) work->scale += GCL_StrToInt( ( char* )opt ); + work->field_9E2 = work->scale; //line not in leak + work->param_item = 1; + + //fprintf(0,"Life=%d Faint=%d Blood=%c Area=%c \n", + // work->param.life, work->param.faint, work->param.blood,work->param.area); + + if( ReadNodes_800C489C( work ) < 0 ) fprintf( 1, aWatcharcactionpointerr_800DFCCC ); + + /* + �????????????��?��????????????????? + */ + if( ( work->target = GM_AllocTarget() ) != NULL ) InitTarget_800C444C( work ) ; + + /* + ???????????��????��???????��?��????????????????? + */ + work->think1 = 0 ; + work->think2 = 0 ; + work->think3 = 0 ; + work->think4 = 0 ; + work->count3 = 0 ; + work->l_count = 0 ; + work->t_count = 0 ; + work->mark_time = 0 ; + + work->mark = NULL ; + work->field_B64 = 0 ; + work->mosaic = NULL ; + + work->next_node = 0; + work->search_flag = 0; + work->act_status = 0; + + work->target_pos = work->nodes[ 0 ] ; + work->target_addr = HZD_GetAddress( work->control.map->hzd, &( work->target_pos ), -1 ) ; + work->target_map = GM_CurrentMap; + + work->alert_level = 0; + work->visible = 1; + work->vision.angle = 0x200; + work->field_9E6 = 0; + work->vision.facedir = 0; + work->field_B94 = 0; + work->field_B96 = 0; + work->pad.sound = 0; + work->pad.time = 0; + work->vision.length = COM_EYE_LENGTH; + work->field_BA4 = COM_NO_POINT; + work->subweapon = 0; + work->field_C48 = 0; + work->faseout = 0; + work->field_C2E = 1; + + work->control.mov = work->nodes[ 0 ] ; + work->param_c_root = work->param_root; + work->field_B7F = work->field_B7C; + + for ( i = 0 ; i <= 4 ; i++ ) + { + work->modetime[i] = 0; + } + + work->field_BA3 = 7; + work->field_BA2 = 0; + work->field_BA0 = -1; + + opt = GCL_GetOption( 'c' ); + if ( opt ) + { + work->field_BA3 |= 0x10; + } + + GM_ConfigControlRadarparam( &work->control , 0, 0x200, COM_EYE_LENGTH, 0 ); + work->start_pos = work->nodes[ 0 ] ; + work->start_map = GM_CurrentMap; + addr = HZD_GetAddress( work->control.map->hzd, &( work->control.mov ), -1 ); + + work->start_addr = addr; + work->field_C08 = addr; + work->field_BF0 = addr; + + work->field_C14 = work->start_pos; +} + +#define EXEC_LEVEL GV_ACTOR_PREV + +void *NewSnakeWatcher(int name, int where, int argc, char **argv) +{ + WatcherWork *work ; + + work = GV_NewActor( EXEC_LEVEL, sizeof( WatcherWork ) ) ; + if ( work != NULL ) { + GV_SetNamedActor( &( work->actor ), WatcherAct_800C430C, WatcherDie_800C487C, aWatcherc_800DFCEC ); + WatcherGetResources_800C4B7C( work, name, where ); + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/enemy/watcher.h b/components/mgs/mgs/source/enemy/watcher.h new file mode 100644 index 00000000..8fc264fb --- /dev/null +++ b/components/mgs/mgs/source/enemy/watcher.h @@ -0,0 +1,6 @@ +#ifndef __MGS_ENEMY_WATCHER_H__ +#define __MGS_ENEMY_WATCHER_H__ + +void *NewSnakeWatcher(int name, int where, int argc, char **argv); + +#endif // __MGS_ENEMY_WATCHER_H__ diff --git a/components/mgs/mgs/source/equip/bandana.c b/components/mgs/mgs/source/equip/bandana.c new file mode 100644 index 00000000..6fd55ada --- /dev/null +++ b/components/mgs/mgs/source/equip/bandana.c @@ -0,0 +1,88 @@ +#include "equip.h" + +#include "common.h" +#include "game/game.h" +#include "linkvar.h" + +extern short snake_weapon_idx_800BDCBA; +extern short snake_weapon_max_ammo_800BDCBC; +extern short snake_mag_size_800BDCB8; + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_AFTER2 + +typedef struct _Work +{ + GV_ACT actor; + OBJECT *parent; + int unused1; + int unused2; +} Work; + +/*---------------------------------------------------------------------------*/ + +STATIC const char *bandana_orig_tex[3] = { + "sna_face", + "sna_face2", + "sna_face3" +}; +STATIC const char *bandana_new_tex[3] = { + "sna_mf1", + "sna_mf2", + "sna_mf3" +}; + +static void SwapTextures(OBJECT *parent) +{ + int i; + + for (i = 0; i < 2; i++) + { + DG_FreeObjPacket(&parent->objs->objs[6], i); + } + + for (i = 0; i < 3; i++) + { + EQ_ChangeTexture(bandana_orig_tex[i], bandana_new_tex[i]); + } +} + +static void Act(Work *work) +{ + int ammo; + + if (snake_weapon_idx_800BDCBA >= 0) + { + ammo = GM_CurrentWeapon; + if (ammo < snake_weapon_max_ammo_800BDCBC) + { + GM_Weapons[snake_weapon_idx_800BDCBA] = snake_weapon_max_ammo_800BDCBC; + } + + ammo = GM_Magazine; + if (ammo < snake_mag_size_800BDCB8) + { + GM_Magazine = snake_mag_size_800BDCB8; + } + } +} + +static void Die(Work *work) +{ + SwapTextures(work->parent); +} + +/*---------------------------------------------------------------------------*/ + +void *NewBandana(CONTROL *control, OBJECT *parent, int num_parent) +{ + Work *work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "bandana.c"); + work->parent = parent; + SwapTextures(parent); + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/equip/bodyarm.c b/components/mgs/mgs/source/equip/bodyarm.c new file mode 100644 index 00000000..f3ec5a30 --- /dev/null +++ b/components/mgs/mgs/source/equip/bodyarm.c @@ -0,0 +1,74 @@ +#include "equip.h" + +#include "common.h" +#include "libdg/libdg.h" +#include "linkvar.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_AFTER + +typedef struct _Work +{ + GV_ACT actor; + OBJECT *parent; +} Work; + +/*---------------------------------------------------------------------------*/ + +STATIC const char *bodyarm_orig_tex[4] = { + "sna_chest1", + "sna_chest2", + "sna_chest3", + "sna_hip1" +}; +STATIC const char *bodyarm_new_tex[4] = { + "sna_armer1", + "sna_armer2", + "sna_armer3", + "sna_armer4" +}; + +static void SwapTextures(OBJECT *a1) +{ + int i; + + for (i = 0; i < 2; ++i) + { + DG_FreeObjPacket(&a1->objs->objs[0], i); + DG_FreeObjPacket(&a1->objs->objs[1], i); + } + + for (i = 0; i < 4; i++) + { + EQ_ChangeTexture(bodyarm_orig_tex[i], bodyarm_new_tex[i]); + } +} + +static void Die(Work *work) +{ + if (!(GM_OptionFlag & OPTION_TUXEDO)) + { + SwapTextures(work->parent); + } +} + +/*---------------------------------------------------------------------------*/ + +void *NewBodyArmor(CONTROL *control, OBJECT *parent, int num_parent) +{ + Work *work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, NULL, Die, "bodyarm.c"); + + work->parent = parent; + + if (!(GM_OptionFlag & OPTION_TUXEDO)) + { + SwapTextures(parent); + } + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/equip/box.c b/components/mgs/mgs/source/equip/box.c new file mode 100644 index 00000000..cd3ca559 --- /dev/null +++ b/components/mgs/mgs/source/equip/box.c @@ -0,0 +1,165 @@ +#include "equip.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "linkvar.h" +#include "game/game.h" +#include "thing/sight.h" + +extern int dword_8009F604; +extern GM_CameraSystemWork GM_Camera; + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_AFTER +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_GBOUND | DG_FLAG_SHADE | DG_FLAG_ONEPIECE ) + +#define CB_BOX_SIGHT 0xe2a9 // GV_StrCode("cb_box") + +#define CB_BOX_MODEL GV_StrCode("cb_box") +#define CB_BOX_MSG GV_StrCode("段ボール") + +typedef struct _Work +{ + GV_ACT actor; + OBJECT_NO_ROTS object; + CONTROL *control; + OBJECT *parent; + int invisible; + const char **names; + int unused; +} Work; + +/*---------------------------------------------------------------------------*/ + +STATIC int box_1p_view_flag = FALSE; + +STATIC const char *box_texture_names[8] = { + "cb_box11", "cb_box12", + "cb_box41", "cb_box42", // cb_box3 (Canyon) was cut + "cb_box21", "cb_box22", + "cb_box41", "cb_box42" +}; + +static int CheckMessage(Work *work) +{ + CONTROL *control = work->control; + GV_MSG *msg; + int n_msgs; + u_short code; + + control->n_msg = GV_ReceiveMessage(control->name, &control->msg); + msg = control->msg; + + for (n_msgs = control->n_msg; n_msgs > 0; n_msgs--) + { + if (CB_BOX_MSG == msg->message[0]) + { + code = msg->message[1]; + + if (code == 1) + { + work->invisible = TRUE; + return 1; + } + } + + msg++; + } + + return 0; +} + +static void Act(Work *work) +{ + GM_CurrentMap = work->control->map->index; + + DG_GroupObjsEx(work->object.objs); + + CheckMessage(work); + if ( work->invisible ) + { + DG_InvisibleObjs(work->object.objs); + } + else if ( GM_Camera.first_person && (work->parent->objs->flag & DG_FLAG_INVISIBLE) ) + { + DG_InvisibleObjs(work->object.objs); + + if ( dword_8009F604 != CB_BOX_SIGHT ) + { + box_1p_view_flag = TRUE; + NewSight(CB_BOX_SIGHT, CB_BOX_SIGHT, (short*)&box_1p_view_flag, IT_Scope, NULL); + } + } + else + { + box_1p_view_flag = FALSE; + DG_VisibleObjs(work->object.objs); + } +} + +static void Die(Work *work) +{ + const char **name; + int i; + + GM_FreeObject((OBJECT *)&work->object); + + name = work->names; + for (i = 0; i < 2; i++) + { + EQ_ChangeTexture(name[i], box_texture_names[i]); + } + + box_1p_view_flag = FALSE; +} + +static int GetResources(Work *work, OBJECT *parent) +{ + OBJECT_NO_ROTS *object = &work->object; + const char **name; + int i; + + GM_InitObjectNoRots(object, CB_BOX_MODEL, BODY_FLAG, 0); + + if (!work->object.objs) + { + return -1; + } + + work->object.objs->objs[0].raise = 250; + GM_ConfigObjectRoot((OBJECT *)object, parent, 0); + + name = &box_texture_names[(GM_CurrentItemId - IT_Box1) * 2]; + work->names = name; + + for (i = 0; i < 2; i++) + { + EQ_ChangeTexture(box_texture_names[i], name[i]); + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewBox(CONTROL *control, OBJECT *parent, int num_parent) +{ + Work *work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "box.c"); + + if (GetResources(work, parent) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + work->control = control; + work->parent = parent; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/equip/effect.c b/components/mgs/mgs/source/equip/effect.c new file mode 100644 index 00000000..d968c505 --- /dev/null +++ b/components/mgs/mgs/source/equip/effect.c @@ -0,0 +1,84 @@ +#include "common.h" +#include "libdg/libdg.h" +#include "game/game.h" + +#define TEX_COPY_SIZE (sizeof(DG_TEX) - offsetof(DG_TEX, used)) + +static void EQ_MoveTexture(u_short *src, u_short *dst) +{ + u_short i; + + for (i = 0; i < TEX_COPY_SIZE / sizeof(u_short); i++) + { + *dst++ = *src++; + } +} + +void EQ_ChangeTexture(const char *itemName1, const char *itemName2) +{ + u_short temp[TEX_COPY_SIZE / sizeof(u_short)]; + DG_TEX *tex1; + DG_TEX *tex2; + + tex1 = DG_GetTexture(GV_StrCode(itemName1)); + tex2 = DG_GetTexture(GV_StrCode(itemName2)); + + EQ_MoveTexture((u_short *)&tex1->used, temp); + EQ_MoveTexture((u_short *)&tex2->used, (u_short *)&tex1->used); + EQ_MoveTexture(temp, (u_short *)&tex2->used); +} + +// Remove head model +void EQ_InvisibleHead(OBJECT *object, short *n_packs, short *raise) +{ + if (object->objs->n_models >= 7) + { + *n_packs = object->objs->objs[6].n_packs; + *raise = object->objs->objs[6].raise; + DG_FreeObjPacket(&object->objs->objs[6], 0); + DG_FreeObjPacket(&object->objs->objs[6], 1); + object->objs->objs[6].n_packs = 1; + object->objs->objs[6].raise = -30000; + } +} + +// Put head model back +void EQ_VisibleHead(OBJECT *object, short *n_packs, short *raise) +{ + if (object->objs->n_models >= 7) + { + object->objs->objs[6].n_packs = *n_packs; + object->objs->objs[6].raise = *raise; + DG_FreeObjPacket(&object->objs->objs[6], 0); + DG_FreeObjPacket(&object->objs->objs[6], 1); + } +} + +#ifdef VR_EXE +// Copies of EQ_InvisibleHead/EQ_VisibleHead +// but with additional parameter "idx" + +void EQ_InvisibleUnit(OBJECT *object, int idx, short *n_packs, short *raise) +{ + if (idx < object->objs->n_models) + { + *n_packs = object->objs->objs[idx].n_packs; + *raise = object->objs->objs[idx].raise; + DG_FreeObjPacket(&object->objs->objs[idx], 0); + DG_FreeObjPacket(&object->objs->objs[idx], 1); + object->objs->objs[idx].n_packs = 1; + object->objs->objs[idx].raise = -30000; + } +} + +void EQ_VisibleUnit(OBJECT *object, int idx, short *n_packs, short *raise) +{ + if (idx < object->objs->n_models) + { + object->objs->objs[idx].n_packs = *n_packs; + object->objs->objs[idx].raise = *raise; + DG_FreeObjPacket(&object->objs->objs[idx], 0); + DG_FreeObjPacket(&object->objs->objs[idx], 1); + } +} +#endif // VR_EXE diff --git a/components/mgs/mgs/source/equip/equip.h b/components/mgs/mgs/source/equip/equip.h new file mode 100644 index 00000000..3d166d6e --- /dev/null +++ b/components/mgs/mgs/source/equip/equip.h @@ -0,0 +1,34 @@ +#ifndef __MGS_EQUIP_H__ +#define __MGS_EQUIP_H__ + +#include "game/game.h" // for OBJECT, CONTROL + +void *NewBandana( CONTROL *control, OBJECT *parent, int num_parent ); +void *NewBodyArmor( CONTROL *control, OBJECT *parent, int num_parent ); +void *NewBox( CONTROL *control, OBJECT *parent, int num_parent ); +void *NewGasMask( CONTROL *control, OBJECT *parent, int num_parent ); +void *NewKogaku2( CONTROL *control, OBJECT *object, int num_parent ); +void *NewKogaku3( CONTROL *control, OBJECT *object, int num_parent ); +void *NewScope( CONTROL *control, OBJECT *parent, int num_parent ); +void *NewTabako( CONTROL *control, OBJECT *parent, int num_parent ); + +void *NewGoggleManager( int type ); +void *NewGoggleSight( int type ); +void *NewGasmaskSight(); // This has an issue with unused params + +/* these should be private to jpegcam.c, but bss.c needs it */ +typedef signed char TMat8x8B[8][8]; // 8 x 8 byte matrix +typedef signed char TMat16x16B[16][16]; // 16 x 16 byte matrix +typedef int TMat8x8I[8][8]; // 8 x 8 integer matrix + +void *NewJpegcam( CONTROL *control, OBJECT *parent, int num_parent ); + +void EQ_ChangeTexture(const char *itemName1, const char *itemName2); +void EQ_InvisibleHead(OBJECT *object, short *n_packs, short *raise); +void EQ_VisibleHead(OBJECT *object, short *n_packs, short *raise); +#ifdef VR_EXE +void EQ_InvisibleUnit(OBJECT *object, int idx, short *n_packs, short *raise); +void EQ_VisibleUnit(OBJECT *object, int idx, short *n_packs, short *raise); +#endif + +#endif // __EQUIP_EQUIP_H__ diff --git a/components/mgs/mgs/source/equip/gasmask.c b/components/mgs/mgs/source/equip/gasmask.c new file mode 100644 index 00000000..6c9a7330 --- /dev/null +++ b/components/mgs/mgs/source/equip/gasmask.c @@ -0,0 +1,130 @@ +#include "equip.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" +#include "linkvar.h" + +extern int dword_8009F46C; +extern GM_CameraSystemWork GM_Camera; + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_AFTER +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_GBOUND | DG_FLAG_SHADE | DG_FLAG_ONEPIECE ) + +#define GASMASK_MODEL GV_StrCode("gas_mask") + +typedef struct _Work +{ + GV_ACT actor; + OBJECT_NO_ROTS object; + CONTROL *control; + OBJECT *parent; + int num_parent; + int time; + GV_ACT *sight; + short unused; + short saved_packs; + short saved_raise; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void Act(Work *work) +{ + int map; + + map = work->control->map->index; + DG_GroupObjsEx(work->object.objs); + GM_CurrentMap = map; + + if (work->parent->objs->flag & DG_FLAG_INVISIBLE) + { + DG_InvisibleObjs(work->object.objs); + } + else + { + DG_VisibleObjs(work->object.objs); + } + + if (GM_Camera.first_person && + (work->parent->objs->flag & DG_FLAG_INVISIBLE) && + GM_CurrentWeaponId != WP_Stinger && + GM_CurrentWeaponId != WP_Rifle && + dword_8009F46C != 1) + { + work->time++; + if (work->time >= 9 && !work->sight) + { + work->sight = NewGasmaskSight(); + } + } + else + { + work->time = 0; + + if (work->sight) + { + GV_DestroyOtherActor(work->sight); + work->sight = NULL; + } + } +} + +static void Die(Work *work) +{ + GM_FreeObject((OBJECT *)&work->object); + EQ_VisibleHead(work->parent, &work->saved_packs, &work->saved_raise); + + if (work->sight) + { + GV_DestroyOtherActor(work->sight); + } +} + +static int GetResources(Work *work, OBJECT *parent, int num_parent) +{ + OBJECT_NO_ROTS *object = &work->object; + + work->parent = parent; + work->num_parent = num_parent; + + work->time = 0; + + GM_InitObjectNoRots(object, GASMASK_MODEL, BODY_FLAG, 0); + if (!work->object.objs) + { + return -1; + } + + GM_ConfigObjectRoot((OBJECT *)object, parent, num_parent); + GM_ConfigObjectLight((OBJECT *)object, parent->light); + + EQ_InvisibleHead(parent, &work->saved_packs, &work->saved_raise); + + work->sight = NULL; + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewGasMask(CONTROL *control, OBJECT *parent, int num_parent) +{ + Work *work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "gasmask.c"); + + if (GetResources(work, parent, num_parent) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + work->control = control; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/equip/gglmng.c b/components/mgs/mgs/source/equip/gglmng.c new file mode 100644 index 00000000..02535a2c --- /dev/null +++ b/components/mgs/mgs/source/equip/gglmng.c @@ -0,0 +1,84 @@ +#include "equip.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "game/game.h" +#include "linkvar.h" + +extern GM_CameraSystemWork GM_Camera; +extern int dword_8009F46C; + +/*---------------------------------------------------------------------------*/ +// Goggle Manager + +#define EXEC_LEVEL GV_ACTOR_AFTER2 + +typedef struct _Work +{ + GV_ACT actor; + int type; // type of goggles (5 = night vision, 6 = thermal) + int time; + GV_ACT *sight; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void Act(Work *work) +{ + if (GM_Camera.first_person != 0) + { + if (GM_CurrentWeaponId == WP_Stinger || + GM_CurrentWeaponId == WP_Rifle || + dword_8009F46C == 1 || + (GM_PlayerStatus & PLAYER_NOT_SIGHT)) + { + work->time = 0; + + if (work->sight) + { + GV_DestroyOtherActor(work->sight); + work->sight = NULL; + } + } + else if (++work->time == 8) + { + work->sight = NewGoggleSight(work->type); + } + } + else + { + if (work->sight) + { + GV_DestroyOtherActor(work->sight); + work->sight = NULL; + } + + work->time = 0; + } +} + +static void Die(Work *work) +{ + if (work->sight) + { + GV_DestroyOtherActor(work->sight); + } +} + +/*---------------------------------------------------------------------------*/ + +void *NewGoggleManager(int type) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "gglmng.c"); + work->type = type; + work->time = 0; + work->sight = NULL; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/equip/gglsight.c b/components/mgs/mgs/source/equip/gglsight.c new file mode 100644 index 00000000..f1b39720 --- /dev/null +++ b/components/mgs/mgs/source/equip/gglsight.c @@ -0,0 +1,580 @@ +#include "equip.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" +#include "menu/menuman.h" +#include "thing/sight.h" +#include "chara/snake/sna_init.h" +#include "linkvar.h" + +extern int dword_8009F604; + +/*---------------------------------------------------------------------------*/ +// Night Vision/Thermal Goggles sight + +#define EXEC_LEVEL GV_ACTOR_AFTER2 + +#define NV_GOGGLE_ID 0x9c26 // GV_StrCode("nv_ggle") +#define NV_GOGGLE_SIGHT1 0x8504 // GV_StrCode("nv_ggle1") +#define NV_GOGGLE_SIGHT2 0x8505 // GV_StrCode("nv_ggle2") +#define NV_GOGGLE_SIGHT3 0x8506 // GV_StrCode("nv_ggle3") + +#define IR_GOGGLE_ID 0x5425 // GV_StrCode("ir_ggle") +#define IR_GOGGLE_SIGHT1 0x84db // GV_StrCode("ir_ggle1") +#define IR_GOGGLE_SIGHT2 0x84dc // GV_StrCode("ir_ggle2") +#define IR_GOGGLE_SIGHT3 0x84dd // GV_StrCode("ir_ggle3") + +#define HUD_DISP_DELAY 6 +#define HUD_MOVE_DELAY 10 + +#define NV_HUD_COLOR COLOR_RED +#define IR_HUD_COLOR MAKE_RGB0(65,160,74) + +typedef struct _Work +{ + GV_ACT actor; + int type; + int field_24; + int color; + DVECTOR field_2C_4Array[4]; + int timer; + TILE_1 field_40_tile1[2][24]; + LINE_F2 field_280_lineF2[2][3]; + POLY_F4 field_2E0_polyF4[2][3]; + DR_TPAGE field_370_dr_tpage[2]; + int scan_timer; +} Work; + +STATIC short word_8009F714[] = {0, 0}; + +/*---------------------------------------------------------------------------*/ + +/** + * @brief Draw the horizontal lines of heading numbers on the overlay. + * + * @param work The goggles sight actor. + */ +static void DrawHudNumbers(Work *work) +{ + int r, g, b; + TILE_1 *tile; + OT_TYPE *ot; + + short a1; + short a2; + short a3; + short a4; + + short var_s0; // last number printed on the overlay (0-32) + short var_s1; + short x; + + if (work->timer < HUD_DISP_DELAY) + { + return; + } + + tile = work->field_40_tile1[GV_Clock]; + ot = DG_ChanlOTag(1); + // TextConfig_Flags_eCentreAlign_02 | TextConfig_Flags_eSemiTransparent_20 | TextConfig_Flags_eDark_100 + MENU_Locate(0, 0, 0x122); + + if (work->type == IT_NVG) + { + r = GET_R_FROM_RGBA(NV_HUD_COLOR); + g = GET_G_FROM_RGBA(NV_HUD_COLOR); + b = GET_B_FROM_RGBA(NV_HUD_COLOR); + } + else // thermal goggles + { + r = GET_R_FROM_RGBA(IR_HUD_COLOR); + g = GET_G_FROM_RGBA(IR_HUD_COLOR); + b = GET_B_FROM_RGBA(IR_HUD_COLOR); + } + + // set the color of the first person overlay menu + MENU_Color(r, g, b); + + // PlayerHeading + a1 = GM_PlayerControl->rot.vy & 0xfff; + a2 = a1 / 64; + a3 = a1 % 64; + a4 = ((a3 * 24) / 64) + 160; + x = a4 - ((a4 / 24) * 24); + var_s1 = a2 + (a4 / 24); + + while (x < 300) + { + if (x >= 20) + { + var_s0 = var_s1 - 64; + + if (var_s1 < 65) + { + if (var_s1 < 0) + { + var_s0 = -var_s1; + } + else + { + var_s0 = var_s1; + + if (var_s1 > 32) + { + var_s0 = 64 - var_s1; + } + } + } + + MENU_Locate(x, 148, 0x122); + MENU_Printf("%02d", var_s0); + + tile->x0 = x; + addPrim(ot, tile); + tile++; + } + + x += 24; + var_s1--; + } +} + +/** + * @brief Draw the 3 numbers on the overlay based on the player's heading. + * + * @param work the goggles sight work actor. + */ +static void DrawHudVector(Work *work) +{ + int r, g, b; + int vy; + + if (work->timer >= HUD_DISP_DELAY) + { + MENU_Locate(40, 56, 0x120); + + if (work->type == IT_NVG) + { + r = GET_R_FROM_RGBA(NV_HUD_COLOR); + g = GET_G_FROM_RGBA(NV_HUD_COLOR); + b = GET_B_FROM_RGBA(NV_HUD_COLOR); + } + else + { + r = GET_R_FROM_RGBA(IR_HUD_COLOR); + g = GET_G_FROM_RGBA(IR_HUD_COLOR); + b = GET_B_FROM_RGBA(IR_HUD_COLOR); + } + MENU_Color(r, g, b); + vy = GM_PlayerControl->rot.vy; + MENU_Printf("%ld\n", 8 * (vy & 0x07ff)); + MENU_Printf("%ld\n", 4 * (vy & 0x0fff)); + MENU_Printf("%ld\n", 16 * (vy & 0x03ff)); + } +} + +static void DrawHudBarGraph(Work *work) +{ + LINE_F2 *line; + POLY_F4 *poly; + DR_TPAGE *tpage; + OT_TYPE *ot; + int y, y2; + + if (work->timer < HUD_DISP_DELAY) + { + return; + } + + line = work->field_280_lineF2[GV_Clock]; + poly = work->field_2E0_polyF4[GV_Clock]; + tpage = &work->field_370_dr_tpage[GV_Clock]; + + ot = DG_Chanl(1)->ot[GV_Clock]; + + y = GM_PlayerControl->rot.vy & 4095; + y2 = ((y + 1024) & 2047) >> 5; + + if (y2 < 32) + { + y2 = 64 - y2; + } + + poly->y0 = poly->y1 = line->y0 = line->y1 = 140 - y2; + + addPrim(ot, line); + addPrim(ot, poly); + + line++; + poly++; + + y2 = (unsigned int)y >> 6; + + if (y2 < 32) + { + y2 = 64 - y2; + } + + poly->y0 = poly->y1 = line->y0 = line->y1 = 140 - y2; + + addPrim(ot, line); + addPrim(ot, poly); + + line++; + poly++; + + y2 = ((y - 1024) & 1023) >> 4; + + if (y2 < 32) + { + y2 = 64 - y2; + } + + poly->y0 = poly->y1 = line->y0 = line->y1 = 140 - y2; + + addPrim(ot, line); + addPrim(ot, poly); + addPrim(ot, tpage); +} + +/** + * @brief Draw the "SCAN" and "MODE" text on the overlay. + * + * @param work The goggles sight actor. + */ +static void DrawHudText(Work *work) +{ + int time; + int r, g, b; + + if (work->timer >= HUD_DISP_DELAY) + { + time = work->scan_timer; + // TextConfig_Flags_eLargeFont_10 | TextConfig_Flags_eSemiTransparent_20 | TextConfig_Flags_eDark_100 + MENU_Locate(41, 42, 304); + if (work->type == IT_NVG) + { + r = GET_R_FROM_RGBA(NV_HUD_COLOR); + g = GET_G_FROM_RGBA(NV_HUD_COLOR); + b = GET_B_FROM_RGBA(NV_HUD_COLOR); + } + else + { + r = GET_R_FROM_RGBA(IR_HUD_COLOR); + g = GET_G_FROM_RGBA(IR_HUD_COLOR); + b = GET_B_FROM_RGBA(IR_HUD_COLOR); + } + MENU_Color(r, g, b); + + work->scan_timer++; + if (work->scan_timer >= 17) + { + work->scan_timer = -16; + } + + if (time > 0) + { + MENU_Printf("SCAN"); + } + + MENU_Locate(137, 42, 304); + + if (work->type == IT_NVG) + { + MENU_Printf("MODE - B"); + } + else + { + MENU_Printf("MODE - A"); + } + } +} + +static void MoveSightElement(int time, u_short status, DVECTOR *axis, int dir, short sens, short max) +{ + if (time < HUD_MOVE_DELAY) + { + return; + } + + if (GV_PauseLevel || (GM_PlayerStatus & PLAYER_PAD_OFF)) + { + status = 0; + } + + if (dir & 1) + { + if (status & (PAD_LEFT | PAD_RIGHT)) + { + if (status & PAD_RIGHT) + { + if (!(dir & 4)) + { + if (axis->vx < max) + { + axis->vx += sens; + } + } + else if (axis->vx > -max) + { + axis->vx -= sens; + } + } + else if (!(dir & 4)) + { + if (axis->vx > -max) + { + axis->vx -= sens; + } + } + else if (axis->vx < max) + { + axis->vx += sens; + } + } + else if (axis->vx > 0) + { + axis->vx -= sens; + } + else if (axis->vx < 0) + { + axis->vx += sens; + } + } + + if (dir & 2) + { + if (status & (PAD_DOWN | PAD_UP)) + { + if (status & PAD_DOWN) + { + if (!(dir & 4)) + { + if (axis->vy < max) + { + axis->vy += sens; + } + } + else if (axis->vy > -max) + { + axis->vy -= sens; + } + } + else if (dir & 4) + { + if (axis->vy < max) + { + axis->vy += sens; + } + } + else if (axis->vy > -max) + { + axis->vy -= sens; + } + } + else if (axis->vy > 0) + { + axis->vy -= sens; + } + else if (axis->vy < 0) + { + axis->vy += sens; + } + } +} + +/*---------------------------------------------------------------------------*/ + +static void Act(Work *work) +{ + short *ptr = word_8009F714; + int type = work->type; + int f24 = work->field_24; + u_short status; + int time; + + if (GM_PlayerStatus & PLAYER_SECOND_CONTROLLER) + { + status = GV_PadData[1].status; + } + else + { + status = GV_PadData[0].status; + } + + GM_CheckShukanReverse(&status); + + ptr[0] = 1; + + if (type == IT_NVG && dword_8009F604 != f24) + { + NewSight(NV_GOGGLE_SIGHT1, f24, ptr, IT_Scope, NULL); + NewSight(NV_GOGGLE_SIGHT2, f24, ptr, IT_Scope, (short *)&work->field_2C_4Array[1]); + NewSight(NV_GOGGLE_SIGHT3, f24, ptr, IT_Scope, (short *)&work->field_2C_4Array[2]); + } + else if (dword_8009F604 != f24) + { + NewSight(IR_GOGGLE_SIGHT1, f24, ptr, IT_Scope, NULL); + NewSight(IR_GOGGLE_SIGHT2, f24, ptr, IT_Scope, (short *)&work->field_2C_4Array[1]); + NewSight(IR_GOGGLE_SIGHT3, f24, ptr, IT_Scope, (short *)&work->field_2C_4Array[2]); + } + + time = work->timer++; + + if (!(GM_PlayerStatus & PLAYER_NORMAL_WATCH)) + { + status &= ~(PAD_UP | PAD_DOWN); + } + + MoveSightElement(time, status, &work->field_2C_4Array[1], 3, 2, 20); + MoveSightElement(time, status, &work->field_2C_4Array[2], 5, 1, 12); + + // Draw the horizontal lines of heading numbers + DrawHudNumbers(work); + // Draw the SCAN and MODE text + DrawHudText(work); + // Draw the 3 heading integer indicators + DrawHudVector(work); + // Draw the 3 heading vertical lines + DrawHudBarGraph(work); +} + +/*---------------------------------------------------------------------------*/ + +static void Die(Work *work) +{ + word_8009F714[0] = 0; +} + +/*---------------------------------------------------------------------------*/ + +static void GoggleSightSetup1(Work *work) +{ + int i; + TILE_1 *tile = &work->field_40_tile1[0][0]; + + for (i = 0; i < 48; i++) + { + *(int *)&tile->r0 = work->color; + setTile1(tile); + tile->y0 = 144; + tile++; + } +} + +static void GoggleSightSetup2(Work *actor) +{ + int pos, count; + + DR_TPAGE *tpage; + LINE_F2 *line; + POLY_F4 *poly; + + tpage = actor->field_370_dr_tpage; + line = actor->field_280_lineF2[0]; + poly = actor->field_2E0_polyF4[0]; + pos = 40; + + for (count = 0; count < 6; count++) { + if (count == 3) pos = 40; + + *(int *)&line->r0 = actor->color; + + setLineF2(line); + setSemiTrans(line, 1); + line->x0 = pos + 3; + line->x1 = pos + 6; + + *(int *)&poly->r0 = actor->color; + + setPolyF4(poly); + setSemiTrans(poly, 1); + poly->x2 = pos; + poly->x0 = pos; + poly->x3 = pos + 3; + poly->x1 = pos + 3; + poly->y3 = 0x8c; + poly->y2 = 0x8c; + + line++; + poly++; + + pos += 12; + } + + SetDrawTPage(&tpage[0], 0, 1, 32); + SetDrawTPage(&tpage[1], 0, 1, 32); +} + +/*---------------------------------------------------------------------------*/ + +void *NewGoggleSight(int type) +{ + Work *work; + int status, count; + short *arr; + short *arr2; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "gglsight.c"); + + work->type = type; + + if (type == IT_NVG) + { + work->field_24 = NV_GOGGLE_ID; + work->color = NV_HUD_COLOR; + } + else if (type == IT_ThermG) + { + work->field_24 = IR_GOGGLE_ID; + work->color = IR_HUD_COLOR; + } + else + { + status = -1; + goto cleanup; + } + + GoggleSightSetup1(work); + GoggleSightSetup2(work); + + count = 0; + arr = (short *)work; + + // The compiler is optimising this and causing a mismatch if we try to do this cleanly + for (; count < 4; count++) + { + arr2 = &arr[22]; + arr2[0] = 0; + arr2[1] = 0; + arr += 2; + arr2 = arr; + } + + work->scan_timer = -16; + work->timer = 0; + + word_8009F714[0] = 0; + + status = 0; + + cleanup: + if (status < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/equip/gmsight.c b/components/mgs/mgs/source/equip/gmsight.c new file mode 100644 index 00000000..cd514069 --- /dev/null +++ b/components/mgs/mgs/source/equip/gmsight.c @@ -0,0 +1,76 @@ +// #include "equip.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "game/game.h" +#include "thing/sight.h" +#include "sound/g_sound.h" +#include "linkvar.h" + +extern int dword_8009F604; +extern short word_800BDCC0; + +/*---------------------------------------------------------------------------*/ +// Gas Mask sight + +#define EXEC_LEVEL GV_ACTOR_AFTER + +#define MASK_SIGHT 0x1303 // GV_StrCode("mask") +#define BREATH_DELAY 45 + +typedef struct _Work +{ + GV_ACT actor; + int time; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void Act(Work *work) +{ + if (dword_8009F604 != MASK_SIGHT) + { + NewSight(MASK_SIGHT, MASK_SIGHT, &word_800BDCC0, IT_Scope, NULL); + } + + if ((++work->time == BREATH_DELAY) && !(GM_PlayerStatus & PLAYER_NOT_SIGHT)) + { + GM_SeSet2(0, 63, SE_GASMASK_BREATH); + work->time = 0; + } +} + +static void Die(Work *work) +{ + word_800BDCC0 = 0; +} + +static int GetResources(Work *work, OBJECT *parent, int num_parent) +{ + word_800BDCC0 = 1; + work->time = 0; + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewGasmaskSight(CONTROL *control, OBJECT *parent, int num_parent) +{ + Work *work; + + word_800BDCC0 = 0; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, &Act, &Die, "gmsight.c"); + + if (GetResources(work, parent, num_parent) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/equip/jpegcam.c b/components/mgs/mgs/source/equip/jpegcam.c new file mode 100644 index 00000000..7f398e49 --- /dev/null +++ b/components/mgs/mgs/source/equip/jpegcam.c @@ -0,0 +1,1091 @@ +#include "equip.h" + +#include +#include +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "chara/snake/sna_init.h" +#include "game/game.h" +#include "linkvar.h" +#include "menu/menuman.h" +#include "memcard/memcard.h" +#include "thing/sight.h" +#include "sound/g_sound.h" +#include "strcode.h" + +extern GM_CameraSystemWork GM_Camera; +extern int dword_8009F604; +extern TMat8x8B gJpegcamMatrix2_800BDCD8; +extern GM_SnakeCameraWork GM_SnakeCamera; +extern char *dword_800BDCC8; +extern int dword_800BDCCC; +extern int dword_800BDCD0; +extern GM_SnakeCameraWork GM_SnakeCamera; + +extern short dword_800ABBD4; +extern short dword_800ABBDC; + +extern char memoryCardFileName[]; // = "BISLPM-99999 "; + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_MANAGER + +#define CAMERA_SIGHT 0xeee9 // GV_StrCode("camera") +#define CAMERA_SIGHT2 0xb3cd // GV_StrCode("camera_2") + +#define SEGMENT_ATR ( HZD_SEG_NO_DISP_RADAR|HZD_SEG_NO_COLLIDE ) + +typedef struct _Work +{ + GV_ACT actor; + CONTROL *control; + OBJECT *parent; + OBJECT_NO_ROTS goggles; + short head_saved_packs; + short head_saved_raise; + GV_PAD *pad_data; + SVECTOR field_54_vec; + SVECTOR field_5C_ang; + int state; + int field_68; + MAP *map; + int field_70; + int field_74; + int field_78; + int field_7C; + int field_80; + char *field_84; + char *field_88; + int field_8C_size; + void *field_90_pSight; + int field_94_bMakeVisible; + int field_98; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void jpegcam_unk1_80063704(char *buf, MEM_CARD *pMemcard, int arg2, int arg3); +static void jpegcam_unk2_80063888(char *param_1, int param_2); +static void jpegcam_unk3_800638B4(int *arg0); + +STATIC DATA_INFO stru_8009F2D8 = { + {67, 4}, 0, 2, "SAVE PHOTO", + (void *)jpegcam_unk1_80063704, + (void *)jpegcam_unk2_80063888, + (void *)jpegcam_unk3_800638B4 +}; + +STATIC signed char gJpegcamZigZagTable_8009F2EC[64] = { + 0x00, 0x01, 0x08, 0x10, 0x09, 0x02, 0x03, 0x0A, + 0x11, 0x18, 0x20, 0x19, 0x12, 0x0B, 0x04, 0x05, + 0x0C, 0x13, 0x1A, 0x21, 0x28, 0x30, 0x29, 0x22, + 0x1B, 0x14, 0x0D, 0x06, 0x07, 0x0E, 0x15, 0x1C, + 0x23, 0x2A, 0x31, 0x38, 0x39, 0x32, 0x2B, 0x24, + 0x1D, 0x16, 0x0F, 0x17, 0x1E, 0x25, 0x2C, 0x33, + 0x3A, 0x3B, 0x34, 0x2D, 0x26, 0x1F, 0x27, 0x2E, + 0x35, 0x3C, 0x3D, 0x36, 0x2F, 0x37, 0x3E, 0x3F +}; + +STATIC signed char gJpegcamQuantTable_8009F32C[64] = { + 0x02, 0x10, 0x13, 0x16, 0x1A, 0x1B, 0x1D, 0x22, + 0x10, 0x10, 0x16, 0x18, 0x1B, 0x1D, 0x22, 0x25, + 0x13, 0x16, 0x1A, 0x1B, 0x1D, 0x22, 0x22, 0x26, + 0x16, 0x16, 0x1A, 0x1B, 0x1D, 0x22, 0x25, 0x28, + 0x16, 0x1A, 0x1B, 0x1D, 0x20, 0x23, 0x28, 0x30, + 0x1A, 0x1B, 0x1D, 0x20, 0x23, 0x28, 0x30, 0x3A, + 0x1A, 0x1B, 0x1D, 0x22, 0x26, 0x2E, 0x38, 0x45, + 0x1B, 0x1D, 0x23, 0x26, 0x2E, 0x38, 0x45, 0x53 +}; + +STATIC TMat8x8B gJpegcamMatrix1_8009F36C = { + { 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D }, + { 0x3F, 0x35, 0x23, 0x0C, 0xF4, 0xDD, 0xCB, 0xC1 }, + { 0x3B, 0x18, 0xE8, 0xC5, 0xC5, 0xE8, 0x18, 0x3B }, + { 0x35, 0xF4, 0xC1, 0xDD, 0x23, 0x3F, 0x0C, 0xCB }, + { 0x2D, 0xD3, 0xD3, 0x2D, 0x2D, 0xD3, 0xD3, 0x2D }, + { 0x23, 0xC1, 0x0C, 0x35, 0xCB, 0xF4, 0x3F, 0xDD }, + { 0x18, 0xC5, 0x3B, 0xE8, 0xE8, 0x3B, 0xC5, 0x18 }, + { 0x0C, 0xDD, 0x35, 0xC1, 0x3F, 0xCB, 0x23, 0xF4 } +}; + +STATIC SVECTOR dword_8009F3AC[2] = { + {0, 0, 0, 0}, + {0, 0, 3200, 0} +}; + +/****************************************************************************** + * photo save functions + */ + +static void jpegcam_unk1_80063704(char *buf, MEM_CARD *pMemcard, int arg2, int arg3) +{ + char photo_id[8]; + unsigned int blocks_avail; + int index; + + memoryCardFileName[12] = 'C'; + + blocks_avail = 0; + + for (index = 0; index < pMemcard->file_count; index++) + { + if (strncmp(pMemcard->files[index].name, memoryCardFileName, 13) == 0) + { + blocks_avail |= 1 << (pMemcard->files[index].name[18] - 64); + } + } + + for (index = 1; index <= 16; index++) + { + if (!(blocks_avail & (1 << index))) + { + break; + } + } + + photo_id[0] = '\x82'; + photo_id[1] = (index / 10) + 0x4F; + photo_id[2] = '\x82'; + photo_id[3] = (index % 10) + 0x4F; + photo_id[4] = '\x00'; + + stru_8009F2D8.field_2 = index; + + /* MGS∫ */ + /*   */ + /* PHOTO */ + // Shift-JIS + sprintf(buf, "%s%s%s%s", "\x82\x6C\x82\x66\x82\x72\x81\xE7", "\x81\x40", "\x82\x6F\x82\x67\x82\x6E\x82\x73\x82\x6E", photo_id); +} + +static void jpegcam_unk2_80063888(char *param_1, int param_2) +{ + sprintf(param_1, "PHOTO %02d\n", *(char *)(param_2 + 6) - 0x40); +} + +static void jpegcam_unk3_800638B4(int *arg0) +{ + dword_800BDCD0 = (char)dword_800BDCD0 | 0x80808000; + printf("save header = %x\n", dword_800BDCD0); + + GV_CopyMemory(&dword_800BDCD0, &arg0[0], 4); + GV_CopyMemory(dword_800BDCC8, &arg0[1], dword_800BDCCC); +} + +/****************************************************************************** + * JPEG encoding functions + */ + +static void JpegInitMatrix(Work *work) +{ + // Copy matrix gJpegcamMatrix1_8009F36C transposed to gJpegcamMatrix2_800BDCD8 + + int i, j; + for (i = 0; i < 8; i++) + { + for (j = 0; j < 8; j++) + { + gJpegcamMatrix2_800BDCD8[j][i] = gJpegcamMatrix1_8009F36C[i][j]; + } + } +} + +static void JpegColorCvt_XBGR1555_to_BGRX8888(unsigned short *src, char *dst) +{ + int i; + + for (i = 0; i < 256; i++, dst += 4, src++) + { + dst[2] = (*src << 3) & 0xF8; + dst[1] = (*src >> 2) & 0xF8; + dst[0] = (*src >> 7) & 0xF8; + } +} + +static void JpegMacroblock_RGB_to_YUV(char *stream, char *y_out, char *u_out, char *v_out) +{ + int i; + int r, g, b; + + for (i = 0; i < 256; i++, stream += 4) + { + r = stream[0]; + g = stream[1]; + b = stream[2]; + + *y_out++ = (r * 299 + g * 587 + b * 114) / 1000 + 128; + *u_out++ = (r * -1687 + g * -3313 + b * 5000) / 10000; + *v_out++ = (r * 5000 - g * 4187 - b * 813) / 10000; + } +} + +static void JpegSplitLumaBlocks(TMat16x16B *pSrcY, TMat8x8B *pY1, TMat8x8B *pY2, TMat8x8B *pY3, TMat8x8B *pY4) +{ + // Given a "16 by 16B" luma matrix, copy its four quadrants into four "8 by 8B" matrices + int i, j; + + for (i = 0; i < 8; i++) + { + for (j = 0; j < 8; j++) + { + (*pY1)[i][j] = (*pSrcY)[i + 0][j + 0]; + (*pY2)[i][j] = (*pSrcY)[i + 0][j + 8]; + (*pY3)[i][j] = (*pSrcY)[i + 8][j + 0]; + (*pY4)[i][j] = (*pSrcY)[i + 8][j + 8]; + } + } +} + +static void JpegDownsampleChroma420(char *pInU, char *pInV, char *pOutU, char *pOutV) +{ + int i, j; + + signed char *var_t0; + signed char *var_t1; + signed char *var_t2; + signed char *var_t3; + + int resA; + int resB; + + for (i = 0; i < 8; i++) + { + var_t1 = pInU + i * 16; + var_t3 = var_t1 + 16; + + var_t0 = pInV + i * 16; + var_t2 = var_t0 + 16; + + for (j = 0; j < 8; j++) + { + resA = var_t1[0] + var_t1[1] + var_t3[0] + var_t3[1]; + resB = var_t0[0] + var_t0[1] + var_t2[0] + var_t2[1]; + + *pOutU = resA / 4; + *pOutV = resB / 4; + + var_t1 += 2; + var_t3 += 2; + var_t0 += 2; + var_t2 += 2; + + pOutU++; + pOutV++; + } + } +} + +static void JpegApplyDCT(Work *work, char *pIn, int *pOut) +{ + int *field_84; + int *field_84_ptr; + TMat8x8I *field_84_ptr2; + + int i; + int j; + int k; + + signed char *arg1_ptr; + int camIndex; + int accumulator; + + field_84 = (int *)work->field_84; + field_84 += 256; + field_84_ptr = field_84; + + i = 0; + for (; i < 8; i++) + { + j = 0; + for (; j < 8; j++) + { + arg1_ptr = pIn; + *field_84_ptr = 0; + k = 0; + camIndex = j; + for (; k < 8; k++) + { + *field_84_ptr += arg1_ptr[k] * gJpegcamMatrix2_800BDCD8[k][camIndex]; + } + field_84_ptr++; + } + pIn += 8; + } + + i = 0; + for (; i < 8; i++) + { + j = 0; + for (; j < 8; j++) + { + k = accumulator = 0; + field_84_ptr2 = (TMat8x8I *)field_84; + for (; k < 8; k++) + { + accumulator += gJpegcamMatrix1_8009F36C[i][k] * (*field_84_ptr2)[k][j]; + } + *(pOut++) = (accumulator >> 14); + } + } +} + +static void JpegQuantizeZigzagMatrix(int *pIn, int *pOut, int q_scale) +{ + signed char *pZigzag; + int i; + + *pOut++ = pIn[0] / gJpegcamQuantTable_8009F32C[0]; + pZigzag = &gJpegcamZigZagTable_8009F2EC[1]; + + for (i = 1; i < 64; i++, pZigzag++) + { + *pOut++ = (pIn[*pZigzag] * 16) / (gJpegcamQuantTable_8009F32C[*pZigzag] * q_scale); + } +} + +static int JpegRLEStream(Work *work, int *pData, int q_scale) +{ + int count; + int i; + int end; + int zeros; + unsigned short temp_a1; + short temp_v0; + unsigned char new_var2; + + count = 62; + + q_scale <<= 10; + temp_a1 = (*pData & 0x3FF) | q_scale; + + work->field_88[work->field_80++] = temp_a1 >> 8; + work->field_88[work->field_80++] = temp_a1; + work->field_7C += 2; + + for (i = 1, end = 1; i < count; i++, end++) + { + zeros = 0; + + while ((pData[i] == 0) && (i < count)) + { + zeros++; + i++; + } + + q_scale = zeros; + if (i == count) + { + break; + } + + temp_a1 = pData[i]; + + if ((q_scale < 7) && ((short)temp_a1 < 7) && ((short)temp_a1 >= -8)) + { + new_var2 = (temp_a1 + (q_scale << 4)) + 8; + work->field_88[work->field_80++] = new_var2; + work->field_7C++; + } + else + { + temp_v0 = temp_a1 + 256; + + if (temp_v0 < 0) + { + temp_v0 = 0; + } + + temp_a1 = (q_scale << 9) | 0x8000 | (temp_v0 & 0x1FF); + + work->field_88[work->field_80++] = temp_a1 >> 8; + work->field_88[work->field_80++] = temp_a1; + work->field_7C += 2; + } + } + + work->field_88[work->field_80++] = 15; + work->field_7C++; + + do { return end + 1; } while (0); +} + +static int JpegCompressMacroblock(Work *work, char *pStream, int q_scale) +{ + char *pY1; + char *pY2; + char *pY3; + char *pY4; + char *pU; + char *pV; + + char *pData; + int *pDctResult; + int *pQuantZagResult; + char *pBlocks[6]; + int processed; + int i; + + pData = work->field_84; + + pY1 = pData + 0x300; + pY2 = pData + 0x340; + pY3 = pData + 0x380; + pY4 = pData + 0x3C0; + pU = pData + 0x400; + pV = pData + 0x440; + + work->field_84 += 0x680; + + pDctResult = (int *)(pData + 0x480); + pQuantZagResult = (int *)(pData + 0x580); + + pBlocks[0] = pU; + pBlocks[1] = pV; + pBlocks[2] = pY1; + pBlocks[3] = pY2; + pBlocks[4] = pY3; + pBlocks[5] = pY4; + + JpegMacroblock_RGB_to_YUV(pStream, pData, pData + 0x100, pData + 0x200); + JpegSplitLumaBlocks((TMat16x16B *)pData, (TMat8x8B *)pY1, (TMat8x8B *)pY2, (TMat8x8B *)pY3, (TMat8x8B *)pY4); + JpegDownsampleChroma420(pData + 0x100, pData + 0x200, pU, pV); + + processed = 0; + + for (i = 0; i < 6; i++) + { + JpegApplyDCT(work, pBlocks[i], pDctResult); + JpegQuantizeZigzagMatrix(pDctResult, pQuantZagResult, q_scale * 2); + processed += JpegRLEStream(work, pQuantZagResult, q_scale); + } + + return processed; +} + +#define ROUND(x, a) ((((x) / (a)) + 1) * (a)) /* Round up `x` to next multiple of `a` */ + +static int JpegCompressFrame(Work *work, RECT *pRect, int q_scale) +{ + RECT rect; + int processed; + unsigned short *pColorCvtSrc; + char *f84; + char *pColorCvtDst; + int x, y; + short w, h; + int words; + + processed = 0; + + pColorCvtDst = work->field_84; + pColorCvtSrc = (unsigned short *)(work->field_84 + 1024); + work->field_84 += 1536; + f84 = work->field_84; + + + rect.x = 0; + rect.y = 0; + rect.w = 16; + rect.h = 16; + + w = pRect->w / 16; + h = pRect->h / 16; + + for (x = 0; x < w; x++) + { + rect.x = pRect->x + x * 16; + + for (y = 0; y < h; y++) + { + work->field_84 = f84; + rect.y = pRect->y + y * 16; + StoreImage(&rect, (u_long *)pColorCvtSrc); + JpegColorCvt_XBGR1555_to_BGRX8888(pColorCvtSrc, pColorCvtDst); + processed += JpegCompressMacroblock(work, pColorCvtDst, q_scale); + } + } + + processed = ROUND(processed, 64); + words = processed / 2; + + work->field_88[0] = words >> 8; + work->field_88[1] = words; + work->field_88[work->field_80] = 15; + + return processed * 2; +} + +static void JpegTryCompressFrame(Work *work) +{ + int q_scale; + int iteration; + RECT rect; + q_scale = 5; + iteration = 1; + rect.x = 16; + rect.y = 16; + rect.w = 288; + rect.h = 176; + work->field_8C_size = 20000; + + do { + work->field_7C = 0; + work->field_80 = 2; + + // Not matching with a pointer to the global array + work->field_84 = (char *)0x801B1000; + work->field_88 = (char *)0x801A1000; + + work->field_8C_size = JpegCompressFrame(work, &rect, q_scale); + printf("%d try q_scale = %d size = %d\n", iteration, q_scale, work->field_8C_size); + iteration++; + q_scale++; + + if (work->field_8C_size > 20000) + { + q_scale++; + } + } + while (work->field_8C_size > 16124u); +} + +/****************************************************************************** + * camera item logic + */ + +static int JpegcamGetZoomLimit(Work *work) +{ + MATRIX world; + SVECTOR vector1; + SVECTOR vector2; + int retval; + int cond; + + if (GM_GameStatus < 0) + { + retval = 3200; + } + else + { + world = GM_PlayerBody->objs->world; + world.t[0] = GM_SnakeCamera.position.vx; + world.t[1] = GM_SnakeCamera.position.vy; + world.t[2] = GM_SnakeCamera.position.vz; + DG_SetPos(&world); + + DG_PutVector(dword_8009F3AC, &vector1, 2); + + cond = 0; + if (HZD_OnlineHazardCheck(work->map->hzd, &vector1, &vector2, HZD_CHK_ALL, SEGMENT_ATR) != 0) + { + HZD_GetOnlinePoint(&vector2); + cond = 1; + } + + retval = 3200; + if (cond != 0) + { + GV_SubVec3(&vector2, &vector1, &vector1); + retval = GV_VecLen3(&vector1); + } + } + return retval; +} + +static void JpegcamProcessInput(Work *work) +{ + SVECTOR vec; + u_short status; + u_short press; + int zoom; + int vx, vy; + int zoom_adj; + int zoom_lim; + int xmin, xmax; + int speed; + + status = work->pad_data->status; + press = work->pad_data->press; + + GM_CheckShukanReverse(&status); + GM_CheckShukanReverse(&press); + + if (GV_PauseLevel != 0) + { + status = 0; + press = 0; + } + + if (!(GM_PlayerBody->objs->flag & DG_FLAG_INVISIBLE)) + { + status = 0; + press = 0; + } + + if ((GM_Camera.first_person != 1) || (GM_Camera.flag & 0x100)) + { + status = 0; + press = 0; + } + + if (dword_8009F604 != CAMERA_SIGHT) + { + status = 0; + press = 0; + } + + if (GM_PlayerStatus & PLAYER_PAD_OFF) + { + status = 0; + press = 0; + } + + zoom = GM_Camera.zoom; + + if (GM_PlayerControl) + { + vx = work->field_5C_ang.vx; + + if (vx < dword_800ABBDC) + { + vx = dword_800ABBDC; + } + else if (vx > dword_800ABBD4) + { + vx = dword_800ABBD4; + } + + work->field_5C_ang.vx = vx; + } + + if (status & (PAD_LEFT | PAD_DOWN | PAD_RIGHT | PAD_UP)) + { + vx = work->field_5C_ang.vx; + vy = work->field_5C_ang.vy; + + vec = work->field_54_vec; + + if (GM_PlayerControl) + { + vec.vx = GM_PlayerControl->turn.vx; + } + + if (zoom >= 1024) + { + speed = 1; + + if (zoom < 2048) + { + speed = 4; + } + } + else + { + speed = 16; + } + + xmin = vec.vx - 512; + xmax = vec.vx + 512; + + if (GM_PlayerControl) + { + if (xmin < dword_800ABBDC) + { + xmin = dword_800ABBDC; + } + + if (xmax > dword_800ABBD4) + { + xmax = dword_800ABBD4; + } + } + + if (status & (PAD_DOWN | PAD_UP)) + { + if (status & PAD_UP) + { + vx -= speed; + } + else + { + vx += speed; + } + } + + if (vx < xmin) + { + vx = xmin; + } + + if (vx > xmax) + { + vx = xmax; + } + + if (status & (PAD_LEFT | PAD_RIGHT)) + { + if (status & PAD_RIGHT) + { + vy -= speed; + } + else + { + vy += speed; + } + + vy &= 0xFFF; + } + + work->field_5C_ang.vx = vx; + work->field_5C_ang.vy = vy; + } + + zoom_adj = 3200; + + if (status & 0xF060) + { + zoom_lim = JpegcamGetZoomLimit(work); + + if (zoom_lim < 3200) + { + zoom_adj = zoom_lim; + } + + if (zoom_lim < 320) + { + zoom_adj = 320; + } + + if (zoom > zoom_adj) + { + zoom = zoom_adj; + } + } + + if ((status & (PAD_CROSS | PAD_CIRCLE)) && (((status & PAD_CIRCLE) && (zoom != zoom_adj)) || ((status & PAD_CROSS) && (zoom != 320)))) + { + if ((status & PAD_CIRCLE) && (zoom != zoom_adj)) + { + zoom += zoom / 32; + + if (zoom >= zoom_adj) + { + zoom = zoom_adj; + } + } + else if ((status & PAD_CROSS) && (zoom != 320)) + { + zoom -= zoom / 32; + + if (zoom <= 320) + { + zoom = 320; + } + } + + if (GV_PauseLevel == 0) + { + if ((work->field_68 & 0x3) == 0) + { + GM_SeSet2(0, 63, SE_SCOPE_ZOOM); + } + + work->field_68++; + } + } + + if (press & PAD_SQUARE) + { + if (!(GM_GameStatus & STATE_VOX_STREAM)) + { + work->field_70 = 1; + JpegInitMatrix(work); + work->state = 0; + work->field_68 = 0; + GM_GameStatus |= STATE_TAKING_PHOTO; + + if (!(GV_PauseLevel & GV_PAUSE_STOP)) + { + GV_PauseLevel |= GV_PAUSE_MENU; + } + + if (work->field_90_pSight) + { + GV_DestroyActor(work->field_90_pSight); + } + } + else + { + GM_SeSet2(0, 63, SE_BUZZER); + } + } + + GM_Camera.zoom = zoom; +} + +/** + * Check if the player is standing in a ghost photo spot. + */ +static int JpegcamCheckShinreiSpot(Work *work) +{ + int retval; + + printf("Sinrei Syasin Check Start\n"); // 心霊写真 (lit. "ghost photo") + if (GM_Photocode != 0) + { + printf("Here is Sinrei Spot!\n"); + printf("GM_Photocode = %d\n", GM_Photocode); + + retval = DG_PointCheckOne((DVECTOR *)&GM_PhotoViewPos); + printf("Point Check\n"); + + printf("Result = %d\n", retval); + } + else + { + printf("Not Sinrei Spot!\n"); + retval = 0; + } + return retval; +} + +static void JpegcamTakePhoto(Work *work) +{ + int state = work->state; + + if (state < 3) + { + return; + } + + if (state == 3) + { + GM_SeSet2(0, 63, SE_PHOTO_SHUTTER); + } + else if (state == 4) + { + GV_PauseLevel &= ~GV_PAUSE_MENU; + GV_PauseLevel |= GV_PAUSE_STOP; + DG_FreeObjectQueue(); + GV_SetPacketTempMemory(); + DG_UnDrawFrameCount = 1; + } + else if (state < 9) + { + DG_ClipDispEnv(256, (state - 4) * 56); + DG_UnDrawFrameCount = 1; + } + else if (state == 9) + { + JpegTryCompressFrame(work); + + if (JpegcamCheckShinreiSpot(work) == 1) + { + dword_800BDCD0 = GM_Photocode; + } + else + { + dword_800BDCD0 = 0; + } + + menu_radio_8004D2FC(&stru_8009F2D8); + DG_UnDrawFrameCount = 1; + dword_800BDCC8 = work->field_88; + dword_800BDCCC = work->field_8C_size; + } + else if (state == 10) + { + if (!menu_radio_8004D334(work->pad_data)) + { + --work->state; + } + } + else if (state == 11) + { + work->field_70 = 0; + menu_radio_8004D35C(); + GM_GameStatus &= ~STATE_TAKING_PHOTO; + GV_ResetPacketMemory(); + GV_PauseLevel &= ~GV_PAUSE_STOP; + DG_RestartMainChanlSystem(); + work->state = 0; + work->field_90_pSight = NewSight(CAMERA_SIGHT2, CAMERA_SIGHT, &GM_CurrentItemId, IT_Camera, NULL); + } +} + +/****************************************************************************** + * standard actor functions + */ + +static void Act(Work *work) +{ + OBJECT *parent; + OBJECT_NO_ROTS *object; + + if (GM_PlayerStatus & PLAYER_SECOND_CONTROLLER) + { + work->pad_data = &GV_PadData[3]; + } + else + { + work->pad_data = &GV_PadData[2]; + } + + if (work->field_98 < 16) + { + work->field_98++; + } + + if (!work->field_94_bMakeVisible) + { + parent = work->parent; + if (parent->objs->flag & DG_FLAG_INVISIBLE) + { + object = &work->goggles; + GM_InitObjectNoRots(object, GV_StrCode("goggles"), 109, 0); + if (work->goggles.objs) + { + GM_ConfigObjectRoot((OBJECT *)object, parent, 6); + GM_ConfigObjectLight((OBJECT *)object, parent->light); + EQ_InvisibleHead(parent, &work->head_saved_packs, &work->head_saved_raise); + work->field_94_bMakeVisible = 1; + } + } + } + + if (work->field_94_bMakeVisible) + { + GM_SetCurrentMap(work->control->map->index); + + DG_GroupObjsEx(work->goggles.objs); + + if (GM_PlayerStatus & PLAYER_NOT_SIGHT) + { + if ( !(work->parent->objs->flag & DG_FLAG_INVISIBLE) ) + { + DG_VisibleObjs(work->goggles.objs); + } + GM_Camera.zoom = 320; + return; + } + + DG_InvisibleObjs(work->goggles.objs); + } + + if (GM_LoadRequest) + { + if ((work->field_70 == 1) && (work->state < 4)) + { + GV_PauseLevel &= ~GV_PAUSE_MENU; + } + + GV_DestroyActor(&work->actor); + return; + } + + if (GM_GameOverTimer) + { + return; + } + + switch (work->field_70) + { + case 0: + if (GV_PauseLevel) + { + return; + } + + JpegcamProcessInput(work); + + if (dword_8009F604 != CAMERA_SIGHT) + { + NewSight(CAMERA_SIGHT, CAMERA_SIGHT, &GM_CurrentItemId, IT_Camera, NULL); + work->field_90_pSight = NewSight(CAMERA_SIGHT2, CAMERA_SIGHT, &GM_CurrentItemId, IT_Camera, NULL); + GM_SeSet2(0, 63, SE_ITEM_OPENWINDOW); + } + + if ( !(GM_PlayerStatus & PLAYER_NOT_SIGHT) ) + { + MENU_Locate(200, 25, 0); + MENU_Color(192, 144, 128); + MENU_Printf("zoom : %4d\n", GM_Camera.zoom); + MENU_Printf("angle : %4d, %4d\n", -work->field_5C_ang.vx, work->field_5C_ang.vy); + } + break; + + case 1: + if ((work->state < 5) && (GV_PauseLevel & GV_PAUSE_STOP)) + { + work->state = 0; + return; + } + + JpegcamTakePhoto(work); + break; + } + + work->state++; + + GM_PlayerControl->rot = work->field_5C_ang; + GM_PlayerControl->turn = work->field_5C_ang; +} + +static void Die(Work *work) +{ + GM_Camera.zoom = 320; + GM_SnakeCamera.rotate2 = work->field_54_vec; + + GM_GameStatus &= ~STATE_JPEGCAM; + GM_GameStatus &= ~STATE_TAKING_PHOTO; + + if (work->field_94_bMakeVisible != 0) + { + EQ_VisibleHead(work->parent, &work->head_saved_packs, &work->head_saved_raise); + GM_FreeObject((OBJECT *)&work->goggles); + } +} + +static int GetResources(Work *work, CONTROL *control, OBJECT *parent) +{ + work->parent = parent; + work->pad_data = &GV_PadData[2]; + work->field_54_vec = control->rot; + work->field_5C_ang = work->field_54_vec; + work->state = 0; + work->field_68 = 0; + work->map = control->map; + work->field_70 = 0; + work->field_98 = 0; + GM_GameStatus |= STATE_JPEGCAM; + return 0; +} + +/****************************************************************************** + * public functions + */ + +void *NewJpegcam(CONTROL *control, OBJECT *parent, int num_parent) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "jpegcam.c"); + if (GetResources(work, control, parent) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + work->control = control; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/equip/kogaku2.c b/components/mgs/mgs/source/equip/kogaku2.c new file mode 100644 index 00000000..8885b9e6 --- /dev/null +++ b/components/mgs/mgs/source/equip/kogaku2.c @@ -0,0 +1,504 @@ +#include "equip.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_MANAGER + +#define SNAKE_COLOR MAKE_RGBA(128,160, 96, GPU_CODE_POLY_GT4) +#define NINJA_COLOR MAKE_RGBA(128,128,128, GPU_CODE_POLY_GT4) + +typedef struct _Work +{ + GV_ACT actor; + OBJECT *parent; + int num_parent; + int saved_objs_flag; + int ypos2; // TODO: rename? + int ypos1; // TODO: rename? + int ypos3_ninja; // TODO: rename? + int ninja_var; // TODO: rename? + int player_flag; // Set but not used. + int color; // RGB + primitive code +} Work; + +/*---------------------------------------------------------------------------*/ + +// TODO: Originally mistaken for EQ_InvisibleUnit, give it a better name +static void EQ_InvisibleUnit2(DG_OBJS *objs, unsigned int color, int arg2) +{ + int i; + int n_models; + DG_OBJ *obj; + POLY_GT4 *poly; + DG_OBJ *iter; + int n_packs; + + for (i = 0; i < 2; i++) + { + obj = objs->objs; + + for (n_models = objs->n_models; n_models > 0; n_models--) + { + poly = obj->packs[i]; + + if (!poly) + { + obj++; + continue; + } + + if ((arg2 != 0) && (LLOAD(&poly->r0) == color)) + { + continue; + } + + for (iter = obj; iter; iter = iter->extend) + { + for (n_packs = iter->n_packs; n_packs > 0; n_packs--) + { + LSTORE(color, &poly->r0); + LSTORE(color, &poly->r1); + LSTORE(color, &poly->r2); + LSTORE(color, &poly->r3); + poly++; + } + } + + obj++; + } + } +} + +// TODO: Originally mistaken for EQ_VisibleUnit, give it a better name +static int EQ_VisibleUnit2(short *arg0, char *arg1) +{ + int adjust; + int x, y; + + adjust = ((arg0[0] / 4) - 160); + x = arg0[0] - adjust; + + if (x < 0) + { + x = 0; + } + + if (x > 319) + { + x = 319; + } + + arg1[0] = x; + + y = arg0[1] + 112; + + if (y < 0) + { + y = 0; + } + + if (y > 223) + { + y = 223; + } + + arg1[1] = y; + + return x > 255; +} + +/*---------------------------------------------------------------------------*/ + +static POLY_GT4 *TPageUVUpdate(POLY_GT4 *packs, int n_packs) +{ + int tpage; + int visible; + + if (GV_Clock != 0) + { + tpage = getTPage(2, 0, 0, 0); + } + else + { + tpage = getTPage(2, 0, 320, 0); + } + + while (--n_packs >= 0) + { + if ((packs->tag & 0xff000000) != 0) + { + visible = EQ_VisibleUnit2(&packs->x0, &packs->u0); + visible |= EQ_VisibleUnit2(&packs->x1, &packs->u1); + visible |= EQ_VisibleUnit2(&packs->x2, &packs->u2); + visible |= EQ_VisibleUnit2(&packs->x3, &packs->u3); + + if (visible != 0) + { + packs->tpage = tpage + 1; + packs->u0 -= 64; + packs->u1 -= 64; + packs->u2 -= 64; + packs->u3 -= 64; + } + else + { + packs->tpage = tpage; + } + } + + packs++; + } + + return packs; +} + +static POLY_GT4 *TPageUVRGBUpdate(POLY_GT4 *packs, int n_packs, int ypos) +{ + int tpage; + int visible; + + if (GV_Clock != 0) + { + tpage = getTPage(2, 0, 0, 0); + } + else + { + tpage = getTPage(2, 0, 320, 0); + } + + while (--n_packs >= 0) + { + if (packs->y0 > ypos) + { + if ((packs->tpage & 0x180) != getTPage(2, 0, 0, 0)) + { + LSTORE(NINJA_COLOR, &packs->r0); + LSTORE(NINJA_COLOR, &packs->r1); + LSTORE(NINJA_COLOR, &packs->r2); + LSTORE(NINJA_COLOR, &packs->r3); + } + + if ((packs->tag & 0xff000000) != 0) + { + visible = EQ_VisibleUnit2(&packs->x0, &packs->u0); + visible |= EQ_VisibleUnit2(&packs->x1, &packs->u1); + visible |= EQ_VisibleUnit2(&packs->x2, &packs->u2); + visible |= EQ_VisibleUnit2(&packs->x3, &packs->u3); + + if (visible != 0) + { + packs->tpage = tpage + 1; + packs->u0 -= 64; + packs->u1 -= 64; + packs->u2 -= 64; + packs->u3 -= 64; + } + else + { + packs->tpage = tpage; + } + } + } + + packs++; + } + + return packs; +} + +static void UpdatePrims1(Work *work) +{ + DG_OBJS *objs; // $v0 + int n_models; // $s2 + DG_OBJ *i; // $s1 + POLY_GT4 *pPack; // $v0 + DG_OBJ *j; // $s0 + + objs = work->parent->objs; + n_models = objs->n_models; + for (i = objs->objs; n_models > 0; ++i) + { + pPack = i->packs[1 - GV_Clock]; + if (pPack) + { + for (j = i; j; j = j->extend) + { + pPack = TPageUVUpdate(pPack, j->n_packs); + } + } + --n_models; + } +} + +static void UpdatePrims2(Work *work) +{ + DG_OBJS *objs; // $v0 + int n_models; // $s2 + DG_OBJ *i; // $s1 + POLY_GT4 *pPack; // $v0 + DG_OBJ *j; // $s0 + + objs = work->parent->objs; + n_models = objs->n_models; + for (i = objs->objs; n_models > 0; ++i) + { + pPack = i->packs[1 - GV_Clock]; + if (pPack) + { + for (j = i; j; j = j->extend) + { + pPack = TPageUVRGBUpdate(pPack, j->n_packs, work->ypos2); + } + } + --n_models; + } +} + +static void RestoreModelFlags(Work *work) +{ + DG_OBJS *objs; + DG_OBJ *obj; + int n_models; + + objs = work->parent->objs; + obj = objs->objs; + n_models = objs->n_models; + objs->flag = (objs->flag & DG_FLAG_INVISIBLE) | (work->saved_objs_flag & ~DG_FLAG_INVISIBLE); + while (n_models > 0) + { + DG_WriteObjPacketUV(obj, 0); + DG_WriteObjPacketUV(obj, 1); + ++obj; + --n_models; + } +} + +static void Act2(Work *work); +static void Die2(Work *work); + +static void Act(Work *work) +{ + int ypos2 = work->ypos2; + if (work->ypos1 < ypos2) + { + work->ypos2 = ypos2 - work->ninja_var; + UpdatePrims2(work); + + ypos2 = work->ypos2; + if (work->ypos1 >= ypos2) + { + EQ_InvisibleUnit2(work->parent->objs, work->color, 0); + } + } + else + { + EQ_InvisibleUnit2(work->parent->objs, work->color, 1); + UpdatePrims1(work); + } + if (GM_GameStatus & STATE_THERMG) + { + work->parent->objs->flag = work->saved_objs_flag; + DG_FreeObjsPacket(work->parent->objs, 0); + DG_FreeObjsPacket(work->parent->objs, 1); + work->actor.act = (GV_ACTFUNC)Act2; + work->actor.die = (GV_ACTFUNC)Die2; + } +} + +static void Die(Work *work) +{ + RestoreModelFlags(work); +} + +static void Act2(Work *work) +{ + if (!(GM_GameStatus & STATE_THERMG)) + { + work->parent->objs->flag &= ~DG_FLAG_SHADE; + work->parent->objs->flag &= ~DG_FLAG_BOUND; + work->parent->objs->flag |= DG_FLAG_GBOUND; + EQ_InvisibleUnit2(work->parent->objs, work->color, 0); + work->actor.act = (GV_ACTFUNC)Act; + work->actor.die = (GV_ACTFUNC)Die; + } + else + { + work->ypos2 = work->ypos1; + } +} + +static void Die2(Work *work) +{ + /* do nothing */ +} + +/*---------------------------------------------------------------------------*/ + +void *NewKogaku2(CONTROL *control, OBJECT *parent, int num_parent) +{ + Work *work; + DG_OBJS *objs; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "kogaku2.c"); + + work->parent = parent; + work->num_parent = num_parent; + work->ypos2 = 0; + work->ypos1 = 1; + objs = work->parent->objs; + + work->saved_objs_flag = objs->flag; + DG_UnShadeObjs(objs); + DG_UnBoundObjs(objs); + DG_GBoundObjs(objs); + + if (control->name == SNAKE_NAME) + { + work->player_flag = 1; + work->color = SNAKE_COLOR; + EQ_InvisibleUnit2(objs, work->color , 0); + } + else + { + work->color = NINJA_COLOR; + EQ_InvisibleUnit2(objs, work->color , 0); + } + } + + return (void *)work; +} + +/*---------------------------------------------------------------------------*/ + +void *NewKogaku3(CONTROL *control, OBJECT *parent, int num_parent) +{ + SVECTOR vecs[9]; + long coords[9]; + long unused; + + Work *work; + DG_OBJS *objs; + DG_DEF *def; + int minx, miny, minz; + int maxx, maxy, maxz; + int i; + SVECTOR *vec_iter; + int max, min; + long *coord_iter; + int y; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "kogaku2.c"); + + work->parent = parent; + work->num_parent = num_parent; + + objs = work->parent->objs; + def = work->parent->objs->def; + + DG_UnShadeObjs(objs); + DG_SetPos(&objs->objs[0].screen); + + minx = def->min.vx; + miny = def->min.vy; + minz = def->min.vz; + + maxx = def->max.vx; + maxy = def->max.vy; + maxz = def->max.vz; + + vec_iter = vecs; + + for (i = 9; i > 0; i--) + { + if (i & 1) + { + vec_iter->vx = minx; + } + else + { + vec_iter->vx = maxx; + } + + if (i & 2) + { + vec_iter->vy = miny; + } + else + { + vec_iter->vy = maxy; + } + + if (i & 4) + { + vec_iter->vz = minz; + } + else + { + vec_iter->vz = maxz; + } + + vec_iter++; + } + + RotTransPers3(&vecs[0], &vecs[1], &vecs[2], &coords[0], &coords[1], &coords[2], &unused, &unused); + RotTransPers3(&vecs[3], &vecs[4], &vecs[5], &coords[3], &coords[4], &coords[5], &unused, &unused); + RotTransPers3(&vecs[6], &vecs[7], &vecs[8], &coords[6], &coords[7], &coords[8], &unused, &unused); + + max = -20000; + min = 20000; + + coord_iter = coords; + + for (i = 8; i > 0; i--) + { + y = *coord_iter >> 16; + + if (y > max) + { + max = y; + } + + if (y < min) + { + min = y; + } + + coord_iter++; + } + + work->ypos2 = max; + work->ypos3_ninja = max; + work->ypos1 = min; + work->ninja_var = (max - min) / 10; + + if (work->ninja_var == 0) + { + work->ninja_var = 1; + } + + if (control->name == SNAKE_NAME) + { + work->player_flag = 1; + work->color = SNAKE_COLOR; + } + else + { + work->color = NINJA_COLOR; + } + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/equip/scope.c b/components/mgs/mgs/source/equip/scope.c new file mode 100644 index 00000000..899edfa9 --- /dev/null +++ b/components/mgs/mgs/source/equip/scope.c @@ -0,0 +1,857 @@ +#include "equip.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "menu/menuman.h" +#include "game/game.h" +#include "thing/sight.h" +#include "chara/snake/sna_init.h" +#include "linkvar.h" +#include "sound/g_sound.h" + +extern GM_SnakeCameraWork GM_SnakeCamera; +extern int dword_8009F604; +extern GM_CameraSystemWork GM_Camera; +extern short dword_800ABBDC; +extern short dword_800ABBD4; + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_AFTER2 + +#define SCOPE_SIGHT 0x51c8 // GV_StrCode("scope") +#define SCOPE_MODEL GV_StrCode("goggles") + +#define SEGMENT_ATR ( HZD_SEG_NO_DISP_RADAR|HZD_SEG_NO_COLLIDE ) + +#define HUD_DISP_DELAY 16 + +#define COLOR_DARK_CYAN MAKE_RGB0( 46, 65, 65) +#define COLOR_LIGHT_GREEN MAKE_RGB0(135,177,104) +#define COLOR_DARK_GREEN MAKE_RGB0( 71,113, 40) + +typedef struct _Work +{ + GV_ACT actor; + CONTROL *control; + OBJECT *parent; + OBJECT_NO_ROTS object; + short saved_packs; + short saved_raise; + MAP *map; + GV_PAD *old_pad; + int field_58; + short hud_delay; // Adds some delay before showing the HUD of the scope. + short field_5E; // Initialized with 2 but aparently never read. + short field_60; // Value set to 0 and 1 but aparently never read. + short field_62; // Some helper for side lines. + SVECTOR field_64_vec; + SVECTOR field_6C_turn_vec; + LINE_F2 *side_lines[2]; // Vertical lines on left and right side of the HUD that move when zooming in/out. + LINE_F4 *rect_lines[2]; // Top, right and bottom border of the moving rectangle in the center of the HUD. + short rect_offset[2]; // (x, y) offset of the top left corner of the moving rectangle. + LINE_F3 *bar_graph[2]; // Vertical lines that "randomly" change their height. + LINE_F3 *zoom_line[2]; // The horizontal zoom level line on top of the HUD. + int zoom_counter; // Used to play the sound when zooming in/out at a fixed rate. + int flags; +} Work; + +/*---------------------------------------------------------------------------*/ + +STATIC short scope_created_8009F2C4 = FALSE; + +STATIC SVECTOR svecs_8009F2C8[2] = { + { 0, 0, 0, 0 }, + { 0, 0, 3200, 0 } +}; + +static void addPrimEX(OT_TYPE *ot, void *prim) +{ + if ((GM_PlayerStatus & PLAYER_NOT_SIGHT) == 0) + { + addPrim(ot, prim); + } +} + +// If the scope is zoomed in and you move the view towards an object which is +// closer than the zoom level, then the zoom is automatically decreased. +static int GetZoomLimit(Work *work) +{ + MATRIX *eye; + DG_OBJS *objs; + int bCalcLen; + int vecLen; + MATRIX mtx; + SVECTOR vecs[2]; + + if ( GM_GameStatus < 0 ) + { + eye = &DG_Chanl(0)->eye; + } + else + { + objs = GM_PlayerBody->objs; + eye = &mtx; + mtx = GM_PlayerBody->objs->world; + mtx.t[0] = GM_SnakeCamera.position.vx; + mtx.t[1] = GM_SnakeCamera.position.vy; + mtx.t[2] = GM_SnakeCamera.position.vz; + } + DG_SetPos(eye); + DG_PutVector(svecs_8009F2C8, vecs, 2); + bCalcLen = 0; + if ( HZD_OnlineHazardCheck(work->map->hzd, vecs, &vecs[1], HZD_CHK_ALL, SEGMENT_ATR) ) + { + HZD_GetOnlinePoint(&vecs[1]); + bCalcLen = 1; + } + + vecLen = 3200; + if ( bCalcLen != 0 ) + { + GV_SubVec3(&vecs[1], vecs, vecs); + vecLen = GV_VecLen3(vecs); + } + + return vecLen; +} + +static void SetSideLinesPairPosition(LINE_F2 *lines, int x, int y) +{ + int offsetIndex; + lines->x1 = x; + lines->x0 = x; + offsetIndex = 4; + lines[offsetIndex].x1 = SCREEN_WIDTH - x; + lines[offsetIndex].x0 = SCREEN_WIDTH - x; + lines[offsetIndex].y0 = y; + lines->y0 = y; + lines[offsetIndex].y1 = SCREEN_HEIGHT - y; // Bottom y. + lines->y1 = SCREEN_HEIGHT - y; +} + +static void DrawSideLines(LINE_F2 *lines, int param_2) +{ + int i; + int x; + int y; + + x = param_2 + 258; + y = 66 - (param_2 * 9) / 15; + + for (i = 0; i < 4; i++) + { + SetSideLinesPairPosition(lines, x, y); + lines++; + x += 15; + y -= 9; + } +} + +static void scope_act_helper_8006258C(Work *work) +{ + int iVar1; + int iVar3; + int temp; + OT_TYPE *ot; + LINE_F2 *lines; + int i; + + temp = work->field_58; + + if (GV_PauseLevel == 0) + { + work->field_58++; + } + + if (temp >= 6) + { + temp = 6; + work->flags |= 1 << GV_Clock; + } + + iVar1 = 12 * temp; + iVar3 = 320 - iVar1; + + ot = DG_ChanlOTag(1); + lines = work->side_lines[GV_Clock]; + + for (i = 0; i < 4; i++) + { + SetSideLinesPairPosition(lines, iVar3, 66 - (((iVar3 - 258) * 9) / 15)); + addPrimEX(ot, lines); + addPrimEX(ot, lines + 4); + + iVar3 += iVar1 / 4; + lines++; + } +} + +static void ManagePadInput(Work *work, unsigned short pad_status) +{ + SVECTOR vec; + int zoomLevel; + int iVar5; + int vx; + int vy; + int vxMin; + int vxMax; + CONTROL *control; + short *pRectOffset; // (x, y) offset of the top left corner of the moving rectangle. + + zoomLevel = GM_Camera.zoom; + pRectOffset = work->rect_offset; + + if (GM_PlayerControl) + { + vx = work->field_6C_turn_vec.vx; + + if (vx < dword_800ABBDC) + { + vx = dword_800ABBDC; + } + else if (vx > dword_800ABBD4) + { + vx = dword_800ABBD4; + } + + work->field_6C_turn_vec.vx = vx; + } + + if ((pad_status & (PAD_UP | PAD_DOWN | PAD_LEFT | PAD_RIGHT)) != 0) + { + vx = work->field_6C_turn_vec.vx; + vy = work->field_6C_turn_vec.vy; + + vec = work->field_64_vec; + + if (GM_PlayerControl) + { + vec.vx = GM_PlayerControl->turn.vx; + } + + if (zoomLevel <= 1023) + { + iVar5 = 16; + } + else + { + if (zoomLevel < 2048) + { + iVar5 = 4; + } + else + { + iVar5 = 1; + } + } + + vxMin = vec.vx - 512; + vxMax = vec.vx + 512; + + if (GM_PlayerControl) + { + if (vxMin < dword_800ABBDC) + { + vxMin = dword_800ABBDC; + } + + if (vxMax > dword_800ABBD4) + { + vxMax = dword_800ABBD4; + } + } + + if ((pad_status & (PAD_UP | PAD_DOWN)) != 0) + { + if ((pad_status & PAD_UP) != 0) + { + vx -= iVar5; + + if (--pRectOffset[1] < -8) + { + pRectOffset[1] = -8; + } + } + else + { + vx += iVar5; + + if (++pRectOffset[1] > 8) + { + pRectOffset[1] = 8; + } + } + } + + if (vx < vxMin) + { + vx = vxMin; + } + + if (vx > vxMax) + { + vx = vxMax; + } + + if ((pad_status & (PAD_LEFT | PAD_RIGHT)) != 0) + { + if ((pad_status & PAD_RIGHT) != 0) + { + pRectOffset[0]++; + + vy = vy - iVar5; + vy &= 0xfff; + + if (pRectOffset[0] > 8) + { + pRectOffset[0] = 8; + } + } + else + { + pRectOffset[0]--; + + vy = vy + iVar5; + vy &= 0xfff; + + if (pRectOffset[0] < -8) + { + pRectOffset[0] = -8; + } + } + } + + work->field_6C_turn_vec.vx = vx; + work->field_6C_turn_vec.vy = vy; + } + else if (GV_PauseLevel == 0) + { + if (pRectOffset[0] != 0) + { + iVar5 = (pRectOffset[0] > 0) ? -1 : 1; + pRectOffset[0] += iVar5; + } + + if (pRectOffset[1] != 0) + { + iVar5 = (pRectOffset[1] > 0) ? -1 : 1; + pRectOffset[1] += iVar5; + } + } + + control = GM_PlayerControl; + + if (control) + { + control->turn = work->field_6C_turn_vec; + control->rot = control->turn; + } +} + +static void ManageZoom(Work *work, OT_TYPE *ot, int pad_status) +{ + short zoomLineLength; + int iVar3; + int zoomLevel; + int maxZoomLevel; + LINE_F2 *pSideLine_f2; + LINE_F3 *pZoomLevelLine_F3; + int i; + int maxDistance; + + zoomLevel = GM_Camera.zoom; + pSideLine_f2 = work->side_lines[GV_Clock]; + pZoomLevelLine_F3 = work->zoom_line[GV_Clock]; + + maxZoomLevel = 3200; + + if (pad_status & (PAD_UP | PAD_DOWN | PAD_LEFT | PAD_RIGHT | PAD_CROSS | PAD_CIRCLE)) + { + maxDistance = GetZoomLimit(work); + + if (maxDistance < 3200) + { + maxZoomLevel = maxDistance; + } + + if (maxDistance < 320) + { + maxZoomLevel = 320; + } + + if (maxZoomLevel < zoomLevel) + { + zoomLevel = maxZoomLevel; + } + } + + iVar3 = work->field_62; + + if ((((pad_status & PAD_CIRCLE) != 0) && (zoomLevel != maxZoomLevel)) || (((pad_status & PAD_CROSS) != 0) && (zoomLevel != 320))) + { + if (((pad_status & PAD_CIRCLE) != 0) && (zoomLevel != maxZoomLevel)) + { + zoomLevel += zoomLevel / 32; + + if (zoomLevel >= maxZoomLevel) + { + zoomLevel = maxZoomLevel; + } + else + { + iVar3++; + } + + if (iVar3 == 15) + { + iVar3 = 0; + } + } + else + { + zoomLevel -= zoomLevel / 32; + + if (zoomLevel <= 320) + { + zoomLevel = 320; + } + else + { + iVar3--; + } + + if (iVar3 == -1) + { + iVar3 = 14; + } + } + + DrawSideLines(pSideLine_f2, iVar3); + work->field_62 = iVar3; + work->field_60 = 1; + + if (GV_PauseLevel == 0) + { + if ((work->zoom_counter & 3U) == 0) // When field is 0, 4, 8, 12... + { + GM_SeSet2(0, 0x3F, SE_SCOPE_ZOOM); + } + + work->zoom_counter++; + } + } + else + { + DrawSideLines(pSideLine_f2, iVar3); + work->field_60 = 0; + work->zoom_counter = 0; + } + + if (zoomLevel < 320) + { + zoomLevel = 320; + } + + zoomLineLength = ((zoomLevel - 320) / 16) + 48; + + pZoomLevelLine_F3->x2 = zoomLineLength; + pZoomLevelLine_F3->x1 = zoomLineLength; + addPrimEX(ot, pZoomLevelLine_F3); + for (i = 0; i < 8; i++) + { + addPrimEX(ot, pSideLine_f2); + pSideLine_f2++; + } + + GM_Camera.zoom = zoomLevel; +} + +static void DrawMovingRectangle(Work *work, OT_TYPE *ot) +{ + LINE_F4 *pRect; // Top, right and bottom border. + LINE_F2 *pLeftBorder; + short xy; // Left x, then top y. + short xRight; + short y0; + + pRect = work->rect_lines[GV_Clock]; + pLeftBorder = (LINE_F2 *)(pRect + 1); + + xy = work->rect_offset[0] + 130; + xRight = work->rect_offset[0] + 189; + y0 = work->rect_offset[1]; + + pLeftBorder->x1 = xy; // Bottom point. + pLeftBorder->x0 = xy; // Top point. + pRect->x3 = xy; // Bottom left corner. + pRect->x0 = xy; // Top left corner. + + xy = y0 + 102; // x -> y swap. + y0 = y0 + 137; // Top y -> bottom y. + + pRect->x2 = xRight; // Bottom right corner. + pRect->x1 = xRight; // Top right corner. + pLeftBorder->y0 = xy; // Top point. + pRect->y1 = xy; // Top right corner. + pRect->y0 = xy; // Top left corner. + pLeftBorder->y1 = y0; // Bottom point. + pRect->y3 = y0; // Bottom left corner. + pRect->y2 = y0; // Bottom right corner. + + addPrimEX(ot, pRect); + addPrimEX(ot, pLeftBorder); +} + +static void DrawMovingBarGraph(Work *work, OT_TYPE *ot) +{ + short lineHeight; + int primCount; + int i; + LINE_F3 *pLine_F3; + OT_TYPE *otMin; + OT_TYPE *chnlOt; + u_long *curPrim; + int numOTEntries; + + pLine_F3 = work->bar_graph[GV_Clock]; + chnlOt = DG_Chanl(0)->ot[1 - GV_Clock]; + + numOTEntries = DG_Chanl(0)->ot_size - 4; + for (i = 0; i < 16; i++) + { + otMin = chnlOt + (i << numOTEntries); + for (curPrim = chnlOt + ((i + 1) << numOTEntries), primCount = 0; (otMin < curPrim) || (curPrim < chnlOt); + curPrim = nextPrim(curPrim)) + { + if (getlen(curPrim) != 0) + { + primCount++; + } + + if (primCount == 0x128) + { + break; + } + } + + primCount /= 2; + lineHeight = 178 - primCount; + pLine_F3->y1 = lineHeight; + pLine_F3->y0 = lineHeight; + addPrimEX(ot, pLine_F3); + pLine_F3++; + } +} + +static void DrawHudText(Work *work) +{ + if ( (GM_PlayerStatus & PLAYER_NOT_SIGHT) == 0 ) + { + MENU_Locate(20, 34, 0); + MENU_Color(127, 166, 97); + MENU_Printf("- ZOOM LEVEL - - %d -", 100 * (GM_Camera.zoom / 320)); + MENU_Color(101, 133, 77); + MENU_Locate(32, 101, 1); + MENU_Printf("%d", -work->field_6C_turn_vec.vx); + MENU_ResetText(); + } +} + +static void Act(Work *work) +{ + int model; + OBJECT *parent_obj; + OBJECT_NO_ROTS *obj; + OT_TYPE *ot; + u_short pad_status; + + if (!(work->flags & 0x8000)) + { + parent_obj = work->parent; + + if (work->parent->objs->n_models >= 7 && (work->parent->objs->flag & DG_FLAG_INVISIBLE)) + { + obj = &work->object; + model = SCOPE_MODEL; + + GM_InitObjectNoRots(obj, model, 0x6d, 0); + + if (work->object.objs) + { + GM_ConfigObjectRoot((OBJECT *)obj, parent_obj, 6); + GM_ConfigObjectLight((OBJECT *)obj, parent_obj->light); + EQ_InvisibleHead(parent_obj, &work->saved_packs, &work->saved_raise); + work->flags |= 0x8000; + } + } + } + + if (work->flags & 0x8000) + { + GM_CurrentMap = work->control->map->index; + DG_GroupObjsEx(work->object.objs); + + if ((GM_PlayerStatus & PLAYER_NOT_SIGHT) != 0) + { + if (!(work->parent->objs->flag & DG_FLAG_INVISIBLE)) + { + DG_VisibleObjs(work->object.objs); + } + + GM_Camera.zoom = 320; + return; + } + + DG_InvisibleObjs(work->object.objs); + } + + // Add some delay before showing the HUD of the scope (don't know why it is needed). + if (work->hud_delay > 0) + { + if (GV_PauseLevel == 0) + { + work->hud_delay--; + } + + return; + } + + if (dword_8009F604 != SCOPE_SIGHT) + { + NewSight(SCOPE_SIGHT, SCOPE_SIGHT, &GM_CurrentItemId, IT_Scope, NULL); + GM_SeSet2(0, 63, SE_ITEM_OPENWINDOW); + } + + if ((work->flags & 3) != 3) + { + scope_act_helper_8006258C(work); + return; + } + + if ((GM_PlayerStatus & PLAYER_SECOND_CONTROLLER) != 0) + { + work->old_pad = &GV_PadData[3]; + } + else + { + work->old_pad = &GV_PadData[2]; + } + + pad_status = work->old_pad->status; + GM_CheckShukanReverse(&pad_status); + + if ((GV_PauseLevel != 0) || (GM_PlayerStatus & PLAYER_PAD_OFF) || + (GM_GameStatus < 0)) + { + pad_status = 0; + } + + ot = DG_ChanlOTag(1); + + ManagePadInput(work, pad_status); + ManageZoom(work, ot, pad_status); + DrawMovingRectangle(work, ot); + DrawMovingBarGraph(work, ot); + DrawHudText(work); +} + +/*---------------------------------------------------------------------------*/ + +static void Die(Work *work) +{ + if ( work->side_lines[0] ) + { + GV_DelayedFree(work->side_lines[0]); + } + + if ( work->rect_lines[0] ) + { + GV_DelayedFree(work->rect_lines[0]); + } + + if ( work->bar_graph[0] ) + { + GV_DelayedFree(work->bar_graph[0]); + } + + if ( work->zoom_line[0] ) + { + GV_DelayedFree(work->zoom_line[0]); + } + + GM_Camera.zoom = 320; + + if ( (work->flags & 0x8000) != 0 ) + { + EQ_VisibleHead(work->parent, &work->saved_packs, &work->saved_raise); + GM_FreeObject((OBJECT *)&work->object); + } + + scope_created_8009F2C4 = FALSE; +} + +/*---------------------------------------------------------------------------*/ + +static void InitSideLines(LINE_F2 *lines) +{ + int i; + + for (i = 0; i < 16; i++) + { + LSTORE(COLOR_DARK_CYAN, &lines->r0); + setLineF2(lines); + setSemiTrans(lines, 1); + lines++; + } +} + +static void InitMovingRectangle(LINE_F4 *lines) +{ + int i; + + for (i = 0; i < 2; i++) + { + LSTORE(COLOR_LIGHT_GREEN, &lines->r0); // Top, right and bottom borders. + setLineF4(lines); + lines++; + + LSTORE(COLOR_LIGHT_GREEN, &lines->r0); // Left border. + setLineF2(lines); + lines++; + } +} + +static void InitMovingBarGraph(Work *work) +{ + LINE_F3 *line; + int i, j; + + line = work->bar_graph[0]; + for (i = 0; i < 2; i++) + { + for (j = 0; j < 16; j++) + { + line->x0 = 68 + j * 12; + line->x2 = 76 + j * 12; + line->x1 = 76 + j * 12; + + LSTORE(COLOR_DARK_GREEN, &line->r0); + + setLineF3(line); + setSemiTrans(line, 1); + + line->y2 = 178; + line++; + } + } +} + +static void InitZoomLevelLine(LINE_F3 *pZoomLevelLine) +{ + int i; + + for (i = 0; i < 2; i++) + { + LSTORE(COLOR_DARK_GREEN, &pZoomLevelLine->r0); + setLineF3(pZoomLevelLine); + pZoomLevelLine->x0 = pZoomLevelLine->x1 = pZoomLevelLine->x2 = 48; + pZoomLevelLine->y0 = pZoomLevelLine->y1 = 46; + pZoomLevelLine->y2 = 52; + pZoomLevelLine++; + } +} + +static int GetResources(Work *work, CONTROL *control, OBJECT *parent) +{ + MAP *map; + + work->side_lines[0] = GV_Malloc(sizeof(LINE_F2) * 16); + if (!work->side_lines[0]) + { + return -1; + } + + work->side_lines[1] = work->side_lines[0] + 8; + + work->rect_lines[0] = GV_Malloc(sizeof(LINE_F4) * 4); + if (!work->rect_lines[0]) + { + return -1; + } + + work->rect_lines[1] = work->rect_lines[0] + 2; + + work->bar_graph[0] = GV_Malloc(sizeof(LINE_F3) * 32); + + if (!work->bar_graph[0]) + { + return -1; + } + + work->bar_graph[1] = work->bar_graph[0] + 16; + + work->zoom_line[0] = GV_Malloc(sizeof(LINE_F3) * 2); + if (!work->zoom_line[0]) + { + return -1; + } + + work->zoom_line[1] = work->zoom_line[0] + 1; + + InitSideLines(work->side_lines[0]); + InitMovingRectangle(work->rect_lines[0]); + InitMovingBarGraph(work); + InitZoomLevelLine(work->zoom_line[0]); + + work->old_pad = &GV_PadData[2]; + work->hud_delay = HUD_DISP_DELAY; + work->field_58 = 0; + work->field_5E = 2; + work->field_60 = 0; + work->field_62 = 0; + + work->field_6C_turn_vec.vy = work->field_64_vec.vy = control->turn.vy; + work->field_6C_turn_vec.vx = work->field_64_vec.vx = control->turn.vx; + + work->field_64_vec.vz = 0; + work->field_6C_turn_vec.vz = 0; + + work->rect_offset[1] = 0; + work->rect_offset[0] = 0; + + map = control->map; + + work->flags = 0; + work->control = control; + work->parent = parent; + work->map = map; + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewScope(CONTROL *control, OBJECT *parent, int num_parent) +{ + Work *work; + + if ( scope_created_8009F2C4 ) + { + return NULL; + } + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if ( work ) + { + GV_SetNamedActor(&work->actor, Act, Die, "scope.c"); + if ( GetResources(work, control, parent) < 0 ) + { + GV_DestroyActor(&work->actor); + return NULL; + } + scope_created_8009F2C4 = TRUE; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/equip/tabako.c b/components/mgs/mgs/source/equip/tabako.c new file mode 100644 index 00000000..b93f3d25 --- /dev/null +++ b/components/mgs/mgs/source/equip/tabako.c @@ -0,0 +1,157 @@ +#include "equip.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" +#include "linkvar.h" +#include "anime/animconv/anime.h" + +/*---------------------------------------------------------------------------*/ +// Cigarettes + +typedef struct _Work +{ + GV_ACT actor; + OBJECT_NO_ROTS object; + CONTROL *control; + OBJECT *parent; + int num_parent; + DG_PRIM *prim; + SVECTOR vertex; + RECT prim_rect; +} Work; + +#define EXEC_LEVEL GV_ACTOR_AFTER + +/*---------------------------------------------------------------------------*/ + +int tabako_dword_8009F2C0 = 0; + +static void Act(Work *work) +{ + OBJECT_NO_ROTS *object = &work->object; + SVECTOR vec; + MATRIX rotMtx; + + GM_SetCurrentMap(work->control->map->index); + + GM_ActObject2((OBJECT *)object); + + if ((work->parent->objs->flag & DG_FLAG_INVISIBLE) != 0) + { + DG_InvisibleObjs(object->objs); + DG_InvisiblePrim(work->prim); + } + else + { + DG_VisibleObjs(object->objs); + DG_VisiblePrim(work->prim); + + DG_SetPos(&work->prim->world); + DG_MovePos(&work->vertex); + + ReadRotMatrix(&rotMtx); + vec.vx = rotMtx.t[0]; + vec.vy = rotMtx.t[1]; + vec.vz = rotMtx.t[2]; + + if (GV_Time % 150 >= 121 && tabako_dword_8009F2C0 == 1 && + (GM_PlayerStatus & PLAYER_MOVE) == 0) + { + NewAnime_8005E6A4(&vec); + } + } + + // Snake, smoking is bad for your health! + if (!(GV_Time & 63) && GM_SnakeCurrentHealth >= 2) + { + GM_SnakeCurrentHealth--; + GM_GameStatus |= STATE_DAMAGED; + } +} + +static void Die(Work *work) +{ + GM_FreeObject((OBJECT *)&work->object); + GM_FreePrim(work->prim); +} + +static int GetResources(Work *work, OBJECT *parent, int num_parent) +{ + OBJECT_NO_ROTS *object = &work->object; + RECT *pRect; + DG_PRIM *pPrim; + DG_TEX *pTex; + int i; + POLY_FT4 *pPoly; + + GM_InitObjectNoRots(object, GV_StrCode("cigar"), 109, 0); + + if (!object->objs) + { + return -1; + } + + GM_ConfigObjectRoot((OBJECT *)object, parent, num_parent); + + pRect = &work->prim_rect; + pRect->x = pRect->y = 6; + pRect->w = pRect->h = 12; + + work->prim = pPrim = GM_MakePrim(DG_PRIM_RECTANGLE | DG_PRIM_POLY_FT4, 1, &work->vertex, pRect); + + if (!pPrim) + { + return -1; + } + + work->vertex.vx = 37; + work->vertex.vy = -55; + work->vertex.vz = 140; + + pPrim->raise = 250; + pTex = DG_GetTexture(GV_StrCode("rcm_l")); + + for (i = 0; i < 2; i++) + { + pPoly = pPrim->packs[i]; + + LSTORE(COLOR_GRAY, &pPoly->r0); + + setPolyFT4(pPoly); + setSemiTrans(pPoly, 1); + DG_SetPacketTexture4(pPoly, pTex); + } + + work->prim->root = work->object.objs->root; + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewTabako(CONTROL *control, OBJECT *parent, int num_parent) +{ + Work *work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "tabako.c"); + + if (GetResources(work, parent, num_parent) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + work->control = control; + work->parent = parent; + work->num_parent = num_parent; + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/font/font.c b/components/mgs/mgs/source/font/font.c new file mode 100644 index 00000000..20540c42 --- /dev/null +++ b/components/mgs/mgs/source/font/font.c @@ -0,0 +1,1426 @@ +#include "font.h" + +#include +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "linkvar.h" + +// This struct describes the structure of +// "rubi.res" file. +// +// Note that 'r' (0x72) can be either a ".rar", +// ".res" or ".rpk" file. It looks like different +// ".res" files could have different data structure, +// for example "rubi.res" file has a different structure +// and it is not the same as ResHeader in menu/. +// +typedef struct RubiRes +{ + char field_0; + char field_1[9]; +} RubiRes; + +STATIC char *gFontBegin = NULL; +STATIC char *gFontEnd = NULL; +STATIC int rubi_display_flag = TRUE; +STATIC RubiRes *gRubiRes_800AB6B4 = NULL; +STATIC int dword_800AB6B8 = 0; +STATIC int font_color = 0; +STATIC int r_flag = 0; +STATIC int rubi_flag = 0; + +// Menu-related? + +char *SECTION(".sbss") dword_800ABB28; +int SECTION(".sbss") rubi_left_x; +int SECTION(".sbss") rubi_left_y; +int SECTION(".sbss") rubi_orikaeshi_xmax; + +char *zendata[] = {NULL, NULL, NULL, NULL}; + +#define HASH_font 0xCA68 // GV_StrCode("font") +#define HASH_rubi 0xE0E3 // GV_StrCode("rubi") + +#define PEEK_CHAR( code, m ) \ + if ( *( m ) < 0x80 ) \ + { \ + ( code ) = 0x8000 | *( m ); \ + } \ + else \ + { \ + ( code ) = ( *( m ) << 8 ) | *( m + 1 ); \ + } + +#define GETNEXTCHAR( code, m ) \ + if ( *( m ) < 0x80 ) \ + { \ + ( code ) = 0x8000 | *( m ); \ + ( m )++; \ + } \ + else \ + { \ + ( code ) = ( *( m ) << 8 ) | *( m + 1 ); \ + ( m ) += 2; \ + } + + +#define SKIPNEXTCHAR( m ) \ + if ( *( m ) < 0x80 ) \ + { \ + ( m )++; \ + } \ + else \ + { \ + ( m ) += 2; \ + } + +#define GETCODE( code ) ( ( code ) & ~( TOP_KINSOKU_MASK | BACK_KINSOKU_MASK ) ) + +void font_load(void) +{ + char *temp_a1; + char *ptr; + + // Load 'font.res' file: + dword_800ABB28 = GV_GetCache(GV_CacheID(HASH_font, 'r')); + + if (dword_800ABB28) + { + // Load 'rubi.res' file: + gRubiRes_800AB6B4 = (RubiRes *)GV_GetCache(GV_CacheID(HASH_rubi, 'r')); + + temp_a1 = dword_800ABB28; + LSTORE((temp_a1[0] << 24) | (temp_a1[1] << 16) | (temp_a1[2] << 8) | temp_a1[3], temp_a1); + + temp_a1 = dword_800ABB28; + LSTORE((temp_a1[4] << 24) | (temp_a1[5] << 16) | (temp_a1[6] << 8) | temp_a1[7], temp_a1 + 4); + + gFontBegin = temp_a1 + 8; + gFontEnd = temp_a1 + LLOAD(temp_a1 + 0); + zendata[0] = temp_a1 + LLOAD(temp_a1 + 4); + + for (ptr = temp_a1 + 8; ptr < gFontEnd; ptr += 4) + { + LSTORE((ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3], ptr); + } + } +} + +void font_set_font_addr(int type, void *addr) +{ + zendata[type] = addr; +} + +void font_free(void) +{ + return; +} + +int font_init_kcb(KCB *kcb, RECT *rect, int x, int y) +{ + memset(kcb, 0, 44); + kcb->rect_ptr = rect; + kcb->rect = *rect; + kcb->crect.w = 16; + kcb->crect.h = 1; + kcb->crect.x = x; + kcb->crect.y = y; + return font_set_kcb(kcb, -1, -1, 0, 0, 4, 0); +} + +// c_skip - space between letters +// lower values - letters close to each other +// h i g h e r v a l u e s - letters farther apart +// l_skip - vertical space before a line +// width - if the text is longer than (roughly) width number of characters it will wrap +// -1 - no limit +int font_set_kcb(KCB *kcb, int width, int height, int c_skip, int l_skip, int t_skip, int flag) +{ + int w, h; + int w2; + + if (flag >= 0) + { + kcb->flag = flag; + } + + if (t_skip >= 0) + { + kcb->t_skip = t_skip; + } + + if (c_skip >= 0) + { + kcb->c_skip = c_skip; + } + + if (l_skip >= 0) + { + kcb->l_skip = l_skip; + } + + w = (kcb->rect_ptr->w * 4) / (kcb->c_skip + 12); + h = kcb->rect_ptr->h / (kcb->l_skip + 12); + + w2 = w + 1; + if ( !( flag & FONT_NO_KINSOKU ) ) + { + w2 = w; + } + + if (width > 0 && width <= w2) + { + kcb->c_width = width; + } + else if (kcb->c_width == 0 || w2 < kcb->c_width) + { + kcb->c_width = w2; + } + + if (height > 0 && height <= h) + { + kcb->c_height = height; + } + else if (kcb->c_height == 0 || h < kcb->c_height) + { + kcb->c_height = h; + } + + return 0; +} + +void font_set_color(KCB *kcb, int code, int fore, int back) +{ + int fr, fg, fb; + int br, bg, bb; + int ir, ig, ib; + unsigned short *pClut; + int i; + + if (code > 3) + { + return; + } + + pClut = (unsigned short *)kcb->cbuffer + code * 4; + + fr = fore & 0x1f; + fg = (fore >> 5) & 0x1f; + fb = (fore >> 10) & 0x1f; + + br = back & 0x1f; + bg = (back >> 5) & 0x1f; + bb = (back >> 10) & 0x1f; + + ir = (fr + br * 2) / 3; + ig = (fg + bg * 2) / 3; + ib = (fb + bb * 2) / 3; + pClut[0] = back; + pClut[1] = ir | (ig << 5) | (ib << 10); + + // Dead code that messes up the codegen :) + ir = (fr * 5 + br * 3) / 8; + ig = (fg * 5 + bg * 3) / 8; + ib = (fb * 5 + bb * 3) / 8; + + ir = (fr * 2 + br) / 3; + ig = (fg * 2 + bg) / 3; + ib = (fb * 2 + bb) / 3; + pClut[2] = ir | (ig << 5) | (ib << 10); + pClut[3] = fore; + + for (i = 0; i < 4; i++) + { + if (pClut[i] != 0) + { + pClut[i] |= 0x8000; + } + } +} + +int font_get_buffer_size(KCB *kcb) +{ + short w = (kcb->c_skip + 12) * kcb->c_width - kcb->c_skip; + int val_2 = (int)w + 7; + kcb->width = w; + if (val_2 < 0) + { + val_2 = kcb->width + 14; + } + + kcb->row = ((val_2 >> 3) << 2); + kcb->height = (kcb->l_skip + 12) * kcb->c_height + 2; + kcb->rect.w = kcb->row / 2; + kcb->rect.h = kcb->height; + + return kcb->row * kcb->height + 32; +} + +void font_set_buffer(KCB *kcb, void *buffer) +{ + kcb->cbuffer = buffer; + kcb->buffer = buffer + 0x20; +} + +void *font_get_buffer_ptr(KCB *kcb) +{ + return kcb->cbuffer; +} + +static int get_zen_font_data(int code) +{ + int new_var2; + int var_v0; + int temp_v0; + int temp_a0; + + // Clear flags from code + code &= ~0x6000; + + if (code < 0x8200) + { + var_v0 = code - 0x8101; + } + else if (code < 0x8300) + { + var_v0 = code - 0x81ae; + } + else if (code < 0x9600) + { + var_v0 = (code - 0x8F71 - (code / 256)) + 0xA9; + } + else if (code < 0x9A00) + { + var_v0 = ((code - 0x956B) - (code / 256)) | 0x1000; + } + else + { + temp_v0 = (code - 0x9a00) / 512; + temp_a0 = (code - 0x9A00) - (temp_v0 * 512); + new_var2 = temp_v0 + 2; + var_v0 = (temp_a0 - 1 - (temp_a0 / 256)) | (new_var2 * 4096); + } + + return var_v0 + 1; +} + +static unsigned int get_hantable(int a1) +{ + if (a1 > 0) + { + if (a1 > 128) + { + a1 -= 34; + } + + a1 -= 32; + } + + return LLOAD(&gFontBegin[4 * a1]); +} + +static void put_zenkaku_4bpp(char *buffer, int x, int y, int width, char *glyph) +{ + unsigned int i; + int j; + int temp_a1; + char *temp_v1; + int var_a2; + int var_t5; + int var_v1; + unsigned int var_v0; + char *temp_a0; + char *temp_v1_2; + char *var_a1; + char *var_t1; + unsigned int temp_t3; + char temp_v0; + char var_t4; + int new_var; + int new_var2; + + temp_v0 = font_color; + temp_t3 = (temp_v0 << 6) | (temp_v0 << 2); + + if (dword_800AB6B8 != 0) + { + var_v1 = y - 4; + + if (rubi_display_flag == FALSE) + { + var_v1 = y - 2; + } + + temp_v1 = buffer + ((x + 5) / 2); + temp_a0 = temp_v1 + ((var_v1 - 1) * width); + temp_a1 = temp_t3 | 0x33; + *temp_a0 |= temp_a1; + temp_v1_2 = temp_v1 + (var_v1 * width); + *temp_v1_2 |= temp_a1; + } + + var_a1 = glyph; + buffer = buffer + (x / 2) + ((++y) * width); + + if (!(x & 1)) + { + if (var_a1 == NULL) + { + for (i = 0; i < 12; i++) + { + *buffer++ = temp_t3; + *buffer++ = temp_t3; + *buffer++ = temp_t3; + *buffer++ = temp_t3; + *buffer++ = temp_t3; + *buffer++ = temp_t3; + buffer += width - 6; + } + + return; + } + + for (i = 0; i < 12; i++) + { + *buffer++ = temp_t3 | ((*var_a1 >> 6) | (*var_a1 & 0x30)); + *buffer++ = temp_t3 | (((*var_a1 & 0xC) >> 2) | ((*var_a1 & 3) * 16)); + var_a1++; + + *buffer++ = temp_t3 | ((*var_a1 >> 6) | (*var_a1 & 0x30)); + *buffer++ = temp_t3 | (((*var_a1 & 0xC) >> 2) | ((*var_a1 & 3) * 0x10)); + var_a1++; + + *buffer++ = temp_t3 | ((*var_a1 >> 6) | (*var_a1 & 0x30)); + *buffer = temp_t3 | (((*var_a1 & 0xC) >> 2) | ((*var_a1 & 3) * 0x10)); + var_a1++; + + buffer = (buffer + width) - 5; + } + + return; + } + + if (var_a1 == NULL) + { + for (i = 0; i < 12; i++) + { + *buffer++ = (*buffer & 0xF) | (temp_t3 & 0xF0); + *buffer++ = temp_t3; + *buffer++ = temp_t3; + *buffer++ = temp_t3; + *buffer++ = temp_t3; + *buffer++ = temp_t3; + *buffer = (*buffer & 0xF0) | (temp_t3 & 0xF); + buffer += width - 6; + } + + return; + } + + var_t5 = 4; + new_var2 = (font_color & 0xff) << 2; + var_t4 = *var_a1; + + for (i = 0; i < 12; i++) + { + var_a2 = (1 - (x % 2)) * 4; + var_t1 = buffer; + temp_t3 = new_var2; + + for (j = 0; j < 12; j++) + { + var_v0 = var_t4; + var_v0 >>= 6; + new_var = temp_t3 | var_v0; + + if (--var_t5 == 0) + { + var_a1++; + var_t4 = *var_a1; + var_t5 = 4; + } + else + { + var_t4 <<= 2; + } + + *var_t1 |= (new_var & 0xFF) << (4 - var_a2); + + if (var_a2 == 0) + { + var_t1++; + var_a2 = 4; + } + else + { + var_a2 = 0; + } + } + + buffer += width; + } +} + +// Observations about the font.res file used by the font library: +// Big Endian in memory, byte swapped at runtime in font_load(). +// The file begins with an 8-byte 'header' that has two 32-bit words. +// Bytes 0-3 is the size of 'Table 1' which describes the ASCII glyphs. (96 entries for font.res) +// Bytes 4-7 is the offset for the font loaded to index 0. (offset 2306 for font.res) +// +// Following this there is a table of 32-bit words, the size is determined by bytes 0-3. +// The first table appears to have the following layout from reading the code. +// +// Table 1 entry: +// +----------------------+-------------+------------------------+ +// | 31:28 Y-Offset (0-2) | 27:24 Width | 23:0 Index into table2 | +// +----------------------+-------------+------------------------+ +// This table seems to only be used for the ASCII range. +// +// The glyphs used for the ASCII range are 12 pixels tall with a width specified by bits 27:24. +// Each pixel in memory is 2 bits. It is combined with a 2-bit color index in the font buffer. +// The pixel can then be drawn using a 16 entry LUT on the GPU side. +static int put_hankaku_4bpp(char *buffer, int x, int y, int width, unsigned char code) +{ + char *location, *location2, *location3, *location4, *locationIter; + char *font_location; + + unsigned int glyph, glyph3; + char glyph2; + int glyph4, glyph5, glyph6; + unsigned short glyph7, glyph8; + + int shift, shift2; + int i, j, y2, counter, result, loops; + + location = buffer + x / 2 + y * width; + + if (code > 0 && code < 0xFF) + { + glyph = get_hantable(code); + font_location = gFontEnd + (glyph & 0xFFFFFF); + result = (glyph >> 24) & 0xF; + loops = 12; + + if (dword_800AB6B8) + { + shift = (result + 1) / 2 - 1; + + if (rubi_display_flag) + { + y2 = y - 4; + } + else + { + y2 = y - 2; + } + + glyph2 = font_color; + + location3 = buffer + (x + shift) / 2; + location2 = location3 + (y2 - 1) * width; + + glyph3 = (glyph2 << 2) | (glyph2 << 6); + glyph3 |= 0x33; + + *location2 |= glyph3; + + location4 = location3 + y2 * width; + + *location4 |= glyph3; + } + + counter = 4; + + glyph5 = *font_location++; + + location = location + width * (glyph >> 28); + locationIter = location; + + glyph8 = (font_color & 0xFF) << 2; + + for (i = 0; i < loops; location += width, locationIter = location, i++) + { + shift2 = (1 - x % 2) * 4; + + for (j = 0; j < result; j++) + { + glyph7 = (glyph5 & 0xFF) >> 6; + + if (--counter == 0) + { + // Next 4 pixels are in next char. + glyph5 = *font_location++; + counter = 4; + } + else + { + // Advance to next pixel + glyph5 <<= 2; + } + + *locationIter |= ((glyph8 | glyph7) & 0xFF) << (4 - shift2); + + if (shift2 == 0) + { + locationIter++; + shift2 = 4; + } + else + { + shift2 = 0; + } + } + } + } + else + { + if (code) + { + return 0; + } + + y2 = 18; + shift = x % 2; + + glyph6 = font_color & 0xFF; + + location = location + width * 5; + + glyph4 = glyph6 << 6 | (0x33 | glyph6 << 2); + + if (shift > 0) + { + y2 = shift + 16; + *location++ |= (glyph4 & 0xFF) >> (shift * 4); + } + + while (y2 >= 2) + { + y2 -= 2; + *location++ |= glyph4; + } + + if (y2 > 0) + { + shift = (2 - y2) * 4; + *location |= (glyph4 & 0xFF) << (4 - shift); + } + + result = 18; + } + return result; +} + +static unsigned int font_get_glyph_width(int a1) +{ + if (a1 == 0x8000) + { + return 0; + } + + if ( IS_HANKAKU( a1 ) ) + { + if ((a1 != 0x8023) && ((a1 < 0x8010) || (a1 >= 0x8020))) + { + return (get_hantable(a1 & 0xFF) >> 24) & 0xF; + } + } + + return 12; +} + +void font_set_rubi_display_mode(int flag) +{ + rubi_display_flag = flag; +} + +static void set_rubi_left_pos(int xmax, int x, int y) +{ + rubi_left_x = x; + rubi_left_y = y; + rubi_orikaeshi_xmax = xmax; +} + +static void set_rubi_orikaeshi(int xmax) +{ + rubi_orikaeshi_xmax = xmax; +} + +#define ASCII_TO_RUBI(c) (c + 0x8000) + +static int get_rubi_char_index(int c) +{ + if ( IS_HANKAKU( c ) ) + { + if (c >= ASCII_TO_RUBI('0') && c <= ASCII_TO_RUBI('9')) + { + return c - 0x7fdf; + } + if (c >= ASCII_TO_RUBI('A') && c <= ASCII_TO_RUBI('Z')) + { + return c - 0x7fe6; + } + if (c >= ASCII_TO_RUBI('-') && c <= ASCII_TO_RUBI('.')) + { + return c - 0x7fb8; + } + if (c == ASCII_TO_RUBI(127)) // del + { + return 0x76; + } + if (c == ASCII_TO_RUBI('-')) + { + return 0x75; + } + if (c == ASCII_TO_RUBI(' ')) + { + return 0; + } + } + else + { + if (c >= 0x8201 && c <= 0x824D) // Katakana character ('ァ' ... 'ロ') + { + return c - 0x8200; + } + if (c == 0x824f) // 'ヲ' + { + return 0x4e; + } + if (c == 0x8252) + { + return 0x4f; + } + if (c == 0x8253) // 'ン' + { + return 0x50; + } + if (c == 0x9006) + { + return 0x75; + } + if (c == 0x9001) // ' ' + { + return 0; + } + } + return -1; +} + +static int get_rubi_width( int *count, const char *string ) +{ + RubiRes *res; + const char *m; + int n, w; + + res = gRubiRes_800AB6B4; + + w = 0; + n = 0; + m = string; + + for ( ;; ) + { + int code, rubi; + + GETNEXTCHAR( code, m ); + code = GETCODE( code ); + + if ( code == CODE( '}' ) ) + { + break; + } + + rubi = get_rubi_char_index( code ); + if ( rubi < 0 ) + { + printf("Wrong rubi code %x\n", code); + rubi = 0; + } + + if ( rubi == 0 ) + { + w += 4; + } + else + { + w += res[rubi - 1].field_0; + } + + n++; + } + + *count = n; + return w; +} + +static int put_rubi(char *buffer, int x, int y, int width, int arg4) +{ + int oddIter, var_a1, j, var_t3, i, var_v0_2, rubiField0, var_t0; + unsigned int var_v0; + RubiRes *rubiRes; + char *buffer_iter1, *rubiRes_iter, *rubiResPtr, *buffer_iter2; + + i = 0; + rubiRes = &gRubiRes_800AB6B4[arg4]; + rubiResPtr = &rubiRes->field_0; + rubiField0 = *rubiResPtr; + + y++; + buffer_iter1 = buffer + x / 2 + y * width; + buffer_iter2 = buffer_iter1; + rubiResPtr = rubiRes->field_1; + + for (; i < 6; buffer_iter1 += width, buffer_iter2 = buffer_iter1, i++) + { + rubiRes_iter = &rubiResPtr[i / 2 * 3]; + oddIter = i & 1; + var_t3 = (1 - x % 2) * 4; + + if (oddIter) + { + rubiRes_iter++; + var_t0 = *rubiRes_iter; + rubiRes_iter++; + } + else + { + var_t0 = *rubiRes_iter; + rubiRes_iter++; + } + + if (oddIter) + { + var_t0 *= 16; + var_a1 = 2; + } + else + { + var_a1 = 4; + } + + for (j = 0; j < rubiField0; j++) + { + var_v0 = var_t0 & 0xFF; + do + { + var_a1 -= 1; + } while (0); + + var_v0_2 = var_v0 >> 6; + if (var_a1 == 0) + { + var_t0 = *rubiRes_iter; + rubiRes_iter++; + var_a1 = 4; + } + else + { + var_t0 *= 4; + } + + *buffer_iter2 |= var_v0_2 << (4 - var_t3); + if (var_t3 == 0) + { + buffer_iter2++; + var_t3 = 4; + } + else + { + var_t3 = 0; + } + } + } + return rubiField0; +} + +static void draw_rubi_string(char *buffer, int x, int y, int width, const char *arg4) +{ + int iterCount; + int len; + int rubiCode, pos_y_2; + int pos_x; + int pos_x_2; + int x_offset; + int pos_y; + int rubiIndex; + const char *str; + str = arg4; + if (buffer == 0) + { + return; + } + len = get_rubi_width(&iterCount, str); + pos_y_2 = y; + + if (rubi_left_y < pos_y_2) + { + pos_x = rubi_orikaeshi_xmax - rubi_left_x; + pos_y = rubi_left_y; + if (x / 2 >= pos_x) + { + do + { + pos_y = y; + } while (0); + pos_x = x; + rubi_left_x = 0; + } + } + else + { + pos_y = pos_y_2; + pos_x = x - rubi_left_x; + } + + x_offset = 1; + iterCount--; + if (iterCount > 0 && (len - pos_x) < 0) + { + x_offset = (pos_x - len) / iterCount; + if (x_offset >= 5) + { + x_offset = 4; + } + if (x_offset <= 0) + { + x_offset = 1; + } + } + len += x_offset * iterCount; + pos_x = rubi_left_x + (pos_x - len) / 2; + if (rubi_orikaeshi_xmax < pos_x + len) + { + pos_x = rubi_orikaeshi_xmax - len; + } + if (pos_x < 0) + { + pos_x = 0; + } + pos_x_2 = pos_x; + while (1) + { + if (str[0] < 128) + { + do + { + rubiCode = str[0] | 0x8000; + str += 1; + } while (0); + } + else + { + rubiCode = (str[0] << 8) | str[1]; + str += 2; + } + rubiCode &= ~0x6000; + if (rubiCode == 0x807D) + { + break; + } + rubiIndex = get_rubi_char_index(rubiCode); + if (rubiIndex < 0) + { + printf("Wrong rubi code %x\n", rubiCode); + rubiIndex = 0; + } + if (rubiIndex == 0) + { + pos_x_2 += 4 + x_offset; + } + else + { + pos_x_2 += put_rubi(buffer, pos_x_2, pos_y - 6, width, rubiIndex - 1) + x_offset; + } + } +} + +long font_draw_string(KCB *kcb, long xtop, long ytop, const char *string, long color) +{ + void *font_buffer; + int x, y; + + const char *m2; + + int flag1, counter1; + int buf_width; + int height, width_info; + int result; + + const char *m; + + int cr_flag; + + int next_width; + int next_mdata; + + int dx; + int mdata; + int coord; + short character_mask = 0x9FFF; + int d; + + int idx1; + int next_code; + int ascii_closing_bracket = CODE('}'); + + char *ptr; + + // Return if font.res hasn't been loaded + if (!dword_800ABB28) + { + return 0; + } + + counter1 = 0; + dword_800AB6B8 = 0; + buf_width = kcb->width; + font_buffer = kcb->buffer; + height = kcb->height; + + if (!(kcb->flag & FONT_NO_KINSOKU)) + { + buf_width -= 12; + } + + x = xtop; + m = string; + + result = 0; + flag1 = 0; + font_color = color; + kcb->max_height = kcb->l_skip + 14; + kcb->flag &= ~FONT_CLEAN; + kcb->max_width = 0; + cr_flag = 0; + width_info = kcb->row; + y = ytop; + + while (*m) + { + // Fetch byte code and convert ASCII to an unused part of the SJIS map + // 0x80xx is unused by SJIS + PEEK_CHAR(mdata, m); + + // 0x8000 - 0x807F represents the standard ASCII range now + // Character codes are either 8xxx or 9xxx from this point on. + // This leaves two unused bits (0x2000 and 0x4000) that are used + // as flags below. The logic below uses character_mask (0x9FFF) + // in order to get the character code without flags. + next_mdata = mdata & character_mask; + + // If character is an ASCII control code with no flags set, + // consume it and move on to the next step. + if (mdata < CODE(' ')) + { + GETNEXTCHAR(mdata, m); + } + else if (next_mdata == CODE('#')) + { + SKIPNEXTCHAR(m); // skip '#' + + GETNEXTCHAR(d, m); // consume "command" char + d &= character_mask; + + switch (d) + { + case CODE('1'): + next_mdata = CODE('@'); + goto block_136; + + case CODE('N'): + mdata = CODE('\n'); + break; + + case CODE('R'): + m += 2; + // fallthrough + case CODE('{'): + r_flag = 1; + + if (rubi_display_flag) + { + rubi_flag = 1; + set_rubi_left_pos(buf_width, x, y); + + PEEK_CHAR(d, m); + d &= character_mask; + + if (d == CODE('!')) + { + m += 2; + } + } + else + { + PEEK_CHAR(d, m); + d &= character_mask; + + if (d == CODE('!')) + { + while (1) + { + if (d == 0x9002) + { + break; + } + + GETNEXTCHAR(d, m); + d &= character_mask; + + if (d == CODE(',')) + { + goto block_110; + } + } + } + } + break; + + case CODE('-'): + case 0x9006: + dx = put_hankaku_4bpp(font_buffer, x, y, width_info, 0); + goto block_155; + + case CODE('S'): + mdata = 0x8009; + break; + + case CODE('W'): + if (kcb->max_width < x) + { + kcb->max_width = x; + } + dx = 0; + if (flag1) + { + goto OVER; + } + goto block_155; + + case CODE('T'): + m += 2; + if (kcb->l_skip >= 3) + { + dword_800AB6B8 = 1; + } + + r_flag = 1; + break; + + case CODE('2'): + next_mdata = CODE('~'); // This represents the overline character in SJIS + goto block_136; + + case 0x901D: + next_mdata = (GM_OptionFlag & OPTION_BUTTON_MASK) == OPTION_BUTTON_TYPE_C ? 0x9024 : 0x901D; + goto block_136; + + case 0x901E: + case 0x9024: + switch (GM_OptionFlag & OPTION_BUTTON_MASK) + { + case OPTION_BUTTON_TYPE_A: + next_mdata = d; + break; + case OPTION_BUTTON_TYPE_B: + next_mdata = 0x9018; + break; + case OPTION_BUTTON_TYPE_C: + next_mdata = 0x901D; + break; + } + goto block_136; + default: + // go back 4 bytes, but if '#' was one byte then '#X' is at most + // 3 bytes (if 'X' is 2-byte). Bug? + m -= 4; + break; + } + } + else if (r_flag && + (next_mdata == 0x9002 || next_mdata == 0x9004 || next_mdata == ascii_closing_bracket)) + { + if (rubi_flag == 1 && (next_mdata == 0x9002 || next_mdata == 0x9004)) + { + draw_rubi_string(font_buffer, x, y, width_info, m + 2); + } + + rubi_flag = 0; + + do + { + GETNEXTCHAR(next_mdata, m); + next_mdata &= character_mask; + } while (next_mdata != ascii_closing_bracket); + + PEEK_CHAR(next_mdata, m); + if (next_mdata == CODE('#')) + { + SKIPNEXTCHAR(m); + } + + dword_800AB6B8 = 0; + r_flag = 0; + dx = 0; + goto block_155; + } + else if (next_mdata == CODE('|')) + { + SKIPNEXTCHAR(m); + mdata = CODE('\n'); + } + + block_110: + // is it an ASCII control character? + if (mdata < CODE(' ')) + { + switch (mdata) + { + case CODE('\n'): + if (cr_flag == FALSE) + { + /* goto DO_CR; */ + if (kcb->max_width < x) + { + kcb->max_width = x; + } + x = xtop; + coord = kcb->l_skip + 12; + y += coord; + coord = y + 14; + counter1 += 1; + kcb->max_height = y + 14; + if (height <= y + 11) + { + goto OVER; + } + + if (kcb->flag & FONT_NO_ORIKAESHI) + { + goto OVER; + } + if (counter1 >= kcb->c_height) + { + goto OVER; + } + break; + } + cr_flag = 0; + break; + + case CODE('\t'): + coord = kcb->c_skip + 12; + next_width = coord * kcb->t_skip; + if (next_width > 0) + { + x = (x / next_width + 1) * next_width; + } + break; + + case CODE('\f'): + GETNEXTCHAR(mdata, m); + font_color = mdata - CODE('0'); + break; + } + continue; + } + + m2 = m; + GETNEXTCHAR(mdata, m); + next_mdata = mdata & character_mask; + + block_136: + // is it an ASCII character? + if (next_mdata <= CODE(0xFF)) + { + dx = put_hankaku_4bpp(font_buffer, x, y, width_info, next_mdata & 0xFF); + dx += kcb->c_skip; + if (next_mdata == CODE('!') || next_mdata == CODE('?')) + { + PEEK_CHAR(next_mdata, m); + next_mdata = next_mdata & character_mask; + if (next_mdata != CODE('!') && next_mdata != CODE('?')) + { + if (next_mdata != CODE('(') && next_mdata != CODE(')') && + (next_mdata < 0x9009 || next_mdata > 0x900E) && next_mdata != 0x9015) + { + dx += 8; + } + else + { + dx += 2; + } + } + } + } + else + { + idx1 = get_zen_font_data(next_mdata); + if (idx1 > 0) + { + ptr = zendata[idx1 >> 12] + ((idx1 & 0xFFF) - 1) * 36; + } + else + { + ptr = 0; + } + put_zenkaku_4bpp(font_buffer, x, y, width_info, ptr); + coord = kcb->c_skip; + dx = coord + 0xC; + } + + flag1 = 1; + + block_155: + PEEK_CHAR(next_mdata, m); + next_code = next_mdata & character_mask; + + next_width = font_get_glyph_width(next_code); + if ( next_width > 0 + && ( x + dx + next_width + kcb->c_skip - 1 ) >= buf_width ) + { + if (!(kcb->flag & FONT_NO_KINSOKU)) + { + if (mdata & BACK_KINSOKU_MASK) + { + put_zenkaku_4bpp(font_buffer, x, y, width_info, 0); + m = m2; + dx = 0; + } + else if (next_mdata & TOP_KINSOKU_MASK) + { + mdata = next_code; + if (r_flag && (mdata == 0x9002 || mdata == 0x9004 || mdata == ascii_closing_bracket)) + { + goto NO_KINSOKU; + } + else + { + PEEK_CHAR(d, m + 2); + if (mdata == 0x9003 || !(d & TOP_KINSOKU_MASK) || (d & character_mask) == 0x8123) + { + GETNEXTCHAR(mdata, m); + next_mdata = mdata & character_mask; + + if ( IS_HANKAKU( next_mdata ) ) + { + dx += put_hankaku_4bpp(font_buffer, x + dx, y, width_info, next_mdata & 0xFF); + } + else + { + idx1 = get_zen_font_data(next_mdata); + if (idx1 > 0) + { + ptr = zendata[idx1 >> 12] + ((idx1 & 0xFFF) - 1) * 36; + } + else + { + ptr = 0; + } + + put_zenkaku_4bpp(font_buffer, x + dx, y, width_info, ptr); + dx += 12; + } + } + } + } + } + + if (rubi_flag) + { + set_rubi_orikaeshi(x + dx); + } + + result = 1; + cr_flag = 1; + + if (x + dx > kcb->max_width) + { + kcb->max_width = x + dx; + } + + x = xtop; + + coord = kcb->l_skip + 12; + y += coord; + + counter1 += 1; + kcb->max_height = y + 14; + + if (kcb->flag & FONT_NO_ORIKAESHI || y + 12 - 1 >= height || counter1 >= kcb->c_height) + { + if (rubi_flag) + { + rubi_flag = 2; + } + + if (*m) + { + goto OVER; + } + + return 0; + } + } + else + { +NO_KINSOKU: + x += dx; + cr_flag = 0; + } + + if ( kcb->max_width < x ) + { + kcb->max_width = x; + } + } + + return result; + +OVER: + if (kcb->max_width < x) + { + kcb->max_width = x; + } + + // return the position where the error happened, with MSB set to signal error. + return 0x80000000 | (m - string); +} + +void font_clear(KCB *kcb) +{ + int *font_buffer; + int i; + + if (!(kcb->flag & FONT_CLEAN)) + { + font_buffer = kcb->buffer; + i = (unsigned int)(kcb->row * kcb->height) / 4; + for (; i > 0; i--) + { + *font_buffer++ = 0; + } + kcb->flag |= FONT_CLEAN; + } +} + +void font_update(KCB *kcb) +{ + LoadImage(&kcb->rect, kcb->buffer); +} + +void font_clut_update(KCB *kcb) +{ + LoadImage(&kcb->crect, kcb->cbuffer); +} + +void font_print_string(KCB *kcb, const char *string) +{ + font_clear(kcb); + font_draw_string(kcb, 0, kcb->l_skip, string, kcb->color); +} diff --git a/components/mgs/mgs/source/font/font.h b/components/mgs/mgs/source/font/font.h new file mode 100644 index 00000000..ee05be29 --- /dev/null +++ b/components/mgs/mgs/source/font/font.h @@ -0,0 +1,56 @@ +#ifndef __MGS_FONT_H__ +#define __MGS_FONT_H__ + +#include +#include +#include + +typedef struct _KCB +{ + char c_width; + char c_height; + char c_skip; + char l_skip; + char t_skip; + char color; + char flag; + char max_width; + RECT *rect_ptr; + RECT rect; + void *buffer; + short row; + short width; + short height; + short max_height; + RECT crect; + void *cbuffer; +} KCB; + +#define FONT_NO_ORIKAESHI 0x01 // No line break +#define FONT_NO_KINSOKU 0x02 // No line break control +#define FONT_CLEAN 0x10 + +#define TOP_KINSOKU_MASK 0x4000 +#define BACK_KINSOKU_MASK 0x2000 + +#define CODE( a ) ( (a) | 0x8000 ) +#define IS_HANKAKU( a ) ( (a) < 0x8100 ) + +/* font.c */ +void font_load( void ); +void font_set_font_addr( int type, void *addr ); +void font_free( void ); +int font_init_kcb( KCB *kcb, RECT *rect, int x, int y ); +int font_set_kcb( KCB *kcb, int width, int height, int c_skip, int l_skip, int t_skip, int flag ); +void font_set_color( KCB *kcb, int code, int fore, int back ); +int font_get_buffer_size( KCB *kcb ); +void font_set_buffer( KCB *kcb, void *buf ); +void *font_get_buffer_ptr( KCB *kcb ); +void font_set_rubi_display_mode( int flag ); +long font_draw_string( KCB *kcb, long xtop, long ytop, const char *string, long color ); +void font_clear( KCB *kcb ); +void font_update( KCB *kcb ); +void font_clut_update( KCB *kcb ); +void font_print_string( KCB *kcb, const char *string ); + +#endif // __MGS_FONT_H__ diff --git a/components/mgs/mgs/source/game/alert.c b/components/mgs/mgs/source/game/alert.c new file mode 100644 index 00000000..7de6c268 --- /dev/null +++ b/components/mgs/mgs/source/game/alert.c @@ -0,0 +1,250 @@ +#include "game.h" +#include "common.h" +#include "libgcl/libgcl.h" +#include "sound/g_sound.h" + +STATIC int SECTION(".sbss") song_status; +STATIC int SECTION(".sbss") dword_800ABA74; // padding +STATIC int SECTION(".sbss") song_codes[2]; +STATIC short SECTION(".sbss") new_alert_mode; +STATIC short SECTION(".sbss") word_800ABA82; // padding + +char alert_procs[] = {0, 0, 1, 2}; + +extern char noise_sounds[4][3]; +extern int GM_GameOverVox; + +#define STATUS_BIT_0 0x1 +#define STATUS_BIT_1 0x2 + +static void set_song(int song) +{ + if (song == 0) + { + GM_SetSound(0x01ffffff, SD_ASYNC); + } + else + { + GM_SetSound(song | 0x01000000, SD_ASYNC); + } +} + +void GM_SetSong(int song) +{ + if (song == 0) + { + song_status &= ~STATUS_BIT_0; + } + else + { + set_song(song); + song_status |= STATUS_BIT_0; + } +} + +static void set_noise_sound(int noise) +{ + int i; + + for (i = 1; i < 4; i++) + { + noise_sounds[i][noise] = GCL_GetNextInt(); + } + + if (GCL_NextStr()) + { + noise_sounds[0][noise] = GCL_GetNextInt(); + } +} + +int GM_GetNoiseSound(int flag, int noise) +{ + return noise_sounds[(flag >> 8) & 3][noise]; +} + +void GM_SoundStart(void) +{ + if (song_status == 0) + { + set_song(song_codes[GM_AlertMode != ALERT_OFF]); + } +} + +//AlertCmd() ? +void GM_AlertSound(void) +{ + int c_code; + int x_code; + int vox_code; + int proc; + + if (GCL_GetOption('b')) + { + song_codes[0] = GCL_GetNextInt(); + song_codes[1] = GCL_GetNextInt(); + song_status &= ~STATUS_BIT_0; + } + + if (GCL_GetOption('s')) // song + { + set_song(GCL_GetNextInt()); + song_status |= STATUS_BIT_0; + } + + if (GCL_GetOption('c')) + { + c_code = GCL_GetNextInt(); + GM_SetSound(c_code | 0x01ffff00, SD_ASYNC); + + /* song pause or song fade in */ + if (c_code == 0x01ffff01 || (c_code >= 0x01ffff03 && c_code <= 0x01ffff05)) + { + song_status &= ~STATUS_BIT_1; + } + else + { + song_status |= STATUS_BIT_1; + } + } + + if (GCL_GetOption('x')) + { + x_code = GCL_GetNextInt(); + + /* song fade out */ + if (x_code >= 0x01ffff0a && x_code <= 0x01ffff0d) + { + song_status |= STATUS_BIT_0; + } + + GM_SetSound(x_code, SD_ASYNC); + } + + if (GCL_GetOption('e')) // emit + { + GM_SeSet2(GCL_GetNextInt(), // pan + GCL_GetNextInt(), // vol + GCL_GetNextInt()); // se_id + } + + if (GCL_GetOption('v')) // vox + { + vox_code = GCL_GetNextInt(); + + proc = 0; + if (GCL_GetOption('f')) // func + { + proc = GCL_GetNextInt() | 0x80000000; + } + + GM_VoxStream(vox_code, proc); + } + + if (GCL_GetOption('g')) + { + GM_GameOverVox = GCL_GetNextInt(); + } + + if (GCL_GetOption('k')) + { + set_noise_sound(0); + } + + if (GCL_GetOption('r')) + { + set_noise_sound(1); + } + + if (GCL_GetOption('l')) + { + set_noise_sound(2); + } + + if (GCL_GetOption('n')) + { + GM_SoundStart(); + } +} + +void GM_AlertReset( void ) +{ + int i; + + song_status = 0; + song_codes[ 0 ] = 0; + song_codes[ 1 ] = 0; + + noise_sounds[ 0 ][ 0 ] = SE_WALL02; + noise_sounds[ 0 ][ 1 ] = SE_REB01; + + for ( i = 0; i < 4; i++ ) + { + noise_sounds[ i ][ 2 ] = SE_REBDRM01; + } +} + +void GM_AlertAct( void ) +{ + if ( GM_GameStatus & STATE_GAME_OVER ) + { + return; + } + + if ( new_alert_mode >= 0 && new_alert_mode != GM_AlertMode ) + { + if ( song_status == 0 ) + { + switch ( new_alert_mode ) + { + case ALERT_ACTIVE: + if ( GM_AlertMode == ALERT_OFF ) + { + GM_SeSet2( 0, 0x3F, SE_ALERT_SIREN ); + GM_SetSound( 0x01ffff0b, SD_ASYNC ); + set_song( song_codes[1] ); + } + else if ( GM_AlertMode == ALERT_EVASION ) + { + GM_SetSound( 0x01ffff03, SD_ASYNC ); + } + break; + + case ALERT_EVASION: + GM_SetSound( 0x01ffff10, SD_ASYNC ); + break; + + case ALERT_OFF: + set_song( song_codes[0] ); + break; + } + } + + GM_AlertMode = new_alert_mode; + } + + if ( GM_AlertMode == ALERT_EVASION && song_status == 0 && GM_AlertLevel == 60 ) + { + GM_SetSound( 0x01ffff08, SD_ASYNC ); + } + + new_alert_mode = -1; +} + +void GM_AlertModeSet(int mode) +{ + if (mode > new_alert_mode) + { + new_alert_mode = mode; + GM_CallSystemCallbackProc(2, alert_procs[mode]); + } +} + +void GM_AlertModeInit(void) +{ + new_alert_mode = -1; +} + +void GM_AlertModeReset(void) +{ + GM_CallSystemCallbackProc(2, 0); +} diff --git a/components/mgs/mgs/source/game/area.c b/components/mgs/mgs/source/game/area.c new file mode 100644 index 00000000..df96fa9f --- /dev/null +++ b/components/mgs/mgs/source/game/area.c @@ -0,0 +1,64 @@ +#include "common.h" +#include "menu/menuman.h" +#include "game.h" + +extern AreaHistory area_history; + +STATIC char GM_CurrentStageName[8] = {}; + +short SECTION(".sbss") area_name; +short SECTION(".sbss") pad3_; +short SECTION(".sbss") pad3; + +//------------------------------------------------------------------------------ + +void GM_InitArea(void) +{ + memset(&area_history, 0, sizeof(area_history)); +} + +void GM_GetAreaHistory(AreaHistory *history) +{ + *history = area_history; +} + +void GM_SetAreaHistory(AreaHistory *history) +{ + area_history = *history; +} + +int GM_SetArea(int stage_id, char *stage_name) +{ + int i; + + area_name = stage_id; + strcpy(GM_CurrentStageName, stage_name); + + for (i = MAX_HISTORY - 1; i > 0; i--) + { + area_history.history[i] = area_history.history[i - 1]; + } + + area_history.history[0] = stage_id; + return stage_id; +} + +int GM_AreaHistory(int stage_id) +{ + int i; + + for (i = 1; i < MAX_HISTORY; i++) + { + if (area_history.history[i] == stage_id) + { + break; + } + } + + return i; +} + +char *GM_GetArea(int flag) +{ + return GM_CurrentStageName; +} diff --git a/components/mgs/mgs/source/game/camera.c b/components/mgs/mgs/source/game/camera.c new file mode 100644 index 00000000..b1257b2f --- /dev/null +++ b/components/mgs/mgs/source/game/camera.c @@ -0,0 +1,1219 @@ +#include "camera.h" + +#include +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libhzd/libhzd.h" + +#include "game.h" +#include "linkvar.h" +#include "strcode.h" + +STATIC int SECTION(".sbss") dword_800ABA84; +STATIC SVECTOR SECTION(".sbss") svec_800ABA88; +STATIC int SECTION(".sbss") dword_800ABA90; +STATIC int SECTION(".sbss") dword_800ABA94; + +int SECTION(".sbss") GM_CameraShakeOffset; +int SECTION(".sbss") GM_event_camera_flag; + +int GM_CameraTrackSave = 10000; +SVECTOR GM_CameraRotateSave = { 640, 2048, 0, 0 }; +int GM_CameraTrackOrg = 10000; + +STATIC int GM_CameraFirstPersonOrg = 0; +STATIC int GM_CameraFlagsOrg = 0; +STATIC HZD_TRP *GM_800AB444 = NULL; +STATIC int GM_800AB448 = 0; + +extern GM_CameraSystemWork GM_Camera; +extern GM_SnakeCameraWork GM_SnakeCamera; +extern UnkCameraStruct2 gUnkCameraStruct2_800B7868; +extern UnkCameraStruct2 gUnkCameraStruct2_800B76F0; +extern CAMERA GM_CameraList[8]; + +static const unsigned int dword_80010C60[] = +{ + 0, + 0x1E0001, + 0x1E0002, + 0x1E0003, + 0x140003, + 0xA0003, + 0x50003, + 0x30003 +}; + + +/* +Camera Attributes: + FLY: SVECTOR; + TRG: SVECTOR; + ROT: SVECTOR; + TRK: INT; + POS: INT; + ANG: SHORT? + TYPE: UNSIGNED INT (FLAG); +*/ + +static void MakeRotate( SVECTOR *cam, SVECTOR *trg, SVECTOR *rot, int *track ); +static void MakeTarget( SVECTOR *cam, SVECTOR *trg, SVECTOR *rot, int *track ); +static void MakeCamera( SVECTOR *cam, SVECTOR *trg, SVECTOR *rot, int *track ); + +STATIC void sub_8002FAAC(SVECTOR *eye, SVECTOR *center, SVECTOR *rotate, int *track); +STATIC void sub_8002FCF0(void); +STATIC void sub_800303E0(SVECTOR *arg0); + +static inline int camera_clamp(int val, int min, int max) +{ + if (val < min) + { + val = min; + } + + if (val > max) + { + val = max; + } + + return val; +} + +STATIC void sub_8002EADC(int index) +{ + SVECTOR *pBase; + SVECTOR *pVec; + + pBase = (SVECTOR *)&GM_Camera; + pVec = pBase + index; + + GM_Camera.position.vx = camera_clamp(GM_Camera.position.vx, pVec[8].vz, pVec[6].vz); + GM_Camera.position.vy = camera_clamp(GM_Camera.position.vy, pVec[8].pad, pVec[6].pad); + GM_Camera.position.vz = camera_clamp(GM_Camera.position.vz, pVec[9].vx, pVec[7].vx); +} + +STATIC void sub_8002EB80(SVECTOR *vec_1, SVECTOR *vec_2) +{ + int iVar1; + + iVar1 = GV_NearRange((int)vec_1->vx, (int)vec_2->vx, 500); + vec_1->vx = (short)iVar1; + iVar1 = GV_NearRange((int)vec_1->vz, (int)vec_2->vz, 500); + vec_1->vz = (short)iVar1; + iVar1 = GV_NearRange((int)vec_1->vy, (int)vec_2->vy, 0x96); + + vec_1->vy = (short)iVar1; +} + +STATIC void sub_8002EBE8(SVECTOR *arg0, int index) +{ + SVECTOR *pBase; + SVECTOR *pVec; + + pBase = (SVECTOR *)&GM_Camera; + pVec = pBase + index; + + arg0->vx = camera_clamp(arg0->vx, pVec[12].vz, pVec[10].vz); + arg0->vy = camera_clamp(arg0->vy, pVec[12].pad, pVec[10].pad); + arg0->vz = camera_clamp(arg0->vz, pVec[13].vx, pVec[11].vx); + +} + +static void SaturatedAdd( SVECTOR *vec1, SVECTOR *vec2, SVECTOR *vec3 ) +{ + short *v1, *v2, *v3; + int i, val; + + v1 = (short *)vec1; + v2 = (short *)vec2; + v3 = (short *)vec3; + for ( i = 0; i < 3; i++, v1++, v2++, v3++ ) + { + val = *v1 + *v2; + if ( val != (short)val ) val = ( val < 0 ) ? 0x8000 : 0x7FFF; + *v3 = val; + } +} + +static void SaturatedSub( SVECTOR *vec1, SVECTOR *vec2, SVECTOR *vec3 ) +{ + short *v1, *v2, *v3; + int i, val; + + v1 = (short *)vec1; + v2 = (short *)vec2; + v3 = (short *)vec3; + for ( i = 0; i < 3; i++, v1++, v2++, v3++ ) + { + val = *v1 - *v2; + if ( val != (short)val ) val = ( val < 0 ) ? 0x8000 : 0x7FFF; + *v3 = val; + } +} + +// move to camera.h when it exists? +static inline int CheckFlag(int mask) +{ + return GM_Camera.flag & mask; +} + +STATIC int camera_act_helper3_helper_8002ED3C(void) +{ + int uVar1; + int uVar2; + + uVar2 = 0x20; + uVar1 = CheckFlag(uVar2); + while (uVar1) + { + uVar2 >>= 1; + uVar1 = CheckFlag(uVar2); + } + return uVar1; +} + +STATIC void sub_8002ED68(int arg0) +{ + switch (gUnkCameraStruct2_800B76F0.position.pad & 3) + { + case 0: + GV_OriginPadSystem(0); + return; + + case 1: + if (arg0 != 0) + { + MakeRotate(&GM_Camera.position, &GM_Camera.target, &gUnkCameraStruct2_800B76F0.rotate, &gUnkCameraStruct2_800B76F0.track); + } + + GV_OriginPadSystem(gUnkCameraStruct2_800B76F0.rotate.vy + 2048); + break; + + case 2: + GV_OriginPadSystem(gUnkCameraStruct2_800B76F0.position.pad >> 2); + break; + + default: + break; + } +} + +STATIC void sub_8002EE04(void) +{ + GM_Camera.position = gUnkCameraStruct2_800B76F0.position; + GM_Camera.target = gUnkCameraStruct2_800B76F0.target; + GM_Camera.type = 0; + sub_8002ED68(0); +} + +STATIC void sub_8002EE80(void) +{ + gUnkCameraStruct2_800B76F0.target.vz = GM_SnakeCamera.target.vz; + GM_Camera.position = gUnkCameraStruct2_800B76F0.position; + GM_Camera.target = gUnkCameraStruct2_800B76F0.target; + GM_Camera.type = 0; + GV_OriginPadSystem(0); +} + +STATIC void sub_8002EF08(void) +{ + GM_Camera.position = gUnkCameraStruct2_800B76F0.position; + GM_Camera.target = GM_SnakeCamera.target; + GM_Camera.type = 0; + sub_8002ED68(1); +} + +STATIC void sub_8002EF88(void) +{ + int unused[2]; (void)unused; + GM_Camera.target = GM_SnakeCamera.target; + GM_Camera.track = GM_SnakeCamera.track; + GM_Camera.rotate = GM_SnakeCamera.rotate; + GM_Camera.type = 2; + GV_OriginPadSystem(0); +} + +STATIC void camera_act_helper_helper_8002F008() +{ + GM_Camera.position = GM_SnakeCamera.position; + GM_Camera.track = 1000; + GM_Camera.rotate = GM_SnakeCamera.rotate2; + GM_Camera.type = 1; + GV_OriginPadSystem(GM_SnakeCamera.rotate2.vy + 2048); +} + +static void InterpIntoSubject( int interp ) +{ + SVECTOR tmp; + + if ( interp < 6 ) + { + GV_NearExp4V( &GM_Camera.target, &GM_SnakeCamera.position, 3 ); + GV_NearExp4PV( &GM_Camera.rotate, &GM_SnakeCamera.rotate2, 3 ); + GM_Camera.track = GV_NearExp4( GM_Camera.track, 1000 ); + GV_DirVec3( &GM_Camera.rotate, GM_Camera.track, &tmp ); + SaturatedSub( &GM_Camera.target, &tmp, &GM_Camera.position ); + GM_Camera.type = 0; + } + else + { + GV_NearTimeV( &GM_Camera.position, &GM_SnakeCamera.position, 12 - interp, 3 ); + GV_NearTimePV( &GM_Camera.rotate, &GM_SnakeCamera.rotate2, 12 - interp, 3 ); + GM_Camera.track = GV_NearTime( GM_Camera.track, 1000, 12 - interp ); + GV_DirVec3( &GM_Camera.rotate, GM_Camera.track, &tmp ); + SaturatedAdd( &GM_Camera.position, &tmp, &GM_Camera.target ); + GM_Camera.type = 0; + } + + GV_OriginPadSystem( 0 ); +} + +STATIC void camera_act_helper_8002F1C8(void) +{ + if (GM_Camera.flag & 0x100) + { + if (GM_SnakeCamera.interp >= 12) + { + GM_Camera.flag &= ~0x101; + } + + InterpIntoSubject(GM_SnakeCamera.interp); + } + else if (GM_Camera.flag & 0x200) + { + if (GM_Camera.callback[0]) + { + GM_Camera.callback[0](); + } + } + else + { + camera_act_helper_helper_8002F008(); + } + + GM_SnakeCamera.interp++; +} + +STATIC int sub_8002F274(void) +{ + if (GM_Camera.pan.pad == 1) + { + GV_AddVec3(&GM_SnakeCamera.position, &GM_Camera.pan, &GM_SnakeCamera.position); + GM_Camera.pan.pad = 0; + } + + sub_800303E0(&GM_SnakeCamera.target); + + if (GM_Camera.flag & 0x20) + { + sub_8002EF88(); + return 2; + } + + else if (GM_Camera.flag & 0x10) + { + sub_8002EE04(); + } + else if (GM_Camera.flag & 8) + { + sub_8002EE80(); + } + else if (GM_Camera.flag & 4) + { + sub_8002EF08(); + } + else if (GM_Camera.flag & 2) + { + if (GM_Camera.callback[1]) + { + GM_Camera.callback[1](); + } + + if ((GM_Camera.flag & 0x80)) + { + return 1; + } + else if (GM_Camera.flag & 0x40) + { + return 2; + } + else + { + return 0; + } + } + else + { + sub_8002EF88(); + return 1; + } + + return 0; + +} + +static int InterpOutSubject( int interp ) +{ + SVECTOR trg, rot; + int ret, track; + + ret = 0; + + if ( interp < 2 ) + { + GV_NearTimeV( &GM_Camera.target, &GM_SnakeCamera.position, 2 - interp, 3 ); + GV_NearTimePV( &GM_Camera.rotate, &GM_SnakeCamera.rotate2, 2 - interp, 3 ); + GM_Camera.track = GV_NearTime( GM_Camera.track, 1000, 2 - interp ); + GV_OriginPadSystem( 0 ); + } + else + { + int ( *nearfunc )( int, int ); + void ( *nearfuncV )( void *, void *, int ); + void ( *nearfuncPV )( void *, void *, int ); + + if ( interp < 4 ) + { + nearfuncV = &GV_NearExp4V; + nearfuncPV = &GV_NearExp4PV; + nearfunc = &GV_NearExp4; + } + else + { + nearfuncV = &GV_NearExp2V; + nearfuncPV = &GV_NearExp2PV; + nearfunc = &GV_NearExp2; + } + + trg = GM_Camera.target; + rot = GM_Camera.rotate; + + track = GM_Camera.track; + ret = sub_8002F274(); + + switch ( GM_Camera.type ) + { + case 0: + MakeRotate( &GM_Camera.position, &GM_Camera.target, &GM_Camera.rotate, &GM_Camera.track ); + break; + case 1: + MakeTarget( &GM_Camera.position, &GM_Camera.target, &GM_Camera.rotate, &GM_Camera.track ); + break; + } + + nearfuncV( &trg, &GM_Camera.target, 3 ); + nearfuncPV( &rot, &GM_Camera.rotate, 3 ); + track = nearfunc( track, GM_Camera.track ); + + GM_Camera.target = trg; + GM_Camera.rotate = rot; + GM_Camera.track = track; + } + + GM_Camera.type = 2; + return ret; +} + +STATIC int camera_act_helper2_8002F5C4(void) +{ + int ret; + + if (GM_Camera.flag & 1) + { + if (GM_SnakeCamera.interp >= 12) + { + GM_Camera.flag &= ~0x101; + GM_Camera.interp = -1; + GM_Camera.interp_mode = 0; + } + + ret = InterpOutSubject(GM_SnakeCamera.interp); + GM_SnakeCamera.interp++; + + return ret; + } + + return sub_8002F274(); +} + +STATIC void camera_act_helper3_8002F64C(void) +{ + if (GM_Camera.first_person != GM_CameraFirstPersonOrg) + { + GM_SnakeCamera.interp = 0; + GM_Camera.interp_mode = 0; + + if (GM_Camera.first_person == 0) + { + GM_Camera.flag |= 1; + sub_8002FAAC(&GM_Camera.position, &GM_Camera.target, &GM_Camera.rotate, &GM_Camera.track); + } + else if (GM_Camera.first_person == 1) + { + GM_Camera.flag |= 0x100; + sub_8002FAAC(&GM_Camera.position, &GM_Camera.target, &GM_Camera.rotate, &GM_Camera.track); + + if (!(GM_Camera.flag & 1)) + { + if (camera_act_helper3_helper_8002ED3C() & 0x3001E) + { + GM_SnakeCamera.rotate = GM_Camera.rotate; + GM_SnakeCamera.track = GM_Camera.track; + } + else + { + sub_8002FCF0(); + } + } + } + else + { + GM_Camera.first_person = 1; + } + } + + if (GM_Camera.flag != GM_CameraFlagsOrg) + { + GM_SnakeCamera.interp = 0; + } + + GM_CameraFirstPersonOrg = GM_Camera.first_person; + GM_CameraFlagsOrg = GM_Camera.flag; +} + +STATIC void camera_act_helper4_8002F78C(void) +{ + SVECTOR vec; + + gUnkCameraStruct2_800B7868.type = 0; + gUnkCameraStruct2_800B7868.track = GM_Camera.track; + gUnkCameraStruct2_800B7868.zoom = GM_Camera.zoom; + + if (GM_Camera.interp_mode == 0) + { + gUnkCameraStruct2_800B7868.position = GM_Camera.position; + gUnkCameraStruct2_800B7868.rotate = GM_Camera.rotate; + gUnkCameraStruct2_800B7868.target = GM_Camera.target; + } + else + { + MakeRotate(&GM_Camera.position, &GM_Camera.target, &GM_Camera.rotate, &GM_Camera.track); + + GV_SubVec3(&GM_Camera.position, &svec_800ABA88, &vec); + GV_AddVec3(&vec, &gUnkCameraStruct2_800B7868.position, &gUnkCameraStruct2_800B7868.position); + + svec_800ABA88 = GM_Camera.position; + + if (GM_Camera.interp_mode == 3) + { + GV_NearTimeV(&gUnkCameraStruct2_800B7868.position.vx, &GM_Camera.position.vx, GM_Camera.interp, 3); + GV_NearTimePV(&gUnkCameraStruct2_800B7868.rotate.vx, &GM_Camera.rotate.vx, GM_Camera.interp, 3); + } + else if (GM_Camera.interp_mode == 1) + { + GV_NearExp2V(&gUnkCameraStruct2_800B7868.position.vx, &GM_Camera.position.vx, 3); + GV_NearExp2PV(&gUnkCameraStruct2_800B7868.rotate.vx, &GM_Camera.rotate.vx, 3); + } + else if (GM_Camera.interp_mode == 2) + { + GV_NearExp4V(&gUnkCameraStruct2_800B7868.position.vx, &GM_Camera.position.vx, 3); + GV_NearExp4PV(&gUnkCameraStruct2_800B7868.rotate.vx, &GM_Camera.rotate.vx, 3); + } + + if (gUnkCameraStruct2_800B7868.track > 10000) + { + gUnkCameraStruct2_800B7868.track = 10000; + } + + MakeTarget(&gUnkCameraStruct2_800B7868.position, &gUnkCameraStruct2_800B7868.target, &gUnkCameraStruct2_800B7868.rotate, &gUnkCameraStruct2_800B7868.track); + + if (--GM_Camera.interp < 0) + { + GM_Camera.interp_mode = 0; + GM_Camera.interp = -1; + } + } + + if (GM_NoisePower >= 255) + { + if (GM_Camera.first_person == 0) + { + gUnkCameraStruct2_800B7868.position.vy += (GV_RandS(512) * GM_NoiseLength) / 32; + } + else + { + gUnkCameraStruct2_800B7868.position.vy += (GV_RandS(512) * GM_NoiseLength) / 128; + } + } + + if (GM_Camera.first_person == 0) + { + gUnkCameraStruct2_800B7868.position.vy += GM_CameraShakeOffset; + GM_CameraShakeOffset = 0; + } + else + { + gUnkCameraStruct2_800B7868.position.vy += GM_CameraShakeOffset / 4; + GM_CameraShakeOffset = 0; + } +} + +STATIC void sub_8002FAAC( SVECTOR *cam, SVECTOR *trg, SVECTOR *rot, int *track ) +{ + if ( gUnkCameraStruct2_800B7868.type == 0 ) + { + *cam = gUnkCameraStruct2_800B7868.position; + *trg = gUnkCameraStruct2_800B7868.target; + MakeRotate( cam, trg, rot, track ); + gUnkCameraStruct2_800B7868.rotate = *rot; + } +} + +STATIC void camera_act_helper7_8002FB54() +{ + switch (GM_Camera.type) + { + case 0: + break; + case 1: + MakeTarget(&GM_Camera.position, &GM_Camera.target, &GM_Camera.rotate, + &GM_Camera.track); + break; + case 2: + MakeCamera(&GM_Camera.position, &GM_Camera.target, &GM_Camera.rotate, + &GM_Camera.track); + break; + } +} + +static void MakeRotate( SVECTOR *cam, SVECTOR *trg, SVECTOR *rot, int *track ) +{ + SVECTOR tmp; + + GV_SubVec3( trg, cam, &tmp ); + *track = GV_VecLen3( &tmp ); + + rot->vz = 0; + rot->vy = GV_VecDir2( &tmp ); + rot->vx = ratan2( -tmp.vy, SquareRoot0( tmp.vx * tmp.vx + tmp.vz * tmp.vz ) ); +} + +static void MakeTarget( SVECTOR *cam, SVECTOR *trg, SVECTOR *rot, int *track ) +{ + SVECTOR tmp; + + GV_DirVec3( rot, *track, &tmp ); + SaturatedAdd( cam, &tmp, trg ); +} + +static void MakeCamera( SVECTOR *cam, SVECTOR *trg, SVECTOR *rot, int *track ) +{ + SVECTOR tmp; + + GV_DirVec3( rot, *track, &tmp ); + SaturatedSub( trg, &tmp, cam ); +} + +STATIC void sub_8002FCF0(void) +{ + if (!(GM_Camera.flag & 0x20)) + { + GM_SnakeCamera.track = GM_CameraTrackSave; + GM_SnakeCamera.rotate = GM_CameraRotateSave; + } + else + { + GM_SnakeCamera.track = gUnkCameraStruct2_800B76F0.track; + GM_SnakeCamera.rotate = gUnkCameraStruct2_800B76F0.rotate; + } +} + +void GM_SetCameraCallbackFunc(int index, TGMCameraFunc func) +{ + GM_Camera.callback[index] = func; +} + +STATIC void ChangeCamera(int new, int old) +{ + CAMERA *camera; + + int cam_param1; + + GM_Camera.flag &= ~0x3C; + + if (new == -1) + { + if (!(GM_Camera.flag & 2)) + { + sub_8002FCF0(); + } + } + else + { + camera = &GM_CameraList[new]; + cam_param1 = camera->field_10_param1; + + if (camera->field_10_param1 == 0) + { + GM_Camera.flag |= 0x20; + + GM_SnakeCamera.rotate.vx = camera->trg.vx; + GM_SnakeCamera.rotate.vy = camera->trg.vy; + GM_SnakeCamera.rotate.vz = 0; + + gUnkCameraStruct2_800B76F0.target = GM_SnakeCamera.target; + gUnkCameraStruct2_800B76F0.rotate = GM_SnakeCamera.rotate; + gUnkCameraStruct2_800B76F0.track = GM_SnakeCamera.track = camera->trg.vz; + + MakeCamera(&gUnkCameraStruct2_800B76F0.position, &gUnkCameraStruct2_800B76F0.target, &gUnkCameraStruct2_800B76F0.rotate, &gUnkCameraStruct2_800B76F0.track); + } + else + { + switch (camera->type) + { + case 0: + gUnkCameraStruct2_800B76F0.position = camera->pos; + gUnkCameraStruct2_800B76F0.target = camera->trg; + MakeRotate(&gUnkCameraStruct2_800B76F0.position, &gUnkCameraStruct2_800B76F0.target, &gUnkCameraStruct2_800B76F0.rotate, &gUnkCameraStruct2_800B76F0.track); + break; + + case 1: + gUnkCameraStruct2_800B76F0.position = camera->pos; + + gUnkCameraStruct2_800B76F0.rotate.vx = camera->trg.vx; + gUnkCameraStruct2_800B76F0.rotate.vy = camera->trg.vy; + gUnkCameraStruct2_800B76F0.rotate.vz = 0; + gUnkCameraStruct2_800B76F0.track = camera->trg.vz; + + MakeTarget(&gUnkCameraStruct2_800B76F0.position, &gUnkCameraStruct2_800B76F0.target, &gUnkCameraStruct2_800B76F0.rotate, &gUnkCameraStruct2_800B76F0.track); + break; + + case 2: + gUnkCameraStruct2_800B76F0.target = camera->pos; + + gUnkCameraStruct2_800B76F0.rotate.vx = camera->trg.vx; + gUnkCameraStruct2_800B76F0.rotate.vy = camera->trg.vy; + gUnkCameraStruct2_800B76F0.rotate.vz = 0; + gUnkCameraStruct2_800B76F0.track = camera->trg.vz; + + MakeCamera(&gUnkCameraStruct2_800B76F0.position, &gUnkCameraStruct2_800B76F0.target, &gUnkCameraStruct2_800B76F0.rotate, &gUnkCameraStruct2_800B76F0.track); + break; + } + + gUnkCameraStruct2_800B76F0.position.pad = camera->pad_type | ((unsigned short)camera->pos.pad * 4); + gUnkCameraStruct2_800B76F0.type = 0; + + if (cam_param1 == 1) + { + GM_Camera.flag |= 0x10; + } + else if (cam_param1 == 2) + { + GM_Camera.flag |= 0x8; + } + else + { + GM_Camera.flag |= 0x4; + } + } + } + + if ((GM_Camera.first_person == 1) || (DG_UnDrawFrameCount > 0)) + { + GM_Camera.interp_mode = 0; + GM_Camera.interp = -1; + } + else if (old < 0) + { + GM_Camera.interp_mode = 2; + GM_Camera.interp = 30; + } + else + { + camera = &GM_CameraList[old]; + GM_Camera.interp_mode = dword_80010C60[camera->interp]; + GM_Camera.interp = dword_80010C60[camera->interp] >> 16; + } +} + +STATIC void CheckMessage(GV_ACT *work) +{ + GV_MSG *msg; + int n_msg; + int bitmap; + GV_MSG *iter; + int i; + int index; + int type; + + n_msg = GV_ReceiveMessage(0xC356, &msg); + if (n_msg == 0) + { + return; + } + + bitmap = 0; + + iter = msg; + for (i = n_msg; i > 0; i--) + { + index = iter->message[0]; + type = iter->message[1]; + + if (type == HASH_LEAVE) + { + bitmap |= (1 << index); + GM_event_camera_flag &= ~(1 << index); + printf("[%d]cam out %d\n", GV_Time, index); + } + + iter++; + } + + iter = msg; + for (i = n_msg; i > 0; i--) + { + index = iter->message[0]; + type = iter->message[1]; + + if (type == HASH_ENTER) + { + GM_event_camera_flag |= (1 << index); + + if (bitmap & (1 << index)) + { + dword_800ABA90 &= ~(1 << index); + } + + printf("[%d]cam in %d\n", GV_Time, index); + } + + iter++; + } +} + +STATIC int CheckEvents(GV_ACT *work) +{ + int event; + int changed; + + unsigned int mask; + int i; + int old_i; + + event = GM_event_camera_flag; + + if (GM_AlertMode == ALERT_ACTIVE) + { + if (GM_Camera.alert_mask < 8) + { + event &= ~((1 << GM_Camera.alert_mask) - 1); + } + + if (dword_800ABA94 != 0) + { + event &= ~dword_800ABA94; + } + } + + changed = event ^ dword_800ABA90; + if (changed == 0) + { + return 0; + } + + dword_800ABA90 = event; + + for (mask = 1, i = 0; i < 7; i++, mask <<= 1) + { + if (changed & mask) + { + break; + } + } + + changed = event; + old_i = i; + + for (mask = 0x80, i = 7; i >= 0; i--, mask >>= 1) + { + if (changed & mask) + { + if (GM_CameraList[i].field_10_param1 != -1) + { + break; + } + + GM_event_camera_flag &= ~mask; + } + } + + + sub_8002FAAC(&GM_Camera.position, &GM_Camera.target, &GM_Camera.rotate, &GM_Camera.track); + ChangeCamera(i, old_i); + printf("[%d]change camera %d\n", GV_Time, i); + + GM_GameStatus &= ~GAME_FLAG_BIT_07; + +#ifdef __psyz + /* i is -1 when no fixed camera claims the change, and that is NOT an error: + * it means "no fixed camera here", which is how the game hands the shot + * back to the one that follows the player. ChangeCamera must see it. + * + * What is an error is the read below. GM_CameraList[-1] takes the sixteen + * bytes sitting in front of the array -- on the PSX deterministic and + * whatever the game shipped with, here whatever the linker parked there. + * Setting GAME_FLAG_BIT_07 off that is how the view ended up with no + * target: measured on the board, the eye's Y climbed 1816 -> 8770 -> 17178 + * -> 19725 while kept-saturated tracked it 1560 -> 15912, until four + * objects in five projected with an overflowing divide, nothing was + * culled, 28000 models hit a primitive queue sized for a fraction of that, + * and the GPU reader desynced on the wreckage. That was the white screen. + * + * No camera means no camera asking for the flag, so leave it clear. + * + * An earlier attempt pinned i to the previous camera instead. It killed + * the white screen but replaced it with a camera that stays behind when + * the player walks out of the zone -- a fix that swapped one bug for + * another because it treated -1 as a failure rather than as a value. */ + if (i >= 0 && (GM_CameraList[i].trg.pad & 1)) + { + GM_GameStatus |= GAME_FLAG_BIT_07; + } + return 1; +#else + if (GM_CameraList[i].trg.pad & 1) + { + GM_GameStatus |= GAME_FLAG_BIT_07; + } + + return 1; +#endif +} + +STATIC void sub_800303E0(SVECTOR *arg0) +{ + SVECTOR vec; + GM_SnakeCameraWork *pUnkCamera; + int index; + + vec = *arg0; + pUnkCamera = &GM_SnakeCamera; + + if (DG_UnDrawFrameCount > 0) + { + *arg0 = pUnkCamera->position; + } + else + { + sub_8002EB80(&vec, &pUnkCamera->position); + GV_NearExp4V((short *)arg0, (short *)&vec, 3); + } + +#ifdef VR_EXE + index = !!(GM_Camera.flag & 0x28); +#else + index = !!(GM_Camera.flag & 0x20); +#endif + + sub_8002EBE8(arg0, index); +} + +void GM_PanCamera(SVECTOR *pan) +{ + GM_CameraSystemWork *pCamera; + + pCamera = &GM_Camera; + pCamera->pan = *pan; + pCamera->pan.pad = 1; + + GV_AddVec3(&gUnkCameraStruct2_800B7868.position, pan, &gUnkCameraStruct2_800B7868.position); + GV_AddVec3(&gUnkCameraStruct2_800B7868.target, pan, &gUnkCameraStruct2_800B7868.target); + + GV_AddVec3(&pCamera->position, pan, &pCamera->position); + GV_AddVec3(&pCamera->target, pan, &pCamera->target); + + GV_AddVec3(&GM_SnakeCamera.pan, pan, &GM_SnakeCamera.pan); + GV_AddVec3(&GM_SnakeCamera.position, pan, &GM_SnakeCamera.position); + GV_AddVec3(&GM_SnakeCamera.target, pan, &GM_SnakeCamera.target); + + GV_AddVec3(&svec_800ABA88, pan, &svec_800ABA88); +} + +static void Act(GV_ACT *work) +{ + int changed; + + int iVar2; + + if (GM_GameStatus >= 0) + { + if (GV_PauseLevel == 0) + { + CheckMessage(work); + changed = CheckEvents(work); + + camera_act_helper3_8002F64C(); + + if (GM_Camera.first_person == 1) + { + camera_act_helper_8002F1C8(); + camera_act_helper7_8002FB54(); + } + else + { + iVar2 = camera_act_helper2_8002F5C4(); + camera_act_helper7_8002FB54(); + + if (iVar2 > 0) + { + sub_8002EADC(iVar2 - 1); + } + } + + if (changed) + { + svec_800ABA88 = GM_Camera.position; + } + + camera_act_helper4_8002F78C(); + } + + DG_LookAt(DG_Chanl(0), + &gUnkCameraStruct2_800B7868.position, + &gUnkCameraStruct2_800B7868.target, + gUnkCameraStruct2_800B7868.zoom); + } +} + +void *NewCameraSystem(void) +{ + GV_ACT *work; + + work = GV_NewActor(GV_ACTOR_ASSIST, sizeof(GV_ACT)); + if (work) + { + GV_SetNamedActor(work, Act, NULL, "camera.c"); + } + + GM_Camera.zoom = 320; + GM_Camera.first_person = 0; + GM_Camera.flag = 0; + GM_Camera.pan.pad = 0; + gUnkCameraStruct2_800B7868.zoom = 320; + dword_800ABA90 = 0; + GM_event_camera_flag = 0; + GM_CameraFirstPersonOrg = 0; + GM_CameraFlagsOrg = 0; + GM_SnakeCamera.track = 10000; + GM_Camera.track = 10000; + gUnkCameraStruct2_800B7868.track = 10000; + sub_8002FCF0(); + GM_SnakeCamera.rotate2.vx = 0; + GM_SnakeCamera.rotate2.vy = 2048; + GM_SnakeCamera.rotate2.vz = 0; + + return (void *)work; +} + +void GM_Reset_helper3_80030760() +{ + CAMERA *cameraList, *cameraListIter; + GM_CameraSystemWork *gmCamera; + int i; + int field_10_param1; + + field_10_param1 = -1; + i = 7; + + cameraList = GM_CameraList; + cameraListIter = cameraList + 7; + + gmCamera = &GM_Camera; + gmCamera->alert_mask = 100; + + gmCamera->bound[0][0].vx = 0x7fff; + gmCamera->bound[0][0].vy = 0x7fff; + gmCamera->bound[0][0].vz = 0x7fff; + gmCamera->bound[1][0].vx = -0x7fff; + gmCamera->bound[1][0].vy = -0x7fff; + gmCamera->bound[1][0].vz = -0x7fff; + + gmCamera->limit[0][0].vx = 0x7fff; + gmCamera->limit[0][0].vy = 0x7fff; + gmCamera->limit[0][0].vz = 0x7fff; + gmCamera->limit[1][0].vx = -0x7fff; + gmCamera->limit[1][0].vy = -0x7fff; + gmCamera->limit[1][0].vz = -0x7fff; + + gmCamera->bound[0][1].vx = 0x7fff; + gmCamera->bound[0][1].vy = 0x7fff; + gmCamera->bound[0][1].vz = 0x7fff; + gmCamera->bound[1][1].vx = -0x7fff; + gmCamera->bound[1][1].vy = -0x7fff; + gmCamera->bound[1][1].vz = -0x7fff; + + gmCamera->limit[0][1].vx = 0x7fff; + gmCamera->limit[0][1].vy = 0x7fff; + gmCamera->limit[0][1].vz = 0x7fff; + gmCamera->limit[1][1].vx = -0x7fff; + gmCamera->limit[1][1].vy = -0x7fff; + gmCamera->limit[1][1].vz = -0x7fff; + + GM_CameraRotateSave.vx = 0x280; + GM_CameraRotateSave.vy = 0x800; + GM_CameraRotateSave.vz = 0; + + while (i >= 0) + { + cameraListIter->field_10_param1 = field_10_param1; + i--; + cameraListIter--; + } + dword_800ABA94 = 0; +} + +void sub_8003081C() +{ + GM_SnakeCamera.target = GM_SnakeCamera.position; +} + +void GM_CameraSetAlertMask(unsigned int id, unsigned int mask) +{ + if ( mask & 0x2 ) + { + mask = 1 << id; + } + else + { + mask = 0; + } + + dword_800ABA94 &= ~(1 << id); + dword_800ABA94 |= mask; +} + +void GM_CameraSetBound(SVECTOR *min, SVECTOR *max, int which) +{ + GM_Camera.bound[1][which] = *min; + GM_Camera.bound[0][which] = *max; +} + +void GM_CameraSetLimit(SVECTOR *min, SVECTOR *max, int which) +{ + GM_Camera.limit[1][which] = *min; + GM_Camera.limit[0][which] = *max; +} + +void GM_CameraSetRotation(SVECTOR *rot) +{ + GM_CameraRotateSave = *rot; + sub_8002FCF0(); +} + +void GM_CameraSetTrack(int track) +{ + GM_CameraTrackOrg = track; + GM_CameraTrackSave = track; + sub_8002FCF0(); +} + +void GM_CameraEventReset(void) +{ + dword_800ABA90 = 0; + return; +} + +void sub_800309B4(int param_1, int param_2) +{ + GM_Camera.interp_mode = param_1; + GM_Camera.interp = param_2; + svec_800ABA88 = GM_Camera.position; +} + +void sub_800309F8(int param_1, int param_2) +{ + sub_800309B4(param_1, param_2); + GM_Camera.flag |= 2; +} + +void sub_80030A30() +{ + if (GM_Camera.first_person == 0) + { + GM_SnakeCamera.position = GM_SnakeCamera.target = GM_Camera.target; + } + + GM_Camera.flag &= ~2; +} + +void GM_CameraBoundTrace(int param_1) +{ + sub_8002EADC(param_1); +} + +void GM_CameraLimitTrace(int param_1) +{ + sub_8002EBE8(&GM_Camera.target, param_1); +} + +void GM_ExitBehindCamera(void) +{ + if (GM_GameStatus & STATE_BEHIND_CAMERA) + { + GM_GameStatus &= ~STATE_BEHIND_CAMERA; + GM_event_camera_flag &= ~8; + dword_800ABA90 = dword_800ABA90 | 8; + } + + GM_800AB448 = 0; + GM_800AB444 = NULL; +} + +void GM_CheckBehindCamera(HZD_HDL *pHzdMap, CONTROL *pControl) +{ + HZD_TRP *trp; + CAMERA *cam; + short *name; + + if ((GM_StatusEvent & 0x100 || GM_AlertMode != ALERT_ACTIVE) && + (trp = HZD_CheckBehindTrap(pHzdMap, &pControl->mov)) != NULL) + { + if (GM_800AB444 != trp) + { + if (++GM_800AB448 >= 10) + { + GM_800AB444 = trp; + + cam = &GM_CameraList[3]; + cam->field_10_param1 = 1; + cam->interp = 2; + cam->type = 0; + cam->pad_type = 2; + cam->pos.pad = GV_GetPadOrigin(); + + name = (short *)trp->name; // TODO: Is char[] name array in HZD_TRP correct? Is it really a name? + cam->trg.vx = name[0]; + cam->trg.vy = name[1]; + cam->trg.vz = name[2]; + + GM_event_camera_flag |= 8; + dword_800ABA90 &= ~8; + GM_GameStatus |= STATE_BEHIND_CAMERA; + + cam->pos.vx = name[3]; + cam->pos.vy = name[4]; + cam->pos.vz = name[5]; + } + } + } + else + { + GM_ExitBehindCamera(); + } +} diff --git a/components/mgs/mgs/source/game/camera.h b/components/mgs/mgs/source/game/camera.h new file mode 100644 index 00000000..fe0021b1 --- /dev/null +++ b/components/mgs/mgs/source/game/camera.h @@ -0,0 +1,83 @@ +#ifndef __MGS_GAME_CAMERA_H__ +#define __MGS_GAME_CAMERA_H__ + +#include +#include +#include +#include "common.h" +#include "libhzd/libhzd.h" // for HZD_HDL +#include "game/control.h" // for CONTROL + +typedef struct CAMERA +{ + SVECTOR pos; + SVECTOR trg; // pad = alert mask + signed char field_10_param1; // example: d:CAM_FIX + u_char interp; // example: d:CAM_INTERP_LINER + char type; // example: d:CAM_CAM_TO_TRG + char pad_type; +} CAMERA; + +typedef void (*TGMCameraFunc)(void); + +typedef struct +{ + SVECTOR position; + SVECTOR target; + SVECTOR rotate; + int flag; + int track; + short zoom; // Min 320, max 3200. Up to 320 levels? + short first_person; + short alert_mask; + short interp; + short type; + short interp_mode; + SVECTOR pan; + SVECTOR bound[2][2]; + SVECTOR limit[2][2]; + TGMCameraFunc callback[2]; +} GM_CameraSystemWork; + +typedef struct +{ + SVECTOR position; + SVECTOR target; + SVECTOR rotate; + int track; + SVECTOR pan; + int interp; + SVECTOR rotate2; // Not sure why there's two +} GM_SnakeCameraWork; + +typedef struct +{ + SVECTOR position; + SVECTOR target; + SVECTOR rotate; + int track; + int zoom; + int type; +} UnkCameraStruct2; + +/* camera.c */ +void GM_SetCameraCallbackFunc(int index, TGMCameraFunc func); +void GM_PanCamera(SVECTOR *a1); +void *NewCameraSystem(void); +void GM_Reset_helper3_80030760(); +void sub_8003081C(); +void GM_CameraSetAlertMask(unsigned int id, unsigned int mask); +void GM_CameraSetBound(SVECTOR *min, SVECTOR *max, int param_e); +void GM_CameraSetLimit(SVECTOR *min, SVECTOR *max, int param_e); +void GM_CameraSetRotation(SVECTOR *rot); +void GM_CameraSetTrack(int track); +void GM_CameraEventReset(void); +void sub_800309B4(int param_1, int param_2); +void sub_800309F8(int param_1, int param_2); +void sub_80030A30(); +void GM_CameraBoundTrace(int param_1); +void GM_CameraLimitTrace(int param_1); +void GM_ExitBehindCamera(void); +void GM_CheckBehindCamera(HZD_HDL *pHzdMap, CONTROL *pControl); + +#endif // __MGS_GAME_CAMERA_H__ diff --git a/components/mgs/mgs/source/game/cancel.c b/components/mgs/mgs/source/game/cancel.c new file mode 100644 index 00000000..21024f08 --- /dev/null +++ b/components/mgs/mgs/source/game/cancel.c @@ -0,0 +1,122 @@ +#include "cancel.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "mts/mts.h" +#include "mts/mts_pad.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_PREV2 + +typedef struct _Work +{ + GV_ACT actor; + int proc; + int mask; + int timer; + int step; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void Act2(Work *work) +{ + work->timer += work->step; + + if (work->timer > 256) + { + work->timer = 255; + DG_FadeScreen(work->step); + + if (GM_StreamStatus() == -1) + { + GV_DestroyActor(&work->actor); + } + } + else + { + DG_FadeScreen(work->step); + } +} + +static void Act(Work *work) +{ + if (mts_read_pad(1) & work->mask) + { + GM_StreamCancelCallback(); + GM_StreamPlayStop(); + + work->actor.act = (GV_ACTFUNC)Act2; + { extern const char *mgs_dbg_undraw_src; mgs_dbg_undraw_src = "game/cancel.c:54"; } + DG_UnDrawFrameCount = 0x7FFF0000; + work->timer = 0; + GV_PauseLevel |= GV_PAUSE_MENU; + } +} + +static void Die(Work *work) +{ + GV_PauseLevel &= ~GV_PAUSE_MENU; + + if (work->proc >= 0) + { + GCL_ExecProc(work->proc, NULL); + } +} + +static int GetResources(Work *work) +{ + if (GCL_GetOption('p')) + { + work->proc = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->proc = -1; + } + + if (GCL_GetOption('m')) + { + work->mask = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->mask = 0xFFFF; + } + + if (GCL_GetOption('s')) + { + work->step = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->step = 8; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewDemoCancel(int name, int where, int argc, char **argv) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "cancel.c"); + + if (GetResources(work) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/game/cancel.h b/components/mgs/mgs/source/game/cancel.h new file mode 100644 index 00000000..407eebb4 --- /dev/null +++ b/components/mgs/mgs/source/game/cancel.h @@ -0,0 +1,6 @@ +#ifndef __MGS_GAME_CANCEL_H__ +#define __MGS_GAME_CANCEL_H__ + +void *NewDemoCancel(int name, int where, int argc, char **argv); + +#endif // __MGS_GAME_CANCEL_H__ diff --git a/components/mgs/mgs/source/game/chara.c b/components/mgs/mgs/source/game/chara.c new file mode 100644 index 00000000..a27c1eb4 --- /dev/null +++ b/components/mgs/mgs/source/game/chara.c @@ -0,0 +1,85 @@ +#include "common.h" +#include "mts/mts.h" + +#include "game.h" +#include "charadef.h" +#include "libgcl/libgcl.h" +#include "linkvar.h" + +extern CHARA MainCharacterEntries[]; /* in main.c */ +extern CHARA _StageCharacterEntries[]; /* only visible when built-in */ + +// force gp usage +void *SECTION(".sbss") StageCharacterEntries; + +void GM_InitChara(void) +{ +#if defined(DEV_EXE) || defined(__psyz) + StageCharacterEntries = &_StageCharacterEntries[0]; +#else + extern void *mts_get_bss_tail(void); + StageCharacterEntries = mts_get_bss_tail(); +#endif +} + +void GM_ResetChara(void) +{ +#if !defined(DEV_EXE) && !defined(__psyz) + CHARA *chara; + + chara = (CHARA *)StageCharacterEntries; + // overwrite the first entry with the end-of-table marker + *((int *)&chara->func) = 0; + *((int *)&chara->class_id) = 0; +#endif +} + +void *GM_GetChara(unsigned char *script) +{ + return GM_GetCharaID(GCL_StrToInt(script)); +} + +void *GM_GetCharaID(int chara_id) +{ + CHARA *chara; + int i; + + for (i = 0; i < 2; i++) + { + // First, search the built-in charas + chara = &MainCharacterEntries[0]; + if (i != 0) + { + // chara_id wasn't found in the main binary's built-ins, + // so now we'll search the stage overlay's chara table. + chara = (CHARA *)StageCharacterEntries; + } + + for (; chara->func != NULL; chara++) + { + if (chara->class_id == chara_id) + { + return chara->func; + } + } + } + +#ifdef __psyz + { + /* A class the stage asks for that this build cannot supply. Every one + * of these is a thing that silently does not exist in the world -- the + * water surface at the dock is the visible example: Snake starts under + * it, so with the surface actor missing there is nothing above him but + * the background colour. Naming the id makes it findable in + * charalst.h, and whether it is missing from stage_union.c or simply + * not decompiled. */ + static int budget = 24; + if (budget > 0) + { + budget--; + printf("[chara] class %04X NOT AVAILABLE\n", chara_id & 0xFFFF); + } + } +#endif + return NULL; +} diff --git a/components/mgs/mgs/source/game/control.c b/components/mgs/mgs/source/game/control.c new file mode 100644 index 00000000..88e8bbe7 --- /dev/null +++ b/components/mgs/mgs/source/game/control.c @@ -0,0 +1,600 @@ +#include "control.h" + +#include +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "libhzd/libhzd.h" +#include "game/map.h" +#include "strcode.h" + +#define MAX_WHERES 96 + +int SECTION(".sbss") GM_CurrentMap; +int SECTION(".sbss") GM_N_WhereList; + +extern CONTROL *GM_WhereList[96]; +extern CONTROL DummyPlayer; + +static int QueueWhere(CONTROL *where) +{ + int num; + + // スネーク must always be the first item + if (where->name == SNAKE_NAME) + { + GM_WhereList[0] = where; + } + else + { + num = GM_N_WhereList; + + if (num >= MAX_WHERES) + { + return -1; + } + + GM_WhereList[num] = where; + GM_N_WhereList = num + 1; + } + + return 0; +} + +static void DequeueWhere(CONTROL *where) +{ + CONTROL **list; + int i, n; + + list = GM_WhereList; + n = GM_N_WhereList; + + for (i = n; i > 0; i--) + { + if (*list == where) + { + goto found; + } + list++; + } + return; + +found: + if (list != GM_WhereList) + { + n--; + *list = GM_WhereList[n]; + GM_N_WhereList = n; + } + else + { + GM_WhereList[0] = &DummyPlayer; + } +} + +void GM_InitWhereSystem(void) +{ + GM_WhereList[0] = &DummyPlayer; + GM_N_WhereList = 1; +} + +int GM_InitControl(CONTROL *ctrl, int name, int map) +{ + MAP *ctrl_map; + + if (map == 0) + { + map = GM_CurrentMap; + } + + GM_CurrentMap = map; + + GV_ZeroMemory(ctrl, sizeof(CONTROL)); + + ctrl_map = GM_GetMap(map); + ctrl->map = ctrl_map; + if (!ctrl_map) + { + printf("InitControl : no map %X\n", map); + return -1; + } + + ctrl->name = name; + if (name) + { + HZD_SetEvent(&ctrl->evt, name); + if (QueueWhere(ctrl) < 0) + { + return -1; + } + } + + ctrl->height = 850; + ctrl->hzd_height = -32767; + ctrl->r_sphere = ctrl->s_sphere = 450; + ctrl->seg_flag = 2; + ctrl->skip_flag = CTRL_SKIP_TRAP; + ctrl->levels[0] = -32000; + ctrl->levels[1] = 32000; + + return 0; +} + +static inline void CheckMessage(CONTROL *ctrl) +{ + int name; + int n_msg; + GV_MSG *msg; + int map_msg, move_msg; + MAP *ctrl_map; + + name = ctrl->name; + + if ((name != 0) && !(ctrl->skip_flag & CTRL_SKIP_MESSAGE)) + { + n_msg = GV_ReceiveMessage(name, &ctrl->msg); + ctrl->n_msg = n_msg; + + msg = ctrl->msg; + + map_msg = HASH_MAP; + move_msg = HASH_MOVE2; + + for (n_msg--; n_msg >= 0; n_msg--, msg++) + { + if (msg->message[0] == map_msg) + { + ctrl_map = GM_FindMap(msg->message[1]); + + if (ctrl_map) + { + ctrl->map = ctrl_map; + } + } + else if (msg->message[0] == move_msg) + { + ctrl->mov.vx = msg->message[1]; + ctrl->mov.vy = msg->message[2]; + ctrl->mov.vz = msg->message[3]; + ctrl->levels[0] = -32000; + ctrl->levels[1] = 32000; + } + } + } +} + +static inline void CheckCollide(CONTROL *ctrl, HZD_HDL *hzd) +{ + SVECTOR vec; + int vx; + int new_var; + int len; + int diff; + + vx = ctrl->step.vx; + new_var = ctrl->r_sphere / 2; + + if (vx < 0) + { + vx = -vx; + } + + if (ctrl->step.vz > 0) + { + vx += ctrl->step.vz; + } + else + { + vx -= ctrl->step.vz; + } + + if ((vx > new_var) || (ctrl->skip_flag & (CTRL_BOTH_CHECK | CTRL_SKIP_NEAR_CHECK))) + { + GV_AddVec3(&ctrl->mov, &ctrl->step, &vec); + + if (HZD_OnlineHazardCheck(hzd, &ctrl->mov, &vec, HZD_CHK_ALL, ctrl->seg_flag)) + { + ctrl->n_touches = 0x1; + ctrl->segs[0] = HZD_GetOnlineHazard(); + ctrl->is_edge[0] = HZD_GetOnlineHazardAtr(); + + HZD_GetOnlineVector(ctrl->vecs); + + len = GV_VecLen3(ctrl->vecs); + diff = len - new_var; + + if (diff < 0) + { + diff = -diff; + GV_LenVec3(ctrl->vecs, &vec, len, diff); + GV_SubVec3(&DG_ZeroVector, &vec, &vec); + } + else + { + GV_LenVec3(ctrl->vecs, &vec, len, diff); + } + + ctrl->step = vec; + } + } +} + +static inline void CheckNear(CONTROL *ctrl, HZD_HDL *hzd) +{ + SVECTOR vec; + SVECTOR vec2; + int bVar7; + int i; + + bVar7 = 0; + + if (ctrl->skip_flag & CTRL_SKIP_NEAR_CHECK) + { + return; + } + +retry: + i = HZD_NearHazardCheck(hzd, &ctrl->mov, 500, HZD_CHK_SEGMENT, ctrl->seg_flag); + + if (i <= 0) + { + return; + } + + ctrl->n_touches = i; + + HZD_GetNearHazard(ctrl->segs); + HZD_GetIsEdge(ctrl->is_edge); + HZD_GetNearVector(ctrl->vecs); + + if (!HZD_HazardReaction(ctrl->vecs, i, ctrl->r_sphere, &vec) && !bVar7) + { + GV_LenVec3(&ctrl->step, &vec2, GV_VecLen3(&ctrl->step), ctrl->r_sphere / 2); + bVar7 = 1; + vec2.vy = 0; + GV_SubVec3(&ctrl->mov, &vec2, &ctrl->mov); + goto retry; + } + else + { +#ifdef __psyz + { + /* This vector is added straight to the character's position: it is + * how walls push you out of themselves. A wrong one does not make + * the collision "soft", it TELEPORTS -- which is what "it jumps + * all over the place" looks like from the player's side. The push should + * never exceed the collision radius by much, so anything larger + * means the wall normals coming out of libhzd are garbage rather + * than the reaction being merely strong. */ + static int budget = 24; + int px = vec.vx < 0 ? -vec.vx : vec.vx; + int pz = vec.vz < 0 ? -vec.vz : vec.vz; + /* Measured on the board: a correct push lands within a couple of + * units of r_sphere, because that is the distance the character is + * being placed at from the wall. Only flag something well past it, + * which would mean a bad normal rather than a strong reaction. */ + if (budget > 0 && (px > ctrl->r_sphere * 2 || pz > ctrl->r_sphere * 2)) + { + budget--; + printf("[near] %d seg push %d,%d (r_sphere %d) at %d,%d\n", i, + vec.vx, vec.vz, ctrl->r_sphere, ctrl->mov.vx, + ctrl->mov.vz); + } + } +#endif + ctrl->mov.vx += vec.vx; + ctrl->mov.vz += vec.vz; + } +} + +static inline void CheckHeight(CONTROL *ctrl, HZD_HDL *hzd) +{ + int levels[2]; + int vy, vz; + int iVar11; + int uVar14; + int uVar15; + int uVar16; + + vy = ctrl->mov.vy + ctrl->step.vy; + vz = ctrl->height; + + ctrl->grounded = 0; + uVar14 = HZD_LevelHazardCheck(hzd, &ctrl->mov, HZD_CHK_FLOOR); + HZD_GetLevelHeight(levels); + ctrl->vecs[0].pad = HZD_GetFloorLevel(); + uVar15 = uVar14 & 1; + + if (((uVar14 & 2) != 0) && ((levels[1] - ctrl->levels[0]) + 199U < 399)) + { + levels[0] = levels[1]; + uVar14 &= ~2; + uVar14 |= 1; + uVar15 = uVar14 & 1; + } + + uVar16 = uVar14 & 2; + + if (uVar15 == 0) + { + levels[0] = 0; + } + + if (uVar16 == 0) + { + levels[1] = 32000; + } + + iVar11 = vz; + + if (uVar15 != 0) + { + iVar11 = vz + levels[0]; + } + + if (iVar11 > vy) + { + vy = iVar11; + ctrl->grounded = 1; + } + else if (uVar16 != 0) + { + iVar11 = levels[1] - vz; + + if (iVar11 < vy) + { + vy = iVar11; + ctrl->grounded = 2; + } + } + + ctrl->levels[0] = levels[0]; + ctrl->levels[1] = levels[1]; + ctrl->mov.vy = vy; +} + +void GM_ActControl(CONTROL *ctrl) +{ + HZD_HDL *hzd; + int vy; + int time; + + hzd = ctrl->map->hzd; + + CheckMessage(ctrl); + + GM_CurrentMap = ctrl->map->index; + + if (ctrl->r_sphere > 0) + { + ctrl->n_touches = 0; + + if (ctrl->hzd_height != -0x7fff) + { + vy = ctrl->mov.vy; + ctrl->mov.vy = ctrl->hzd_height; + } + + CheckCollide(ctrl, hzd); + +#ifdef __psyz + { + /* Trace ONE character's path, frame by frame. + * + * "The world moves fine but the player does not" cannot be the + * frame rate -- that would blur both equally. It has to be + * something in how this position advances. So print the step the + * character asked for and the position before and after collision + * resolves it: a straight walk should show a near-constant step + * and a monotonic path, and any weaving, reversal or jump shows up + * as a sign change in a column. + * + * Locked to the first control that ever runs so the lines all + * belong to the same actor -- Snake is the first the game creates. + */ + static CONTROL *traced; + static int budget = 240; + if (!traced) + { + traced = ctrl; + } + /* only while actually walking: standing still burns the budget on + * identical lines before anyone is watching */ + if (ctrl == traced && budget > 0 && + (ctrl->step.vx != 0 || ctrl->step.vz != 0)) + { + budget--; + printf("[path] pos %d,%d,%d step %d,%d rot %d turn %d\n", + ctrl->mov.vx, ctrl->mov.vy, ctrl->mov.vz, ctrl->step.vx, + ctrl->step.vz, ctrl->rot.vy, ctrl->turn.vy); + } + } +#endif + + ctrl->mov.vx += ctrl->step.vx; + ctrl->mov.vz += ctrl->step.vz; + + CheckNear(ctrl, hzd); + + if (ctrl->hzd_height != -0x7fff) + { + ctrl->mov.vy = vy; + } + + time = ctrl->interp; + + if (ctrl->interp == 0) + { + GV_NearExp4PV(&ctrl->rot.vx, &ctrl->turn.vx, 3); + } + else + { + GV_NearTimePV(&ctrl->rot.vx, &ctrl->turn.vx, ctrl->interp, 3); + ctrl->interp = time - 1; + } + + CheckHeight(ctrl, hzd); + } + else if (ctrl->r_sphere < 0) + { + ctrl->n_touches = 0; + + time = ctrl->interp; + + ctrl->mov.vx += ctrl->step.vx; + ctrl->mov.vz += ctrl->step.vz; + + if (time == 0) + { + GV_NearExp4PV(&ctrl->rot.vx, &ctrl->turn.vx, 3); + } + else + { + GV_NearTimePV(&ctrl->rot.vx, &ctrl->turn.vx, time, 3); + ctrl->interp = time - 1; + } + + if (ctrl->r_sphere >= -1) + { + CheckHeight(ctrl, hzd); + } + } + + if (!(ctrl->skip_flag & CTRL_SKIP_TRAP)) + { + ctrl->evt.mov = ctrl->mov; + ctrl->evt.mov.pad = ctrl->rot.vy; + HZD_EnterTrap(hzd, &ctrl->evt); + } + + DG_SetPos2(&ctrl->mov, &ctrl->rot); +} + +void GM_FreeControl(CONTROL *ctrl) +{ + if (ctrl->name) + { + DequeueWhere(ctrl); + } +} + +void GM_ConfigControlVector(CONTROL *ctrl, SVECTOR *mov, SVECTOR *rot) +{ + if (mov) + { + ctrl->mov = *mov; + } + + if (rot) + { + ctrl->rot = *rot; + } +} + +void GM_ConfigControlMatrix(CONTROL *ctrl, MATRIX *world) +{ + // GV_ConvertVec3 + ctrl->mov.vx = world->t[0]; + ctrl->mov.vy = world->t[1]; + ctrl->mov.vz = world->t[2]; + + DG_MatrixRotYXZ(world, &ctrl->rot); + ctrl->turn = ctrl->rot; +} + +void GM_ConfigControlString(CONTROL *ctrl, char *mov, char *rot) +{ + if (mov) + { + GCL_StrToSV(mov, (short *)&ctrl->mov); + } + + if (rot) + { + GCL_StrToSV(rot, (short *)&ctrl->rot); + } + + ctrl->turn = ctrl->rot; +} + +void GM_ConfigControlHazard(CONTROL *ctrl, int height, int r_sphere, int s_sphere) +{ + ctrl->height = height; + ctrl->r_sphere = r_sphere; + ctrl->s_sphere = s_sphere; +} + +void GM_ConfigControlAttribute(CONTROL *ctrl, int atr) +{ + ctrl->radar_atr = atr; +} + +void GM_ConfigControlInterp(CONTROL *ctrl, int interp) +{ + ctrl->interp = interp; +} + +int GM_CheckControlTouches(CONTROL *ctrl, int range) +{ + int n_touches; + + n_touches = ctrl->n_touches; + if (n_touches == 0) + { + return 0; + } + + if (ctrl->n_touches == 2) + { + if (ctrl->segs[1]->p1.h < 0 || GV_VecLen3(&ctrl->vecs[1]) <= range) + { + return 2; + } + } + + if (ctrl->segs[0]->p1.h < 0 || GV_VecLen3(&ctrl->vecs[0]) <= range) + { + return 1; + } + + return 0; +} + +// TODO: range should be u_short +void GM_ConfigControlRadarparam(CONTROL *ctrl, u_short dir, u_short dis, int range, u_short r) +{ + RADAR_SIGHT_PARAM *r_param; + + r_param = &ctrl->radar_param; + r_param->dir = dir; + r_param->dis = dis; + r_param->range = range; + r_param->r = r; +} + +void GM_ConfigControlTrapCheck(CONTROL *ctrl) +{ + ctrl->skip_flag &= ~CTRL_SKIP_TRAP; +} + +GV_MSG *GM_CheckMessage(void *work, int name, int msgcode) +{ + GV_MSG *msg; + int n_msg; + + n_msg = GV_ReceiveMessage(name, &msg); + while (--n_msg >= 0) + { + if (msg->message[0] == msgcode) + { + return msg; + } + msg++; + } + return NULL; +} diff --git a/components/mgs/mgs/source/game/control.h b/components/mgs/mgs/source/game/control.h new file mode 100644 index 00000000..16b2a697 --- /dev/null +++ b/components/mgs/mgs/source/game/control.h @@ -0,0 +1,82 @@ +#ifndef __MGS_GAME_CONTROL_H__ +#define __MGS_GAME_CONTROL_H__ + +#include +#include +#include "libgv/libgv.h" +#include "game/map.h" + +enum +{ + CTRL_SKIP_MESSAGE = 0x01, + CTRL_SKIP_TRAP = 0x02, + CTRL_SKIP_NEAR_CHECK = 0x04, + CTRL_BOTH_CHECK = 0x08, +}; // can't typedef char enums :( + +enum // radar_attr +{ + RADAR_OFF = 0, + RADAR_VISIBLE = 0x1, // Enables dot at entity position + RADAR_SIGHT = 0x4, // Enables vision cone for entities + RADAR_ALL_MAP = 0x8, + RADAR_NOISE = 0x10, // Enables noise at position on radar (e.g. DARPA Chief) + RADAR_UNK1 = 0x20, // Extends distance before entity is no longer drawn on the radar? + RADAR_UNK2 = 0x40, // Enables RADAR_SIGHT if within certain height of Snake? + RADAR_UNK4 = 0x1000, + RADAR_UNK3 = 0x2000, +}; + +typedef struct RADAR_SIGHT_PARAM +{ + u_short dir; + u_short dis; + u_short range; + u_short r; +} RADAR_SIGHT_PARAM; + +typedef struct CONTROL +{ + SVECTOR mov; // current position + SVECTOR rot; // current orientation (4096 = 2Pi rad) + HZD_EVT evt; + MAP *map; + u_short name; + short height; // offset to mov.vy + short hzd_height; + short r_sphere; // squared + short s_sphere; // never used, value usually matches step_size + u_short radar_atr; + RADAR_SIGHT_PARAM radar_param; + SVECTOR step; // movement vector + SVECTOR turn; // rotation vector (4096 = 2Pi rad) + signed char interp; // turn speed + char skip_flag; // CTRL_... + signed char n_msg; + signed char grounded; // 1 = below floor, 2 = above ceiling + signed char n_touches; // > 0 if collision detected + char seg_flag; // exclude all surfaces where (flag & seg_flag) != 0 + char is_edge[2]; + GV_MSG *msg; + SVECTOR vecs[2]; + HZD_SEG *segs[2]; // HZD_SEG when tagged, HZD_FLR when untagged + short levels[2]; // floor and ceiling heights +} CONTROL; + +/* control.c */ +void GM_InitWhereSystem(void); +int GM_InitControl(CONTROL *control, int scriptData, int scriptBinds); +void GM_ActControl(CONTROL *control); +void GM_FreeControl(CONTROL *control); +void GM_ConfigControlVector(CONTROL *ctrl, SVECTOR *mov, SVECTOR *rot); +void GM_ConfigControlMatrix(CONTROL *control, MATRIX *world); +void GM_ConfigControlString(CONTROL *control, char *mov, char *rot); +void GM_ConfigControlHazard(CONTROL *ctrl, int height, int r_sphere, int s_sphere); +void GM_ConfigControlAttribute(CONTROL *control, int atr); +void GM_ConfigControlInterp(CONTROL *control, int interp); +int GM_CheckControlTouches(CONTROL *control, int range); +void GM_ConfigControlRadarparam(CONTROL *ctrl, u_short dir, u_short dis, int range, u_short r); +void GM_ConfigControlTrapCheck(CONTROL *control); +GV_MSG *GM_CheckMessage(void *work, int name, int msgcode); + +#endif // __MGS_GAME_CONTROL_H__ diff --git a/components/mgs/mgs/source/game/delay.c b/components/mgs/mgs/source/game/delay.c new file mode 100644 index 00000000..ff50fa7f --- /dev/null +++ b/components/mgs/mgs/source/game/delay.c @@ -0,0 +1,180 @@ +#include "delay.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "game/jimctrl.h" + +typedef union GCL_EXEC_UNION { + int proc_id; + unsigned char *block_top; +} GCL_EXEC_UNION; + +typedef struct _Work +{ + GV_ACT actor; + GCL_EXEC_UNION gcl_exec; + GCL_ARGS args; + int delay_counter; + int active; + long argv_buf[8]; +} Work; + +#define EXEC_LEVEL GV_ACTOR_AFTER + +//not sure if this one belongs here +void sna_act_helper2_helper2_80033054(int id, SVECTOR *vec) +{ + int found; + + found = jimctrl_helper_80037F68(id) & 0xFF; + if (found == 1) + { + vec->vx += 32; + + if (vec->vx >= 128) + { + vec->vx = 128; + + if (vec->vz == 0) + { + vec->vz = GV_RandS(32); + } + } + } + else if (found == 2) + { + if (vec->vx < 64) + { + vec->vx += 16; + + if (vec->vx >= 64) + { + vec->vx = 64; + + if (vec->vz == 0) + { + vec->vz = GV_RandS(16); + } + } + } + else + { + vec->vx -= 16; + + if (vec->vx <= 64) + { + vec->vx = 64; + + if (vec->vz == 0) + { + vec->vz = GV_RandS(16); + } + } + } + } + else if (vec->vx > 0) + { + vec->vx -= 8; + } + + if (vec->vz != 0) + { + if (vec->vz > 0) + { + vec->vz -= 8; + + if (vec->vz < 0) + { + vec->vz = 0; + } + } + else + { + vec->vz += 8; + + if (vec->vz > 0) + { + vec->vz = 0; + } + } + } +} + +static void Act(Work *work) +{ + if (work->active == FALSE && GM_GameOverTimer != 0) + { + GV_DestroyActor(&work->actor); + return; + } + + work->delay_counter--; + if (work->delay_counter > 0) + { + return; + } + + if (GCL_IS_BLOCK_PTR(work->gcl_exec.proc_id)) + { + GCL_ExecBlock(work->gcl_exec.block_top, &work->args); + } + else + { + GCL_ExecProc(work->gcl_exec.proc_id, &work->args); + } + + GV_DestroyActor(&work->actor); +} + +void *GM_DelayedExecCommand(int proc, GCL_ARGS *args, int time) +{ + unsigned short argc; + Work *work; + int i; + long *dst_args; + long *src_args; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + if (!args) + { + work->args.argc = 0; + } + else + { + src_args = args->argv; + + work->args.argv = work->argv_buf; + dst_args = work->args.argv; + + argc = args->argc; + work->args.argc = argc; + for (i = argc; i > 0; i--) + { + *dst_args = *src_args; + src_args++; + dst_args++; + } + } + + if (time < 0) + { + work->active = TRUE; + time = -time; + } + else + { + work->active = FALSE; + } + + work->delay_counter = time; + work->gcl_exec.proc_id = proc; + + GV_SetNamedActor(&work->actor, Act, NULL, "delay.c"); + } + + return work; +} diff --git a/components/mgs/mgs/source/game/delay.h b/components/mgs/mgs/source/game/delay.h new file mode 100644 index 00000000..a05b1fb6 --- /dev/null +++ b/components/mgs/mgs/source/game/delay.h @@ -0,0 +1,8 @@ +#ifndef __MGS_GAME_DELAY_H__ +#define __MGS_GAME_DELAY_H__ + +#include "libgcl/libgcl.h" + +void *GM_DelayedExecCommand(int proc, GCL_ARGS *args, int time); + +#endif // __MGS_GAME_DELAY_H__ diff --git a/components/mgs/mgs/source/game/evpanel.c b/components/mgs/mgs/source/game/evpanel.c new file mode 100644 index 00000000..072a42d2 --- /dev/null +++ b/components/mgs/mgs/source/game/evpanel.c @@ -0,0 +1,1049 @@ +#include "evpanel.h" + +#include +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "linkvar.h" +#include "game/vibrate.h" +#include "takabe/thing.h" +#include "sound/g_sound.h" +#include "strcode.h" + +extern GM_CameraSystemWork GM_Camera; +extern int GM_CameraShakeOffset; + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_PREV + +// #61808a 97,128,138 +// #202020 32, 32, 32 + +typedef struct _Work +{ + GV_ACT actor; + DG_PRIM *field_20; // POLY_FT4 + DG_PRIM *field_24; // POLY_FT4 + unsigned short field_28; + unsigned short field_2A; + unsigned short name; + short field_2E; + short button_count; + short current_button_idx; + char field_34; + char field_35; + short field_36; + short field_38; + short field_3A; + short field_3C; + short field_3E; + short field_40; + short field_42; + short field_44; + short field_46; + short field_48; + short field_4A; + int field_4C; + char *field_50; + int field_54; + int field_58; + int field_5C; + int field_60; + int field_64; + int field_68; + int field_6C; + int field_70; + int field_74; + int field_78; + int field_7C; + int field_80; + SVECTOR f84; + SVECTOR f8C; + SVECTOR field_94[4]; + SVECTOR field_B4[0]; +} Work; + +/*---------------------------------------------------------------------------*/ + +unsigned short s03e_dword_800C3268[] = { + 0x121F, + 0x8D5C, + HASH_ENTER, // GV_StrCode("入る") + HASH_LEAVE, // GV_StrCode("出る") + 0x8591, + 0x6555, + 0x2EAB +}; + +char s03e_dword_800C3278[] = {0x7F, 0x02, 0x00, 0x00}; +char s03e_dword_800C327C[] = {0x55, 0x02, 0x6E, 0x02, 0x1E, 0x0E, 0x41, 0x04, 0x7D, 0x06, 0x5F, 0x06, 0x46, 0x08, 0x37, 0x0A, 0x2D, 0x0C, 0x00, 0x00}; +char s03e_dword_800C3290[] = {0x00, 0x0A, 0x7F, 0x02, 0x00, 0x1E, 0x7F, 0x02, 0x00, 0x00, 0x00, 0x00}; +char s03e_dword_800C329C[] = {0x23, 0x04, 0x4B, 0x04, 0x87, 0x06, 0x41, 0x08, 0x2D, 0x0C, 0x41, 0x06, 0x5F, 0x04, 0x00, 0x00}; + +const char s03e_aMessagein_800CBF18[] = "message in\n"; +const char s03e_aMessageout_800CBF24[] = "message out\n"; +const char s03e_aMessagex_800CBF34[] = "message %X\n"; +const char s03e_aNofloorproc_800CBF40[] = "NO FLOOR PROC\n"; +const char s03e_aRotd_800CBF50[] = "ROT %d\n"; +const char s03e_aReqdoorclose_800CBF58[] = "REQ:DOOR CLOSE\n"; +const char s03e_aInitopen_800CBF68[] = "INiTOPEN\n"; + +Work *SECTION(".bss") s03e_dword_800CC6B8; + +/*---------------------------------------------------------------------------*/ + +static void s03e_evpanel_800C33E0(DG_PRIM *prim, int texid) +{ + int i; + POLY_FT4 *poly; + DG_TEX *tex; + + for (i = 0; i < 2; i++) + { + poly = prim->packs[i]; + tex = DG_GetTexture(texid); + + DG_SetPacketTexture4(poly, tex); + } +} + +static int s03e_evpanel_800C3488(Work *work) +{ + GV_MSG *msg; + int n_msgs; + int message; + int code; + unsigned int i; + + n_msgs = GV_ReceiveMessage(work->name, &msg); + message = 0; + + for (; n_msgs > 0; n_msgs--) + { + code = msg->message[0]; + + if (code == HASH_KILL) + { + GV_DestroyActor(&work->actor); + return 0; + } + + for (i = 0; i < 7; i++) + { + if (s03e_dword_800C3268[i] != code) + { + continue; + } + message |= 1 << i; + + if (code == HASH_ENTER || code == HASH_LEAVE) + { + if (msg->message[1] == SNAKE_NAME) + { + message |= 0x100; + } + else + { + message |= 0x200; + } + + if (code == HASH_ENTER) + { + printf((char *)s03e_aMessagein_800CBF18); + work->field_44++; + } + else + { + printf((char *)s03e_aMessageout_800CBF24); + + if (--work->field_44 < 0) + { + work->field_44 = 0; + } + } + } + else if (code == 0x8591) + { + if (msg->message[1] == HASH_ENTER) + { + message |= 0x1000; + } + else + { + message |= 0x2000; + } + } + else if (code == 0x2EAB) + { + if (msg->message_len >= 2) + { + message |= 0x400; + work->f8C.pad = msg->message[1]; + } + } + else if (code == 0x121F) + { + if ((msg->message_len >= 2) && (msg->message[1] == 0)) + { + work->field_36 = -1; + } + else + { + work->field_36 = 0; + } + } + } + + msg++; + } + + if (message != 0) + { + printf((char *)s03e_aMessagex_800CBF34, message); + } + + return message; +} + +static void s03e_evpanel_800C36B0(Work *work) +{ + GCL_ARGS args; + int code; + int proc; + long data[1]; + char *script; + int i; + + script = work->field_50; + for (i = 0; i < work->button_count; i++) + { + script = GCL_GetNextValue(script, &code, &proc); + if (script == NULL) + { + printf((char *)s03e_aNofloorproc_800CBF40); + } + + if (i == work->current_button_idx) + { + args.argc = 1; + args.argv = data; + + data[0] = work->f8C.vy; + printf((char *)s03e_aRotd_800CBF50, work->f8C.vy); + + GCL_ExecProc(proc, &args); + break; + } + } +} + +static void EvPanelUpdateHighlightedButton_800C3778(Work *work) +{ + int i, j; + POLY_FT4 *poly; + + for (i = 0; i < 2; i++) + { + poly = work->field_20->packs[i]; + + for (j = 0; j < work->button_count; j++) + { + if (j == work->current_button_idx) + { + // Highlighted + setRGB0(poly, 255, 255, 255); + } + else + { + // Not highlighted + setRGB0(poly, 128, 128, 128); + } + + poly++; + } + } +} + +static void s03e_evpanel_800C37FC(Work *work, int index) +{ + int i; + POLY_FT4 *poly; + + if (index == 2) + { + index = (GV_Time / 8) % 2; + } + + for (i = 0; i < 2; i++) + { + poly = work->field_24->packs[i]; + + if (index != 0) + { + setRGB0(poly, 97, 128, 138); + } + else + { + setRGB0(poly, 32, 32, 32); + } + } +} + +static void s03e_evpanel_800C3898(Work *work) +{ + GV_MSG msg; + + msg.address = work->field_3A; + msg.message[0] = HASH_ENTER; + msg.message[1] = SNAKE_NAME; + msg.message_len = 2; + + GV_SendMessage(&msg); + + work->field_42 = 2; + work->field_38 = 0x258; +} + +static void s03e_evpanel_800C38F4(Work *work) +{ + GV_MSG msg; + + msg.address = work->field_3A; + msg.message[0] = HASH_LEAVE; + msg.message[1] = SNAKE_NAME; + msg.message_len = 2; + + GV_SendMessage(&msg); + + printf((char *)s03e_aReqdoorclose_800CBF58); + work->field_42 = 1; +} + +static void s03e_evpanel_800C3950(Work *work, short arg1) +{ + GV_MSG msg; + + if (work->field_46 != 0) + { + msg.address = work->field_46; + msg.message[0] = 0xBCD2; + msg.message[1] = arg1; + msg.message_len = 2; + + GV_SendMessage(&msg); + } +} + +static void s03e_evpanel_800C3994(void) +{ + GM_Camera.position = s03e_dword_800CC6B8->f84; + GM_Camera.rotate = s03e_dword_800CC6B8->f8C; + GM_Camera.type = 1; +} + +static void s03e_evpanel_800C39F8(Work *work) +{ + s03e_dword_800CC6B8 = work; + + GM_SetCameraCallbackFunc(1, s03e_evpanel_800C3994); + + work->field_4C = GM_Camera.flag; + work->f8C.vy = work->field_48; + work->field_4A = work->field_48; + + GM_Camera.flag = 0x2; + GM_Camera.interp_mode = 2; + GM_Camera.interp = 30; + + GM_PlayerStatus |= PLAYER_PAD_OFF; + + DG_InvisibleObjs(GM_PlayerBody->objs); + + if (((1 << GM_CurrentItemId) & 0x101E) != 0) + { + GM_CurrentItemId = IT_None; + } + + if (((1 << GM_CurrentWeaponId) & 0x210) != 0) + { + GM_CurrentWeaponId = WP_None; + } +} + +static void s03e_evpanel_800C3AD0(Work *work) +{ + GM_Camera.flag = work->field_4C; + GM_Camera.interp_mode = 2; + GM_Camera.interp = 30; + + GM_SetCameraCallbackFunc(1, NULL); +} + +static void s03e_evpanel_800C3B14(Work *work, int message) +{ + if ((message & 0x104) == 0x104) + { + DG_VisiblePrim(work->field_20); + + work->field_2E = 3; + + if (work->field_42 == 1) + { + s03e_evpanel_800C3898(work); + } + } +} + +static void Act(Work *work) +{ + int message; + int release; + int status; + int addend; + int code; + int offset; + + message = s03e_evpanel_800C3488(work); + +#ifdef __psyz + /* And if it exists, does it see the press? `message` is what the panel + * decides the player is doing; bit 0 is what moves it out of state 0. Only + * report when something changes, so walking past the lift is silent and a + * button press is not lost in a wall of identical lines. */ + { + static int last_msg = -1, last_state = -1; + if (message != last_msg || work->field_2E != last_state) + { + last_msg = message; + last_state = work->field_2E; + printf("[lift] panel msg %02x state %d\n", message, work->field_2E); + } + } +#endif + + switch(work->field_2E) + { + case 0: + if (message & 0x1) + { + if (work->field_36 < 0) + { + work->field_36 = 0; + } + else + { + work->field_36 = (GV_Time % 6) * 30; + + if ((GV_Time & 0x7) == 0) + { + work->field_36 = 300; + } + } + + work->field_2E = 1; + } + else + { + s03e_evpanel_800C3B14(work, message); + } + + if (!(message & 0x40)) + { + return; + } + + s03e_evpanel_800C39F8(work); + + if (message & 0x400) + { + work->f8C.vy = work->f8C.pad; + work->field_4A = work->f8C.vy; + } + + GM_Camera.interp_mode = 0; + GM_Camera.interp = -1; + + work->field_36 = 25; + work->field_2E = 9; + + printf((char *)s03e_aInitopen_800CBF68); + + NewPadVibration(s03e_dword_800C3290, 1); + NewPadVibration(s03e_dword_800C329C, 2); + + { extern const char *mgs_dbg_undraw_src; mgs_dbg_undraw_src = "game/evpanel.c:461"; } + DG_UnDrawFrameCount = 0x7fff0000; + + GM_SetSound(0xff0000fe, SD_ASYNC); + return; + + case 1: + DG_VisiblePrim(work->field_24); + + s03e_evpanel_800C37FC(work, 1); + s03e_evpanel_800C33E0(work->field_24, work->field_2A); + + if (work->field_36 == 30) + { + GM_SeSet2(0, 63, SE_ELEVATOR_STOP); + } + + if (--work->field_36 < 0) + { + if (work->field_42 != 2) + { + s03e_evpanel_800C3898(work); + } + + work->field_2E = 2; + GM_SeSet2(0, 63, SE_ELEVATOR_CHIME); + } + + s03e_evpanel_800C3B14(work, message); + break; + + case 2: + s03e_evpanel_800C37FC(work, 2); + s03e_evpanel_800C3B14(work, message); + + if (GM_AlertMode != ALERT_OFF) + { + work->field_38 = 600; + } + + if (work->field_42 == 0) + { + work->field_2E = 0; + + s03e_evpanel_800C37FC(work, 1); + s03e_evpanel_800C33E0(work->field_24, work->field_28); + + DG_VisiblePrim(work->field_24); + break; + } + + if (work->field_44 < 1) + { + break; + } + + if (work->field_42 == 1) + { + s03e_evpanel_800C3898(work); + + if ((message & 0x2010) == 0x2010) + { + message &= ~0x2010; + } + } + else + { + work->field_38 = 600; + } + break; + + case 3: + work->field_38 = 100; + + if (message & 0x2) + { + if (GM_AlertMode == ALERT_OFF) + { + if ((GM_Camera.first_person != 0) || (GM_PlayerStatus & PLAYER_WATCH)) + { + if ((GM_UnkFlagBE == 0) || !(GM_PlayerStatus & PLAYER_MOVE_WATCH)) + { + break; + } + } + + if (work->field_44 == 1) + { + s03e_evpanel_800C39F8(work); + work->current_button_idx = work->field_34; + EvPanelUpdateHighlightedButton_800C3778(work); + work->field_2E = 5; + GM_SeSet2(0, 63, SE_ITEM_OPENWINDOW); + } + } + } + else if ((message & 0x108) == 0x108) + { + DG_InvisiblePrim(work->field_20); + + if (work->field_42 == 1) + { + s03e_evpanel_800C3898(work); + } + + work->field_2E = 2; + } + break; + + case 5: + case 6: + work->field_38 = 100; + + DG_InvisibleObjs(GM_PlayerBody->objs); + + if (work->field_2E == 5) + { + if (!(GV_PadData[0].status & (PAD_SQUARE | PAD_CROSS | PAD_CIRCLE | PAD_TRIANGLE))) + { + work->field_2E = 6; + } + } + else + { + release = GV_PadData[0].release; + status = GV_PadData[0].status; + + if (GV_PadData[0].press & (PAD_DOWN | PAD_UP)) + { + if (GV_PadData[0].press & PAD_UP) + { + addend = -1 + work->button_count; // + modulo so that we don't go into negative numbers + } + else + { + addend = 1; + } + + work->current_button_idx = (work->current_button_idx + addend) % work->button_count; + + EvPanelUpdateHighlightedButton_800C3778(work); + GM_SeSet2(0, 63, SE_MENU_CURSOR); + } + + if (status & PAD_LEFT) + { + work->field_4A = (work->field_4A + 32) & 0xFFF; + + if (GV_DiffDirS(work->field_4A, work->field_3C) < 0) + { + work->field_4A = work->field_3C; + } + } + + work->f8C.vy = GV_NearExp4P(work->f8C.vy, work->field_4A); + + if (status & PAD_RIGHT) + { + work->field_4A = (work->field_4A - 32) & 0xFFF; + + if (GV_DiffDirS(work->field_4A, work->field_3E) > 0) + { + work->field_4A = work->field_3E; + } + + work->f8C.vy = GV_NearExp4P(work->f8C.vy, work->field_4A); + } + + if (release & PAD_CIRCLE) + { + if (work->current_button_idx == work->field_34 || work->current_button_idx == work->button_count - 1) + { + message |= 0x20; + } + else + { + work->field_2E = 7; + work->field_38 = 15; + } + + GM_SeSet2(0, 63, SE_ELEVATOR_BUTTON); + } + + if (release & PAD_CROSS) + { + message |= 0x20; + } + } + + if ((GM_AlertMode != ALERT_OFF) || + ((message & 0x20) != 0) || + ((GM_PlayerStatus & PLAYER_DAMAGED) != 0) || + (work->field_44 > 1)) + { + s03e_evpanel_800C3AD0(work); + + if (work->field_42 != 2) + { + s03e_evpanel_800C3898(work); + } + + work->field_2E = 11; + } + break; + + case 11: + if (GM_Camera.interp < 24) + { + work->field_42 = 2; + DG_VisibleObjs(GM_PlayerBody->objs); + GM_PlayerStatus &= ~PLAYER_PAD_OFF; + work->field_2E = 3; + } + break; + + case 7: + if ((work->field_44 > 1) || (GM_PlayerStatus & PLAYER_DAMAGED)) + { + s03e_evpanel_800C3AD0(work); + + if (work->field_42 != 2) + { + s03e_evpanel_800C3898(work); + + if ((message & 0x2010) == 0x2010) + { + message &= ~0x2010; + } + } + + work->field_2E = 11; + break; + } + + if (work->field_42 == 0) + { + work->field_2E = 8; + work->field_36 = 30; + + GM_GameStatus |= STATE_ALL_OFF | STATE_ENEMY_OFF; + DG_InvisibleObjs(GM_PlayerBody->objs); + + if (work->current_button_idx < work->field_34) + { + code = 'a'; + } + else + { + code = 'b'; + } + + s03e_evpanel_800C3950(work, code); + + NewPadVibration(s03e_dword_800C3278, 1); + NewPadVibration(s03e_dword_800C327C, 2); + } + break; + + case 8: + if (work->field_36 >= 1) + { + DG_InvisibleObjs(GM_PlayerBody->objs); + + work->field_36--; + if ((work->field_36 > 2) && (work->field_36 < 10)) + { + GM_CameraShakeOffset = work->field_36 / 2; + + if ((work->field_36 & 0x1) != 0) + { + GM_CameraShakeOffset = -GM_CameraShakeOffset; + } + } + } + else + { + if (work->field_50 != 0) + { + s03e_evpanel_800C36B0(work); + work->field_50 = 0; + } + + DG_InvisibleObjs(GM_PlayerBody->objs); + } + break; + + case 9: + GM_GameStatus |= STATE_PADRELEASE | STATE_PAUSE_ONLY; + + if (work->field_36 >= 1) + { + if (--work->field_36 < 10) + { + if ((work->field_36 & 1) != 0) + { + offset = work->field_36 / 4; + } + else + { + offset = 0; + } + + DG_OffsetDispEnv(offset); + } + } + else if (work->field_36 == 0) + { + work->field_36 = -1; + DG_UnDrawFrameCount = 0; + s03e_evpanel_800C3898(work); + GM_SeSet2(0, 63, SE_ELEVATOR_CHIME); + } + + if ((message & 0x1010) == 0x1010) + { + work->field_36 = 30; + work->field_2E = 10; + } + break; + + case 10: + if (work->field_36-- == 1) + { + s03e_evpanel_800C3AD0(work); + work->field_2E = 11; + GM_GameStatus &= ~( STATE_PADRELEASE | STATE_PAUSE_ONLY ); + s03e_evpanel_800C33E0(work->field_24, work->field_2A); + break; + } + break; + } + + if ((message & 0x2010) == 0x2010) + { + if (work->field_42 == 1) + { + work->field_42 = 0; + work->field_38 = 0; + } + } + else if (work->field_38 > 0) + { + if ((work->field_2E != 7) && (work->field_44 > 0)) + { + work->field_38 = 30; + } + + if (work->field_38-- == 1) + { + s03e_evpanel_800C38F4(work); + } + } +} + +/*---------------------------------------------------------------------------*/ + +static void Die(Work *work) +{ + GM_FreePrim(work->field_20); + GM_FreePrim(work->field_24); +} + +/*---------------------------------------------------------------------------*/ + +static void s03e_evpanel_800C45E4(POLY_FT4 *packs, DG_TEX *tex, int n_packs) +{ + int i; + int x, y, w, h; + + for (i = 0; i < n_packs; i++) + { + setPolyFT4(packs); + setSemiTrans(packs, 1); + + x = tex->off_x; + w = tex->w; + + packs->u2 = x; + packs->u0 = x; + packs->u3 = w + x; + packs->u1 = w + x; + + y = tex->off_y; + h = tex->h + 1; + + packs->v1 = y + (h * i) / n_packs; + packs->v0 = y + (h * i) / n_packs; + packs->v3 = y + ((h * (i + 1)) / n_packs) - 1; + packs->v2 = y + ((h * (i + 1)) / n_packs) - 1; + + packs->tpage = tex->tpage; + packs->clut = tex->clut; + + setRGB0(packs, 128, 128, 128); + + packs++; + } +} + +static void s03e_evpanel_800C470C(SVECTOR *vecs, int n_vecs, int x, int y) +{ + int height; + int top; + int i; + int x0, x1; + int y0, y1; + + height = (y / n_vecs) / 2; + top = (y / 2) - height; + x1 = x / 2; + + for (i = 0; i < n_vecs; i++) + { + x0 = -x1; + y0 = top + height; + y1 = top - height; + top -= height * 2; + + setVector(&vecs[0], x0, y0, 0); + setVector(&vecs[1], x1, y0, 0); + setVector(&vecs[2], x0, y1, 0); + setVector(&vecs[3], x1, y1, 0); + vecs += 4; + } +} + +static int s03e_evpanel_800C47D0(Work *work, DG_PRIM **out, SVECTOR *vec, int n_prims) +{ + MATRIX pos; + SVECTOR trans; + SVECTOR rot; + SVECTOR sp48; + int texid; + int k500; + DG_PRIM *prim; + DG_TEX *tex; + + GCL_StrToSV(GCL_NextStr(), (short *)&trans); + GCL_StrToSV(GCL_NextStr(), (short *)&rot); + GCL_StrToSV(GCL_NextStr(), (short *)&sp48); + + k500 = GCL_GetNextInt(); + texid = GCL_GetNextInt(); + + if (n_prims == 1) + { + work->field_28 = texid; + + if (GCL_NextStr()) + { + work->field_2A = GCL_GetNextInt(); + } + else + { + work->field_2A = texid; + } + } + + prim = GM_MakePrim(DG_PRIM_ONESIDE | DG_PRIM_POLY_FT4, n_prims, vec, NULL); + *out = prim; + if (prim == NULL) + { + return 0; + } + + tex = DG_GetTexture(texid); + if (tex == NULL) + { + return 0; + } + + RotMatrix(&rot, &pos); + + pos.t[0] = trans.vx; + pos.t[1] = trans.vy; + pos.t[2] = trans.vz; + + DG_SetPos(&pos); + DG_PutPrim(prim); + + prim->raise = k500; + + s03e_evpanel_800C45E4(prim->packs[0], tex, n_prims); + s03e_evpanel_800C45E4(prim->packs[1], tex, n_prims); + + s03e_evpanel_800C470C(vec, n_prims, sp48.vx, sp48.vy); + return 1; +} + +static int GetResources(Work *work, int map, int name, int button_count) +{ + GM_CurrentMap = map; + + if (!GCL_GetOption('p')) + { + return -1; + } + + if (!s03e_evpanel_800C47D0(work, &work->field_20, work->field_B4, button_count)) + { + return -1; + } + + if (!GCL_GetOption('b')) + { + return -1; + } + + if (!s03e_evpanel_800C47D0(work, &work->field_24, work->field_94, 1)) + { + return -1; + } + + s03e_evpanel_800C37FC(work, 0); + + work->field_34 = work->current_button_idx = THING_Gcl_GetInt('f'); + work->field_3A = THING_Gcl_GetInt('r'); + work->field_46 = THING_Gcl_GetInt('t'); + + work->button_count = button_count; + work->name = name; + + work->field_50 = GCL_GetOption('e'); + + if (GCL_GetOption('c')) + { + GCL_StrToSV(GCL_NextStr(), (short *)&work->f84); + GCL_StrToSV(GCL_NextStr(), (short *)&work->f8C); + + work->field_3C = GCL_GetNextInt(); + work->field_3E = GCL_GetNextInt(); + + work->field_48 = work->f8C.vy; + + DG_VisiblePrim(work->field_24); + DG_InvisiblePrim(work->field_20); + + s03e_evpanel_800C37FC(work, 1); + EvPanelUpdateHighlightedButton_800C3778(work); + return 0; + } + + return -1; +} + +/*---------------------------------------------------------------------------*/ + +void *NewElevatorPanel(int name, int where, int argc, char **argv) +{ + Work *work; + int button_count; + +#ifdef __psyz + /* Does the panel exist at all? Pressing the action button at the dock's + * lift does nothing, and that has two very different explanations: no + * panel actor was ever spawned, or it exists and never sees the press. + * One line at construction separates them. */ + printf("[lift] panel spawned\n"); +#endif + + button_count = THING_Gcl_GetIntDefault('n', 3); + work = GV_NewActor(EXEC_LEVEL, sizeof(Work) + sizeof(SVECTOR) * button_count * 4); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "evpanel.c"); + if (GetResources(work, where, name, button_count) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/game/evpanel.h b/components/mgs/mgs/source/game/evpanel.h new file mode 100644 index 00000000..b90fc940 --- /dev/null +++ b/components/mgs/mgs/source/game/evpanel.h @@ -0,0 +1,6 @@ +#ifndef __MGS_GAME_EVPANEL_H__ +#define __MGS_GAME_EVPANEL_H__ + +void *NewElevatorPanel(int name, int where, int argc, char **argv); + +#endif // __MGS_GAME_EVPANEL_H__ diff --git a/components/mgs/mgs/source/game/game.h b/components/mgs/mgs/source/game/game.h new file mode 100644 index 00000000..d5cbea3a --- /dev/null +++ b/components/mgs/mgs/source/game/game.h @@ -0,0 +1,397 @@ +#ifndef __MGS_GAME_H__ +#define __MGS_GAME_H__ + +#include +#include +#include + +#include "common.h" +#include "charadef.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libhzd/libhzd.h" +#include "menu/menuman.h" +#include "sound/sd_cli.h" +#include "sound/g_sound.h" + +#include "map.h" +#include "control.h" +#include "target.h" +#include "homing.h" +#include "motion.h" +#include "camera.h" +#include "strctrl.h" +//#include "linkvar.h" + +/*---------------------------------------------------------------------------*/ + +enum GAMED_STATE { // private to gamed.c + WAIT_LOAD = 0, + WORKING = 1 +}; + +typedef struct { // private to gamed.c + GV_ACT actor; + enum GAMED_STATE status; + int killing_count; +} gameWork; + +/*---------------------------------------------------------------------------*/ + +typedef struct HITTABLE +{ + int type; // Not read from, set as either WP_Claymore or WP_C4 + GV_ACT *actor; + CONTROL *control; + void *data; // The thing being collided with, can be walls, floors, targets +} HITTABLE; + +#define DG_MAX_JOINTS 24 + +typedef struct _OBJECT +{ + DG_OBJS *objs; + u_long flag; + MATRIX *light; + u_short map_name; + short action; + short action2; // for override actions + MOTION_CONTROL *m_ctrl; + short height; + short is_end; + short time2; // for override actions, why not is_end2? + u_long pad; // unused + SVECTOR rots[DG_MAX_JOINTS]; +} OBJECT; + +typedef struct _OBJECT_NO_ROTS +{ + DG_OBJS *objs; + u_long flag; + MATRIX *light; + u_short map_name; + short action; + short action2; // for override actions + MOTION_CONTROL *m_ctrl; + short height; + short is_end; + short time2; // for override actions, why not is_end2? + u_long pad; // unused +} OBJECT_NO_ROTS; + +typedef int (*TBombFunction)(CONTROL *, int, int *); +typedef int (*TBombFunction2)(int, CONTROL *, int *); +typedef int (*TBombFunction3)(TARGET *, int); +typedef int (*TPlayerActFunction)(GV_ACT *); + +typedef enum +{ + PLAYER_WATCH = 0x1, + PLAYER_INTRUDE = 0x2, // Crawling in forced first person + PLAYER_ACT_ONLY = 0x4, + PLAYER_NORMAL_WATCH = 0x8, //first person can l r peek + PLAYER_MOVE = 0x10, + PLAYER_SQUAT = 0x20, + PLAYER_GROUND = 0x40, + // stops movement and actions. - item/weap switching + pause/radio still available + PLAYER_NOT_PLAYABLE = 0x80, + // hit flinch, step on claymore + PLAYER_DAMAGED = 0x100, + // knocked down related + PLAYER_DOWNED = 0x200, + // cleared leaving first person + PLAYER_STILL_WATCH = 0x400, + PLAYER_ATTACK = 0x800, + PLAYER_CB_BOX = 0x1000, + // triggers game over when set. could this be the 'dead' flag? + PLAYER_GAME_OVER = 0x2000, + // anther first person flag + PLAYER_INVISIBLE = 0x4000, + PLAYER_MENU_DISABLE = 0x8000, + PLAYER_CAUTION = 0x10000, //on wall + PLAYER_CHECK_WALL = 0x20000, //knocking + PLAYER_MOVE_WATCH = 0x40000, + PLAYER_TIMERBOMB_THROWN = 0x80000, + PLAYER_KETCHUP = 0x100000, + PLAYER_ITEM_DISABLE = 0x200000, + PLAYER_WEAPON_DISABLE = 0x400000, + PLAYER_INVINCIBLE = 0x800000, + PLAYER_IN_THE_WATER = 0x1000000, + PLAYER_HIDDEN = 0x2000000, + // in level transition / mini cutscene, level transition duct + PLAYER_NOT_SIGHT = 0x4000000, + PLAYER_SECOND_CONTROLLER = 0x8000000, //using second controller + PLAYER_SECOND_AVAILABLE = 0x10000000, //can use second controller + // stops movement, actions, and item/weap switching. - pause/radio still available + PLAYER_PAD_OFF = 0x20000000, + // Set from sna_check_dead_8004E384() and sna_anim_dying_80055524(). + PLAYER_NOHIDDEN = 0x40000000, //dead or dying + PLAYER_DEBUG = 0x80000000, +} PlayerStatusFlag; + +enum { + GM_SEMODE_NORMAL, + GM_SEMODE_BOMB, + GM_SEMODE_REAL, + GM_SEMODE_CAMERA +} ; + +#define GAME_FLAG_BIT_07 0x40 // set for camera change +#define GAME_FLAG_BIT_08 0x80 // cleared when unpausing, never set though +#define GAME_FLAG_BIT_09 0x100 // not set anywhere, used in bullet.c +#define GAME_FLAG_BIT_13 0x1000 // enables menu when STATE_VOX_STREAM is set +#define GAME_FLAG_BIT_19 0x40000 // hides all items/weapons in the menu + +enum // GM_GameStatus +{ + STATE_CHAFF = 0x1, + STATE_STUN = 0x2, + STATE_NVG = 0x4, + STATE_THERMG = 0x8, + STATE_BEHIND_CAMERA = 0x10, + STATE_VOX_STREAM = 0x20, + // ... + STATE_ENEMY_OFF = 0x200, + STATE_TAKING_PHOTO = 0x400, + STATE_JPEGCAM = 0x800, + // ... + STATE_RADIO_OFF = 0x2000, + STATE_PAUSE_OFF = 0x4000, + STATE_SHOW_LIFEBAR = 0x8000, + STATE_HIDE_LIFEBAR = 0x10000, + STATE_LIFEBAR_OFF = 0x20000, + // ... + STATE_MENU_OFF = 0x80000, + STATE_SHOW_RADAR = 0x100000, + STATE_HIDE_RADAR = 0x200000, + STATE_RADAR_OFF = 0x400000, + STATE_JAMMING = 0x800000, // "Snake, you can't use the radar in an area with strong harmonic resonance" (Mei Ling) + STATE_DEMO_VERBOSE = 0x1000000, + STATE_DAMAGED = 0x2000000, + STATE_GAME_OVER = 0x4000000, + STATE_PADMASK = 0x8000000, + STATE_PADRELEASE = 0x10000000, + STATE_NOSLOW = 0x20000000, + STATE_PADDEMO = 0x40000000, + STATE_DEMO = 0x80000000, +}; + +#define STATE_ALL_OFF ( STATE_RADAR_OFF | STATE_MENU_OFF | STATE_LIFEBAR_OFF | STATE_PAUSE_OFF | STATE_RADIO_OFF ) // 0x4A6000 +#define STATE_PAUSE_ONLY ( STATE_RADAR_OFF | STATE_MENU_OFF | STATE_LIFEBAR_OFF | STATE_RADIO_OFF ) // 0x4A2000 + +enum // GM_AlertMode +{ + ALERT_OFF = 0, + ALERT_JAMMING = 1, + ALERT_EVASION = 2, + ALERT_ACTIVE = 3 +}; + +/*---------------------------------------------------------------------------*/ +#ifndef __GAME_GAMED_C__ +// clang-format off + +extern int GM_CurrentMap; // for GM_Set/GetCurrentMap, etc. +extern int GM_AlertMax; // for GM_SetAlert +extern int GM_NoisePower; // for GM_SetNoise +extern int GM_NoiseLength; // 〃 +extern SVECTOR GM_NoisePosition; // 〃 + +static inline void *GM_MakePrimChanl( int type, int n_prims, SVECTOR *pos, RECT *rect, int chanl ) +{ + DG_PRIM *prim; + + prim = DG_MakePrim( type, n_prims, chanl, pos, rect ); + if ( prim ) { + DG_QueuePrim( prim ); + DG_GroupPrim( prim, GM_CurrentMap ); + } + return prim; +} + +/* + The beginning of this function was cut off in the memleak. + Just guessing the name, return type, and the types of the first two args. +*/ +static inline void *GM_MakePrim( int type, int n_prims, SVECTOR *pos, RECT *rect ) +{ + return GM_MakePrimChanl( type, n_prims, pos, rect, 0 ) ; +} + +static inline void GM_FreePrim( DG_PRIM *prim ) +{ + if ( prim != NULL ) { + DG_DequeuePrim( prim ) ; + DG_FreePrim( prim ) ; + } +} + +static inline void GM_ConfigPrimRoot( DG_PRIM *prim, OBJECT *obj, int unit ) +{ + prim->root = &( obj->objs->objs[ unit ].world ) ; +} + +static inline void GM_SetCurrentMap( int map ) +{ + GM_CurrentMap = map ; +} + +static inline int GM_GetCurrentMap() +{ + return GM_CurrentMap ; +} + +#if 0 +/* Currently unused. */ +static inline void GM_SetAlert( alert ) +int alert ; +{ + if ( alert > 256 ) alert = 256 ; + if ( alert > GM_AlertMax ) GM_AlertMax = alert ; +} +#endif + +static inline void GM_SetAlertMax( int alert ) +{ + if ( GM_AlertMax < alert ) GM_AlertMax = alert; +} + +static inline void GM_SetNoise( int power, int length, SVECTOR *pos ) +{ + int old ; + + old = GM_NoisePower ; + if ( power < old ) return ; + if ( power == old && length < GM_NoiseLength ) return ; + GM_NoisePower = power ; + GM_NoiseLength = length ; + GM_NoisePosition = *pos ; +} + +// clang-format on +#endif // __GAME_GAMED_C__ +/*---------------------------------------------------------------------------*/ + +/* gamed.c */ +extern int GM_GameStatus; +extern int GM_LoadRequest; +extern int GM_GameOverTimer; +extern SVECTOR *GM_lpsvectWind; +extern TPlayerActFunction GM_lpfnPlayerActControl; +extern TPlayerActFunction GM_lpfnPlayerActObject2; +extern short GM_uBombHoming; +extern short GM_uTenageMotion; +extern TBombFunction GM_lpfnBombHoming; +extern TBombFunction2 GM_lpfnBombBound; +extern TBombFunction3 GM_lpfnBombExplosion; +extern int GM_PadResetDisable; + +#ifndef __GAME_GAMED_C__ +//extern char *GM_StageName; +//extern GV_PAD *GM_CurrentPadData; +extern int dword_800AB9D4; +extern short GM_WeaponChanged; +extern int GM_ClaymoreMap; +extern int GM_AlertMax; +extern unsigned int GM_DisableWeapon; +extern int gTotalFrameTime; +extern short GM_Magazine; +extern int GM_PlayerAddress; +extern CONTROL *GM_PlayerControl; +extern SVECTOR GM_NoisePosition; +extern int GM_AlertMode; +extern int GM_Photocode; +//extern int dword_800ABA08; +extern int GM_PlayerMap; +extern SVECTOR GM_PlayerPosition; +extern int GM_AlertLevel; +extern int dword_800ABA1C; +extern OBJECT *GM_PlayerBody; +extern int GM_NoisePower; +extern int GM_DisableItem; +extern short GM_MagazineMax; +extern int GM_NoiseLength; +extern short GM_O2; +extern short GM_PDA_ClearRank; +extern int GM_LoadComplete; +extern int GM_PadVibration; +extern int GM_PlayerAction; +extern SVECTOR GM_PhotoViewPos; +extern PlayerStatusFlag GM_PlayerStatus; +extern int GM_PadVibration2; +#endif // __GAME_GAMED_C__ + +void GM_InitReadError(void); +void DrawReadError(void); +void GM_SetSystemCallbackProc(int index, int proc); +void GM_CallSystemCallbackProc(int id, int arg); +void GM_SetLoadCallbackProc(int); +void GM_ContinueStart(void); +void GM_GameOver(void); +void GM_StartDaemon(void); + +/* script.c */ +int GM_ResetScript(void); +void GM_InitScript(void); + +/* area.c */ +void GM_InitArea(void); +void GM_GetAreaHistory(AreaHistory *pHistoryCopy); +void GM_SetAreaHistory(AreaHistory *pNewHistory); +int GM_SetArea(int stage_id, char *stage_name); +int GM_AreaHistory(int stage_id); +char *GM_GetArea(int flag); + +/* chara.c */ +void GM_InitChara(void); +void GM_ResetChara(void); +void *GM_GetChara(unsigned char *script); +void *GM_GetCharaID(int chara_id); + +/* alert.c */ +void GM_SetSong(int song); +int GM_GetNoiseSound(int flag, int noise); +void GM_SoundStart(void); +void GM_AlertSound(void); +void GM_AlertReset(void); +void GM_AlertAct(void); +void GM_AlertModeSet(int mode); +void GM_AlertModeInit(void); +void GM_AlertModeReset(void); + +/* sound.c */ +void GM_SeSet(SVECTOR *pos, int se_id); +void GM_SeSet2(int pan, int vol, int se_id); +void GM_SeSetMode(SVECTOR *pos, int se_id, int mode); +void GM_SeSet3(int pan, int vol, int se_id); +void GM_SeSetPan(SVECTOR *pos, int se_id, int vol); +void GM_SeSetSize(SVECTOR *pos, int se_id, int size); +void GM_SetSound(int sound_code, int sync_mode); + +/* object.c */ +void GM_InitObjectNoRots(OBJECT_NO_ROTS *obj, int model, int flag, int motion); +void GM_InitObject(OBJECT *obj, int model, int flag, int motion); +void GM_ActMotion(OBJECT *obj); +void GM_ActObject(OBJECT *obj); +void GM_ActObject2(OBJECT *obj); +void GM_FreeObject(OBJECT *obj); +void GM_ConfigObjectFlags(OBJECT *obj, int flags); +void GM_ConfigObjectLight(OBJECT *obj, MATRIX *light); +void GM_ConfigObjectStep(OBJECT *obj, SVECTOR *step); +void GM_ConfigObjectRoot(OBJECT *obj, OBJECT *parent_obj, int num_parent); +void GM_ConfigObjectJoint(OBJECT *obj); +void GM_ConfigObjectSlide(OBJECT *obj); + +void GM_ConfigObjectAction(OBJECT *obj, int action, int frame, int interp); +void GM_ConfigObjectOverride(OBJECT *obj, int action, int frame, int interp, u_long mask); +int GM_ConfigObjectMotion(OBJECT *obj, int action_flag, int motion); + +int GM_ConfigObjectModel(OBJECT_NO_ROTS *obj, int model); + +/* motion.c */ +int GM_ConfigMotionControl(OBJECT *object, MOTION_CONTROL *m_ctrl, int name, + MOTION_SEGMENT *m_segs1, MOTION_SEGMENT *m_segs2, + CONTROL *control, SVECTOR *rots); +int GM_ConfigMotionAdjust(OBJECT *object, SVECTOR *adjust); + +#endif // __MGS_GAME_H__ diff --git a/components/mgs/mgs/source/game/gamed.c b/components/mgs/mgs/source/game/gamed.c new file mode 100644 index 00000000..eb7e9dd8 --- /dev/null +++ b/components/mgs/mgs/source/game/gamed.c @@ -0,0 +1,787 @@ +#define __GAME_GAMED_C__ +#include "game.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "mts/mts.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libfs/libfs.h" +#include "libgcl/libgcl.h" +#include "memcard/memcard.h" + +#include "linkvar.h" +#include "game/loader.h" +#include "game/over.h" +#include "menu/menuman.h" + +/*---------------------------------------------------------------------------*/ + +//both below are defined in gvd.c +extern char *GM_StageName; +extern char *GM_StageName; + +extern GV_PAD *GM_CurrentPadData; +extern GV_PAD* GM_CurrentPadData; /* defined in libgv/gvd.c */ + +int GM_GameStatus = 0; +int GM_LoadRequest = 0; +int GM_GameOverTimer = 0; + +SVECTOR *GM_lpsvectWind = NULL; + +TPlayerActFunction GM_lpfnPlayerActControl = NULL; +TPlayerActFunction GM_lpfnPlayerActObject2 = NULL; + +short GM_uBombHoming = 0; +short GM_uTenageMotion = -1; + +TBombFunction GM_lpfnBombHoming = NULL; +TBombFunction2 GM_lpfnBombBound = NULL; +TBombFunction3 GM_lpfnBombExplosion = NULL; + +int GM_PadResetDisable = FALSE; + +int SECTION(".sbss") dword_800AB9CC; +int SECTION(".sbss") reset_timer; +int SECTION(".sbss") dword_800AB9D4; +short SECTION(".sbss") GM_WeaponChanged; +short SECTION(".sbss") word_800AB9DA; +int SECTION(".sbss") GM_ClaymoreMap; +int SECTION(".sbss") GM_AlertMax; +unsigned int SECTION(".sbss") GM_DisableWeapon; +int SECTION(".sbss") gTotalFrameTime; +short SECTION(".sbss") GM_Magazine; +int SECTION(".sbss") GM_PlayerAddress; +CONTROL *SECTION(".sbss") GM_PlayerControl; +SVECTOR SECTION(".sbss") GM_NoisePosition; +int SECTION(".sbss") GM_AlertMode; +int SECTION(".sbss") GM_Photocode; +int SECTION(".sbss") dword_800ABA08; +int SECTION(".sbss") GM_PlayerMap; +SVECTOR SECTION(".sbss") GM_PlayerPosition; +int SECTION(".sbss") GM_AlertLevel; +int SECTION(".sbss") dword_800ABA1C; +OBJECT *SECTION(".sbss") GM_PlayerBody; +int SECTION(".sbss") GM_NoisePower; +int SECTION(".sbss") GM_DisableItem; +short SECTION(".sbss") GM_MagazineMax; +int SECTION(".sbss") GM_NoiseLength; +short SECTION(".sbss") GM_O2; +short SECTION(".sbss") GM_PDA_ClearRank; +int SECTION(".sbss") GM_LoadComplete; +int SECTION(".sbss") GM_PadVibration; +int SECTION(".sbss") GM_PlayerAction; +STATIC int SECTION(".sbss") dword_800ABA44; +SVECTOR SECTION(".sbss") GM_PhotoViewPos; + +/** + * Some known settings via GM_SetPlayerStatusFlag(): + * |= 0x20008000 if Snake dies from sna_check_dead_8004E384() and sna_anim_dying_80055524(). + * |= 0x20 if Snake crouches from sna_anim_crouch_800527DC(). + * |= 0x40 if Snake goes prone from sna_anim_prone_begin_80053BE8() and sna_anim_prone_idle_800528BC(). + * |= 0x10 if Snake runs from sna_anim_run_begin_80053B88(), sna_anim_run_8005292C(), + * sna_anim_rungun_begin_80056BDC() and sna_anim_rungun_80056C3C(). + * |= 0x10 if Snake moves while prone from sna_anim_prone_move_800529C0(). + * |= 0x10 if Snake moves while in a box from sna_anim_box_run_8005544C(). + * |= 0x10000 if Snake pushes up against a wall from sna_anim_wall_idle_and_c4_80052A5C(). + * |= 0x10010 if Snake moves while up against a wall from sna_anim_wall_move_80052BA8(). + * |= 0x10020 if Snake crouches while up against a wall from sna_anim_wall_crouch_80052CCC(). + * |= 0x10 from sna_anim_choke_drag_80059054(). + */ +PlayerStatusFlag SECTION(".sbss") GM_PlayerStatus; +int SECTION(".sbss") GM_PadVibration2; + +extern unsigned short GM_SystemCallbackProc[6]; +extern int str_mute_fg; +extern unsigned int str_status; +extern int dword_800BF1A8; +extern int dword_800BF270; +extern int str_off_idx; +extern char exe_name[32]; +extern char *MGS_DiskName[3]; /* in main.c */ +extern int FS_DiskNum; +extern int FS_ResidentCacheDirty; + +extern DG_TEX gMenuTextureRec_800B58B0; + +extern gameWork GameWork; + +extern unsigned char *GV_ResidentMemoryBottom; + +extern void *StageCharacterEntries; +extern int gOverlayBinSize_800B5290; + +/*---------------------------------------------------------------------------*/ + +static void GM_ClearWeaponAndItem(void) +{ + GM_CurrentWeaponId = WP_None; + GM_CurrentItemId = IT_None; +} + +static void GM_InitGameSystem(void) +{ + int i; + +#ifdef __psyz + /* Say which disc this is, because nothing else does. + * + * GM_OptionFlag is linkvarbuf[2] and starts at zero; on the console it is + * filled from the memory card, or by the player visiting the options + * screen. Boot straight into a stage with no save and it stays zero, which + * means "Japanese" -- and the layout of the data differs between releases. + * + * radiomes.c reads the codec fragment's size from a different byte of the + * same word depending on this flag: bits 16-23 for English, 24-31 for + * Japanese. On this USA disc byte 3 is zero, so the read was issued for + * ZERO BYTES, the buffer kept its fill pattern, and the parser found no + * faces -- which is the whole reason no Colonel conversation has ever been + * drawn. Measured: `LOADER ... sector 31392 size 0`. + * + * The English fragment is 0x800 bytes, exactly what the code's own comment + * upstream says it should be, so this is not a guess about the format. + * Setting it here rather than in radiomes.c looked right -- the flag is + * wrong for every reader, and movie.c, jimctrl.c and radio.c all branch on + * it. But it is OFF, because doing that globally had a side effect: the + * elevator stopped placing its codec call. Before the change the log showed + * `change camera 3` and `[codec] answered` on pressing the action button; + * after it, the trigger still fires and the doors still animate, and no + * call is placed. Nothing else about the game's behaviour changed in + * between, so this is the suspect. + * + * The flag genuinely IS needed for the codec fragment size (measured: the + * read was issued for 0 bytes without it, 8192 with it), so this is not a + * wrong finding -- it is a change whose blast radius is bigger than the + * problem it solves. Scope it to the one computation that was proven to + * need it, once the elevator is confirmed working again without it. */ +#if 0 + GM_OptionFlag |= OPTION_ENGLISH; +#endif +#endif + + GM_PlayerAddress = -1; + GM_GameStatus = 0; + GM_GameOverTimer = 0; + GM_PlayerStatus = 0; + GM_NoisePower = 0; + GM_NoiseLength = 0; + GM_ClaymoreMap = 0; + GM_AlertLevel = 0; + GM_AlertMax = 0; + GM_AlertMode = ALERT_OFF; + GM_WeaponChanged = 0; + GM_Magazine = 0; + GM_MagazineMax = 0; + GM_DisableItem = 0; + GM_DisableWeapon = 0; + GM_O2 = 1024; + GM_StageName = NULL; + GM_EnvironTemp = 0; + GM_PlayerPosition.vx = GM_SnakePosX; + GM_PlayerPosition.vy = GM_SnakePosY; + GM_PlayerPosition.vz = GM_SnakePosZ; + + for (i = 5; i >= 0; i--) + { + GM_SystemCallbackProc[i] = 0; + } +} + +static void GM_InitNoise(void) +{ + int length; + int max; + + // isn't this one of the inlines? + length = GM_NoiseLength; + if (GM_NoiseLength > 0) + { + length = GM_NoiseLength - 1; + } + if (!length) + { + GM_NoisePower = 0; + } + + max = GM_AlertMax; + GM_NoiseLength = length; + GM_AlertMax = 0; + GM_AlertLevel = max; +} + +static void GM_ResetSystem(void) +{ + menuman_Reset(); + GV_ResetSystem(); + DG_ResetPipeline(); + GCL_ResetSystem(); +} + +static void GM_ResetMemory(void) +{ + DG_ResetTextureCache(); + GV_ResetMemory(); + GM_ResetChara(); +} + +// GM_InitStage? +static void GM_CreateLoader(void) +{ + char *stage = "init"; + if (GM_CurrentStageFlag != 0) + { + stage = GM_GetArea(GM_CurrentStageFlag); + } + NewLoader(stage); +} + +static void GM_HidePauseScreen(void) +{ + GV_PauseLevel &= ~GV_PAUSE_PAUSE; + GM_SetSound(0x01ffff02, SD_ASYNC); + MENU_JimakuClear(); + GM_GameStatus &= ~GAME_FLAG_BIT_08; +} + +static void GM_ShowPauseScreen(void) +{ + char *areaName; + + areaName = ""; + GV_PauseLevel |= GV_PAUSE_PAUSE; + GM_SetSound(0x01ffff01, SD_ASYNC); + if (GM_StageName) + { + areaName = GM_StageName; + } + MENU_AreaNameWrite(areaName); +} + +static void GM_TogglePauseScreen(void) +{ + int var1; + int var2; + int ret; + + var1 = GV_PauseLevel; + var2 = var1 & ~2; + ret = var2; // Why this waste? + // It should always be true because the only caller + // does the same check before calling this function. + if (var2 == 0) + { + if ((var1 & 2) == 0) + { + GM_ShowPauseScreen(); + return; + } + GM_HidePauseScreen(); + } +} + +static void GM_ActInit(gameWork *work) +{ + GM_Reset_helper3_80030760(); + GM_InitWhereSystem(); + GM_InitTargetSystem(); + GM_ResetHomingTargets(); + GM_ResetScript(); + GM_InitGameSystem(); + GM_AlertModeInit(); +} + +/*---------------------------------------------------------------------------*/ + +#define PCC_READ 0xa0be // GV_StrCode("read") + +void GM_InitReadError(void) +{ + DG_TEX *tex; + + tex = DG_GetTexture(PCC_READ); + gMenuTextureRec_800B58B0 = *tex; + gMenuTextureRec_800B58B0.id = 0; +} + +void DrawReadError(void) +{ + int u_off; + DR_TPAGE tpage; + SPRT sprt; + TILE tile; + + u_off = 16 * gMenuTextureRec_800B58B0.id; + gMenuTextureRec_800B58B0.id = (gMenuTextureRec_800B58B0.id + 1) % 6; + + DG_DisableClipping(); + + setDrawTPage(&tpage, 1, 1, gMenuTextureRec_800B58B0.tpage); + DrawPrim(&tpage); + + LSTORE(0, &tile.r0); + setTile(&tile); + tile.x0 = 287; + tile.y0 = 15; + tile.h = 18; + tile.w = 18; + DrawPrim(&tile); + + LSTORE(0x80808080, &sprt.r0); + setSprt(&sprt); + sprt.w = 16; + sprt.h = 16; + sprt.x0 = 288; + sprt.y0 = 16; + sprt.u0 = gMenuTextureRec_800B58B0.off_x + u_off; + sprt.v0 = gMenuTextureRec_800B58B0.off_y; + sprt.clut = gMenuTextureRec_800B58B0.clut; + DrawPrim(&sprt); +} + +/*---------------------------------------------------------------------------*/ + +static void Act(gameWork *work) +{ + int load_request; + int status; + + unsigned short pad = mts_read_pad(1); + + if (mts_get_pad_vibration_type(1) == 1) + { + GM_OptionFlag &= ~OPTION_VIBRATION_OFF; + } + else + { + GM_OptionFlag |= OPTION_VIBRATION_OFF; + } + + if ((GM_OptionFlag & (OPTION_UNKNOWN_2000 | OPTION_VIBRATION_OFF)) == 0) + { + int vibration2; + if (GM_PadVibration != 0) + { + mts_set_pad_vibration(1, 10); + } + else + { + mts_set_pad_vibration(1, 0); + } + + vibration2 = GM_PadVibration2; + if (vibration2 > 255) + { + vibration2 = 255; + } + mts_set_pad_vibration2(1, vibration2); + } + + GM_PadVibration2 = 0; + GM_PadVibration = 0; + + if ((GV_PauseLevel & GV_PAUSE_READERROR) != 0) + { + if (!str_mute_fg && CDBIOS_TaskState() != 3) + { + GV_PauseLevel &= ~GV_PAUSE_READERROR; + } + else + { + DrawReadError(); + } + } + else if (str_mute_fg || CDBIOS_TaskState() == 3) + { + GV_PauseLevel |= GV_PAUSE_READERROR; + } + + if ((GV_PauseLevel & GV_PAUSE_PAUSE) == 0) + { + int minutes; + gTotalFrameTime += GV_PassageTime; + minutes = gTotalFrameTime / 60; + GM_TotalHours = minutes / 3600; + GM_TotalSeconds = minutes % 3600; + } + + status = work->status; + if (status != WAIT_LOAD) + { + if (status != WORKING) + { + return; + } + } + else + { + if (GM_LoadComplete == 0) + { + return; + } + + GM_LoadComplete = 1; + + if ((GM_LoadRequest & 0x80) != 0) + { + DG_UnDrawFrameCount = 0; + } + + if (FS_ResidentCacheDirty) + { + GV_SaveResidentFileCache(); + DG_SaveResidentTextureCache(); + } + + GM_ResetMap(); + NewCameraSystem(); + DG_StorePalette(); + GM_AlertReset(); + + if ((GM_LoadRequest & 0x10) != 0) + { + GCL_SaveVar(); + } + + printf("exec scenario\n"); + load_request = GM_LoadRequest; + GM_LoadRequest = 0; + + if ((load_request & 0x20) != 0) + { + GCL_ExecProc((unsigned int)load_request >> 16, 0); + } + else + { + GCL_ExecScript(); + } + + printf("end scenario\n"); + MENU_ResetTexture(); + GM_AlertModeReset(); + GM_SoundStart(); + work->status = WORKING; + + return; + } + + if ((work->killing_count <= 0)) + { + if (GM_GameOverTimer != 0) + { + if (GM_GameOverTimer > 0) + { + if ((GM_GameOverTimer == status)) + { + if (GM_StreamStatus() == -1) + { + if (NewGameOver(TRUE)) + { + GM_GameOverTimer = -1; + } + else + { + GV_DestroyActorSystem( GV_KILL_LEVEL_NORMAL ); + } + } + else if (GM_StreamStatus() == status) + { + GM_StreamPlayStop(); + } + } + else + { + GM_GameOverTimer--; + } + } + } + else + { + if (GM_LoadRequest != 0 && (GV_PauseLevel & GV_PAUSE_PAUSE) == 0) + { + if ((GM_LoadRequest & 0x80) != 0) + { + { extern const char *mgs_dbg_undraw_src; mgs_dbg_undraw_src = "game/gamed.c:476"; } + DG_UnDrawFrameCount = 0x7fff0000; + } + + GV_DestroyActorSystem( GV_KILL_LEVEL_NORMAL ); + GV_PauseLevel &= ~GV_PAUSE_READERROR; + GM_ResetMapModel(); + GM_StreamPlayStop(); + work->killing_count = 3; + GM_GameStatus |= STATE_PADRELEASE | STATE_ALL_OFF; + + return; + } + + if (GV_PauseLevel == 0) + { + GM_AlertAct(); + } + + if ((GM_GameStatus & (STATE_VOX_STREAM | STATE_PAUSE_OFF | STATE_PADMASK | STATE_PADRELEASE | + STATE_PADDEMO | STATE_DEMO)) == 0) + { + if (((GV_PauseLevel & ~2) == 0) && ((GM_CurrentPadData->press & PAD_START) != 0)) + { + GM_TogglePauseScreen(); + } + } + else if ((GV_PauseLevel & GV_PAUSE_PAUSE) != 0) + { + GM_HidePauseScreen(); + } + + GM_UpdateMap(); + } + +#define RESET_COMBO (PAD_LR|PAD_START|PAD_SELECT) +#define RESET_DELAY (90) + + if (((pad & RESET_COMBO) == RESET_COMBO) && (GM_PadResetDisable == FALSE)) + { + // User must hold the combo for 90 frames. + if (--reset_timer < 0) + { + sprintf(exe_name, "cdrom:\\MGS\\%s;1", MGS_DiskName[FS_DiskNum]); + EnterCriticalSection(); + SetDispMask(0); + PadStopCom(); + SpuInit(); + CdInit(); + SpuSetIRQ(SPU_OFF); + mts_shutdown(); + memcard_exit(); + ResetGraph(3); + StopCallback(); + SetConf(0x10, 4, 0x801FFFF0); // note: hardcoded addresses + ResetCallback(); + StopCallback(); + _96_remove(); + _96_init(); + + do { + printf("load %s\n", exe_name); + LoadExec(exe_name, 0x801FFF00, 0); + } while (1); + } + } + else + { + // Reset the countdown if the combo's been released. + reset_timer = RESET_DELAY; + } + +#undef RESET_COMBO +#undef RESET_DELAY + + if ((GM_GameStatus < 0) && ((GM_CurrentPadData[2].press & (PAD_START | PAD_CROSS)) != 0)) + { + GM_StreamPlayStop(); + } + + if ((mts_read_pad(2) & PAD_CIRCLE) != 0) + { + char spu_status[24]; + char spu_stat; + int i; + unsigned int spu_key; + + SpuGetAllKeysStatus(spu_status); + spu_key = 0; + for (i = 0; i < 24; ++i) + { + spu_key *= 2; + spu_stat = spu_status[i]; + spu_key |= spu_stat & 1; + } + + printf("str_status %d irq %x %X %X\n", str_status, dword_800BF1A8, dword_800BF270, + str_off_idx); + printf("key %08X\n", spu_key); + } + + if (GV_PauseLevel == 0) + { + GM_InitNoise(); + } + } + else + { + GV_PauseLevel &= ~GV_PAUSE_READERROR; + + if ((--work->killing_count <= 0)) + { + if (GM_StreamStatus() == -1) + { + if ((GV_PauseLevel & 5) == 0) + { + work->status = 0; + work->killing_count = 0; + GM_ResetMapHazard(); + GM_ResetSystem(); + GM_ActInit(work); + + if ((GM_LoadRequest & 0x40) == 0) + { + GM_ResetMemory(); + GM_CreateLoader(); + } + + return; + } + else if ((GM_LoadRequest & 0x80) != 0) + { + DG_UnDrawFrameCount = 0; + } + } + + work->killing_count = status; + } + + if (GV_PauseLevel == 0) + { + GM_InitNoise(); + } + } +} + +void GM_SetSystemCallbackProc(int index, int proc) +{ + GM_SystemCallbackProc[index] = proc; +} + +void GM_CallSystemCallbackProc(int id, int arg) +{ + int proc; + + if (id == 4 && GM_PlayerControl != NULL) + { + HZD_ReExecEvent(GM_PlayerControl->map->hzd, + &GM_PlayerControl->evt, 0x301); + } + + proc = GM_SystemCallbackProc[id]; + if (proc != 0) + { + GCL_ARGS args; + long argbuf[2]; + + args.argc = 1; + args.argv = argbuf; + argbuf[0] = arg; + GCL_ForceExecProc(proc, &args); + } +} + +void GM_SetLoadCallbackProc(int proc_id) +{ + if (proc_id == -1) + { + GM_LoadRequest = 0xc0; + } + else + { + GM_LoadRequest = proc_id << 16 | 0xe0; + } +} + +void GM_ContinueStart(void) +{ + int total_continues; + int current_stage; + + GM_CallSystemCallbackProc(1, 0); + total_continues = GM_TotalContinues; + current_stage = GM_CurrentStageFlag; + GCL_RestoreVar(); + if (GM_CurrentStageFlag != current_stage) + { + GM_LoadRequest = 1; + } + else + { + GM_SetLoadCallbackProc(-1); + } + + GM_TotalContinues = total_continues + 1; + + // Set the bomb to no less than 10 seconds to prevent instant death + // note: casting needed to produce sltiu and lhu vs lh + if ((unsigned int)(unsigned short)GM_TimerBombFlag - 1 < 9) + { + GM_TimerBombFlag = 10; + } +} + +void GM_GameOver(void) +{ + if (!GM_GameOverTimer) + { + GM_GameOverTimer = 4; + GM_CallSystemCallbackProc(0, 0); + GM_GameStatus |= (STATE_RADIO_OFF | STATE_PAUSE_OFF | STATE_MENU_OFF); + } +} + +/** + * @brief Overlay binary initialization handler + * + * Copies the overlay from its temporary buffer to the overlay base address. + * The overlay's filename will always be xxx.bin (where "xxx" is the name of + * the current stage). + * + * @param[in] buf pointer to cached overlay buffer + * @param[in] id strcode of the overlay's basename + * + * @retval 1 on success + * @retval <= 0 on failure (but this can't happen) + */ +static int GM_LoadInitBin(void *buf, int id) +{ +#ifdef DEV_EXE + return 1; // the overlay is embedded in the executable in dev variant +#endif + + if (((u_char *)StageCharacterEntries + gOverlayBinSize_800B5290) > GV_ResidentMemoryBottom) + { + printf("TOO LARGE STAGE BINARY!!\n"); + } + + memcpy(StageCharacterEntries, buf, gOverlayBinSize_800B5290); + return 1; +} + +void GM_StartDaemon(void) +{ + gTotalFrameTime = 0; + GM_GameOverTimer = 0; + GM_LoadRequest = 0; + GM_LoadComplete = 0; + MENU_StartDeamon(); + GM_InitArea(); + GM_InitChara(); + GM_InitScript(); + GV_SetLoader('b', GM_LoadInitBin); + GM_ClearWeaponAndItem(); + GV_InitActor(GV_ACTOR_MANAGER, &GameWork.actor, NULL); + GV_SetNamedActor(&GameWork.actor, Act, NULL, "gamed.c"); + GM_ResetSystem(); + GM_ActInit(&GameWork); + GM_ResetMemory(); + GM_CurrentPadData = GV_PadData; + GM_CurrentDiskFlag = FS_DiskNum + 1; + GV_SaveResidentTop(); + GameWork.status = 0; + GameWork.killing_count = 0; + GM_CreateLoader(); +} diff --git a/components/mgs/mgs/source/game/homing.c b/components/mgs/mgs/source/game/homing.c new file mode 100644 index 00000000..519c7c66 --- /dev/null +++ b/components/mgs/mgs/source/game/homing.c @@ -0,0 +1,159 @@ +#include "homing.h" + +#include +#include +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "game.h" + +extern HOMING homing_targets[NUM_HOMING_TARGETS]; + +void GM_ResetHomingTargets(void) +{ + HOMING *hom; + int i; + + hom = homing_targets; + for (i = NUM_HOMING_TARGETS; i > 0; i--) + { + hom->used = FALSE; + hom++; + } +} + +HOMING *GM_AllocHomingTarget(MATRIX *world, CONTROL *control) +{ + HOMING *hom; + int i; + + hom = homing_targets; + for (i = NUM_HOMING_TARGETS; i > 0; i--) + { + if (!hom->used) + { + break; + } + + hom++; + } + + if (i == 0) + { + printf("Homing target over !!!\n"); + return 0; + } + + hom->world = world; + hom->control = control; + hom->flag = FALSE; + hom->used = TRUE; + return hom; +} + +void GM_FreeHomingTarget(HOMING *hom) +{ + if (hom) + { + hom->used = FALSE; + } +} + +void GM_GetHomingTarget(MATRIX *world, int ang, int *yaw, int *pitch, int map) +{ + SVECTOR pos; + SVECTOR hom_pos; + SVECTOR diff; + int min; + HOMING *hom; + int i; + int len; + int dir; + + min = 6100; + hom = homing_targets; + + pos.vx = world->t[0]; + pos.vy = world->t[1]; + pos.vz = world->t[2]; + + *yaw = -1; + *pitch = 0; + + for (i = NUM_HOMING_TARGETS; i > 0; i--) + { + if (hom->used && (hom->control->map->index & map) && (hom->flag == TRUE)) + { + hom_pos.vx = hom->world->t[0]; + hom_pos.vy = hom->world->t[1]; + hom_pos.vz = hom->world->t[2]; + + GV_SubVec3(&hom_pos, &pos, &diff); + len = GV_VecLen3(&diff); + + if (len < min) + { + dir = GV_VecDir2(&diff); + if (GV_DiffDirAbs(ang, dir) < 512) + { + min = len; + *yaw = dir; + *pitch = (ratan2(len, hom_pos.vy - pos.vy) & 4095) - 1024; + } + } + } + + hom++; + } +} + +void GM_GetHomingTarget2(MATRIX *world, int ang, int *yaw, int *pitch, int map, int max_dist, int max_ang) +{ + SVECTOR pos; + SVECTOR hom_pos; + SVECTOR diff; + int min; + HOMING *hom; + int i; + int len; + int dir; + + min = max_dist + 100; + hom = homing_targets; + + pos.vx = world->t[0]; + pos.vy = world->t[1]; + pos.vz = world->t[2]; + + *yaw = -1; + *pitch = 0; + + for (i = NUM_HOMING_TARGETS; i > 0; i--) + { + if (hom->used && (hom->control->map->index & map) && (hom->flag == TRUE)) + { + hom_pos.vx = hom->world->t[0]; + hom_pos.vy = hom->world->t[1]; + hom_pos.vz = hom->world->t[2]; + + GV_SubVec3(&hom_pos, &pos, &diff); + len = GV_VecLen3(&diff); + + if (len < min) + { + dir = GV_VecDir2(&diff); + if (GV_DiffDirAbs(ang, dir) < max_ang) + { + min = len; + *yaw = dir; + *pitch = (ratan2(len, hom_pos.vy - pos.vy) & 4095) - 1024; + } + } + } + + hom++; + } +} diff --git a/components/mgs/mgs/source/game/homing.h b/components/mgs/mgs/source/game/homing.h new file mode 100644 index 00000000..c8ee8c86 --- /dev/null +++ b/components/mgs/mgs/source/game/homing.h @@ -0,0 +1,29 @@ +#ifndef __MGS_GAME_HOMING_H__ +#define __MGS_GAME_HOMING_H__ + +#include +#include +#include +#include "game/control.h" + +#ifdef VR_EXE +#define NUM_HOMING_TARGETS 64 +#else +#define NUM_HOMING_TARGETS 8 +#endif + +typedef struct HOMING +{ + MATRIX *world; + CONTROL *control; + int flag; + int used; +} HOMING; + +void GM_ResetHomingTargets(void); +HOMING *GM_AllocHomingTarget(MATRIX *world, CONTROL *control); +void GM_FreeHomingTarget(HOMING *hom); +void GM_GetHomingTarget(MATRIX *world, int ang, int *yaw, int *pitch, int map); +void GM_GetHomingTarget2(MATRIX *world, int ang, int *yaw, int *pitch, int map, int max_dist, int max_ang); + +#endif // __MGS_GAME_HOMING_H__ diff --git a/components/mgs/mgs/source/game/item.c b/components/mgs/mgs/source/game/item.c new file mode 100644 index 00000000..ef49d240 --- /dev/null +++ b/components/mgs/mgs/source/game/item.c @@ -0,0 +1,900 @@ +#include "item.h" + +#include +#include +#include +#include +#include + +#include "common.h" +#include "mts/mts.h" // for fprintf +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "linkvar.h" +#include "sound/g_sound.h" +#include "strcode.h" + +extern unsigned short GM_ItemTypes[]; + +/*---------------------------------------------------------------------------*/ + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; + OBJECT_NO_ROTS body; + char unused[8]; + MATRIX light[2]; + int map; + short spin; // y-axis spin per frame (4096 = 1.0) + short alive_time; // item disappears once alive_time is 0, -1 = never disappears + short text_counter; // controls text position + u_char state; + u_char type; // 0 = add item, 1 = add weapon, 2 = add ammo + short id; // weapon or item ID + short num; // number of items or bullets to add + const char *message; + const char *message_full; + int end_proc; + LINE_F4 text_line[2]; // lines around the item text + DG_PRIM *shadow; + SVECTOR shadow_verts[4]; +} Work; + +#define EXEC_LEVEL GV_ACTOR_USER + +#define BODY_FLAG ( DG_FLAG_TEXT | DG_FLAG_TRANS | DG_FLAG_SHADE \ + | DG_FLAG_GBOUND | DG_FLAG_ONEPIECE \ + | DG_FLAG_AMBIENT | DG_FLAG_IRTEXTURE ) + +/*---------------------------------------------------------------------------*/ + +static int add_weapon(int id, int num) +{ + if (GM_Weapons[id] < 0) + { + GM_Weapons[id] = 0; + } + + if (GM_Weapons[id] >= GM_WeaponsMax[id]) + { + return 0; + } + + GM_Weapons[id] += num; + if (GM_Weapons[id] > GM_WeaponsMax[id]) + { + GM_Weapons[id] = GM_WeaponsMax[id]; + } + + return 1; +} + +static int add_ammo(int id, int num) +{ + if (GM_Weapons[id] < 0) + { + return 2; + } + + if (GM_Weapons[id] >= GM_WeaponsMax[id]) + { + return 0; + } + + GM_Weapons[id] += num; + if (GM_Weapons[id] > GM_WeaponsMax[id]) + { + GM_Weapons[id] = GM_WeaponsMax[id]; + } + + return 1; +} + +#define SET_ITEM_BIT(id, num) do { GM_Items[(id)] |= (1 << (num)); } while (0) + +static int add_item(int id, int num) +{ + int max; + + if (id == 0xff) + { + enable_equipment(); + return 1; + } + + if (GM_ItemTypes[id + 1] & ITEMTYPE_BITMASK) + { + SET_ITEM_BIT(id, num - 1); + return 1; + } + + if (GM_ItemTypes[id + 1] & ITEMTYPE_CONSUMABLE) + { + max = GM_Items[id + 11]; + + if (GM_Items[id] >= max) + { + return 0; + } + + if (GM_Items[id] < 0) + { + GM_Items[id] = 0; + } + + GM_Items[id] += num; + if (GM_Items[id] > max) + { + *(&GM_Items[id]) = max; + } + + return 1; + } + + GM_Items[id] = num; + return 1; +} + +void disable_equipment(void) +{ + int i; + + for (i = 0; i < IT_Max; i++) + { + GM_Items[i] |= ITEMTYPE_DISABLED; + } + + for (i = 0; i < WP_Max; i++) + { + GM_Weapons[i] |= ITEMTYPE_DISABLED; + } +} + +void enable_equipment(void) +{ + int i; + + for (i = 0; i < IT_Max; i++) + { + if (GM_Items[i] != -1) + { + GM_Items[i] &= ~ITEMTYPE_DISABLED; + } + } + + for (i = 0; i < WP_Max; i++) + { + if (GM_Weapons[i] != -1) + { + GM_Weapons[i] &= ~ITEMTYPE_DISABLED; + } + } +} + +static int in_pickup_range(Work *work) +{ + SVECTOR vec; + int diff; + + if (!(work->map & GM_PlayerMap)) + { + return 0; + } + + vec = GM_PlayerPosition; + + diff = work->control.mov.vy - vec.vy; + if (diff < 0) + { + diff = -diff; + } + + if (diff > 1000) + { + return 0; + } + + vec.vy = work->control.mov.vy; + + diff = work->control.mov.vx - vec.vx; + if (abs(diff) > 500) + { + return 0; + } + + diff = work->control.mov.vz - vec.vz; + if (abs(diff) > 500) + { + return 0; + } + + return 1; +} + +static void init_pack(POLY_FT4 *poly, DG_TEX *tex) +{ + setPolyFT4(poly); + setSemiTrans(poly, 1); + DG_SetPacketTexture4(poly, tex); +} + +static int world_to_screen(SVECTOR *out, SVECTOR *in) +{ + long z; + + gte_SetRotMatrix(&DG_Chanl(0)->eye_inv); + gte_SetTransMatrix(&DG_Chanl(0)->eye_inv); + + gte_ldv0(in); + gte_rtps(); + gte_stsxy(out); + gte_stsz(&z); + + return z > 0; +} + +static void Act(Work *work) +{ + SVECTOR screen_pos; + SVECTOR position; + const char *message; + CONTROL *control; + int shake; + int state; + int dx, dy, dz; + OT_TYPE *ot; + LINE_F4 *line; + int x, y; + + GM_CurrentMap = work->map; + + if (work->message_full) + { + if (world_to_screen(&screen_pos, &work->control.mov)) + { + if (work->text_counter < 45) + { + message = work->message; + } + else + { + message = work->message_full; + } + + if (work->text_counter < 16) + { + screen_pos.vy -= work->text_counter; + } + else + { + screen_pos.vy -= 16; + } + + if (work->message_full != work->message) + { + MENU_Color(255, 48, 48); + } + else + { + MENU_Color(200, 200, 200); + } + + MENU_Locate(screen_pos.vx + 160, screen_pos.vy + 104, 0x12); + MENU_Printf(message); + + MENU_Color(1, 1, 1); + MENU_Locate(screen_pos.vx + 161, screen_pos.vy + 105, 0x12); + MENU_Printf(message); + + MENU_ResetText(); + } + + if (work->text_counter > 90) + { + if (work->state == 2) + { + GV_DestroyActor(&work->actor); + } + else if (in_pickup_range(work)) + { + work->text_counter = 50; + } + else + { + work->message_full = NULL; + } + } + + work->text_counter++; + + if (work->state == 2) + { + return; + } + } + + if ((work->state == 0) && (work->body.objs->bound_mode == 0) && (work->alive_time <= 0)) + { + return; + } + + if (work->alive_time > 0) + { + /* blink once time left is low */ + if (--work->alive_time < 90) + { + if ((work->alive_time % 8) < 2) + { + DG_InvisibleObjs(work->body.objs); + } + else + { + DG_VisibleObjs(work->body.objs); + } + } + + if (work->alive_time == 0) + { + GV_DestroyActor(&work->actor); + return; + } + } + + control = &work->control; + + if (work->spin > 73) + { + work->spin -= 10; + } + + control->turn.vy += work->spin; + control->turn.vy &= 4095; + + if (work->state == 1) + { + if ((control->grounded != 0) && (control->step.vy < 0)) + { + control->step.vy = -control->step.vy / 16; + + if (control->step.vy < 16) + { + GM_ConfigControlHazard(control, -1, -2, -1); + control->step = DG_ZeroVector; + work->state = 0; + } + } + else + { + control->step.vy -= 16; + } + + GM_ActControl(control); + } + else + { + GM_CurrentMap = control->map->index; + + if (work->message_full) + { + position = control->mov; + shake = (90 - work->text_counter) * 8; + + if (shake > 80) + { + shake = 80; + } + + if (GV_Clock & 1) + { + shake = -shake; + } + + position.vx += shake; + DG_SetPos2(&position, &control->turn); + } + else + { + DG_SetPos2(&control->mov, &control->turn); + } + } + + GM_ActObject2((OBJECT *)&work->body); + DG_GetLightMatrix2(&control->mov, work->light); + + if (in_pickup_range(work) && (work->state != 1)) + { + switch (work->type) + { + case 0: + state = add_item(work->id, work->num); + break; + + case 1: + state = add_weapon(work->id, work->num); + break; + + case 2: + state = add_ammo(work->id, work->num); + break; + } + + switch (state) + { + case 1: + DG_InvisibleObjs(work->body.objs); + + if (work->shadow) + { + DG_InvisiblePrim(work->shadow); + } + + work->state = 2; + work->text_counter = 0; + work->message_full = work->message; + + GM_SeSet2(0, 63, SE_ITEM_GET); + return; + + case 0: + if (!work->message_full) + { + work->message_full = "FULL"; + work->text_counter = 0; + GM_SeSet2(0, 63, SE_ITEM_FULL); + } + break; + + case 2: + if (!work->message_full) + { + work->message_full = "GET WEAPON FIRST"; + work->text_counter = 0; + GM_SeSet2(0, 63, SE_ITEM_FULL); + } + break; + } + } + + if (!work->message || work->message_full) + { + return; + } + + if (!(GM_PlayerStatus & (PLAYER_INTRUDE | PLAYER_WATCH)) || (GM_GameStatus & (STATE_PADRELEASE | STATE_DEMO))) + { + work->text_counter = 0; + return; + } + + dx = GM_PlayerControl->mov.vx - control->mov.vx; + if (dx < 0) + { + dx = -dx; + } + + dy = GM_PlayerControl->mov.vy - control->mov.vy; + if (dy < 0) + { + dy = -dy; + } + + dz = GM_PlayerControl->mov.vz - control->mov.vz; + if (dz < 0) + { + dz = -dz; + } + + if ( (dx >= 2000) || (dy >= 2000) || (dz >= 2000) ) + { + return; + } + + if ((work->text_counter++ % 60) <= 20) + { + return; + } + + ot = DG_ChanlOTag(1); + line = &work->text_line[GV_Clock]; + + if (!world_to_screen(&position, &control->mov)) + { + return; + } + + x = position.vx + 160; + y = position.vy + 104; + + if ((y < -32) || (y > 300)) + { + return; + } + + if (y < 32) + { + y = 32; + } + + if (y > FRAME_HEIGHT) + { + y = FRAME_HEIGHT; + } + + line->x0 = x; + line->x1 = x + 16; + line->x2 = x + 16; + + line->y0 = y; + line->y1 = y - 16; + line->y2 = y - 16; + line->y3 = y - 23; + + addPrim(ot, line); + + MENU_Color(200, 200, 200); + MENU_Locate(line->x1 + 5, line->y1 - 8, 0x10); + MENU_Printf("%s", work->message); + + MENU_Color(1, 1, 1); + MENU_Locate(line->x1 + 6, line->y1 - 7, 0x10); + line->x2 = line->x3 = MENU_Printf("%s", work->message) + 3; +} + +/*---------------------------------------------------------------------------*/ + +static void Die(Work *work) +{ + GM_FreeControl(&work->control); + GM_FreeObject((OBJECT *)&work->body); + GM_FreePrim(work->shadow); + + if (work->state == 2 && work->end_proc != 0) + { + if (work->end_proc < 0) + { + GCL_ExecBlock((u_char *)work->end_proc, NULL); + } + else + { + GCL_ExecProc(work->end_proc, NULL); + } + } +} + +/*---------------------------------------------------------------------------*/ + +static int check_type( Work *work, int type ) +{ + int id; + + id = work->id; + if ( id == 0xff ) + { + return 1; + } + + work->type = 0; + switch ( type ) + { + case 2: + case 3: + if ( (GM_DifficultyFlag == DIFFICULTY_VERY_EASY) && (id == WP_Famas) ) + { + return 0; + } + + if ( id >= WP_Max ) + { + return 0; + } + + work->type = 2; + break; + + case 0: + if ( id >= WP_Max ) + { + return 0; + } + + work->type = 1; + break; + + case 4: + if ( id != IT_Ration ) + { + return 0; + } + break; + + case 1: + case 5: + if ( id >= IT_Max ) + { + return 0; + } + break; + } + + return 1; +} + +static int GetResources(Work *work, int name, int where) +{ + CONTROL *control; + char *pos; + char *dir; + int type; + char *opt; + int height; + int code; + int value; + OBJECT_NO_ROTS *body; + int i; + SVECTOR *verts; + DG_PRIM *shadow; + int raise; + DG_TEX *tex; + + GM_CurrentMap = where; + work->map = where; + + control = &work->control; + if (GM_InitControl(control, name, where) < 0) + { + return -1; + } + + GM_ConfigControlHazard(control, -1, -2, -1); + GM_ConfigControlInterp(control, 0); + + pos = GCL_GetOption('p'); + dir = GCL_GetOption('d'); + GM_ConfigControlString(control, pos, dir); + + control->step = DG_ZeroVector; + control->skip_flag = CTRL_SKIP_TRAP | CTRL_SKIP_MESSAGE; + + opt = GCL_GetOption('b'); + if (opt) + { + type = GCL_StrToInt(opt); + } + else + { + type = 0; + } + + opt = GCL_GetOption('i'); + if (!opt) + { + printf("NO ID ITEM\n"); + return -1; + } + + work->id = GCL_StrToInt(opt); + + opt = GCL_GetOption('n'); + if (opt) + { + work->num = GCL_StrToInt(opt); + } + else + { + work->num = 1; + } + + opt = GCL_GetOption('m'); + if (opt) + { + work->message = GCL_GetString(opt); + } + + work->message_full = NULL; + + height = 450; + opt = GCL_GetOption('h'); + if (opt) + { + height = GCL_StrToInt(opt); + } + + work->end_proc = 0; + if (GCL_GetOption('e')) + { + work->end_proc = GCL_StrToInt(GCL_NextStr()); + } + else if (GCL_GetOption('x')) + { + GCL_GetNextValue(GCL_NextStr(), &code, &value); + work->end_proc = value; + } + + if (!check_type(work, type)) + { + return 0; + } + + if ((GM_DifficultyFlag == DIFFICULTY_EXTREME) && (type == 4) && (work->id == IT_Ration)) + { + return 0; + } + + GV_ZeroMemory(work->unused, 8); + + body = &work->body; + GM_InitObjectNoRots(body, KMD_BOX_01 + type, BODY_FLAG, 0); + GM_ConfigObjectJoint((OBJECT *)body); + GM_ConfigObjectLight((OBJECT *)body, work->light); + GM_ConfigObjectStep((OBJECT *)body, &work->control.step); + + if (GCL_GetOption('v')) + { + work->body.objs[2].world.m[1][1] = -10000; + } + + for (i = 0; i < 2; i++) + { + setLineF4(&work->text_line[i]); + setRGB0(&work->text_line[i], 255, 255, 255); + } + + if (height >= 0) + { + raise = 500; + + verts = work->shadow_verts; + verts[0].vy = verts[1].vy = verts[2].vy = verts[3].vy = control->mov.vy; + verts[0].vx = verts[2].vx = control->mov.vx + 256; + verts[0].vz = verts[1].vz = control->mov.vz + 256; + verts[1].vx = verts[3].vx = control->mov.vx - 256; + verts[2].vz = verts[3].vz = control->mov.vz - 256; + + control->mov.vy += height; + + shadow = GM_MakePrim(DG_PRIM_ONESIDE | DG_PRIM_POLY_FT4, 1, work->shadow_verts, NULL); + work->shadow = shadow; + if (!shadow) + { + return -1; + } + + shadow->raise = raise; + + tex = DG_GetTexture(GV_StrCode("shadow")); + if (!tex) + { + return -1; + } + + init_pack(shadow->packs[0], tex); + init_pack(shadow->packs[1], tex); + + setRGB0((POLY_FT4 *)shadow->packs[0], 80, 80, 80); + setRGB0((POLY_FT4 *)shadow->packs[1], 80, 80, 80); + } + else + { + work->shadow = NULL; + } + + GM_ActControl(control); + GM_ActObject2((OBJECT *)body); + return 1; +} + +/*---------------------------------------------------------------------------*/ + +void *NewItem(int name, int where, int argc, char **argv) +{ + Work *work; + int inited; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "item.c"); + work->state = 0; + + inited = GetResources(work, name, where); + if (inited > 0) + { + work->alive_time = -1; + work->map = where; + fprintf(1, "Item map = %d\n", where); + work->spin = 64; + } + else + { + GV_DestroyActor(&work->actor); + + if (inited == 0) + { + return work; + } + else + { + return NULL; + } + } + } + + return work; +} + +/*---------------------------------------------------------------------------*/ + +static int GetResourcesPut(Work *work, SVECTOR *pos, SVECTOR *step, Item_Info *info, int where) +{ + int type; + CONTROL *control; + int i; + + type = info->type; + + work->id = info->id; + work->num = info->num; + work->message = info->message; + work->end_proc = 0; + + if (!check_type(work, type)) + { + return -1; + } + + control = &work->control; + if (GM_InitControl(control, HASH_ITEM, where) < 0) + { + return -1; + } + + GM_ConfigControlHazard(control, 100, 500, 500); + GM_ConfigControlInterp(control, 0); + + control->skip_flag = CTRL_SKIP_TRAP | CTRL_SKIP_MESSAGE; + control->step = *step; + control->step.vy = 160; + control->mov = *pos; + + GM_InitObjectNoRots(&work->body, KMD_BOX_01 + type, BODY_FLAG, 0); + GM_ConfigObjectJoint((OBJECT *)&work->body); + GM_ConfigObjectLight((OBJECT *)&work->body, work->light); + GM_ConfigObjectStep((OBJECT *)&work->body, &control->step); + + for (i = 0; i < 2; i++) + { + setLineF4(&work->text_line[i]); + setRGB0(&work->text_line[i], 255, 255, 255); + } + + return 0; +} + +void *NewItemPut(SVECTOR *pos, SVECTOR *step, Item_Info *info) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "item.c"); + + if (GetResourcesPut(work, pos, step, info, GM_CurrentMap) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + + work->alive_time = info->time; + work->map = GM_CurrentMap; + work->state = 1; + work->spin = 512; + + GM_SeSet2(0, 63, SE_SPAWN_ITEM); + } + + return work; +} diff --git a/components/mgs/mgs/source/game/item.h b/components/mgs/mgs/source/game/item.h new file mode 100644 index 00000000..10b60808 --- /dev/null +++ b/components/mgs/mgs/source/game/item.h @@ -0,0 +1,24 @@ +#ifndef __MGS_GAME_ITEM_H__ +#define __MGS_GAME_ITEM_H__ + +#include +#include +#include +#include "common.h" + +typedef struct Item_Info +{ + const char *message; + short type; + short id; + short num; + short time; +} Item_Info; + +void *NewItem(int name, int where, int argc, char **argv); +void *NewItemPut(SVECTOR *pos, SVECTOR *step, Item_Info *info); + +void disable_equipment(void); +void enable_equipment(void); + +#endif // __MGS_GAME_ITEM_H__ diff --git a/components/mgs/mgs/source/game/jimctrl.c b/components/mgs/mgs/source/game/jimctrl.c new file mode 100644 index 00000000..31adb392 --- /dev/null +++ b/components/mgs/mgs/source/game/jimctrl.c @@ -0,0 +1,420 @@ +#include "jimctrl.h" + +#include +#include "common.h" +#include "libfs/libfs.h" +#include "linkvar.h" +#include "menu/menuman.h" +#include "sound/sd_cli.h" + +extern array_800B933C_child array_800B933C[array_800B933C_SIZE]; + +extern JimakuCtrlWork jimctrl_work; + +extern int str_status; +extern int dword_800B9358; + +char *dword_8009E28C = NULL; + +static void jimctrl_act_helper_set_first_80037F2C(int index, int value) +{ + array_800B933C_child *helper = &array_800B933C[index]; + if (value == 4) + { + helper->field_2 = 1; + } + else if (value < 4) + { + helper->field_3 = value; + } +} + +// return the index for the face frame, 0 for idle face +unsigned int jimctrl_helper_80037F68(unsigned int header) +{ + u_short field_2_preClear; + array_800B933C_child *pIter; + u_char *pField; + int i; + + pIter = &array_800B933C[1]; + i = 0; + pField = &pIter->field_2; + for (; i < array_800B933C_SIZE - 1; i++) + { + if (pIter->field_0 == header) + { + field_2_preClear = *pField; + *pField = 0; + return (field_2_preClear << 8 | *++pField); + } + pField += sizeof(array_800B933C_child); + pIter++; + } + + return 0; +} + +static void jimctrl_init_helper_clear_80037FB8(void) +{ + int i = array_800B933C_SIZE - 2; + array_800B933C_child *pIter = &array_800B933C[i] + 1; + for (; i >= 0; i--) + { + pIter->field_0 = 0; + pIter--; + } +} + +static void jimctrl_act_helper_clear_first_80037FE0(int index, int value) +{ + array_800B933C_child *pIter = &array_800B933C[1]; + pIter[--index].field_0 = value; +} + +static void jimctrl_helper_null_80037FFC(int a, int b) +{ + /* do nothing */ +} + +static void jimctrl_kill_helper_clear_80038004(JimakuCtrlWork *work) +{ + array_800B933C_child *pIter; + int i; + + if (work->field_44_subtitles) + { + MENU_JimakuClear(); + } + + if (work->field_38 != 0) + { + i = 0; + pIter = &array_800B933C[i] + 1; + for (; i < array_800B933C_SIZE - 1; i++) + { + pIter->field_2 = 0; + pIter->field_3 = 0; + pIter++; + } + } +} + +static inline void jimctrl_act_helper_80038070(JimakuCtrlWork *work, int str_counter) +{ + int *pSubtitles = work->field_44_subtitles; + int f48; + char *pData; + int size; + + if (!pSubtitles) + { + return; + } + + f48 = work->field_48; + + if (f48 == 0) + { + if (str_counter < pSubtitles[1]) + { + return; + } + + pData = (char *)(pSubtitles + 4); + + switch (work->field_24) + { + case 0: + case 2: + MENU_JimakuWrite(pData, 0); + break; + + case 1: + break; + } + + dword_8009E28C = pData; + dword_800B9358++; + work->field_48 = pSubtitles[1] + pSubtitles[2]; + } + else if (f48 <= str_counter) + { + switch (work->field_24) + { + case 0: + case 2: + MENU_JimakuClear(); + break; + + case 1: + break; + } + + work->field_48 = NULL; + + size = *pSubtitles; + dword_8009E28C = NULL; + + if (size != 0) + { + pSubtitles = (int *)((char *)pSubtitles + size); + } + else + { + pSubtitles = NULL; + } + + work->field_44_subtitles = pSubtitles; + } +} + +static inline void jimctrl_act_helper2_80038070(JimakuCtrlWork *work, int str_counter) +{ + int value; + + while (work->field_30 < str_counter) + { + work->field_30++; + work->field_40--; + + while (work->field_40 < 1) + { + value = *work->field_38++; + + if (value & 0x80) + { + jimctrl_act_helper_set_first_80037F2C(work->field_3C, (value >> 4) & 0x7); + work->field_40 = value & 0xF; + } + else + { + switch (value >> 4) + { + case 0: + printf("KUTIPAKU END\n"); + jimctrl_act_helper_set_first_80037F2C(work->field_3C, 0); + work->field_38 = NULL; + return; + + case 1: + jimctrl_act_helper_set_first_80037F2C(work->field_3C, value & 0xF); + work->field_40 = *work->field_38++; + break; + + case 2: + jimctrl_act_helper_clear_first_80037FE0(value & 0xF, (work->field_38[0] << 8) | work->field_38[1]); + work->field_38 += 2; + break; + + case 4: + work->field_3C = value & 0xF; + break; + + case 5: + jimctrl_act_helper_set_first_80037F2C(work->field_3C, value & 0xF); + work->field_40 = (work->field_38[0] << 8) | work->field_38[1]; + work->field_38 += 2; + break; + + case 6: + work->field_40 = *work->field_38++; + jimctrl_helper_null_80037FFC((work->field_38[0] << 8) | work->field_38[1], (work->field_38[2] << 8) | work->field_38[3]); + break; + } + } + } + } +} + + +static void Act(JimakuCtrlWork *work) +{ + int str_counter; + void *pStrData; + void *pStrData2; + int size; + int *pSubtitles; + SubtitleHeader *pHeader; + SubtitleHeader *pHeader2; + + if (FS_StreamIsForceStop()) + { + GV_DestroyActor(&work->actor); + } + + str_counter = get_str_counter(); + if ((str_counter < 0) || (str_status == 0)) + { + if (work->field_30 < 0) + { + return; + } + + GV_DestroyActor(&work->actor); + return; + } + + str_counter >>= 2; + + pStrData2 = FS_StreamGetData(work->field_27); + if (pStrData2) + { + FS_StreamClear(pStrData2); + } + + if (work->field_30 < 0) + { + work->field_30 = 0; + } + + switch (work->field_20) + { + case 0: + pStrData = FS_StreamGetData(work->field_26); + + if (!pStrData) + { + return; + } + + size = FS_StreamGetSize(pStrData); + memcpy(jimctrl_work.field_50_buffer, pStrData, size); + FS_StreamClear(pStrData); + + if (!work->field_34) + { + pHeader = (SubtitleHeader *)jimctrl_work.field_50_buffer; + + work->field_34 = (int *)pHeader; + work->field_38 = (char *)pHeader + pHeader->data_offset; + + pSubtitles = (int *)((char *)pHeader + pHeader->subtitle_offset); + pHeader2 = pHeader; + + if ((pSubtitles[0] == 0) && (pSubtitles[1] == 0) && (pSubtitles[2] == 0)) + { + pSubtitles = NULL; + } + + work->field_44_subtitles = pSubtitles; + work->field_48 = 0; + work->field_40 = 0; + + font_set_font_addr(3, (char *)pHeader + pHeader2->font_offset); + } + + work->field_20 = 1; + + case 1: + if (str_counter < *work->field_34) + { + return; + } + + work->field_30 = *work->field_34; + work->field_20 = 2; + break; + + case 2: + break; + + default: + return; + } + + jimctrl_act_helper_80038070(work, str_counter); + + if (work->field_38) + { + jimctrl_act_helper2_80038070(work, str_counter); + } + + if (work->field_34[1] < str_counter) + { + work->field_34 = NULL; + work->field_20 = 0; + } +} + + +static void Die(JimakuCtrlWork *work) +{ + jimctrl_kill_helper_clear_80038004(work); + dword_8009E28C = NULL; + FS_StreamClose(); +} + +void *NewJimakuControl(u_long flags) +{ + int *seekResult; + u_long toSeek = 4; + JimakuCtrlWork *work = &jimctrl_work; + + if (flags & 0x80) + { + toSeek = 7; + } + seekResult = FS_StreamGetData(toSeek); + + if (GM_OptionFlag & OPTION_ENGLISH) + { + jimctrl_work.field_27 = 3; + jimctrl_work.field_26 = 6; + } + else + { + jimctrl_work.field_27 = 6; + jimctrl_work.field_26 = 3; + } + + if (seekResult != 0) + { + FS_StreamClear(seekResult); + } + + if (work->actor.act == (GV_ACTFUNC)Act) + { + return (void *)work; + } + else + { + flags &= 0xf; + work->field_28 = 0; + jimctrl_init_helper_clear_80037FB8(); + GV_InitActor(GV_ACTOR_MANAGER, &work->actor, NULL); + + GV_SetNamedActor(&work->actor, Act, Die, "jimctrl.c"); + + work->field_24 = flags; + + FS_StreamOpen(); + + work->field_30 = -1; + work->field_2C = 0; + work->field_34 = 0; + work->field_20 = 0; + dword_800B9358 = 0; + + return (void *)&jimctrl_work; + } +} + +/*---------------------------------------------------------------------------*/ + +char* menu_radio_codec_helper_helper17_80038678(void) +{ + return dword_8009E28C; +} + + +int jimctrl_80038688(void) +{ + return dword_800B9358; +} + + +array_800B933C_child *jimctrl_80038698(void) +{ + return &array_800B933C[1]; +} diff --git a/components/mgs/mgs/source/game/jimctrl.h b/components/mgs/mgs/source/game/jimctrl.h new file mode 100644 index 00000000..aea9d527 --- /dev/null +++ b/components/mgs/mgs/source/game/jimctrl.h @@ -0,0 +1,61 @@ +#ifndef __MGS_GAME_JIMCTRL_H__ +#define __MGS_GAME_JIMCTRL_H__ + +#define array_800B933C_SIZE 7 + +#include "libgv/libgv.h" + +typedef struct +{ + u_short field_0; + char field_2; + unsigned char field_3; +} array_800B933C_child; + +typedef struct +{ + u_long dword_800B9358; + u_long dword_800B935C; +} dword_800B9358_struct; + +// GV_ACT used for subtitles. +typedef struct +{ + GV_ACT actor; + int field_20; + short field_24; + char field_26; + char field_27; + int field_28; + int field_2C; + int field_30; + int *field_34; + char *field_38; // Pointer to data used to update Codec portraits, mainly by 80038070(). + int field_3C; + int field_40; + // Pointer to subtitles, mainly used by 80038070(): + // - @ 0x80038240 (R): reads the current subtitle; if disabled, the subtitles no longer update. + // - @ 0x80038340 (W): updates the pointer; if disabled, all the subtitles play in one go. + int *field_44_subtitles; + int field_48; + int field_4C; + char field_50_buffer[4090]; +} JimakuCtrlWork; + +typedef struct +{ + char *field_0; + int *field_4; + short data_offset; + short subtitle_offset; + int font_offset; +} SubtitleHeader; + +unsigned int jimctrl_helper_80037F68(unsigned int); + +void *NewJimakuControl(u_long flags); + +int jimctrl_80038688(void); +array_800B933C_child *jimctrl_80038698(void); + +#endif // __MGS_GAME_JIMCTRL_H__ diff --git a/components/mgs/mgs/source/game/lamp.c b/components/mgs/mgs/source/game/lamp.c new file mode 100644 index 00000000..625e64a8 --- /dev/null +++ b/components/mgs/mgs/source/game/lamp.c @@ -0,0 +1,390 @@ +#include "lamp.h" + +#include +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "strcode.h" + +extern char *next_str_ptr; + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_USER + +typedef struct _Work +{ + GV_ACT actor; + DG_PRIM *field_20_prim; + short field_24; + char field_26; + char field_27; + unsigned short field_28_name; + short loops; + int field_2C_rgb; + int field_30; + unsigned char *field_34_next_str_ptr; + unsigned char *field_38; + unsigned char *field_3C; + SVECTOR field_40_children[0]; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void d11c_800C326C(Work *work, int textureId) +{ + DG_PRIM *prim; + DG_TEX *tex; + POLY_FT4 *polyIter; + int offx, offy, width, height; + int i, j, k; + + prim = work->field_20_prim; + if (textureId) + { + tex = DG_GetTexture(textureId); + for (i = 0; i < 2; i++) + { + polyIter = prim->packs[i]; + for (j = 0; j < work->field_27; j++) + { + for (k = 0; k < work->field_26; k++, polyIter++) + { + setlen(polyIter, 9); // a part of setPolyFT4 macro + LSTORE(work->field_2C_rgb, &polyIter->r0); + + width = tex->w + 1; + offx = tex->off_x; + + polyIter->u0 = polyIter->u2 = offx + width * k / work->field_26; + polyIter->u1 = polyIter->u3 = offx + width * (k + 1) / work->field_26 - 1; + + height = tex->h + 1; + offy = tex->off_y; + + polyIter->v0 = polyIter->v1 = offy + height * j / work->field_27; + polyIter->v2 = polyIter->v3 = offy + height * (j + 1) / work->field_27 - 1; + + polyIter->tpage = tex->tpage; + polyIter->clut = tex->clut; + } + } + } + DG_VisiblePrim(prim); + } + else + { + DG_InvisiblePrim(prim); + } +} + +static unsigned char *d11c_800C34C4(Work *work, int arg1) +{ + unsigned char *strptr; + + strptr = work->field_34_next_str_ptr; + while (strptr[0] == 'P') + { + if (strptr[1] == arg1) + { + return strptr + 3; + } + strptr = strptr + strptr[2] + 2; + } + return NULL; +} + +static void d11c_800C3518(Work *work, int arg1) +{ + unsigned char *temp_v0; + + temp_v0 = d11c_800C34C4(work, arg1); + work->field_38 = temp_v0; + work->field_3C = temp_v0; + work->field_30 = 0; + work->loops = -1; +} + +static void CheckMessage(Work *work) +{ + GV_MSG *msg; + int len; + + len = GV_ReceiveMessage(work->field_28_name, &msg); + for (; len > 0; len--, msg++) + { + switch (msg->message[0]) + { + case HASH_ON: + d11c_800C326C(work, msg->message[1]); + work->field_3C = 0; + break; + + case HASH_OFF: + d11c_800C326C(work, 0); + work->field_3C = 0; + break; + + case 0xBCD2: + d11c_800C3518(work, msg->message[1]); + break; + } + } +} + +static void d11c_800C361C(Work *work) +{ + int param1, param2, loops; + int type; + + if (work->field_3C == NULL) + { + work->field_30 = -1; + return; + } + GCL_SetArgTop(work->field_3C); + + while (GCL_NextStr()) + { + type = GCL_StrToInt(GCL_NextStr()); + + switch (type) + { + case 0xDD19: + param1 = GCL_StrToInt(GCL_NextStr()); + param2 = GCL_StrToInt(GCL_NextStr()); + d11c_800C326C(work, param1); + work->field_30 = param2; + work->field_3C = GCL_NextStr(); + return; + + case HASH_LOOP: + if (GCL_NextStr()) + { + loops = GCL_StrToInt(GCL_NextStr()); + } + else + { + loops = 0; + } + + if (work->loops >= 0) + { + if (loops > 0) + { + if (--work->loops < 2) + { + work->field_30 = -1; + work->field_3C = 0; + return; + } + } + } + else + { + work->loops = loops; + } + + GCL_SetArgTop(work->field_38); + break; + + case 0x11F8: + d11c_800C3518(work, GCL_StrToInt(GCL_NextStr())); + GCL_SetArgTop(work->field_3C); + + default: + printf("TEX:PARSE ERROR\n"); + break; + } + } +} + +static void Act(Work *work) +{ + CheckMessage(work); + if (work->field_30 >= 0) + { + if (work->field_30 > 0) + { + work->field_30--; + return; + } + d11c_800C361C(work); + } +} + +/*---------------------------------------------------------------------------*/ + +static void Die(Work *work) +{ + GM_FreePrim(work->field_20_prim); +} + +/*---------------------------------------------------------------------------*/ + +static void d11c_800C382C(SVECTOR *vecs, int arg1, int arg2, int len2, int len1) +{ + SVECTOR *vecsIter; + int i, j; + int quot1, quot2; + int vx, vy; + + vecsIter = vecs; + vy = arg2 / 2; + quot1 = arg1 / len2; + quot2 = arg2 / len1; + + for (i = 0; i < len1; i++) + { + vx = -arg1 / 2; + + for (j = 0; j < len2; vecsIter += 4, j++) + { + int vx2 = vx + quot1; + vecsIter[0].vx = vx; + vecsIter[2].vx = vx; + vx = vx2; + vecsIter[1].vx = vx2; + vecsIter[3].vx = vx2; + + vecsIter[0].vy = vy; + vecsIter[1].vy = vy; + vecsIter[2].vy = vy - quot2; + vecsIter[3].vy = vy - quot2; + } + + vy -= quot2; + } +} + +static int GetResources(Work *work, int map, int name, int a3, int a4) +{ + MATRIX mat; + SVECTOR svec1; + SVECTOR svec2; + int param1; + DG_PRIM *prim; + SVECTOR *field_40_children; + int param2; + int prim_count; + int param3; + int primType; + int param4; + int param5; + + GM_CurrentMap = map; + GCL_StrToSV(GCL_NextStr(), (short *)&svec1); + GCL_StrToSV(GCL_NextStr(), (short *)&svec2); + param1 = GCL_GetNextInt(); + param2 = GCL_GetNextInt(); + param3 = GCL_GetNextInt(); + work->field_28_name = name; + work->field_38 = 0; + work->field_3C = 0; + work->field_30 = -1; + work->field_34_next_str_ptr = next_str_ptr; + + if (GCL_GetOption('I')) + { + param4 = GCL_GetNextInt(); + primType = 0x1012; + } + else + { + param4 = 0; + primType = 0x1012; + } + + if (GCL_GetOption('T')) + { + primType = 0x12; + } + + if (GCL_GetOption('R')) + { + param5 = GCL_GetNextInt(); + } + else + { + param5 = 0x01808080; + } + + work->field_2C_rgb = param5 | 0x2C000000; + if (GCL_GetOption('S')) + { + work->field_2C_rgb |= 0x02000000; + } + + prim_count = a3 * a4; + field_40_children = work->field_40_children; + + prim = GM_MakePrim(primType, prim_count, field_40_children, NULL); + + work->field_20_prim = prim; + work->field_24 = prim_count; + + if (prim == NULL) + { + return 0; + } + + RotMatrix(&svec2, &mat); + + mat.t[0] = svec1.vx; + mat.t[1] = svec1.vy; + mat.t[2] = svec1.vz; + + DG_SetPos(&mat); + DG_PutPrim(prim); + + prim->raise = param3; + work->field_26 = a3; + work->field_27 = a4; + + d11c_800C326C(work, param4); + d11c_800C382C(field_40_children, param1, param2, a3, a4); + + return 1; +} + +/*---------------------------------------------------------------------------*/ + +void *NewTextureLamp(int name, int where, int argc, char **argv) +{ + Work *work; + unsigned char *nextStrPtr; + int param1, param2; + + nextStrPtr = next_str_ptr; + + if (GCL_GetOption('D')) + { + param1 = GCL_GetNextInt(); + param2 = GCL_GetNextInt(); + } + else + { + param2 = 1; + param1 = 1; + } + + next_str_ptr = nextStrPtr; + + work = GV_NewActor(EXEC_LEVEL, ((param1 * param2) * sizeof(SVECTOR) * 4) + sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, Die, "lamp.c"); + if (GetResources(work, where, name, param1, param2) == 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/game/lamp.h b/components/mgs/mgs/source/game/lamp.h new file mode 100644 index 00000000..ab8211a7 --- /dev/null +++ b/components/mgs/mgs/source/game/lamp.h @@ -0,0 +1,6 @@ +#ifndef __MGS_GAME_LAMP_H__ +#define __MGS_GAME_LAMP_H__ + +void *NewTextureLamp(int name, int where, int argc, char **argv); + +#endif // __MGS_GAME_LAMP_H__ diff --git a/components/mgs/mgs/source/game/loader.c b/components/mgs/mgs/source/game/loader.c new file mode 100644 index 00000000..981ea3f6 --- /dev/null +++ b/components/mgs/mgs/source/game/loader.c @@ -0,0 +1,175 @@ +#include "loader.h" + +#include +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libfs/libfs.h" +#include "game/game.h" + +typedef struct _Work +{ + GV_ACT actor; + void *info; + int type; + int reading; + int time; +} Work; + +#define EXEC_LEVEL GV_ACTOR_ASSIST + +#ifdef __psyz +/* Which stage a requested name actually resolves to on this board, or NULL if + * the request cannot be honoured at all. + * + * Two things need the same answer, and they run at different moments: the + * script command that decides to change stage (game/script.c) and the loader + * that carries the change out. Deciding twice is how "title" first got mapped + * to a playable stage here while being rejected as missing over there, which + * left the game with nothing to load and a blank screen. + * + * "title" is special because no title stage is packed and the retail select + * menu's only entry is exactly that -- following it just reloads the menu + * forever. Send it to the start of the game instead: s00a is the dock Snake + * surfaces in, so a board carrying it plays MGS from the beginning, and s07a + * is the single room the first builds shipped with, kept as the fallback. + * Which stages are packed is decided by port/pack_stagedir.py, so ask + * STAGE.DIR rather than assume. */ +extern const char *const Mgs_PlayableStages[]; /* port/stage_union.c */ + +/* A stage is usable only if BOTH are true: its data is reachable, and its + * actor table is one of the ones linked into this build. The two used to be + * the same question because only the stages we could run were packed, but a + * board reading the full STAGE.DIR off a card can reach all 96 -- including + * the 28 whose tables are still raw MIPS addresses. */ +static int stage_is_usable(const char *dir) +{ + int i; + + if (FS_CdGetStageFileTop((char *)dir) < 0) + { + return 0; + } + if (strcmp(dir, "init") == 0) /* the boot stage; it spawns no actors */ + { + return 1; + } + for (i = 0; Mgs_PlayableStages[i]; i++) + { + if (strcmp(dir, Mgs_PlayableStages[i]) == 0) + { + return 1; + } + } + return 0; +} + +/* Where the game starts. Set MGS_START_STAGE from the build to jump straight + * into any stage in Mgs_PlayableStages -- useful for testing somewhere with + * enemies rather than walking there from the dock every time. The list is a + * fallback chain: the first entry that is actually present wins, so a board + * with a trimmed data set still boots. */ +#ifndef MGS_START_STAGE +#define MGS_START_STAGE "s00a" +#endif + +const char *Mgs_ResolveStage(const char *dir) +{ + static const char *const first[] = {MGS_START_STAGE, "s00a", "s07a"}; + int i; + + if (strcmp(dir, "title") == 0) + { + for (i = 0; i < (int)(sizeof(first) / sizeof(first[0])); i++) + { + if (stage_is_usable(first[i])) + { + return first[i]; + } + } + return NULL; + } + + return stage_is_usable(dir) ? dir : NULL; +} +#endif + +static void Act(Work *work) +{ + work->time++; + + if (work->type != 2) + { + if (work->type == 3) + { + DG_OffsetDispEnv(work->time & 2); + GM_PadVibration2 = 100; + } + } + + if (work->reading) + { + if (!FS_LoadStageSync(work->info)) + { + work->reading = FALSE; + } + } + else + { + GV_DestroyActor(&work->actor); + } +} + +static void Die(Work *work) +{ + printf("LoadEnd\n"); + FS_LoadStageComplete(work->info); + GM_LoadComplete = -1; +} + +void *NewLoader(const char *dir) +{ + Work *work; + +#if defined(DEV_EXE) || defined(__psyz) + /* normally already done by the script command, but the game reaches here + * from a few other paths (game over, restart) that never went through it */ + { + const char *resolved = Mgs_ResolveStage(dir); + if (resolved && resolved != dir) + { + printf("[stage] '%s' -> '%s'\n", dir, resolved); + dir = resolved; + } + } +#endif + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (!work) + { + /* the game cleared GM_LoadComplete before asking and waits for the + * loader's Die to set it: report the load as over so it does not + * wait forever on an actor that never existed */ + printf("[loader] no actor memory for %s\n", dir); + GM_LoadComplete = -1; + return NULL; + } + + printf("LoadReq\n"); + work->info = FS_LoadStageRequest(dir); + + if (!work->info) + { + printf("NOT FOUND STAGE %s\n", dir); + } + + GV_SetNamedActor(&work->actor, Act, Die, "loader.c"); + + /* a request that failed has nothing to sync: the actor tears down on + * its first Act instead of handing NULL to FS_LoadStageSync */ + work->reading = work->info != NULL; + work->type = (GM_LoadRequest & 0x0f); + GM_LoadComplete = 0; + + return (void *)work; +} diff --git a/components/mgs/mgs/source/game/loader.h b/components/mgs/mgs/source/game/loader.h new file mode 100644 index 00000000..8397d1d6 --- /dev/null +++ b/components/mgs/mgs/source/game/loader.h @@ -0,0 +1,13 @@ +#ifndef __MGS_GAME_LOADER_H__ +#define __MGS_GAME_LOADER_H__ + +void *NewLoader(const char *dir); + +#ifdef __psyz +/* game/loader.c -- the stage a requested name resolves to on this board, or + * NULL when it is not packed. Both the script command that starts a stage + * change and the loader itself must agree, so they share this one answer. */ +const char *Mgs_ResolveStage(const char *dir); +#endif + +#endif // __MGS_GAME_LOADER_H__ diff --git a/components/mgs/mgs/source/game/map.c b/components/mgs/mgs/source/game/map.c new file mode 100644 index 00000000..de2ab753 --- /dev/null +++ b/components/mgs/mgs/source/game/map.c @@ -0,0 +1,501 @@ +#include "map.h" + +#include +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libhzd/libhzd.h" +#include "libgcl/libgcl.h" +#include "game.h" + +extern MAP gMapRecs_800B7910[ 16 ]; +extern DG_OBJS *StageObjs[ 32 ]; + +STATIC MAP* SECTION(".sbss") pHzdIter_800ABAA0; +STATIC int SECTION(".sbss") N_StageObjs; +STATIC int SECTION(".sbss") gMapCount_800ABAA8; +STATIC int SECTION(".sbss") gMapsChanged_800ABAAC; + +STATIC void GM_UpdateMapGroup( int preshade ) +{ + MAP *map; + int group, hzd_group; + int i, j; + DG_OBJS **objs; + LIT *lit; + + map = gMapRecs_800B7910; + + group = 0; + hzd_group = 0; + + DG_ResetFixedLight(); + + for ( i = gMapCount_800ABAA8; i > 0; map++, i-- ) + { + if ( !map->used ) + { + continue; + } + + objs = StageObjs; + group |= map->index; + + for ( j = N_StageObjs; j > 0; j-- ) + { + if ( map->index & objs[ 0 ]->group_id ) + { + lit = map->lit; + + if ( lit ) + { + DG_SetFixedLight( lit->lights, lit->n_lights ); + + if ( preshade ) + { + DG_MakePreshade( objs[ 0 ], lit->lights, lit->n_lights ); + } + } + else + { + DG_SetFixedLight( NULL, 0 ); + + if ( preshade ) + { + DG_MakePreshade( objs[ 0 ], NULL, 0 ); + } + } + } + + objs++; + } + + hzd_group |= 1 << ( map->hzd->grp - map->hzd->def->groups ); + } + + GM_PlayerMap = group; + HZD_CurrentGroup = hzd_group; + DG_SetCurrentGroup( group ); + HZD_BindMapChange( group ); +} + +STATIC MAP *GM_GetNextMap(int name) +{ + int i; + MAP *map; + + map = gMapRecs_800B7910; + for (i = gMapCount_800ABAA8; i > 0; i--) + { + map++; + } + + map->name = name; + map->used = 0; + map->index = 1 << gMapCount_800ABAA8; + + gMapCount_800ABAA8++; + return map; +} + +#define MAP_FLAG (DG_FLAG_ONEPIECE | DG_FLAG_BOUND | DG_FLAG_TRANS | DG_FLAG_PAINT | DG_FLAG_TEXT) + +STATIC void GM_LoadMapModel(int name, MAP *map) +{ + DG_DEF *def; + DG_OBJS *objs; + + def = GV_GetCache(GV_CacheID(name, 'k')); + objs = DG_MakeObjs(def, MAP_FLAG, 0); + + DG_SetPos(&DG_ZeroMatrix); + DG_PutObjs(objs); + + if (map->lit) + { + DG_MakePreshade(objs, map->lit->lights, map->lit->n_lights); + } + else + { + DG_MakePreshade(objs, NULL, 0); + } + + DG_QueueObjs(objs); + DG_GroupObjs(objs, map->index); + + StageObjs[N_StageObjs] = objs; + N_StageObjs++; +} + +STATIC HZD_HDL *GM_LoadHazard(int name, int area, int index, int dyn_walls, int dyn_floors) +{ + HZD_DEF *def; + HZD_HDL *hzd; + + def = GV_GetCache(GV_CacheID(name, 'h')); + hzd = HZD_MakeHandler(def, area, dyn_walls, dyn_floors); + hzd->map = index; + + return hzd; +} + +void GM_UpdateMap(void) +{ + if (gMapsChanged_800ABAAC) + { + GM_UpdateMapGroup(gMapsChanged_800ABAAC - 1); + gMapsChanged_800ABAAC = 0; + } +} + +void GM_ResetMapHazard(void) +{ + int count; // $s0 + MAP *pIter; // $s1 + + pIter = gMapRecs_800B7910; + for (count = gMapCount_800ABAA8; count > 0; pIter++, count--) + { + if (pIter->hzd) + { + HZD_FreeHandler(pIter->hzd); + } + } +} + +void GM_ResetMapModel(void) +{ + DG_OBJS **objs; + int i; + + objs = StageObjs; + for (i = N_StageObjs; i > 0; i--) + { + if (*objs) + { + DG_DequeueObjs(*objs); + DG_FreePreshade(*objs); + DG_FreeObjs(*objs); + } + + *objs++ = NULL; + } + + N_StageObjs = 0; +} + +void GM_ResetMap(void) +{ + DG_OBJS **objs; + int i; + + objs = StageObjs; + for (i = 32; i > 0; --i) + { + *objs++ = NULL; + } + + N_StageObjs = 0; + gMapCount_800ABAA8 = 0; + gMapsChanged_800ABAAC = 0; +} + +MAP *GM_CreateMap(void) +{ + MAP *map; + int dyn_walls; + int dyn_floors; + int name; + int area; + + map = GM_GetNextMap(GCL_GetNextInt()); + + if (GCL_GetOption('d')) // dynamic + { + dyn_walls = GCL_GetNextInt(); + dyn_floors = GCL_GetNextInt(); + } + else + { + dyn_walls = 48; + dyn_floors = 24; + } + + if (!GCL_GetOption('h')) // hzd + { + printf("no hzd\n"); + return NULL; + } + + name = GCL_GetNextInt(); + area = GCL_GetNextInt(); + map->hzd = GM_LoadHazard(name, area, map->index, dyn_walls, dyn_floors); + + if (GCL_GetOption('l')) // lit + { + map->lit = GV_GetCache(GV_CacheID(GCL_GetNextInt(), 'l')); + } + else + { + map->lit = NULL; + } + + if (GCL_GetOption('k')) // kmd + { + while (GCL_NextStr()) + { + GM_LoadMapModel(GCL_GetNextInt(), map); + } + } + + map->zone = 0; + if (GCL_GetOption('z')) // zone + { + while (GCL_NextStr()) + { + map->zone = map->zone | (1 << GCL_GetNextInt()); + } + } + + return map; +} + +STATIC void GM_SetMap(int area, int name) +{ + MAP *map; + + printf("set map %d\n", area); + + map = GM_GetNextMap(area); + map->hzd = GM_LoadHazard(name, 0, map->index, 48, 24); + map->lit = GV_GetCache(GV_CacheID(name, 'l')); + GM_LoadMapModel(name, map); +} + +MAP *GM_DefineMap(void) +{ + int area; + int name; + + while (GCL_NextStr()) + { + area = GCL_GetNextInt(); + name = GCL_GetNextInt(); + GM_SetMap(area, name); + } + + return gMapRecs_800B7910; +} + +int GM_AddMap(int name) +{ + MAP *map; + int i; + + map = gMapRecs_800B7910; + for (i = gMapCount_800ABAA8; i > 0; i--) + { + if (map->name == name) + { + printf("add map %d\n", map->index); + map->used = 1; + gMapsChanged_800ABAAC = 1; + return 1; + } + + map++; + } + + printf("addmap : not found map %d\n", name); + return 0; +} + +int GM_DelMap(int name) +{ + MAP *map; + int i; + + map = gMapRecs_800B7910; + for (i = gMapCount_800ABAA8; i > 0; i--) + { + if (map->name == name) + { + printf("del map %d\n", map->index); + map->used = 0; + gMapsChanged_800ABAAC = 1; + return 1; + } + + map++; + } + + printf("delmap: not found map %d\n", name); + return 0; +} + +int GM_ReloadMap(int preshade) +{ + MAP *map; + int i; + u_short name; + + map = gMapRecs_800B7910; + for (i = gMapCount_800ABAA8; i > 0; i--) + { + map->used = 0; + map++; + } + + while (GCL_NextStr()) + { + name = GCL_GetNextInt(); + GM_AddMap(name); + } + + gMapsChanged_800ABAAC = preshade + 1; + return 0; +} + +MAP *GM_GetMap(int id) +{ + MAP *map; + MAP *found; + int i; + + map = gMapRecs_800B7910; + found = NULL; + + for (i = gMapCount_800ABAA8; i > 0; i--) + { + if (id == 0) + { + break; + } + + if (id & 0x1) + { + found = map; + } + + id >>= 1; + map++; + } + + return found; +} + +MAP *GM_FindMap(int name) +{ + MAP *map; + int i; + + map = gMapRecs_800B7910; + for (i = gMapCount_800ABAA8; i > 0; i--, map++) + { + if (map->name == name) + { + return map; + } + } + + return NULL; +} + +int GM_MapUsed(MAP *find) +{ + MAP *map; + int i; + + map = gMapRecs_800B7910; + for (i = gMapCount_800ABAA8; i > 0; i--) + { + if (map == find && map->used) + { + return 1; + } + + map++; + } + + return 0; +} + +HZD_HDL *GM_IterHazard(HZD_HDL *cur) +{ + int i; + + if ( !cur ) + { + pHzdIter_800ABAA0 = gMapRecs_800B7910; + } + else + { + pHzdIter_800ABAA0 = pHzdIter_800ABAA0 + 1; + } + + for (i = gMapCount_800ABAA8 - (pHzdIter_800ABAA0 - gMapRecs_800B7910); i > 0; i--) + { + if (pHzdIter_800ABAA0->used) + { + return pHzdIter_800ABAA0->hzd; + } + + pHzdIter_800ABAA0++; + } + + return NULL; +} + +MAP *GM_FindMapZone(int zone) +{ + MAP *map; + int i; + + map = gMapRecs_800B7910; + for (i = gMapCount_800ABAA8; i > 0; i--) + { + if (map->zone & zone) + { + return map; + } + + map++; + } + + return NULL; +} + +// clang-format off +void GM_ReshadeObjs( DG_OBJS *obj ) +{ + MAP *map; + LIT *lit; + + map = GM_GetMap( obj->group_id ); + if( map == NULL ){ + printf( "Reshade NULL map\n" ); + } + lit = map->lit; + if( lit != NULL ){ + DG_MakePreshade( obj, lit->lights, lit->n_lights ); + } +} + +/* + すべてのマップモデルデータの Reshade ( 使っていない? ) +*/ + +void GM_ReshadeMapAll( void ) +{ + DG_OBJS **obj; + int i; + + obj = StageObjs; + for ( i = N_StageObjs; i > 0; --i ) { + GM_ReshadeObjs( *obj ); + obj++; + } +} + +/*----------------------------------------------------------------*/ +// clang-format on diff --git a/components/mgs/mgs/source/game/map.h b/components/mgs/mgs/source/game/map.h new file mode 100644 index 00000000..3be11cb5 --- /dev/null +++ b/components/mgs/mgs/source/game/map.h @@ -0,0 +1,51 @@ +#ifndef __MGS_GAME_MAP_H__ +#define __MGS_GAME_MAP_H__ + +#include +#include +#include +#include "libhzd/libhzd.h" +#include "libdg/libdg.h" + +// This is originally a u_long *, +// with (DG_LIT *)( lit + 1 ) used to access the lights +typedef struct LIT +{ + int n_lights; + DG_LIT lights[0]; +} LIT; + +typedef struct MAP +{ + int index; + u_short name; + u_short used; + HZD_HDL *hzd; + LIT *lit; + int zone; +} MAP; + +// Local to game/area.c +#define MAX_HISTORY 8 +typedef struct AreaHistory +{ + short history[MAX_HISTORY]; +} AreaHistory; + +MAP *GM_FindMap(int name); +int GM_AddMap(int name); +int GM_DelMap(int name); +MAP *GM_GetMap(int id); +MAP *GM_DefineMap(void); +int GM_ReloadMap(int preshade); +void GM_UpdateMap(void); +void GM_ResetMap(void); +HZD_HDL *GM_IterHazard(HZD_HDL *cur); +void GM_ResetMapHazard(void); +void GM_ResetMapModel(void); +MAP *GM_FindMapZone(int zone); +MAP *GM_CreateMap(void); +void GM_ReshadeObjs(DG_OBJS *objs); +void GM_ReshadeMapAll(void); + +#endif // __MGS_GAME_MAP_H__ diff --git a/components/mgs/mgs/source/game/motion.c b/components/mgs/mgs/source/game/motion.c new file mode 100644 index 00000000..0e18d615 --- /dev/null +++ b/components/mgs/mgs/source/game/motion.c @@ -0,0 +1,1234 @@ +#include "motion.h" + +#include "common.h" +#include "game.h" +#include "control.h" +#include "libgv/libgv.h" + +short word_8009D69C[15][64] = { + { 0x01EE, 0x0428, 0x056E, 0x0666, 0x072E, 0x07DA, 0x086E, 0x08F4, + 0x096C, 0x09DA, 0x0A3E, 0x0A9A, 0x0AEE, 0x0B3E, 0x0B88, 0x0BCE, + 0x0C0E, 0x0C4C, 0x0C86, 0x0CBC, 0x0CEF, 0x0D20, 0x0D4E, 0x0D78, + 0x0DA2, 0x0DC8, 0x0DEE, 0x0E10, 0x0E32, 0x0E52, 0x0E6F, 0x0E8C, + 0x0EA6, 0x0EC0, 0x0ED8, 0x0EEF, 0x0F04, 0x0F19, 0x0F2C, 0x0F3F, + 0x0F50, 0x0F60, 0x0F6F, 0x0F7E, 0x0F8B, 0x0F98, 0x0FA3, 0x0FAE, + 0x0FB8, 0x0FC2, 0x0FCA, 0x0FD2, 0x0FD9, 0x0FE0, 0x0FE6, 0x0FEB, + 0x0FEF, 0x0FF3, 0x0FF7, 0x0FFA, 0x0FFC, 0x0FFE, 0x0FFF, 0x0FFF }, + { 0x00B2, 0x0288, 0x03C6, 0x04C0, 0x0590, 0x0644, 0x06E4, 0x0774, + 0x07F6, 0x086C, 0x08DA, 0x0940, 0x09A0, 0x09F8, 0x0A4C, 0x0A9A, + 0x0AE2, 0x0B28, 0x0B6A, 0x0BA8, 0x0BE4, 0x0C1C, 0x0C52, 0x0C86, + 0x0CB6, 0x0CE4, 0x0D10, 0x0D3A, 0x0D62, 0x0D8A, 0x0DAE, 0x0DD1, + 0x0DF2, 0x0E13, 0x0E32, 0x0E4F, 0x0E6B, 0x0E86, 0x0E9F, 0x0EB8, + 0x0ECF, 0x0EE5, 0x0EFA, 0x0F0E, 0x0F22, 0x0F34, 0x0F46, 0x0F56, + 0x0F66, 0x0F75, 0x0F83, 0x0F90, 0x0F9D, 0x0FA9, 0x0FB4, 0x0FBE, + 0x0FC8, 0x0FD1, 0x0FDA, 0x0FE2, 0x0FE9, 0x0FF0, 0x0FF6, 0x0FFC }, + { 0x005C, 0x0196, 0x0298, 0x0374, 0x0434, 0x04E0, 0x057C, 0x060C, + 0x0690, 0x070A, 0x077C, 0x07E6, 0x084A, 0x08AA, 0x0902, 0x0958, + 0x09A8, 0x09F4, 0x0A3E, 0x0A84, 0x0AC6, 0x0B06, 0x0B42, 0x0B7C, + 0x0BB4, 0x0BEA, 0x0C1D, 0x0C4E, 0x0C7E, 0x0CAC, 0x0CD7, 0x0D02, + 0x0D2A, 0x0D51, 0x0D76, 0x0D9A, 0x0DBE, 0x0DDF, 0x0E00, 0x0E1E, + 0x0E3C, 0x0E59, 0x0E75, 0x0E90, 0x0EAA, 0x0EC2, 0x0EDA, 0x0EF1, + 0x0F07, 0x0F1C, 0x0F30, 0x0F44, 0x0F57, 0x0F69, 0x0F7A, 0x0F8B, + 0x0F9A, 0x0FAA, 0x0FB8, 0x0FC6, 0x0FD3, 0x0FE0, 0x0FEC, 0x0FF7 }, + { 0x0038, 0x0110, 0x01D2, 0x0284, 0x0326, 0x03BE, 0x044C, 0x04D0, + 0x054C, 0x05C2, 0x0632, 0x069C, 0x0700, 0x0760, 0x07BC, 0x0814, + 0x0868, 0x08BA, 0x0906, 0x0952, 0x099A, 0x09DE, 0x0A20, 0x0A62, + 0x0AA0, 0x0ADC, 0x0B14, 0x0B4C, 0x0B83, 0x0BB8, 0x0BEA, 0x0C1B, + 0x0C4A, 0x0C78, 0x0CA6, 0x0CD0, 0x0CFA, 0x0D23, 0x0D4A, 0x0D71, + 0x0D96, 0x0DBA, 0x0DDD, 0x0DFE, 0x0E20, 0x0E40, 0x0E5E, 0x0E7C, + 0x0E9A, 0x0EB6, 0x0ED1, 0x0EEC, 0x0F06, 0x0F1F, 0x0F37, 0x0F4E, + 0x0F65, 0x0F7C, 0x0F91, 0x0FA6, 0x0FBA, 0x0FCD, 0x0FE0, 0x0FF2 }, + { 0x0026, 0x00C0, 0x0152, 0x01DC, 0x0260, 0x02DE, 0x0356, 0x03CA, + 0x043A, 0x04A4, 0x050C, 0x056E, 0x05CE, 0x062C, 0x0684, 0x06DC, + 0x0730, 0x0780, 0x07D0, 0x081C, 0x0866, 0x08AE, 0x08F6, 0x093A, + 0x097C, 0x09BC, 0x09FC, 0x0A3A, 0x0A74, 0x0AAF, 0x0AE8, 0x0B20, + 0x0B56, 0x0B8A, 0x0BBE, 0x0BF0, 0x0C20, 0x0C50, 0x0C7F, 0x0CAC, + 0x0CD9, 0x0D04, 0x0D2F, 0x0D58, 0x0D81, 0x0DA8, 0x0DD0, 0x0DF6, + 0x0E1A, 0x0E3E, 0x0E62, 0x0E84, 0x0EA6, 0x0EC8, 0x0EE8, 0x0F08, + 0x0F26, 0x0F44, 0x0F62, 0x0F7F, 0x0F9B, 0x0FB6, 0x0FD2, 0x0FEC }, + { 0x001C, 0x008C, 0x00FA, 0x0166, 0x01CE, 0x0234, 0x0298, 0x02F8, + 0x0358, 0x03B4, 0x0410, 0x0468, 0x04BE, 0x0514, 0x0568, 0x05B8, + 0x0608, 0x0656, 0x06A4, 0x06EE, 0x0738, 0x0782, 0x07C8, 0x080E, + 0x0852, 0x0896, 0x08D8, 0x0918, 0x0958, 0x0996, 0x09D4, 0x0A10, + 0x0A4C, 0x0A86, 0x0AC0, 0x0AF8, 0x0B2F, 0x0B66, 0x0B9C, 0x0BD0, + 0x0C04, 0x0C36, 0x0C69, 0x0C9A, 0x0CCB, 0x0CFB, 0x0D2A, 0x0D58, + 0x0D86, 0x0DB3, 0x0DE0, 0x0E0A, 0x0E36, 0x0E60, 0x0E8A, 0x0EB2, + 0x0EDA, 0x0F02, 0x0F2A, 0x0F50, 0x0F76, 0x0F9C, 0x0FC0, 0x0FE4 }, + { 0x0014, 0x0068, 0x00BC, 0x0110, 0x0162, 0x01B2, 0x0204, 0x0252, + 0x02A2, 0x02F0, 0x033C, 0x038A, 0x03D4, 0x0420, 0x046A, 0x04B4, + 0x04FC, 0x0544, 0x058C, 0x05D2, 0x0618, 0x065E, 0x06A4, 0x06E8, + 0x072A, 0x076E, 0x07B0, 0x07F2, 0x0832, 0x0874, 0x08B4, 0x08F2, + 0x0932, 0x0970, 0x09AE, 0x09EC, 0x0A28, 0x0A64, 0x0AA0, 0x0ADA, + 0x0B16, 0x0B50, 0x0B8A, 0x0BC2, 0x0BFA, 0x0C34, 0x0C6A, 0x0CA2, + 0x0CD8, 0x0D10, 0x0D46, 0x0D7A, 0x0DB0, 0x0DE4, 0x0E18, 0x0E4C, + 0x0E80, 0x0EB2, 0x0EE4, 0x0F16, 0x0F48, 0x0F7A, 0x0FAA, 0x0FDB }, + { 0x000F, 0x004F, 0x008F, 0x00CF, 0x010F, 0x014F, 0x018F, 0x01CF, + 0x020F, 0x024F, 0x028F, 0x02CF, 0x030F, 0x034F, 0x038F, 0x03CF, + 0x040F, 0x044F, 0x048F, 0x04CF, 0x050F, 0x054F, 0x058F, 0x05CF, + 0x060F, 0x064F, 0x068F, 0x06CF, 0x070F, 0x074F, 0x078F, 0x07CF, + 0x080F, 0x084F, 0x088F, 0x08CF, 0x090F, 0x094F, 0x098F, 0x09CF, + 0x0A0F, 0x0A4F, 0x0A8F, 0x0ACF, 0x0B0F, 0x0B4F, 0x0B8F, 0x0BCF, + 0x0C0F, 0x0C4F, 0x0C8F, 0x0CCF, 0x0D0F, 0x0D4F, 0x0D8F, 0x0DCF, + 0x0E0F, 0x0E4F, 0x0E8F, 0x0ECF, 0x0F0F, 0x0F4F, 0x0F8F, 0x0FCF }, + { 0x000C, 0x003C, 0x006C, 0x009E, 0x00CE, 0x0100, 0x0132, 0x0166, + 0x0198, 0x01CC, 0x0200, 0x0234, 0x0269, 0x029E, 0x02D4, 0x030A, + 0x0340, 0x0378, 0x03B0, 0x03E8, 0x0420, 0x0458, 0x0492, 0x04CC, + 0x0506, 0x0540, 0x057C, 0x05B8, 0x05F4, 0x0632, 0x066F, 0x06AE, + 0x06EC, 0x072A, 0x076A, 0x07AA, 0x07EC, 0x082C, 0x086E, 0x08B2, + 0x08F4, 0x0938, 0x097E, 0x09C2, 0x0A08, 0x0A4E, 0x0A96, 0x0ADE, + 0x0B26, 0x0B70, 0x0BB8, 0x0C04, 0x0C4E, 0x0C9A, 0x0CE8, 0x0D36, + 0x0D84, 0x0DD2, 0x0E22, 0x0E74, 0x0EC6, 0x0F18, 0x0F6C, 0x0FC0 }, + { 0x0008, 0x002C, 0x0050, 0x0076, 0x009C, 0x00C2, 0x00E8, 0x0110, + 0x0138, 0x0160, 0x018A, 0x01B4, 0x01DE, 0x020A, 0x0235, 0x0262, + 0x028F, 0x02BD, 0x02EC, 0x031C, 0x034C, 0x037C, 0x03AE, 0x03E0, + 0x0414, 0x0448, 0x047E, 0x04B4, 0x04EA, 0x0522, 0x055C, 0x0596, + 0x05D0, 0x060C, 0x0648, 0x0686, 0x06C6, 0x0706, 0x0748, 0x078A, + 0x07CE, 0x0812, 0x085A, 0x08A0, 0x08EA, 0x0934, 0x0980, 0x09CE, + 0x0A1E, 0x0A6E, 0x0AC0, 0x0B14, 0x0B6A, 0x0BC2, 0x0C1C, 0x0C78, + 0x0CD6, 0x0D36, 0x0D98, 0x0DFC, 0x0E64, 0x0ECE, 0x0F3A, 0x0FAA }, + { 0x0006, 0x0020, 0x003A, 0x0056, 0x0072, 0x008E, 0x00AB, 0x00C9, + 0x00E8, 0x0107, 0x0127, 0x0148, 0x0169, 0x018B, 0x01AE, 0x01D2, + 0x01F6, 0x021C, 0x0242, 0x026A, 0x0292, 0x02BA, 0x02E4, 0x0310, + 0x033C, 0x0368, 0x0396, 0x03C6, 0x03F6, 0x0428, 0x045A, 0x048E, + 0x04C4, 0x04FA, 0x0532, 0x056C, 0x05A6, 0x05E4, 0x0622, 0x0662, + 0x06A2, 0x06E6, 0x072C, 0x0772, 0x07BC, 0x0808, 0x0856, 0x08A6, + 0x08F8, 0x094E, 0x09A6, 0x0A00, 0x0A5E, 0x0AC0, 0x0B26, 0x0B8E, + 0x0BFC, 0x0C6C, 0x0CE4, 0x0D5E, 0x0DE0, 0x0E66, 0x0EF4, 0x0F8A }, + { 0x0004, 0x0016, 0x0028, 0x003B, 0x004F, 0x0064, 0x0078, 0x008E, + 0x00A4, 0x00BC, 0x00D4, 0x00EC, 0x0106, 0x0120, 0x013B, 0x0157, + 0x0174, 0x0191, 0x01B0, 0x01CF, 0x01EF, 0x0210, 0x0233, 0x0256, + 0x027B, 0x02A0, 0x02C8, 0x02F0, 0x0318, 0x0344, 0x036F, 0x039C, + 0x03CC, 0x03FC, 0x042E, 0x0460, 0x0496, 0x04CD, 0x0506, 0x0540, + 0x057E, 0x05BC, 0x05FE, 0x0642, 0x0688, 0x06D2, 0x071E, 0x076C, + 0x07C0, 0x0816, 0x086E, 0x08CC, 0x0930, 0x0996, 0x0A04, 0x0A76, + 0x0AEE, 0x0B70, 0x0BF8, 0x0C8A, 0x0D28, 0x0DD2, 0x0E8A, 0x0F56 }, + { 0x0002, 0x000D, 0x0019, 0x0025, 0x0032, 0x0040, 0x004E, 0x005C, + 0x006C, 0x007C, 0x008D, 0x009F, 0x00B1, 0x00C4, 0x00D8, 0x00ED, + 0x0102, 0x0119, 0x0130, 0x0148, 0x0162, 0x017C, 0x0198, 0x01B4, + 0x01D1, 0x01F0, 0x020F, 0x0230, 0x0252, 0x0276, 0x029A, 0x02C1, + 0x02E8, 0x0312, 0x033D, 0x036A, 0x0398, 0x03C8, 0x03FA, 0x042F, + 0x0466, 0x049E, 0x04DA, 0x0518, 0x055A, 0x059E, 0x05E4, 0x062E, + 0x067E, 0x06D0, 0x0728, 0x0784, 0x07E4, 0x084C, 0x08BA, 0x0930, + 0x09B0, 0x0A38, 0x0ACE, 0x0B72, 0x0C28, 0x0CF4, 0x0DE2, 0x0EFC }, + { 0x0001, 0x0006, 0x000C, 0x0012, 0x0019, 0x0021, 0x0029, 0x0032, + 0x003C, 0x0046, 0x0050, 0x005C, 0x0068, 0x0075, 0x0083, 0x0092, + 0x00A1, 0x00B1, 0x00C2, 0x00D4, 0x00E6, 0x00FA, 0x010F, 0x0124, + 0x013B, 0x0153, 0x016C, 0x0186, 0x01A2, 0x01BE, 0x01DC, 0x01FC, + 0x021C, 0x023E, 0x0262, 0x0288, 0x02B0, 0x02D8, 0x0304, 0x0331, + 0x0360, 0x0392, 0x03C6, 0x03FE, 0x0438, 0x0474, 0x04B4, 0x04F8, + 0x0540, 0x058C, 0x05DC, 0x0632, 0x068E, 0x06F0, 0x075A, 0x07CC, + 0x0848, 0x08D0, 0x0968, 0x0A10, 0x0AD2, 0x0BB4, 0x0CCC, 0x0E42 }, + { 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0007, 0x000A, 0x000D, + 0x0012, 0x0017, 0x001C, 0x0022, 0x0029, 0x0031, 0x0039, 0x0042, + 0x004C, 0x0056, 0x0061, 0x006D, 0x007A, 0x0088, 0x0097, 0x00A6, + 0x00B7, 0x00C9, 0x00DC, 0x00F0, 0x0105, 0x011B, 0x0132, 0x014B, + 0x0166, 0x0181, 0x019E, 0x01BC, 0x01DD, 0x01FF, 0x0223, 0x0249, + 0x0271, 0x029B, 0x02C8, 0x02F6, 0x0328, 0x035D, 0x0394, 0x03D0, + 0x0410, 0x0452, 0x049A, 0x04E6, 0x0538, 0x0592, 0x05F2, 0x065A, + 0x06CC, 0x074A, 0x07D8, 0x0878, 0x0930, 0x0A0C, 0x0B24, 0x0CB8 } +}; + +int dword_8009DE1C[] = +{ + 65536 / 1, + 65536 / 1, + 65536 / 2, + 65536 / 3, + 65536 / 4, + 65536 / 5, + 65536 / 6, + 65536 / 7, + 65536 / 8, + 65536 / 9, + 65536 / 10, + 65536 / 11, + 65536 / 12, + 65536 / 13, + 65536 / 14, + 65536 / 15, +}; + +short dword_8009DE5C[] = +{ + 4096 / 1, + 4096 / 2, + 4096 / 3, + 4096 / 4, + 4096 / 5, + 4096 / 6, + 4096 / 7, + 4096 / 8, + 4096 / 9, + 4096 / 10, + 4096 / 11, + 4096 / 12, + 4096 / 13, + 4096 / 14, + 4096 / 15, + 4096 / 16, +}; + +MATRIX matrix_8009DE7C = {{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, {0, 0, 0}}; +MATRIX matrix_8009DE9C = {{{0, 0, 0}, {0, 0, 0}, {0, 0, 0}}, {0, 0, 0}}; + +STATIC void InterpolateJoint(MOTION_SEGMENT *m_seg, SVECTOR *out); +STATIC void ApplyJointRotation(MOTION_CONTROL *m_ctrl, MOTION_INFO *m_info); +STATIC int BeginAction(MOTION_CONTROL *m_ctrl, MOTION_INFO *m_info, int action); +STATIC int BeginActionOffset(MOTION_CONTROL *m_ctrl, MOTION_INFO *m_info, int action, int frame); +STATIC void SkipToKeyframe(MOTION_SEGMENT *m_seg, int frame); +STATIC int CorrectWaistRotation(SVECTOR *arg0, SVECTOR *arg1); +STATIC int NextKeyframe(MOTION_CONTROL *m_ctrl, MOTION_INFO *m_info); +STATIC void InflateKeyframe(MOTION_SEGMENT *m_seg); + +// maybe static +void ExtendVector(SVECTOR *vec) +{ + vec->vx = FP_Extend(vec->vx); + vec->vy = FP_Extend(vec->vy); + vec->vz = FP_Extend(vec->vz); +} + +int GM_ConfigMotionControl(OBJECT *object, MOTION_CONTROL *m_ctrl, int name, MOTION_SEGMENT *m_segs1, MOTION_SEGMENT *m_segs2, CONTROL *control, SVECTOR *rots) +{ + m_ctrl->oar = GV_GetCache(GV_CacheID(name, 'o')); + m_ctrl->height = &object->height; + + m_ctrl->info1.frames_left = 0; + m_ctrl->info1.frame = 0; + m_ctrl->info1.field_4 = 0; + m_ctrl->info1.mask = 0; + m_ctrl->info1.m_segs = m_segs1; + m_ctrl->info1.action = 0; + m_ctrl->info1.field_12 = 0; + m_ctrl->info1.field_14 = 0; + m_ctrl->info1.time = 0; + + m_ctrl->info2.frames_left = 0; + m_ctrl->info2.frame = 0; + m_ctrl->info2.field_4 = 0; + m_ctrl->info2.mask = 0xffffffff; + m_ctrl->info2.action = 0; + m_ctrl->info2.field_12 = 0; + m_ctrl->info2.field_14 = 0; + m_ctrl->info2.time = 0; + + m_ctrl->interp = 0; + m_ctrl->rots = rots; + m_ctrl->rot = &control->rot; + m_ctrl->step = &control->step; + m_ctrl->info2.m_segs = m_segs2; + + object->objs->rots = rots; + object->objs->waist_rot = &m_ctrl->waist_rot; + object->m_ctrl = m_ctrl; + + return 0; +} + +int GM_ConfigMotionAdjust(OBJECT *object, SVECTOR *adjust) +{ + object->objs->adjust = adjust; + return 0; +} + +void GM_ConfigAction(MOTION_CONTROL *m_ctrl, int action, int frame) +{ + MATRIX saved; + MOTION_INFO *m_info; + + ReadRotMatrix(&saved); + + m_info = &m_ctrl->info1; + if (m_info->frames_left != 0) + { + ApplyJointRotation(m_ctrl, m_info); + } + + if (frame == 0) + { + BeginAction(m_ctrl, m_info, action); + } + else + { + BeginActionOffset(m_ctrl, m_info, action, frame); + } + + if (m_info->frames_left == 1) + { + m_info->field_14 = 2; + m_info->action = action; + } + else + { + m_info->field_14 = 1; + m_info->action = action; + } + + SetRotMatrix(&saved); +} + +void GM_ConfigActionOverride(MOTION_CONTROL *m_ctrl, int action, int frame) +{ + MATRIX saved; + MOTION_INFO *m_info; + + ReadRotMatrix(&saved); + + m_info = &m_ctrl->info2; + if (m_info->frames_left != 0) + { + ApplyJointRotation(m_ctrl, m_info); + } + + if (frame == 0) + { + BeginAction(m_ctrl, m_info, action); + } + else + { + BeginActionOffset(m_ctrl, m_info, action, frame); + } + + if (m_info->frames_left == 1) + { + m_info->field_14 = 2; + m_info->action = action; + } + else + { + m_info->field_14 = 1; + m_info->action = action; + } + + SetRotMatrix(&saved); +} + +STATIC int BeginAction( MOTION_CONTROL *m_ctrl, MOTION_INFO *m_info, int action ) +{ + char unused[16]; + MOTION_SEGMENT *m_seg; + int n_joint; + int i; + MOTION_TABLE *table; + int n_frame; + int size; + MOTION_ARCHIVE *archive; + unsigned int temp1, temp2; + unsigned short vec_size; + char channel_size; + SVECTOR* svec; + + n_joint = m_ctrl->oar->n_joint; + size = n_joint + 2; + m_seg = m_info->m_segs; + + table = &m_ctrl->oar->table[ size * action ]; + + n_frame = table[ 0 ]; + table++; //progresses it to the start of the archive offsets + + m_info->frame = 0; + m_info->field_4 = 0; + m_info->frames_left = n_frame; + + archive = &m_ctrl->oar->archive[ table[ 0 ] ]; //start of bitstream vectors + table++; //progresses to the next offset + + m_seg->base.vy = archive[ 0 ]; + archive++; //progress to next vector + + temp1 = archive[ 0 ] + ( archive [ 1 ] << 16 ); + + archive++; //progress to next vector + + m_seg->bit_offset = 0; + m_seg->stream = archive; + + temp2 = temp1 & 0xF; + temp1 &= 0xFFFF; + temp1 >>= 4; + + m_seg->delta.vx = temp2; + + temp2 = temp1 & 0xF; + temp1 >>= 4; + temp1 &= 0xF; + + m_seg->delta.vy = temp2; + m_seg->delta.vz = temp1; + + m_seg++; + + for ( i = 0; i < n_joint; i++, m_seg++, table++ ) + { + archive = &m_ctrl->oar->archive[ table[ 0 ] ]; + + vec_size = archive [ 0 ]; + + m_seg->stream = archive; + + vec_size &= 0xFFF; + + m_seg->bit_offset = 12; + + channel_size = vec_size & 0xF; + vec_size >>= 4; + m_seg->x_size = channel_size; + + channel_size = vec_size & 0xF; + vec_size >>= 4; + m_seg->y_size = channel_size; + + channel_size = vec_size & 0xF; + vec_size >>= 4; + m_seg->z_size = channel_size; + + InflateKeyframe( m_seg ); + + if ( i == 0 ) + { + CorrectWaistRotation( &m_seg->base, &m_seg->delta ); + } + else + { + svec = &m_seg->delta; + svec->vx = FP_Subtract_2( svec->vx, m_seg->base.vx ); + svec->vy = FP_Subtract_2( svec->vy, m_seg->base.vy ); + svec->vz = FP_Subtract_2( svec->vz, m_seg->base.vz ); + } + } + + return 0; +} + +STATIC void InflateKeyframe(MOTION_SEGMENT *m_seg) +{ + unsigned int val, val2; + unsigned int shiftLeft, field_1C; + char shiftRight, nextShiftRight; + unsigned short *field_14; + + shiftRight = m_seg->bit_offset; + field_14 = m_seg->stream; + + nextShiftRight = shiftRight + 8; + val = (unsigned int)(field_14[0] + (field_14[1] << 16)) >> shiftRight; + field_1C = val & 0xFF; + shiftRight = nextShiftRight; + if (nextShiftRight & 0x10) + { + field_14++; + shiftRight = nextShiftRight & 0xF; + } + val2 = val & 0xF; + field_1C >>= 4; + m_seg->field_1A = val2; + m_seg->field_1C = field_1C & 0xF; + m_seg->field_18 = val2 - 1; + + shiftLeft = m_seg->x_size; + m_seg->interp = dword_8009DE1C[m_seg->field_1A]; + val = (unsigned int)(field_14[0] + (field_14[1] << 16)) >> shiftRight & ((1 << shiftLeft) - 1); + if (val & 1 << (shiftLeft - 1)) + { + val |= ~((1 << shiftLeft) - 1); + } + m_seg->delta.vx = val; + nextShiftRight = shiftRight + shiftLeft; + shiftRight = nextShiftRight; + if (nextShiftRight & 0x10) + { + field_14++; + shiftRight = nextShiftRight & 0xF; + } + + shiftLeft = m_seg->y_size; + val = (unsigned int)(field_14[0] + (field_14[1] << 16)) >> shiftRight & ((1 << shiftLeft) - 1); + if (val & 1 << (shiftLeft - 1)) + { + val |= ~((1 << shiftLeft) - 1); + } + m_seg->delta.vy = val; + nextShiftRight = shiftRight + shiftLeft; + shiftRight = nextShiftRight; + if (nextShiftRight & 0x10) + { + field_14++; + shiftRight = nextShiftRight & 0xF; + } + + shiftLeft = m_seg->z_size; + val = (unsigned int)(field_14[0] + (field_14[1] << 16)) >> shiftRight & ((1 << shiftLeft) - 1); + if (val & 1 << (shiftLeft - 1)) + { + val |= ~((1 << shiftLeft) - 1); + } + m_seg->delta.vz = val; + nextShiftRight = shiftRight + shiftLeft; + shiftRight = nextShiftRight; + if (nextShiftRight & 0x10) + { + field_14++; + shiftRight = nextShiftRight & 0xF; + } + m_seg->bit_offset = shiftRight; + m_seg->stream = field_14; +} + +int GM_PlayAction(MOTION_CONTROL *m_ctrl) +{ + MATRIX mtx; + MOTION_INFO *m_info; + int time; + + ReadRotMatrix(&mtx); + + m_ctrl->step->vx = 0; + m_ctrl->step->vz = 0; + + m_info = &m_ctrl->info1; + if (m_info->field_14 != 0) + { + time = NextKeyframe(m_ctrl, m_info); + + if (time == -1) + { + if (m_info->field_14 & 1) + { + BeginAction(m_ctrl, m_info, m_info->action); + } + + if (m_info->field_14 & 2) + { + if (m_ctrl->interp != 0) + { + BeginAction(m_ctrl, m_info, m_info->action); + } + else + { + m_info->field_14 = 0; + } + } + + time = 0; + } + + m_info->time = time; + } + + m_info = &m_ctrl->info2; + if (m_info->field_14 != 0) + { + time = NextKeyframe(m_ctrl, m_info); + + if (time == -1) + { + if (m_info->field_14 & 1) + { + BeginAction(m_ctrl, m_info, m_info->action); + } + + if (m_info->field_14 & 2) + { + if (m_ctrl->interp != 0) + { + BeginAction(m_ctrl, m_info, m_info->action); + } + else + { + m_info->field_14 = 0; + + if (m_ctrl->info1.field_14 == 0) + { + m_ctrl->info1.field_14 = 2; + } + } + } + + time = 0; + } + + m_info->time = time; + } + + SetRotMatrix(&mtx); + return time; +} + +//TODO: move below elsewhere +#define Shift_Op(v, a, b) \ +{ \ + v = a; \ + if ( a & 0x10 ) \ + { \ + b++; \ + v = a & 0xF; \ + } \ +} + +#define Mask_Op(sv, v, y, s) \ +{ \ + v >>= s; \ + \ + \ + v &= ((1 << y) - 1); \ + \ + if (v & (1 << (y - 1))) \ + { \ + v |= ~((1 << y) - 1); \ + } \ + \ + sv = v; \ +} +// TODO: better name needed +#define FP_Subtract2(v, a, b) \ +{ \ + (v) = (a) - (b); \ + \ + if ((v) >= 0) \ + { \ + (v) &= 0xFFF; \ + \ + if ((v) >= 2048) \ + { \ + (v) -= 4096; \ + } \ + } \ + else \ + { \ + (v) |= 0xF000; \ + \ + if ((v) < -2048) \ + { \ + (v) += 4096; \ + } \ + } \ + \ + (a) = (v); \ +} + +static inline void FP_Subtract3( SVECTOR *svec, SVECTOR *svec2 ) +{ + int value; + FP_Subtract2(value, svec2->vx, svec->vx); + FP_Subtract2(value, svec2->vy, svec->vy); + FP_Subtract2(value, svec2->vz, svec->vz); +} + +static inline unsigned int extract_archive( MOTION_ARCHIVE *archive ) +{ + return archive[0] + (archive[1] << 16); +} + +STATIC int NextKeyframe(MOTION_CONTROL *m_ctrl, MOTION_INFO *m_info) +{ + MATRIX rotation; + SVECTOR vec; + + MOTION_SEGMENT *m_seg; + int record_size; + int ret; + int delta; + + SVECTOR *rots; + + unsigned int vx, vy, vz; + + MOTION_ARCHIVE *archive; + + int i; + + int interp; + + int flags; + unsigned char shift; + + SVECTOR *b; + + int res; + int t0; + char shift_temp; + + m_seg = m_info->m_segs; + record_size = m_ctrl->oar->n_joint; + + ret = 0; + + if (m_info->frames_left == 0) + { + return -1; + } + + m_info->frame++; + + delta = dword_8009DE5C[m_ctrl->interp]; + matrix_8009DE7C.m[0][0] = delta; + matrix_8009DE7C.m[1][1] = delta; + matrix_8009DE7C.m[2][2] = delta; + + gte_SetLightMatrix(&matrix_8009DE7C); + + if (!(m_info->mask & 0x1)) + { + if (m_info->field_4 == 0) + { + shift = m_seg->bit_offset; + archive = m_seg->stream; + + RotMatrix(m_ctrl->rot, &rotation); + gte_SetRotMatrix(&rotation); + + // vx + vx = extract_archive(archive); + Mask_Op(res, vx, m_seg->delta.vx, shift); + + shift_temp = shift + m_seg->delta.vx; + Shift_Op(shift, shift_temp, archive); + + vec.vx = res; + + // vy + vy = extract_archive(archive); + Mask_Op(res, vy, m_seg->delta.vy, shift); + + shift_temp = shift + m_seg->delta.vy; + Shift_Op(shift, shift_temp, archive); + + t0 = res + m_seg->base.vy; + vec.vy = 0; + + // vz + vz = extract_archive(archive); + Mask_Op(res, vz, m_seg->delta.vz, shift); + + shift_temp = shift + m_seg->delta.vz; + Shift_Op(shift, shift_temp, archive); + + vec.vz = res; + + gte_ldv0(&vec); + gte_rtv0(); + res = m_ctrl->step->vy; + gte_stsv(m_ctrl->step); + m_ctrl->step->vy = res; + + m_seg->bit_offset = shift; + m_seg->stream = archive; + + interp = (t0 - *m_ctrl->height) * delta; + *m_ctrl->height += interp / 4096; + } + else if (m_info->field_4 == 1) + { + m_ctrl->step->vx = 0; + m_ctrl->step->vz = 0; + } + } + + if (m_info->frames_left == 1) + { + ret = -1; + } + + flags = m_info->mask; + rots = m_ctrl->rots; + + m_seg++; + + if (!(flags & 0x2)) + { + + b = &m_ctrl->waist_rot; + + InterpolateJoint(m_seg, (SVECTOR *)&rotation.m[0][0]); + + rotation.m[0][0] = (rotation.m[0][0] + m_seg->base.vx) & 0xFFF; + rotation.m[0][1] = (rotation.m[0][1] + m_seg->base.vy) & 0xFFF; + rotation.m[0][2] = (rotation.m[0][2] + m_seg->base.vz) & 0xFFF; + + FP_Subtract3(b, (SVECTOR*)&rotation.m[0][0]); + + gte_ldv0(&rotation); + gte_llv0(); + gte_stsv(&rotation); + + b->vx = (b->vx + rotation.m[0][0]) & 0xFFF; + b->vy = (b->vy + rotation.m[0][1]) & 0xFFF; + b->vz = (b->vz + rotation.m[0][2]) & 0xFFF; + } + + for (i = 0; i < record_size; i++, m_seg++, rots++, flags >>= 1) + { + InterpolateJoint(m_seg, (SVECTOR *)&rotation.m[1][1]); + + rotation.m[1][1] = (rotation.m[1][1] + m_seg->base.vx) & 0xFFF; + rotation.m[1][2] = (rotation.m[1][2] + m_seg->base.vy) & 0xFFF; + rotation.m[2][0] = (rotation.m[2][0] + m_seg->base.vz) & 0xFFF; + + if (--m_seg->field_18 < 0) + { + m_seg->base = *(SVECTOR *)&rotation.m[1][1]; + + if (ret == 0) + { + InflateKeyframe(m_seg); + FP_Subtract3(&m_seg->base, (SVECTOR*)&m_seg->delta); + } + } + + if (!(flags & 0x1)) + { + if (i == 0) + { + CorrectWaistRotation(rots, (SVECTOR *)&rotation.m[1][1]); + } + else + { + FP_Subtract3(rots, (SVECTOR*)&rotation.m[1][1]); + } + + gte_ldv0(&rotation.m[1][1]); + gte_llv0(); + gte_stsv(&rotation.m[1][1]); + + rots->vx = (rots->vx + rotation.m[1][1]) & 0xFFF; + rots->vy = (rots->vy + rotation.m[1][2]) & 0xFFF; + rots->vz = (rots->vz + rotation.m[2][0]) & 0xFFF; + } + } + + if (--m_info->frames_left == 1) + { + ret = 1; + } + + return ret; +} + +STATIC void InterpolateJoint(MOTION_SEGMENT *m_seg, SVECTOR *out) +{ + int interp; + + interp = m_seg->interp * (m_seg->field_1A - m_seg->field_18); + interp >>= 10; + + if (interp != 64) + { + interp = word_8009D69C[m_seg->field_1C][interp]; + } + else + { + interp = 4096; + } + + matrix_8009DE9C.m[0][0] = interp; + matrix_8009DE9C.m[1][1] = interp; + matrix_8009DE9C.m[2][2] = interp; + + gte_SetRotMatrix(&matrix_8009DE9C); + gte_ldv0(&m_seg->delta); + gte_rtv0(); + gte_stsv(out); +} + +// maybe static +void GM_FixMotion_80035FFC(MOTION_CONTROL *m_ctrl) +{ + ApplyJointRotation(m_ctrl, &m_ctrl->info1); +} + +// maybe static +void GM_FixMotion2_8003601C(MOTION_CONTROL *m_ctrl) +{ + ApplyJointRotation(m_ctrl, &m_ctrl->info2); +} + +STATIC void ApplyJointRotation(MOTION_CONTROL *m_ctrl, MOTION_INFO *m_info) +{ + SVECTOR delta; + MOTION_SEGMENT *m_seg; + int n_joints; + int i; + + m_seg = m_info->m_segs; + m_seg++; + + n_joints = m_ctrl->oar->n_joint; + + m_info->frames_left = 0; + m_info->field_4 = 1; + + for (i = 0; i < n_joints; i++, m_seg++) + { + InterpolateJoint(m_seg, &delta); + m_seg->base.vx += delta.vx; + m_seg->base.vy += delta.vy; + m_seg->base.vz += delta.vz; + } +} + +STATIC int BeginActionOffset(MOTION_CONTROL *m_ctrl, MOTION_INFO *m_info, int action, int frame) +{ + char unused[8]; + int i; + MOTION_SEGMENT *m_seg; + int numRecords; + unsigned int v0, v1; + MOTION_TABLE *table; + MOTION_ARCHIVE *archive; + unsigned int temp3, temp6; + unsigned int temp4; + int temp5; + unsigned short test; + + unsigned int a0, a1; + + numRecords = m_ctrl->oar->n_joint; + m_seg = m_info->m_segs; + + table = &m_ctrl->oar->table[(numRecords + 2) * action]; + + m_info->frames_left = table[0]; //number of frames + m_info->frame = 0; + + temp5 = m_info->frames_left; + test = m_info->frames_left; + + table++; + + if (frame >= temp5) + { + frame = temp5 - 1; + } + + m_info->frames_left = test - frame; + m_info->field_4 = 0; + + archive = &m_ctrl->oar->archive[table[0]]; + m_seg->base.vy = archive[0]; + archive++; + + v1 = archive[0] + (archive[1] << 16); + a1 = v1 & 0xF; + + v1 &= 0xFFFF; + v1 >>= 4; + v0 = v1 & 0xF; + + a0 = a1 + v0; + v1 >>= 4; + v1 &= 0xF; + + a0 = a0 + v1; + a0 = a0 & 0xFFFF; + a0 = a0 * frame; + + table++; + archive++; + + m_seg->delta.vx = a1; + m_seg->delta.vy = v0; + m_seg->delta.vz = v1; + + archive = &archive[(a0 / 16) & 0xF ]; + m_seg->bit_offset = a0 & 0xF; + m_seg->stream = archive; + m_seg++; + + for (i = 0; i < numRecords; i++, m_seg++, table++) + { + archive = &m_ctrl->oar->archive[table[0]]; + + temp4 = (archive[0] + (archive[1] << 16)) & 0xFFF; + temp6 = temp4; + + temp3 = temp4; + temp4 = temp4 >> 4; + temp6 = temp6 >> 8; + + m_seg->stream = archive; + + m_seg->bit_offset = 12; + m_seg->x_size = temp3 & 0xF; + m_seg->y_size = temp4 & 0xF; + m_seg->z_size = temp6 & 0xF; + + m_seg->field_18 = -1; + + SkipToKeyframe(m_seg, frame); + FP_Subtract3(&m_seg->base, &m_seg->delta); + } + + return 0; +} + +STATIC void SkipToKeyframe( MOTION_SEGMENT *m_seg, int frame ) +{ + MOTION_ARCHIVE *archive; + MOTION_ARCHIVE *archive2; + char shift; + char shift2; + char shift_temp; + int t5; + char x, y, z; + int vec_size; + unsigned int a2; + unsigned int vx, vy, vz; + unsigned int vx2, vy2, vz2; + unsigned int b; + int temp2; + int temp; + + shift = m_seg->bit_offset; + archive = m_seg->stream; + t5 = 0; + + if ((m_seg->field_18 + 1) > frame) + { + m_seg->field_18 -= frame; + return; + } + + frame -= m_seg->field_18 + 1; + + x = m_seg->x_size; + y = m_seg->y_size; + z = m_seg->z_size; + vec_size = x + y + z; + + for (;;) + { + temp = (extract_archive(archive) >> shift); + a2 = temp & 0xFF; + temp2 = temp & 0xF; + + if (temp2 > frame) + { + z = shift + 8; + break; + } + + frame -= temp2; + + shift2 = shift; + archive2 = archive; + t5 = 1; + + shift_temp = (shift2 + 8) + vec_size; + archive = &archive2[shift_temp / 16]; + shift = shift_temp & 0xF; + } + + Shift_Op(shift, z, archive); + b = (a2 >> 4); + b = b & 0xF; + + m_seg->field_1A = temp2; + do {} while(0); + m_seg->field_1C = b; + + + a2 = frame; + + m_seg->field_18 = (m_seg->field_1A - a2) - 1; + m_seg->interp = dword_8009DE1C[m_seg->field_1A]; + + vec_size = m_seg->x_size; + vx = extract_archive(archive); + Mask_Op(m_seg->delta.vx, vx, vec_size, shift); + + z = shift + vec_size; + Shift_Op(shift, z, archive); + + vec_size = m_seg->y_size; + vy = extract_archive(archive); + Mask_Op(m_seg->delta.vy, vy, vec_size, shift); + + z = shift + vec_size; + Shift_Op(shift, z, archive); + + vec_size = m_seg->z_size; + vz = extract_archive(archive); + Mask_Op(m_seg->delta.vz, vz, vec_size, shift); + + z = shift + vec_size; + Shift_Op(shift, z, archive); + + m_seg->bit_offset = shift; + m_seg->stream = archive; + if (t5) + { + archive2 = &archive2[shift2 / 16]; + + z = (shift2 & 0xF) + 8; + Shift_Op(shift2, z, archive2); + + vec_size = m_seg->x_size; + vx2 = extract_archive(archive2); + Mask_Op(m_seg->delta.vx, vx2, vec_size, shift2); + + z = shift2 + vec_size; + Shift_Op(shift2, z, archive2); + + vec_size = m_seg->y_size; + vy2 = extract_archive(archive2); + Mask_Op(m_seg->delta.vy, vy2, vec_size, shift2); + + z = shift2 + vec_size; + Shift_Op(shift2, z, archive2); + + vec_size = m_seg->z_size; + vz2 = extract_archive(archive2); + Mask_Op(m_seg->delta.vz, vz2, vec_size, shift2); + } +} + +STATIC int CorrectWaistRotation(SVECTOR *arg0, SVECTOR *arg1) +{ + SVECTOR vec1; + SVECTOR vec2; + SVECTOR *vec1Ptr; + int var_a2; // side? + int diff; + + vec1 = *arg0; + vec2 = *arg1; + + var_a2 = 0; + + if (arg0->vy > 0) + { + vec1.vy = (2048 - arg0->vy) & 0xFFF; + } + else + { + vec1.vy = (-2048 - arg0->vy) & 0xFFF; + } + + if (arg0->vx > 0) + { + vec1.vx = (arg0->vx - 2048) & 0xFFF; + } + else + { + vec1.vx = (arg0->vx + 2048) & 0xFFF; + } + + if (arg0->vz > 0) + { + vec1.vz = (arg0->vz - 2048) & 0xFFF; + } + else + { + vec1.vz = (arg0->vz + 2048) & 0xFFF; + } + + // TODO: for some reason using FP_Extend2, FP_ExtendN inlines doesn't work here? + diff = arg1->vx - arg0->vx; + if (diff >= 0) + { + diff &= 0xfff; + if (diff >= 2048) + { + diff -= 4096; + } + arg1->vx = diff; + } + else + { + diff |= 0xf000; + if (diff < -2048) + { + diff += 4096; + } + arg1->vx = diff; + } + + diff = arg1->vy - arg0->vy; + if (diff >= 0) + { + arg1->vy = FP_Extend2(diff); + } + else + { + arg1->vy = FP_ExtendN(diff); + } + + diff = arg1->vz - arg0->vz; + if (diff >= 0) + { + arg1->vz = FP_Extend2(diff); + } + else + { + arg1->vz = FP_ExtendN(diff); + } + + vec1Ptr = &vec1; + + diff = vec2.vx - vec1Ptr->vx; + if (diff >= 0) + { + vec2.vx = FP_Extend2(diff); + } + else + { + vec2.vx = FP_ExtendN(diff); + } + + diff = vec2.vy - vec1Ptr->vy; + if (diff >= 0) + { + vec2.vy = FP_Extend2(diff); + } + else + { + vec2.vy = FP_ExtendN(diff); + } + + diff = vec2.vz - vec1Ptr->vz; + if (diff >= 0) + { + vec2.vz = FP_Extend2(diff); + } + else + { + vec2.vz = FP_ExtendN(diff); + } + + if (abs(arg1->vx) <= abs(vec2.vx)) + { + var_a2++; + } + else + { + var_a2--; + } + + if (abs(arg1->vy) <= abs(vec2.vy)) + { + var_a2++; + } + else + { + var_a2--; + } + + if (abs(arg1->vz) <= abs(vec2.vz)) + { + var_a2++; + } + else + { + var_a2--; + } + + if (var_a2 < 0) + { + *arg0 = vec1; + *arg1 = vec2; + return -1; + } + + return 0; +} diff --git a/components/mgs/mgs/source/game/motion.h b/components/mgs/mgs/source/game/motion.h new file mode 100644 index 00000000..de5d9d6e --- /dev/null +++ b/components/mgs/mgs/source/game/motion.h @@ -0,0 +1,46 @@ +#ifndef __MGS_GAME_MOTION_H__ +#define __MGS_GAME_MOTION_H__ + +#include +#include +#include + +#include "fmt_mot.h" +#include "libdg/libdg.h" + +typedef struct MOTION_INFO +{ + short frames_left; + // Controls the sound of Snake's footsteps; values, ranging from 0x0-0x11 (with intermediary jumps to 0x80 and + // 0xff), appear to be the current frame of the sound sample (with frame 0x8 corresponding to the hard footstep + // sound). Disabling any of the reads or writes causes Snake's footsteps no longer to make any noise, such that he + // can for instance walk in puddles without being heard by enemies. + short frame; + short field_4; + short field_6; + u_long mask; + MOTION_SEGMENT *m_segs; + short action; + short field_12; + short field_14; // 0 = init frame, middle frames = 1, 2 = last frame + u_short time; +} MOTION_INFO; + +typedef struct _MOTION_CONTROL +{ + DG_OAR *oar; + MOTION_INFO info1; + MOTION_INFO info2; + SVECTOR *rot; // pointer to the associated CONTROL's rot vector. + SVECTOR *step; // pointer to the associated CONTROL's step vector. + short *height; // pointer to the associated OBJECT's height. + u_long interp; + SVECTOR waist_rot; + SVECTOR *rots; // pointer to the associated OBJECT's per-joint rots. +} MOTION_CONTROL; + +void GM_ConfigAction(MOTION_CONTROL *m_ctrl, int action, int frame); +void GM_ConfigActionOverride(MOTION_CONTROL *m_ctrl, int action, int frame); +int GM_PlayAction(MOTION_CONTROL *m_ctrl); + +#endif // __MGS_GAME_MOTION_H__ diff --git a/components/mgs/mgs/source/game/movie.c b/components/mgs/mgs/source/game/movie.c new file mode 100644 index 00000000..5ad3bf58 --- /dev/null +++ b/components/mgs/mgs/source/game/movie.c @@ -0,0 +1,478 @@ +#include "movie.h" + +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libfs/libfs.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "linkvar.h" +#include "sound/sd_cli.h" +#include "mts/mts.h" +#include "mts/mts_pad.h" + +/*---------------------------------------------------------------------------*/ + +typedef struct _Work +{ + GV_ACT actor; + FS_MOVIE_FILE *info; + RECT rect; + int f2C; + int ticks; + short width; + short height; + short f38; + short dctin_index; + short dctout_index; + short n_frames; + short f40; + short f42; + int end_proc; + void *font; + int *jimaku; + int jimaku_length; + void *vlc; + void *ring; + void *dctin[2]; + void *dctout[2]; +} Work; + +static Work movie_work; + +/*---------------------------------------------------------------------------*/ + +static inline int MovieType(void) +{ + if (GM_OptionFlag & OPTION_ENGLISH) + { + return 1; + } + + return (GM_OptionFlag & OPTION_ENGLISH) ? 1 : 2; +} + +static inline void int_to_loc(int pos, CdlLOC *loc) +{ + int seconds; + + seconds = pos / 75; + loc->sector = itob(pos % 75); + loc->second = itob(seconds % 60); + loc->minute = itob(seconds / 60); +} + +/*---------------------------------------------------------------------------*/ + +static void Movie_800C4484(int pos) +{ + CdlLOC loc; + + do + { + int_to_loc(pos, &loc); + while (CdControl(CdlSetloc, (u_char *)&loc, NULL) == 0); + } while (CdRead2(CdlModeStream2 | CdlModeSpeed | CdlModeRT) == 0); +} + +static int Movie_800C45F4(Work *work) +{ + u_long *addr; + StHEADER *header; + int start; + u_short type; + int ret; + int size; + char *font; + char *font2; + int *jimaku; + + start = VSync(-1); + while (StGetNext(&addr, (u_long **)&header) != 0) + { + if ((VSync(-1) - start) > 300) + { + return 0; + } + } + + type = header->type; + + if (type == 0x8001) + { + if (header->frameCount >= work->n_frames) + { + work->n_frames = header->frameCount; + + if ((work->info->frame - 2) >= header->frameCount) + { + work->width = header->width; + work->height = header->height; + + work->dctin_index = 1 - work->dctin_index; + + DecDCTvlc2(addr, work->dctin[work->dctin_index], work->vlc); + ret = 1; + goto free; + } + } + + return 0; + } + + ret = -1; + if (type != MovieType()) + { + if (work->n_frames > 16) + { + return 0; + } + + size = header->nSectors * 2016; + GV_AllocMemory2(GV_PACKET_MEMORY0, size, &work->font); + GV_CopyMemory(addr, work->font, size); + + font = work->font; + font2 = font; + + jimaku = (int *)(font + *(short *)(font + 0xA)); + if ((jimaku[0] == 0) && (jimaku[1] == 0) && (jimaku[2] == 0)) + { + jimaku = NULL; + } + + work->jimaku = jimaku; + work->jimaku_length = 0; + + font_set_font_addr(3, font2 + *(int *)(font + 0xC)); + work->ticks = mts_get_tick_count(); + ret = -1; + } + +free: + StFreeRing(addr); + return ret; +} + +static void Movie_800C47E8(void) +{ + movie_work.rect.x += 16; + LoadImage(&movie_work.rect, movie_work.dctout[movie_work.dctout_index]); + + movie_work.dctout_index = 1 - movie_work.dctout_index; + + if (movie_work.rect.x < movie_work.f38) + { + DecDCTout(movie_work.dctout[movie_work.dctout_index], movie_work.height * 8); + return; + } + + movie_work.f2C = 1; +} + +static void set_fade(int fade) +{ + TILE tile; + DR_TPAGE tpage; + DR_TPAGE *tpagep; + TILE *tilep; + + tpagep = &tpage; + setDrawTPage(tpagep, 1, 0, getTPage(0, 2, 0, 0)); + DrawPrim(tpagep); + + tilep = &tile; + fade = CLAMP(fade, 0, 255); + LSTORE((fade << 16) | (fade << 8) | fade, &tilep->r0); + setTile(tilep); + setSemiTrans(tilep, 1); + setXY0(tilep, 0, 24); + setWH(tilep, 320, 160); + DrawPrim(&tile); + + DrawSync(0); +} + +static void Act2(Work *work) +{ + RECT *rect; + int res_flag; + int start; + int elapsed; + int *jimaku; + int skip; + int div; + int fade; + + DrawSync(0); + + rect = &work->rect; + rect->x = (GV_Clock == 0) ? 304 : -16; + rect->y = 24; + rect->w = 16; + rect->h = MIN(work->height, FRAME_HEIGHT); + + work->f2C = 0; + work->f38 = rect->x + work->width; + + DecDCTout(work->dctout[work->dctout_index], work->height * 8); + DecDCTin(work->dctin[work->dctin_index], 2); + + while ((res_flag = Movie_800C45F4(work)) < 0); + + if (res_flag != 0) + { + start = VSync(-1); + while (work->f2C == 0) + { + if ((VSync(-1) - start) > 120) + { + res_flag = 0; + break; + } + } + } + + elapsed = mts_get_tick_count() - work->ticks; + elapsed = (elapsed * 105) / 256; + + jimaku = work->jimaku; + if (jimaku != NULL) + { + if (work->jimaku_length == 0) + { + if (elapsed >= jimaku[1]) + { + MENU_JimakuWrite((char *)jimaku + 16, 0); + work->jimaku_length = jimaku[1] + jimaku[2]; + } + } + else if (elapsed >= work->jimaku_length) + { + MENU_JimakuClear(); + work->jimaku_length = 0; + + skip = jimaku[0]; + jimaku = (int *)((char *)jimaku + skip); + if (skip == 0) + { + jimaku = NULL; + } + + work->jimaku = jimaku; + } + } + + if (work->f40 > work->n_frames) + { + fade = ((work->f40 - work->n_frames) * 255) / work->f40; + set_fade(fade); + } + else if (work->f42 < work->n_frames) + { + div = work->info->frame - 1; + div -= work->f42; + fade = ((work->n_frames - work->f42) * 255) / div; + set_fade(fade); + } + + if (mts_read_pad(0) & PAD_CROSS) + { + res_flag = 0; + } + + if (res_flag == 0) + { + stop_xa_sd(); + DecDCToutCallback(NULL); + GV_DestroyActor(&work->actor); + DG_UnDrawFrameCount = 2; + } +} + +static void Act(Work *work) +{ + int i; + int status; + + if (work->f2C-- > 0) + { + DG_FreeObjectQueue(); + return; + } + + start_xa_sd(); + + GV_ResetPacketMemory(); + GV_AllocMemory2(GV_PACKET_MEMORY0, 0x11000, &work->vlc); + GV_AllocMemory2(GV_PACKET_MEMORY0, 0x10000, &work->ring); + + for (i = 0; i < 2; i++) + { + GV_AllocMemory2(GV_PACKET_MEMORY1, 0x17800, &work->dctin[i]); + GV_AllocMemory2(GV_PACKET_MEMORY0, 0x1E00, &work->dctout[i]); + } + + DecDCTReset(0); + DecDCToutCallback(Movie_800C47E8); + + StSetRing(work->ring, 32); + StSetStream(0, 0, 0xFFFFFFFF, NULL, NULL); + + Movie_800C4484(work->info->pos); + + DecDCTvlcBuild(work->vlc); + + work->actor.act = (GV_ACTFUNC)Act2; + work->dctin_index = 0; + work->dctout_index = 0; + + DG_FrameRate = 1; + + work->ticks = mts_get_tick_count(); + + do + { + status = Movie_800C45F4(work); + } while (status < 0); + + if (status == 0) + { + stop_xa_sd(); + GV_DestroyActor(&work->actor); + } +} + +/*---------------------------------------------------------------------------*/ + +static void Die(Work *work) +{ + DecDCToutCallback(NULL); + StUnSetRing(); + CdControlB(CdlPause, NULL, NULL); + + GV_ResetPacketMemory(); + + GV_PauseLevel &= ~GV_PAUSE_STOP; + + DG_RestartMainChanlSystem(); + + DG_FrameRate = 2; + + work->info = NULL; + + MENU_JimakuClear(); + + { extern const char *mgs_dbg_undraw_src; mgs_dbg_undraw_src = "game/movie.c:373"; } + DG_UnDrawFrameCount = 0x7fff0000; + GM_GameStatus &= ~STATE_DEMO; + + if (work->end_proc >= 0) + { + GCL_ExecProc(work->end_proc, NULL); + } +} + +/*---------------------------------------------------------------------------*/ + +void *NewMovie(unsigned int code) +{ + FS_MOVIE_FILE *file; + int frame; + + GM_GameStatus |= STATE_DEMO; + + if (movie_work.info != NULL) + { + return NULL; + } + + GV_ZeroMemory(&movie_work, sizeof(Work)); + printf("MOVIE %d\n", code); + +#ifdef __psyz + /* No full-motion video on this board. + * + * The PSX decodes FMV in hardware through the MDEC, and psyz's DecDCTin / + * DecDCTout / DecDCTvlc are all NOT_IMPLEMENTED stubs. Without them the + * player starts, decodes nothing, and its Die parks DG_UnDrawFrameCount at + * 0x7fff0000 -- "the movie owns the screen" -- which nothing then resets: + * the game keeps running, transforms its models, and draws not one + * primitive. That is a black screen with a perfectly healthy frame loop, + * and it is exactly what appeared the moment ZMOVIE.STR was copied to the + * card and the movie index came alive. + * + * Decline the same way a missing movie file declines. That path is part of + * the original game, every caller already handles the NULL, and it is the + * behaviour the board had before the file existed. Reinstate this once an + * MDEC decoder exists -- though at 320x240 on this CPU, FMV is unlikely to + * be worth the frame budget. */ + printf("[movie] no MDEC decoder on this board -- skipping movie %d\n", + code); + return NULL; +#endif + + file = FS_GetMovieInfo(code); + if (file == NULL) + { + printf("NOT FOUND\n"); + return NULL; + } + + GV_InitActor(GV_ACTOR_MANAGER, &movie_work.actor, NULL); + GV_SetNamedActor(&movie_work.actor, Act, Die, "movie.c"); + + movie_work.info = file; + movie_work.f2C = 1; + movie_work.f40 = 1; + + frame = file->frame; + + DG_UnDrawFrameCount = 1; + + movie_work.end_proc = -1; + movie_work.f40 = 11; + + GV_PauseLevel |= GV_PAUSE_STOP; + + movie_work.f42 = frame - 1; + movie_work.n_frames = 0; + movie_work.f42 = frame - 11; + + return (void *)&movie_work; +} + +void *NewMovieGCL(unsigned int code) +{ + Work *work; + + work = (Work *)NewMovie(code); + if (work == NULL) + { + return NULL; + } + + if (GCL_GetOption('i')) + { + work->f40 += GCL_StrToInt(GCL_NextStr()); + } + + if (GCL_GetOption('o')) + { + work->f42 -= GCL_StrToInt(GCL_NextStr()); + } + + if (GCL_GetOption('p')) + { + work->end_proc = GCL_StrToInt(GCL_NextStr()); + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/game/movie.h b/components/mgs/mgs/source/game/movie.h new file mode 100644 index 00000000..3e2990ac --- /dev/null +++ b/components/mgs/mgs/source/game/movie.h @@ -0,0 +1,7 @@ +#ifndef __MGS_GAME_MOVIE_H__ +#define __MGS_GAME_MOVIE_H__ + +void *NewMovie(unsigned int code); +void *NewMovieGCL(unsigned int code); + +#endif // __MGS_GAME_MOVIE_H__ diff --git a/components/mgs/mgs/source/game/navi.c b/components/mgs/mgs/source/game/navi.c new file mode 100644 index 00000000..f9000efd --- /dev/null +++ b/components/mgs/mgs/source/game/navi.c @@ -0,0 +1,93 @@ +#include "navi.h" + +void NagivateInit(NAVIGATE *nav, HZD_HDL *hzd, SVECTOR *pos) +{ + nav->addr = HZD_GetAddress(hzd, pos, -1); + nav->target = -1; + nav->target_next = -1; + nav->next = nav->addr; + nav->next_pos = DG_ZeroVector; + nav->target_pos = DG_ZeroVector; +} + +void NavigateSetTarget(NAVIGATE *nav, HZD_HDL *hzd, SVECTOR *pos) +{ + nav->target = HZD_GetAddress(hzd, pos, -1); + nav->target_pos = *pos; + nav->target_next = -1; +} + +void NavigateSetTargetPlayer(NAVIGATE *nav) +{ + nav->target = GM_PlayerAddress; + nav->target_pos = GM_PlayerPosition; +} + +int NavigateGetTargetDist(NAVIGATE *nav, SVECTOR *pos) +{ + SVECTOR vec; + SVECTOR diff; + + vec = *pos; + vec.vy = nav->target_pos.vy; + + GV_SubVec3(&nav->target_pos, &vec, &diff); + return GV_VecLen3(&diff); +} + +int NavigateTargetNear(NAVIGATE *nav, SVECTOR *pos, int dist) +{ + return NavigateGetTargetDist(nav, pos) < dist; +} + +int GM_ZoneNavi(NAVIGATE *nav, CONTROL *control) +{ + SVECTOR diff; + SVECTOR *pos; + HZD_HDL *hzd; + int addr; + int target_next; + int target; + int next; + int reach; + int zon; + HZD_ZON *zone; + + pos = &control->mov; + hzd = control->map->hzd; + + addr = nav->addr; + target_next = nav->target_next; + target = nav->target; + next = nav->next; + + reach = HZD_ReachTo(hzd, addr, next); + + if ((target != target_next) || (reach <= 0)) + { + nav->target_next = target; + + if (HZD_ReachTo(hzd, addr, target) < 2) + { + nav->next_pos = nav->target_pos; + nav->next = target; + + GV_SubVec3(&nav->target_pos, pos, &diff); + control->turn.vy = GV_VecDir2(&diff); + return -1; + } + + zon = HZD_Navigate(hzd, addr, target, &control->mov); + zone = &hzd->def->zones[zon]; + + nav->next_pos.vx = zone->x; + nav->next_pos.vy = zone->y; + nav->next_pos.vz = zone->z; + + nav->next = (char)zon | ((char)zon << 8); + } + + GV_SubVec3(&nav->next_pos, pos, &diff); + control->turn.vy = GV_VecDir2(&diff); + return 0; +} diff --git a/components/mgs/mgs/source/game/navi.h b/components/mgs/mgs/source/game/navi.h new file mode 100644 index 00000000..bdb6f757 --- /dev/null +++ b/components/mgs/mgs/source/game/navi.h @@ -0,0 +1,23 @@ +#ifndef __MGS_CHARA_NAVIGATE_H__ +#define __MGS_CHARA_NAVIGATE_H__ + +#include "game/game.h" + +typedef struct _NAVIGATE +{ + int addr; + int target; + int target_next; + int next; + SVECTOR next_pos; + SVECTOR target_pos; +} NAVIGATE; + +void NagivateInit(NAVIGATE *nav, HZD_HDL *hzd, SVECTOR *pos); +void NavigateSetTarget(NAVIGATE *nav, HZD_HDL *hzd, SVECTOR *pos); +void NavigateSetTargetPlayer(NAVIGATE *nav); +int NavigateGetTargetDist(NAVIGATE *nav, SVECTOR *pos); +int NavigateTargetNear(NAVIGATE *nav, SVECTOR *pos, int dist); +int GM_ZoneNavi(NAVIGATE *nav, CONTROL *control); + +#endif // __MGS_CHARA_NAVIGATE_H__ diff --git a/components/mgs/mgs/source/game/object.c b/components/mgs/mgs/source/game/object.c new file mode 100644 index 00000000..ab9f5ce3 --- /dev/null +++ b/components/mgs/mgs/source/game/object.c @@ -0,0 +1,271 @@ +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" + +#include "game.h" +#include "motion.h" + +/*-----sbss---------------------------------------*/ +int SECTION(".sbss") fc_rt; // Unused? +int SECTION(".sbss") mt_rt1; // Unused? +int SECTION(".sbss") mt_rt2; +int SECTION(".sbss") fc_cnt; // Unused? +int SECTION(".sbss") mt_count; + +STATIC int SECTION(".sbss") dword_800ABAC4; +/*------------------------------------------------*/ + +//not sure if this one belongs here +SVECTOR *sub_80034834(SVECTOR *arg0, SVECTOR *arg1, SVECTOR arg2) +{ + SVECTOR vec; + + // TODO: this should be able to be matched without casting to unsigned short + vec.vx = FP_Subtract((unsigned short)arg2.vx, (unsigned short)arg1->vx); + vec.vy = FP_Subtract((unsigned short)arg2.vy, (unsigned short)arg1->vy); + vec.vz = FP_Subtract((unsigned short)arg2.vz, (unsigned short)arg1->vz); + + *arg0 = vec; + return arg0; +} + +void GM_ActObjectMotion(OBJECT *obj) +{ + long intime, outtime; + intime = GetRCnt(RCntCNT1); + + SetSpadStack(SPAD_STACK_ADDR); + GM_PlayAction(obj->m_ctrl); // motion streaming related + ResetSpadStack(); + + obj->is_end = obj->m_ctrl->info1.time; + obj->time2 = obj->m_ctrl->info2.time; +//#ifdef DEBUG + outtime = GetRCnt(RCntCNT1); + mt_rt2 += (outtime - intime) & 0xffff; + mt_count++; +//#endif + if (obj->m_ctrl->interp) + obj->m_ctrl->interp--; /* 補完カウンタ変更 */ + return; +} + +/*----------------------------------------------------------------*/ + +void GM_InitObjectNoRots( OBJECT_NO_ROTS *obj, int model, int flag, int motion ) +{ + ASSERT( model != 0 ); + GV_ZeroMemory( obj, sizeof(OBJECT_NO_ROTS) ); + obj->flag = flag; + obj->light = &DG_LightMatrix; + obj->map_name = GM_CurrentMap; + /* + モデル/モーション初期化 + */ + GM_ConfigObjectModel( obj, model ); +} + +void GM_InitObject( OBJECT *obj, int model, int flag, int motion ) +{ + GV_ZeroMemory( obj->rots, sizeof(SVECTOR) * DG_MAX_JOINTS ); + GM_InitObjectNoRots( (OBJECT_NO_ROTS *)obj, model, flag, motion ); +} + +/*--------- M.Sonoyama 実験中 --------------*/ + +// adds initial step to mutation from another function +void GM_ActMotion(OBJECT *obj) +{ + SVECTOR step; + + if (obj->m_ctrl) + { + step = *obj->m_ctrl->step; + GM_ActObjectMotion(obj); + GV_AddVec3(&step, obj->m_ctrl->step, obj->m_ctrl->step); + } +} + +// sets objects name and objs group id +// if object has a motion control its step is zero'd +void GM_ActObject(OBJECT *obj) +{ + DG_PutObjs(obj->objs); + + if (obj->map_name != GM_CurrentMap) + { + obj->map_name = GM_CurrentMap; + DG_GroupObjs(obj->objs, GM_CurrentMap); + } + + if (obj->m_ctrl) + { + *obj->m_ctrl->step = DG_ZeroVector; + } +} + +// sets objects name objs groups id +// if object has a motion control a separate function +// to set up motion is called +void GM_ActObject2(OBJECT *obj) +{ + DG_PutObjs(obj->objs); + + if (obj->map_name != GM_CurrentMap) + { + obj->map_name = GM_CurrentMap; + DG_GroupObjs(obj->objs, GM_CurrentMap); + } + + if (obj->m_ctrl) + { + GM_ActObjectMotion(obj); + } +} + +// frees an objs object +void GM_FreeObject(OBJECT *obj) +{ + DG_OBJS *objs = obj->objs; + + if (objs) + { + DG_DequeueObjs(objs); + DG_FreeObjs(objs); + } +} + +// configures object flag attribute +void GM_ConfigObjectFlags(OBJECT *obj, int flags) +{ + obj->flag = flags; + obj->objs->flag = flags; +} + +// configures object light attribute +void GM_ConfigObjectLight(OBJECT *obj, MATRIX *light) +{ + obj->light = light; + obj->objs->light = light; +} + +// configures object step attribute but is stubbed in game +void GM_ConfigObjectStep(OBJECT *obj, SVECTOR *step) +{ + /* do nothing */ +} + +// configures object's root to the parents world attribute +// also sets the light depending on if parent exists or not +void GM_ConfigObjectRoot(OBJECT *obj, OBJECT *parent_obj, int num_parent) +{ + MATRIX *light; + DG_OBJS *objs = obj->objs; + + if (!parent_obj || num_parent < 0) + { + light = &DG_LightMatrix; + objs->root = 0; + } + else + { + objs->root = &parent_obj->objs->objs[num_parent].world; + light = parent_obj->light; + } + + obj->light = light; + objs->light = light; +} + +// configures object rots attribute +void GM_ConfigObjectJoint(OBJECT *obj) +{ + obj->objs->rots = obj->rots; +} + +// configures object flag slide attribute +void GM_ConfigObjectSlide(OBJECT *obj) +{ + obj->objs->movs = obj->rots; +} + +// configures the attributes of an objects motion control struct +void GM_ConfigObjectAction(OBJECT *obj, int action, int frame, int interp) +{ + if (obj->m_ctrl) + { + GM_ConfigAction(obj->m_ctrl, action, frame); + obj->action = action; + obj->is_end = 0; + obj->m_ctrl->interp = interp; + } +} + +// +void GM_ConfigObjectOverride(OBJECT *obj, int action, int frame, int interp, u_long mask) +{ + if (mask != 0) + { + if (!obj->m_ctrl) + { + return; + } + + GM_ConfigActionOverride(obj->m_ctrl, action, frame); + } + else + { + obj->m_ctrl->info2.field_14 = 0; + if (!obj->m_ctrl->info1.field_14) + { + obj->m_ctrl->info1.field_14 = 2; + } + } + + obj->action2 = action; + obj->time2 = 0; + obj->m_ctrl->interp = interp; + obj->m_ctrl->info1.mask = mask; + obj->m_ctrl->info2.mask = ~mask; +} + +// calls configObjectAction with given values +int GM_ConfigObjectMotion(OBJECT *obj, int action, int interp) +{ + GM_ConfigObjectAction(obj, obj->action, 0, interp); + return 0; +} + +// gets the objects buffer from cache and allocates memory for +// creating a new object. Dequeues existing objects that it may have +int GM_ConfigObjectModel(OBJECT_NO_ROTS *obj, int model) +{ + int id; + void *buf; + DG_OBJS *objs; + DG_OBJS *current_objs = obj->objs; + + id = GV_CacheID(model, 'k'); + buf = GV_GetCache(id); + objs = DG_MakeObjs(buf, obj->flag, 0); + + if (!objs) + return -1; + + if (current_objs) + { + DG_DequeueObjs(current_objs); + DG_FreeObjs(current_objs); + } + + obj->objs = objs; + objs->light = obj->light; + objs->group_id = obj->map_name; + DG_QueueObjs(objs); + + return 0; +} diff --git a/components/mgs/mgs/source/game/over.c b/components/mgs/mgs/source/game/over.c new file mode 100644 index 00000000..dcf63f99 --- /dev/null +++ b/components/mgs/mgs/source/game/over.c @@ -0,0 +1,636 @@ +#include "over.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" +#include "linkvar.h" + +extern GV_PAD *GM_CurrentPadData; +extern GV_PAD *GM_CurrentPadData; + +int GM_GameOverVox = -1; + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_DAEMON + +#define NUM_LINES 120 // ((countof(over_logo_data)-1)/4) + +#define OPTION_CONTINUE 0 +#define OPTION_EXIT 1 + +#define PCX_GO_CONTINUE 0x669d // GV_StrCode("GO_CONTINUE") +#define PCX_GO_EXIT 0x4d9a // GV_StrCode("GO_EXIT") + +#define FLARE_COLOR MAKE_RGB0(0,64,128) + +typedef struct _Work +{ + GV_ACT actor; + short step; // Step value controlling GAME OVER animation + short count; // Count value controlling when animations/inputs are enabled + short option; // 0 = CONTINUE, 1 = EXIT + short gradient; // 64-step sweep controlling color of CONTINUE and EXIT buttons + short can_continue; // Disables the CONTINUE option (e.g. when Snake dies during the torture sequence) + POLY_G4 polys[2][12]; + LINE_G2 lines[2][NUM_LINES]; + DR_TPAGE anim_tpage[2]; + TILE tiles[2]; + DR_TPAGE fade_tpage[2]; + DVECTOR directions[NUM_LINES]; +} Work; + +/*---------------------------------------------------------------------------*/ + +STATIC short over_logo_data[] = { + NUM_LINES, +/*-- X0 Y0 X1 Y1 --*/ + 37, 88, 25, 104, /* 0 */ + 25, 104, 31, 121, /* 1 */ + 31, 121, 40, 121, /* 2 */ + 40, 121, 32, 135, /* 3 */ + 32, 135, 42, 135, /* 4 */ + 69, 88, 37, 88, /* 5 */ + 64, 96, 41, 96, /* 6 */ + 41, 96, 35, 104, /* 7 */ + 35, 104, 38, 113, /* 8 */ + 38, 113, 45, 113, /* 9 */ + 62, 100, 45, 100, /* 10 */ + 45, 100, 40, 108, /* 11 */ + 40, 108, 48, 108, /* 12 */ + 48, 108, 45, 113, /* 13 */ + 69, 88, 64, 96, /* 14 */ + 55, 121, 65, 121, /* 15 */ + 74, 88, 84, 88, /* 16 */ + 67, 100, 85, 100, /* 17 */ + 69, 96, 85, 96, /* 18 */ + 67, 100, 55, 121, /* 19 */ + 69, 96, 74, 88, /* 20 */ + 84, 88, 85, 96, /* 21 */ + 85, 100, 87, 121, /* 22 */ + 87, 121, 78, 121, /* 23 */ + 73, 108, 77, 108, /* 24 */ + 78, 121, 77, 108, /* 25 */ + 73, 108, 65, 121, /* 26 */ + 62, 100, 42, 135, /* 27 */ + 89, 121, 98, 121, /* 28 */ + 97, 88, 108, 88, /* 29 */ + 118, 88, 130, 88, /* 30 */ + 122, 121, 113, 121, /* 31 */ + 113, 121, 117, 104, /* 32 */ + 117, 104, 108, 121, /* 33 */ + 108, 121, 103, 121, /* 34 */ + 103, 121, 102, 104, /* 35 */ + 102, 104, 98, 121, /* 36 */ + 95, 96, 108, 96, /* 37 */ + 114, 96, 128, 96, /* 38 */ + 94, 100, 108, 100, /* 39 */ + 112, 100, 127, 100, /* 40 */ + 97, 88, 95, 96, /* 41 */ + 94, 100, 89, 121, /* 42 */ + 108, 88, 108, 96, /* 43 */ + 108, 100, 108, 108, /* 44 */ + 112, 100, 108, 108, /* 45 */ + 114, 96, 118, 88, /* 46 */ + 127, 100, 122, 121, /* 47 */ + 128, 96, 130, 88, /* 48 */ + 134, 88, 156, 88, /* 49 */ + 126, 121, 148, 121, /* 50 */ + 132, 96, 154, 96, /* 51 */ + 131, 100, 153, 100, /* 52 */ + 138, 107, 151, 107, /* 53 */ + 138, 107, 137, 113, /* 54 */ + 150, 113, 137, 113, /* 55 */ + 134, 88, 132, 96, /* 56 */ + 131, 100, 126, 121, /* 57 */ + 150, 113, 148, 121, /* 58 */ + 151, 107, 153, 100, /* 59 */ + 154, 96, 156, 88, /* 60 */ + 173, 121, 199, 121, /* 61 */ + 181, 88, 207, 88, /* 62 */ + 179, 96, 205, 96, /* 63 */ + 187, 100, 184, 113, /* 64 */ + 184, 113, 192, 113, /* 65 */ + 195, 100, 192, 113, /* 66 */ + 207, 88, 205, 96, /* 67 */ + 204, 100, 199, 121, /* 68 */ + 181, 88, 179, 96, /* 69 */ + 178, 100, 173, 121, /* 70 */ + 178, 100, 187, 100, /* 71 */ + 195, 100, 204, 100, /* 72 */ + 213, 121, 224, 121, /* 73 */ + 241, 88, 231, 88, /* 74 */ + 210, 88, 219, 88, /* 75 */ + 211, 96, 220, 96, /* 76 */ + 211, 100, 220, 100, /* 77 */ + 225, 100, 235, 100, /* 78 */ + 227, 96, 237, 96, /* 79 */ + 225, 100, 221, 108, /* 80 */ + 231, 88, 227, 96, /* 81 */ + 235, 100, 224, 121, /* 82 */ + 237, 96, 241, 88, /* 83 */ + 211, 100, 213, 121, /* 84 */ + 210, 88, 211, 96, /* 85 */ + 219, 88, 220, 96, /* 86 */ + 220, 100, 221, 108, /* 87 */ + 244, 88, 266, 88, /* 88 */ + 236, 121, 258, 121, /* 89 */ + 242, 96, 264, 96, /* 90 */ + 241, 100, 263, 100, /* 91 */ + 247, 113, 260, 113, /* 92 */ + 248, 108, 261, 108, /* 93 */ + 248, 108, 247, 113, /* 94 */ + 244, 88, 242, 96, /* 95 */ + 241, 100, 236, 121, /* 96 */ + 263, 100, 261, 108, /* 97 */ + 260, 113, 258, 121, /* 98 */ + 266, 88, 264, 96, /* 99 */ + 270, 88, 292, 88, /* 100 */ + 268, 96, 286, 96, /* 101 */ + 292, 88, 295, 97, /* 102 */ + 267, 100, 282, 100, /* 103 */ + 275, 107, 277, 107, /* 104 */ + 262, 121, 272, 121, /* 105 */ + 275, 107, 272, 121, /* 106 */ + 277, 107, 285, 135, /* 107 */ + 285, 135, 295, 135, /* 108 */ + 286, 106, 295, 135, /* 109 */ + 286, 106, 288, 106, /* 110 */ + 288, 106, 295, 97, /* 111 */ + 282, 100, 286, 96, /* 112 */ + 267, 100, 262, 121, /* 113 */ + 268, 96, 270, 88, /* 114 */ + 67, 100, 55, 121, /* 115 */ + 53, 125, 47, 135, /* 116 */ + 53, 125, 279, 125, /* 117 */ + 282, 135, 47, 135, /* 118 */ + 282, 135, 279, 125 /* 119 */ +}; + +/*---------------------------------------------------------------------------*/ + +static char *SetOptionPrim( char *buffer, short x, short y, int texture_id, unsigned int color, OT_TYPE *ot ) +{ + DG_TEX *tex; + SPRT *sprt; + DR_TPAGE *tpage; + + tex = DG_GetTexture( texture_id ); + sprt = (SPRT *)buffer; + tpage = (DR_TPAGE *)(buffer + sizeof(SPRT)); + + setXY0( sprt, x, y ); + setUV0( sprt, tex->off_x, tex->off_y ); + setWH( sprt, tex->w + 1, tex->h + 1 ); + sprt->clut = tex->clut; + LSTORE( color, &sprt->r0 ); + setSprt( sprt ); + addPrim( ot, sprt ); + + setDrawTPage( tpage, 1, 0, tex->tpage ); + addPrim( ot, tpage ); + + return (buffer + sizeof(SPRT) + sizeof(DR_TPAGE)); +} + +static int GetLineColor( int shade, int step ) +{ + int r, gb; + + if ( step < 8 ) + { + r = 128; + gb = 255; + } + else + { + step = MIN( step - 8, 8 ); + r = 128 - step * 16; + gb = 255 - ((255 - shade) * step) / 8; + } + + return MAKE_RGB(r, gb, gb); +} + +/*---------------------------------------------------------------------------*/ + +static void DrawAnimation(Work *work, OT_TYPE *ot) +{ + int x0, y0; + int x1, y1; + int width, height; + int step; + int count; + int color1, color2, color3, color4, color5; + DR_TPAGE *tpage; + short *lines; + int flag; + void *polys; + DVECTOR *directions; + POLY_G4 *poly; + LINE_G2 *line; + + step = work->step; + + lines = over_logo_data; + count = over_logo_data[0]; + + directions = work->directions; + line = work->lines[GV_Clock]; + poly = work->polys[GV_Clock]; + + lines++; + + if (step > 0) + { + if (step < count + 16) + { + work->step = step + 2; + } + else + { + work->step = 0; + } + } + + flag = 1; + if (step == 0) + { + flag = 0; + step = 16; + } + + for (; count > 0; lines += 4, directions++, count--) + { + height = 0; + width = 0; + if (flag) + { + if (step < 8) + { + width = directions->vx >> step; + height = directions->vy >> step; + if (count % 4 == 2) + { + width = -width; + height = -height; + } + } + if (--step < 0) + { + break; + } + } + + x0 = lines[0]; + x1 = lines[2]; + x0 += width; + color1 = x0 - 160; + x1 += width * 2; + color2 = x1 - 160; + y0 = lines[1] + height; + y1 = lines[3] + height * 2; + + setXY2(line, x0, y0, x1, y1); + + if (color1 < 0) + { + color1 = -color1; + } + if (color2 < 0) + { + color2 = -color2; + } + + color1 = 255 - color1; + color2 = 255 - color2; + + if (color1 > 255) + { + color1 = 255; + } + if (color2 > 255) + { + color2 = 255; + } + + LSTORE(GetLineColor(color1, step), &line->r0); + LSTORE(GetLineColor(color2, step), &line->r1); + + setLineG2(line); + addPrim(ot, line); + line++; + + if (step >= 8 && step <= 13) + { + width = -(directions->vy >> (step - 6)); + height = directions->vx >> (step - 6); + + setXY4(poly, + x1 - width, y1 - height, + x0 - width, y0 - height, + (x0 + x1) / 2, + (y0 + y1) / 2, + x0 + width, y0 + height); + + LSTORE(COLOR_BLACK, &poly->r0); + LSTORE(COLOR_BLACK, &poly->r1); + LSTORE(FLARE_COLOR, &poly->r2); + LSTORE(COLOR_BLACK, &poly->r3); + + setPolyG4(poly); + setSemiTrans(poly, 1); + addPrim(ot, poly); + poly++; + + setXY4(poly, + x1 - width, y1 - height, + x1 + width, y1 + height, + (x0 + x1) / 2, + (y0 + y1) / 2, + x0 + width, y0 + height); + + LSTORE(COLOR_BLACK, &poly->r0); + LSTORE(COLOR_BLACK, &poly->r1); + LSTORE(FLARE_COLOR, &poly->r2); + LSTORE(COLOR_BLACK, &poly->r3); + + setPolyG4(poly); + setSemiTrans(poly, 1); + addPrim(ot, poly); + poly++; + } + } + + tpage = &work->anim_tpage[GV_Clock]; + setDrawTPage(tpage, 1, 1, getTPage(0, 1, 0, 0)); + addPrim(ot, tpage); + + if (work->step == 0) + { + if (work->gradient > 32) + { + color3 = 48 + (64 - work->gradient) * 4; + } + else + { + color3 = 48 + work->gradient * 4; + } + + polys = work->polys[GV_Clock]; + + if (work->can_continue) + { + if (work->option == OPTION_CONTINUE) + { + color4 = color3 << 8 | color3 << 16; + color5 = 0x303000; + } + else + { + color4 = 0x303000; + color5 = color3 << 8 | color3 << 16; + } + + polys = SetOptionPrim(polys, 70, 126, PCX_GO_CONTINUE, color4, ot); + SetOptionPrim(polys, 199, 126, PCX_GO_EXIT, color5, ot); + } + else + { + work->option = OPTION_EXIT; + color5 = color3 << 8 | color3 << 16; + SetOptionPrim(polys, 128, 126, PCX_GO_EXIT, color5, ot); + } + } +} + +static void DrawBackgroundFade(Work *work, OT_TYPE *ot, int shade) +{ + TILE *tile; + DR_TPAGE *tpage; + + if (shade > 0xff) + { + shade = 0xff; + } + + tile = &work->tiles[GV_Clock]; + LSTORE((shade << 16) | (shade << 8) | shade, &tile->r0); + setTile(tile); + setSemiTrans(tile, 1); + setXY0(tile, 0, 0); + setWH(tile, SCREEN_WIDTH, SCREEN_HEIGHT); + addPrim(ot, tile); + + tpage = &work->fade_tpage[GV_Clock]; + setDrawTPage(tpage, 1, 1, getTPage(0, 2, 0, 0)); + addPrim(ot, tpage); +} + +/*---------------------------------------------------------------------------*/ + +static void Act( Work *work ) +{ + OT_TYPE *ot; + GV_PAD *pad; + u_short press; + int shade; + + ot = DG_ChanlOTag(1); + + if (GV_PauseLevel & GV_PAUSE_READERROR) + { + return; + } + + if (work->count < 256) + { + DrawAnimation(work, ot); + DrawBackgroundFade(work, ot, work->count * 2); + + if (work->count == NUM_LINES) + { + if (GM_GameOverVox >= 0) + { + GM_StreamPlayStart(); + } + + DG_ReloadPalette(); + } + + work->count += 3; + + if (work->count >= 256) + { + if (work->step > 0) + { + work->count = 255; + return; + } + + GV_PauseLevel |= GV_PAUSE_STOP; + DG_FreeObjectQueue(); + DG_ReloadPalette(); + DG_SetRGB(0, 0, 0); + DG_FrameRate = 2; + work->count = 256; + GM_GameStatus |= STATE_ALL_OFF; + } + } + else if (work->count == 256) + { + pad = &GM_CurrentPadData[2]; + DrawAnimation(work, ot); + GM_GameStatus &= ~(STATE_PADMASK | STATE_PADRELEASE | STATE_PADDEMO); + press = pad->press; + + if (press & (PAD_START | PAD_CIRCLE)) + { + work->count = 257; + work->gradient = 32; + + if (work->option == OPTION_CONTINUE) + { + GM_SeSet3(0, 0x3f, SE_MENU_GUNSHOT); + } + else + { + GM_SeSet3(0, 0x3f, SE_MENU_EXIT); + } + + return; + } + + if (work->can_continue) + { + if ((work->option == OPTION_CONTINUE) && (press & PAD_RIGHT)) + { + GM_SeSet3(0, 0x3f, SE_MENU_CURSOR); + work->option = OPTION_EXIT; + work->gradient = 0; + } + else if ((work->option == OPTION_EXIT) && (press & PAD_LEFT)) + { + GM_SeSet3(0, 0x3f, SE_MENU_CURSOR); + work->option = OPTION_CONTINUE; + work->gradient = 0; + } + } + + work->gradient = (work->gradient + 1) % 64; + } + else + { + shade = MIN(work->count - 256, 255); + DrawBackgroundFade(work, ot, shade); + DrawAnimation(work, ot); + + work->count += 4; + + if (work->count > 542) + { + work->count = 542; + + if (GM_StreamStatus() == -1) + { + GV_DestroyActor(&work->actor); + } + } + } +} + +static void Die( Work *work ) +{ + char *stage_name; + + GV_PauseLevel &= ~GV_PAUSE_STOP; + DG_RestartMainChanlSystem(); + GM_StreamPlayStop(); + GM_GameOverTimer = 0; + if ( work->option == OPTION_CONTINUE ) + { + GM_ContinueStart(); + return; + } + if ( ( GM_OptionFlag & OPTION_TUXEDO ) || + ( GM_DifficultyFlag == DIFFICULTY_VERY_EASY ) ) + { + GV_InitResidentMemory(); + GV_InitCacheSystem(); + DG_ClearResidentTexture(); + stage_name = "init"; + GM_OptionFlag &= ~OPTION_TUXEDO; + } + else + { + stage_name = "title"; + } + GM_SetArea( GV_StrCode( stage_name ), stage_name ); + GM_LoadRequest = 0x81; +} + +static void GetResources( Work *work ) +{ + int i; + short *lines; + DVECTOR *directions; + SVECTOR *scratch; + + i = over_logo_data[0]; + lines = &over_logo_data[1]; + directions = work->directions; + + ((SVECTOR *)getScratchAddr(0))->vz = 0; + + for (; i > 0; lines += 4, directions++, i--) + { + scratch = (SVECTOR *)getScratchAddr(0); + scratch[0].vx = lines[0] - lines[2]; + scratch[0].vy = lines[1] - lines[3]; + + VectorNormalSS(&scratch[0], &scratch[1]); + + directions->vx = -scratch[1].vx / 8; + directions->vy = -scratch[1].vy / 8; + } +} + +/*---------------------------------------------------------------------------*/ + +void *NewGameOver(int can_continue) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, &Act, &Die, "over.c"); + + work->step = 1; + work->count = 0; + work->can_continue = can_continue; + + GetResources(work); + + if (GM_GameOverVox >= 0) + { + GM_VoxStream(GM_GameOverVox, 0x40000000); + } + } + + GM_SetSound(0xff000008, SD_ASYNC); + GM_SetSound(0xff0000fe, SD_ASYNC); + GM_SetSound(0x01ffff0b, SD_ASYNC); + + GM_SeSet3(0, 0x3f, SE_GAMEOVER); + + DG_FrameRate = 3; + GM_GameStatus |= STATE_GAME_OVER; + + return (void *)work; +} diff --git a/components/mgs/mgs/source/game/over.h b/components/mgs/mgs/source/game/over.h new file mode 100644 index 00000000..69ffc0a2 --- /dev/null +++ b/components/mgs/mgs/source/game/over.h @@ -0,0 +1,6 @@ +#ifndef __MGS_GAME_OVER_H__ +#define __MGS_GAME_OVER_H__ + +void *NewGameOver(int can_continue); + +#endif // __MGS_GAME_OVER_H__ diff --git a/components/mgs/mgs/source/game/pad.c b/components/mgs/mgs/source/game/pad.c new file mode 100644 index 00000000..892969a1 --- /dev/null +++ b/components/mgs/mgs/source/game/pad.c @@ -0,0 +1,104 @@ +#include "common.h" +#include "game/game.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_USER + +typedef struct _Work +{ + GV_ACT actor; + int name; //0x20 + u_short status; //0x24 + short unk2; //0x26 + char* unk3; //0x28 + int unk4; //0x2C +} Work; + +/*---------------------------------------------------------------------------*/ + +static int GetResources( Work *work ) +{ + GCL_SetArgTop( work->unk3 ) ; + + if ( !work->unk3 ) + { + return 0 ; + } + + + if ( GCL_NextStr() ) + { + work->status = GCL_StrToInt( GCL_NextStr() ) ; + work->unk2 = GCL_StrToInt( GCL_NextStr() ) ; + work->unk3 = GCL_NextStr() ; + return 1 ; + } + + return 0 ; +} + +static void Act( Work *work ) +{ + if ( GM_CheckMessage( &work->actor, work->name, HASH_KILL ) ) + { + GV_DestroyActor( &work->actor ); + return; + } + + if ( (work->unk2 <= 0) && ( GetResources( work ) == 0) ) + { + GV_DestroyActor( &work->actor ); + } + + --work->unk2; + GM_GameStatus |= STATE_PADDEMO; + GV_DemoPadStatus[0] = work->status; +} + +static void Die( Work *work ) +{ + GV_DemoPadStatus[0] = 0 ; + GM_GameStatus &= ~STATE_PADDEMO; + + if ( work->unk4 > 0 ) + { + GCL_ExecProc( work->unk4 , NULL) ; + } +} + +/*---------------------------------------------------------------------------*/ + +void *NewPadControl(int name, int where, int argc, char **argv) +{ + char *ops; + Work *work ; + + work = GV_NewActor( EXEC_LEVEL, sizeof( Work ) ) ; + + if ( work != NULL ) { + /* ワークにコールバックを登録する */ + GV_SetNamedActor( &( work->actor ), Act, Die, "pad.c" ) ; + + ops = GCL_GetOption( 's' ) ; + if ( !ops ) { + GV_DestroyActor( &work->actor ) ; + } + work->unk3 = ops ; + work->unk2 = 0; + work->name = name ; + + if ( GCL_GetOption( 'p' ) ) + { + work->unk4 = GCL_GetNextInt(); + } else + { + work->unk4 = -1; + } + + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/game/pad.h b/components/mgs/mgs/source/game/pad.h new file mode 100644 index 00000000..8bbe8b1c --- /dev/null +++ b/components/mgs/mgs/source/game/pad.h @@ -0,0 +1,6 @@ +#ifndef __MGS_GAME_PAD_H__ +#define __MGS_GAME_PAD_H__ + +void *NewPadControl(int name, int where, int argc, char **argv); + +#endif // __MGS_GAME_PAD_H__ diff --git a/components/mgs/mgs/source/game/point.c b/components/mgs/mgs/source/game/point.c new file mode 100644 index 00000000..2b9fd8b2 --- /dev/null +++ b/components/mgs/mgs/source/game/point.c @@ -0,0 +1,107 @@ +#include "common.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_USER + +typedef struct _Work +{ + GV_ACT actor; + CONTROL control; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void Act(Work *work) +{ + CONTROL *control; + int n_msgs; + GV_MSG *msg; + int message; + + control = &work->control; + n_msgs = GV_ReceiveMessage(control->name, &control->msg); + + if (n_msgs <= 0) + { + return; + } + + msg = control->msg; + while (--n_msgs >= 0) + { + message = msg->message[0]; + + if (message == HASH_ON) + { + control->radar_atr |= RADAR_VISIBLE; + } + + if (message == HASH_OFF) + { + control->radar_atr &= ~RADAR_VISIBLE; + } + + if (message == HASH_KILL) + { + GV_DestroyActor(&work->actor); + } + } +} + +/*---------------------------------------------------------------------------*/ + +static void Die(Work *work) +{ + GM_FreeControl(&work->control); +} + +/*---------------------------------------------------------------------------*/ + +static int GetResources(Work *work, int where, int name) +{ + char *pos, *dir; + int color; + + if (GM_InitControl(&work->control, name, where) < 0) + { + return 0; + } + + pos = GCL_GetOption('p'); + dir = GCL_GetOption('d'); + GM_ConfigControlString(&work->control, pos, dir); + + color = 1 << 12; + if (GCL_GetOption('c')) + { + color = GCL_GetNextInt() << 12; + } + + GM_ConfigControlAttribute(&work->control, color | RADAR_ALL_MAP | RADAR_VISIBLE); + return 1; +} + +/*---------------------------------------------------------------------------*/ + +void *NewPoint(int name, int where, int argc, char **argv) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "point.c"); + + if (!GetResources(work, where, name)) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/game/point.h b/components/mgs/mgs/source/game/point.h new file mode 100644 index 00000000..1ae87c32 --- /dev/null +++ b/components/mgs/mgs/source/game/point.h @@ -0,0 +1,6 @@ +#ifndef __MGS_GAME_POINT_H__ +#define __MGS_GAME_POINT_H__ + +void *NewPoint(int name, int where, int argc, char **argv); + +#endif // __MGS_GAME_POINT_H__ diff --git a/components/mgs/mgs/source/game/script.c b/components/mgs/mgs/source/game/script.c new file mode 100644 index 00000000..7b98449f --- /dev/null +++ b/components/mgs/mgs/source/game/script.c @@ -0,0 +1,1229 @@ +#include +#include + +#include "common.h" +#include "charadef.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "libfs/libfs.h" /* FS_CdGetStageFileTop: is this stage on the board? */ +#include "game/game.h" +#include "game/loader.h" /* Mgs_ResolveStage */ +#include "linkvar.h" +#include "game/item.h" +#include "game/delay.h" +#include "menu/menuman.h" +#include "kojo/demo.h" +#include "strcode.h" + +extern CAMERA GM_CameraList[8]; +extern GM_CameraSystemWork GM_Camera; +extern HZD_BND gBindsArray_800b58e0[128]; +extern int dword_8009F46C; +extern SVECTOR svector_8009F478; + +/*---------------------------------------------------------------------------*/ + +STATIC char SECTION(".sbss") dword_800ABA58[8]; +STATIC int SECTION(".sbss") gBinds_800ABA60; +STATIC int SECTION(".sbss") gBindsCount_800ABA64; + +extern char *GM_StageName; +extern char* GM_StageName; /* defined in libgv/gvd.c */ + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_light(char *top) +{ + char *light_dir; + char *light_col; + char *light_ambient; + SVECTOR vec; + + if ((light_dir = GCL_GetOption('d'))) + { + GCL_StrToSV(light_dir, (short *)&vec); + DG_SetMainLightDir(vec.vx, vec.vy, vec.vz); + } + if ((light_col = GCL_GetOption('c'))) + { + GCL_StrToSV(light_col, (short *)&vec); + DG_SetMainLightCol(vec.vx, vec.vy, vec.vz); + } + if ((light_ambient = GCL_GetOption('a'))) + { + GCL_StrToSV(light_ambient, (short *)&vec); + DG_SetAmbient(vec.vx, vec.vy, vec.vz); + } + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +/* +proc AGL_FIRST_VF { +#pos -3362 1759 4936 +#trg -2475 770 6672 +#rot 306 308 0 + camera set 4 d:CAM_FIX d:CAM_INTERP_LINER d:CAM_CAM_TO_TRG \ + -3362,1759,4936 -2475,770,6672 1 +*/ + +static int GM_Command_camera(char *top) +{ + int isEnabled, param_p, camera_id; + SVECTOR vec1, vec2; + CAMERA *cam; + + isEnabled = GCL_GetOption('e') != 0; // enabled + + if (GCL_GetOption('b')) // bound + { + GCL_StrToSV(GCL_NextStr(), (short *)&vec1); + GCL_StrToSV(GCL_NextStr(), (short *)&vec2); + GM_CameraSetBound(&vec1, &vec2, isEnabled); + } + + if (GCL_GetOption('t')) // track + { + GM_CameraSetTrack(GCL_GetNextInt()); + } + + if (GCL_GetOption('l')) // limit + { + GCL_StrToSV(GCL_NextStr(), (short *)&vec1); + GCL_StrToSV(GCL_NextStr(), (short *)&vec2); + GM_CameraSetLimit(&vec1, &vec2, isEnabled); + } + + if (GCL_GetOption('r')) // rotate + { + GCL_StrToSV(GCL_NextStr(), (short *)&vec1); + GM_CameraSetRotation(&vec1); + } + + param_p = GCL_GetOption('p') != 0; + + if (GCL_GetOption('s')) // set + { + camera_id = GCL_GetNextInt(); + if (camera_id < 8) + { + printf("set camera %d\n", camera_id); + cam = &GM_CameraList[camera_id]; + + cam->field_10_param1 = GCL_GetNextInt(); + cam->interp = GCL_GetNextInt(); + cam->type = GCL_GetNextInt(); + cam->pad_type = param_p; + + GCL_StrToSV(GCL_NextStr(), (short *)&cam->pos); + GCL_StrToSV(GCL_NextStr(), (short *)&cam->trg); + + if (GCL_NextStr()) + { + cam->trg.pad = GCL_GetNextInt(); + } + else + { + cam->trg.pad = 0; + } + + GM_CameraSetAlertMask(camera_id, cam->trg.pad); + } + } + + if (GCL_GetOption('a')) + { + GM_Camera.alert_mask = GCL_GetNextInt(); + } + + if (GCL_GetOption('c')) + { + if (GCL_GetNextInt() == 0) + { + GM_GameStatus &= ~GAME_FLAG_BIT_07; + } + else + { + GM_GameStatus |= GAME_FLAG_BIT_07; + } + } + + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_map(char *top) +{ + MAP *pMapRecord; + SVECTOR colorVec; + + if (GCL_GetOption('d')) + { + GM_DefineMap(); + } + + if (GCL_GetOption('s')) + { + GM_ReloadMap(1); + if (!(GM_GameStatus & STATE_DEMO)) + { + DG_UnDrawFrameCount = 4; + } + } + + if (GCL_GetOption('c')) + { + GM_ReloadMap(0); + + if (!(GM_GameStatus & STATE_DEMO)) + { + if (GCL_GetOption('u')) + { + DG_UnDrawFrameCount = 4; + } + else + { + DG_UnDrawFrameCount = 0; + } + } + } + + if (GCL_GetOption('a')) + { + gBinds_800ABA60 = 0; + while (GCL_NextStr()) + { + pMapRecord = GM_FindMap(GCL_GetNextInt()); + if (pMapRecord == 0) + { + return GCL_ERROR; + } + gBinds_800ABA60 = gBinds_800ABA60 | pMapRecord->index; + } + } + + if (GCL_GetOption('p')) + { + while (GCL_NextStr()) + { + GM_AddMap(GCL_GetNextInt()); + } + } + + if (GCL_GetOption('m')) + { + while (GCL_NextStr()) + { + GM_DelMap(GCL_GetNextInt()); + } + } + + if (GCL_GetOption('b')) + { + GCL_StrToSV(GCL_NextStr(), (short *)&colorVec); + DG_SetRGB(colorVec.vx, colorVec.vy, colorVec.vz); + } + + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_mapdef(char *top) +{ + if (!GM_CreateMap()) + { + return GCL_ERROR; + } + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_trap(char *top) +{ + HZD_BND *pBind; + int i, arg, code, value; + int tmp; + + if (0x7f < gBindsCount_800ABA64) + { + printf("binds over\n"); + } + + i = gBindsCount_800ABA64; + pBind = gBindsArray_800b58e0 + i; + + // Trap id + arg = GCL_GetNextInt(); + if (arg == HASH_TRAP_ALL) + { + arg = 0; + } + gBindsArray_800b58e0[i].field_4 = arg; + + // Entity id + arg = GCL_GetNextInt(); + if (arg == HASH_TRAP_ALL) + { + arg = 0; + } + pBind->field_0 = arg; + + // Event condition + arg = GCL_GetNextInt(); + if (arg == HASH_TRAP_ALL) + { + arg = 0; + } + gBindsArray_800b58e0[i].field_2_param_m = arg; // mask + + gBindsArray_800b58e0[i].field_B_param_e = 0; // exec + gBindsArray_800b58e0[i].field_8_param_i_c_flags = 0; + + GCL_GetNextValue(GCL_NextStr(), &code, &value); + gBindsArray_800b58e0[i].command = value; + gBindsCount_800ABA64++; + + tmp = gBinds_800ABA60; + gBindsArray_800b58e0[i].map = (short)tmp; + HZD_SetBind(0, gBindsArray_800b58e0, gBindsCount_800ABA64); + + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_ntrap(char *top) +{ + // int bindIdx; + HZD_BND *pBind; + int flags; + int arg; + int tmp; + + if (gBindsCount_800ABA64 > 127) // 780 gp + { + printf("binds over\n"); + } + // bindIdx = gBindsCount_800ABA64; // 780 gp + pBind = gBindsArray_800b58e0 + gBindsCount_800ABA64; + arg = GCL_GetNextInt(); + if (arg == HASH_TRAP_ALL) + { + arg = 0; + } + pBind->field_4 = arg; + arg = GCL_GetNextInt(); + if (arg == HASH_TRAP_ALL) + { + arg = 0; + } + pBind->field_0 = arg; + pBind->field_8_param_i_c_flags = 0; // v0 + flags = 0; // still s1 + if (GCL_GetOption('m')) // mask + { + arg = GCL_GetNextInt(); + if (arg == HASH_TRAP_ALL) + { + arg = 0; + } + pBind->field_2_param_m = arg; + } + else + { + pBind->field_2_param_m = 0; + } + if (GCL_GetOption('d')) // dir + { + flags |= 1; + pBind->field_C_param_d = GCL_GetNextInt(); + + if (GCL_NextStr()) + { + pBind->field_E_param_d_or_512 = GCL_GetNextInt(); + } + else + { + pBind->field_E_param_d_or_512 = 0x200; + } + } + if (GCL_GetOption('b')) // button + { + flags |= 4; + pBind->field_A_param_b = GCL_GetNextInt(); + } + if (GCL_GetOption('s')) // stance + { + flags |= 2; + pBind->field_9_param_s = GCL_GetNextInt(); + } + if (GCL_GetOption('r')) // repeat + { + flags |= 0x40; + } + if (GCL_GetOption('i')) + { + pBind->field_8_param_i_c_flags |= 1; + } + if (GCL_GetOption('c')) + { + pBind->field_8_param_i_c_flags |= 2; + } + if (GCL_GetOption('t')) // time + { + if ((flags & 0x40) != 0) + { + printf("ntrap:can't set every\n"); + } + pBind->time = GCL_GetNextInt(); + } + if (GCL_GetOption('p')) // proc + { + flags |= 0x80; + pBind->command = GCL_GetNextInt(); + } + if (GCL_GetOption('e')) // exec + { + int code; + int value; + if ((flags & 0x80) != 0) + { + printf("ntrap:can't set proc and block\n"); + } + GCL_GetNextValue(GCL_NextStr(), &code, &value); + pBind->command = value; + } + pBind->field_B_param_e = flags; + gBindsCount_800ABA64++; + tmp = gBinds_800ABA60; + pBind->map = (short)tmp; + printf("BIND %08X\n", tmp); + HZD_SetBind(0, gBindsArray_800b58e0, gBindsCount_800ABA64); + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_delay(char *top) +{ + int time = 0; + int proc = 0; + + if (GCL_GetOption('t')) // time + { + time = GCL_GetNextInt(); + } + if (GCL_GetOption('p')) // proc + { + proc = GCL_GetNextInt(); + } + if (GCL_GetOption('e')) // exec + { + int code; + int value; + GCL_GetNextValue(GCL_NextStr(), &code, &value); + proc = value; + } + if (GCL_GetOption('g')) + { + time = -time; + } + if (time && proc) + { + GM_DelayedExecCommand(proc, NULL, time); + return GCL_OK; + } + return GCL_ERROR; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_mesg(char *top) +{ + unsigned char *uParm1; + int iVar1; + int ret; + short *pMsgDst; + GV_MSG mesg; + int count; + + mesg.address = GCL_GetNextInt(); + pMsgDst = &mesg.message[0]; + count = 0; + while (uParm1 = GCL_NextStr(), uParm1 != 0x0) + { + int iVar2 = GCL_StrToInt(uParm1); + *pMsgDst = (short)iVar2; + pMsgDst++; + count++; + } + mesg.message_len = count; + iVar1 = GV_SendMessage(&mesg); + ret = GCL_OK; + if (iVar1 < 0) + { + ret = GCL_ERROR; + } + return ret; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_chara(int argc, char **argv) +{ + int ret; + int name; + NEWCHARA *func; + + func = GM_GetChara(GCL_NextStr()); + if (func != NULL) + { + name = GCL_StrToInt(GCL_NextStr()); + (*func)(name, gBinds_800ABA60, argc, argv); + ret = GCL_OK; + } + else + { + ret = GCL_ERROR; + } + return ret; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_start(char *top) +{ + if (GCL_GetOption('s')) + { + GM_InitReadError(); + } + + if (GCL_GetOption('m')) // menu + { + menuman_init_80038954(); + printf("MENU INIT END\n"); + } + + if (GCL_GetOption('f')) // font + { + font_load(); + } + + if (GCL_GetOption('v')) + { + GCL_InitVar(); + MENU_InitRadioMemory(); + gTotalFrameTime = 0; + } + + if (GCL_GetOption('d')) // demo (1 to use demo.gcx instead of scenerio.gcx) + { + GCL_ChangeSenerioCode(GCL_GetNextInt()); + } + + if (GCL_GetOption('c')) + { + GCL_InitClearVar(); + MENU_InitRadioMemory(); + gTotalFrameTime = 0; + } + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_load(char *top) +{ + char *scriptStageName; + SVECTOR vec; + + scriptStageName = GCL_GetString(GCL_NextStr()); + if (*scriptStageName == '\0') + { + GM_LoadRequest = 1; + return GCL_OK; + } + + if (GCL_GetOption('r')) + { + if (!GCL_GetNextInt()) + { + // Hard restart? + strcpy(dword_800ABA58, GM_GetArea((int)scriptStageName)); + GV_InitResidentMemory(); + GV_InitCacheSystem(); + DG_ClearResidentTexture(); + GM_SetArea(GV_StrCode(scriptStageName), scriptStageName); + } + else + { + // Soft restart? + scriptStageName = dword_800ABA58; + GM_SetArea(GM_CurrentStageFlag, scriptStageName); + } + + GM_LoadRequest = 1; + return GCL_OK; + } + +#ifdef __psyz + /* Only a handful of the disc's 96 stage blocks fit on the board, so sooner + * or later a door leads somewhere that was not packed. Nothing downstream + * checks, because on the console every stage is always there: with the + * stage missing from STAGE.DIR, FS_LoadStageRequest issues a read at + * sector -1, no sector ever arrives, and FS_LoadStageSync answers "still + * busy" forever -- by which point the previous stage has already been torn + * down. The result is a permanently black screen with the actor list idle, + * which is what "NOT FOUND s07b" did to the whole game. + * + * This is the last point where the world is still intact, so refuse the + * transition here: the door simply does not lead anywhere and play carries + * on in the current room. */ + { + const char *resolved = Mgs_ResolveStage(scriptStageName); + if (resolved == NULL) + { + printf("[stage] '%s' is not packed on this board, staying put\n", + scriptStageName); + return GCL_OK; + } + if (resolved != scriptStageName) + { + printf("[stage] '%s' -> '%s'\n", scriptStageName, resolved); + scriptStageName = (char *)resolved; + } + } +#endif + + GM_PreviousStageFlag = GM_CurrentStageFlag; + GM_CurrentStageFlag = GV_StrCode(scriptStageName); + + GM_SetArea(GM_CurrentStageFlag, scriptStageName); + + if (GCL_GetOption('m')) // map + { + GM_CurrentMapFlag = GCL_GetNextInt(); + } + + if (GCL_GetOption('p')) // pos + { + GCL_StrToSV(GCL_NextStr(), (short *)&vec); + GM_SnakePosX = vec.vx; + GM_SnakePosY = vec.vy; + GM_SnakePosZ = vec.vz; + } + + if (GCL_GetOption('s')) + { + GM_LoadRequest = GCL_GetNextInt(); + if (GM_LoadRequest) + { + GM_LoadRequest |= 0x80; + } + } + else + { + GM_LoadRequest = 1; + } + + if (!GCL_GetOption('n')) + { + GM_LoadRequest |= 0x10; + } + + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_radio(char *top) +{ + int contactFrequency; + int radioTableCode; + + if (GCL_GetOption('b')) + { + while (GCL_NextStr()) + { + contactFrequency = GCL_GetNextInt(); + radioTableCode = GCL_GetNextInt(); + MENU_SetRadioBaseCall(contactFrequency, radioTableCode); + } + } + if (GCL_GetOption('o')) + { + int hash = GCL_GetNextInt(); + if (hash == HASH_ENTER) + { + hash = 0; + } + else if (hash == HASH_LEAVE) + { + hash = 1; + } + else + { + printf("Wrong Code for radio over\n"); + } + while (GCL_NextStr()) + { + contactFrequency = GCL_GetNextInt(); + radioTableCode = GCL_GetNextInt(); + if (hash) + { + radioTableCode = -1; + } + MENU_SetRadioOverCall(contactFrequency, radioTableCode); + } + } + if (GCL_GetOption('c')) // call + { + MENU_RadioCall(GCL_GetNextInt(), // contactFrequency + GCL_GetNextInt(), // radioTableCode + GCL_GetNextInt()); // ring duration ? + } + if (GCL_GetOption('p')) // proc + { + MENU_SetRadioCallbackProc(GCL_GetNextInt()); + } + if (GCL_GetOption('r')) // reset + { + MENU_ResetCall(); + } + if (GCL_GetOption('m')) // mesg string (example: "clear") + { + MENU_SetRadioMemory(GCL_GetNextInt(), // contactFrequency + GCL_GetString(GCL_NextStr())); // string + } + if (GCL_GetOption('d')) // disable? + { + GM_GameStatus |= STATE_RADIO_OFF; + } + if (GCL_GetOption('e')) // enable? + { + GM_GameStatus &= ~STATE_RADIO_OFF; + } + if (GCL_GetOption('a')) + { + MENU_ClearRadioTable(); + } + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_restart(char *top) +{ + int proc_id; + + if (GCL_GetOption('p')) // proc + { + proc_id = GCL_GetNextInt(); + } + else + { + proc_id = -1; + } + + GM_SetLoadCallbackProc(proc_id); + + if (GCL_GetOption('s')) // save + { + GM_LoadRequest |= 0x10; + } + + if (GCL_GetOption('a')) // area + { + GM_SetArea(GM_CurrentStageFlag, GM_GetArea(0)); + } + + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_varsave(char *top) +{ + unsigned char *param; + + param = GCL_NextStr(); + if (GCL_GetOption('a')) + { + GCL_SaveVar(); + return GCL_OK; + } + while (*param) + { + if ( GCL_TAG( *param ) != GCL_VAR ) + { + printf("VARSAVE: NOT VAR !!\n"); + } + param = GCL_VarSaveBuffer(param); + } + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_system(char *top) +{ + static char options[5] = "gcawi"; + + int i, proc; + + for (i = 0; i <= (int)sizeof(options); i++) + { + if (GCL_GetOption(options[i])) + { + proc = GCL_GetNextInt(); + if (!proc) + { + printf("SYSTEM:%c:change proc name\n", options[i]); + } + GM_SetSystemCallbackProc(i, proc); + } + } + + if (GCL_GetOption('s')) + { + GM_StageName = GCL_GetString(GCL_NextStr()); + } + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_demo(char *top) +{ + int code, cb_proc; + char *msg; + + if ( !GCL_GetOption( 's' ) ) // scene ? + { + printf( "DEMO:NO CODE\n" ); + } + code = GCL_GetNextInt(); + + if ( GCL_GetOption( 'p' ) ) // proc + { + cb_proc = GCL_GetNextInt() | STATE_DEMO; + } + else + { + cb_proc = 0; + } + + GM_CurrentMap = gBinds_800ABA60; + + if ( code >= 0 ) + { + DG_UnDrawFrameCount = 0x7FFF0000; + GM_GameStatus |= STATE_DEMO; + GM_DemoStream( code, cb_proc ); + } + else + { + if ( cb_proc < 0 ) + { + cb_proc &= 0xFFFF; + } + else + { + cb_proc = -1; + } + + if ( GCL_GetOption( 'f' ) ) // file + { + msg = GCL_GetString( GCL_NextStr() ); + } + else + { + msg = "DEMO ( no data on CD )"; + } + NewJimakuStr( msg, cb_proc ); + } + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_pad(char *top) +{ + if (GCL_GetOption('m')) + { + GV_PadMask = GCL_GetNextInt(); + GM_GameStatus |= STATE_PADMASK; + } + if (GCL_GetOption('r')) // resume + { + GM_GameStatus |= STATE_PADRELEASE; + } + else if (GCL_GetOption('s')) // stop + { + GM_GameStatus &= ~(STATE_PADRELEASE | STATE_PADMASK | GAME_FLAG_BIT_08); + } + GV_UpdatePadSystem(); + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_sound(char *top) +{ + GM_AlertSound(); + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static unsigned int GM_Command_menu_helper(void) +{ + unsigned int ret = 0; + int next; + + while (GCL_NextStr()) + { + next = GCL_GetNextInt(); + + if (next > 32) + { + return 0; + } + + ret |= 1 << next; + } + + return ret; +} + +static int GM_Command_menu(char *top) +{ + if (GCL_GetOption('j')) + { + if (GCL_GetNextInt() & 1) + { + GM_GameStatus |= STATE_JAMMING; + } + else + { + GM_GameStatus &= ~STATE_JAMMING; + } + } + + if (GCL_GetOption('n')) + { + if (GCL_GetNextInt() & 1) + { + disable_equipment(); + } + else + { + enable_equipment(); + } + } + + if (GCL_GetOption('m')) + { + if (!(GCL_GetNextInt() & 1)) + { + GM_GameStatus |= STATE_MENU_OFF; + } + else + { + GM_GameStatus &= ~STATE_MENU_OFF; + } + } + + if (GCL_GetOption('l')) // lifebar + { + switch (GCL_GetNextInt()) + { + case 0: + GM_GameStatus |= STATE_LIFEBAR_OFF; + break; + case 1: + case 3: + GM_GameStatus &= ~STATE_LIFEBAR_OFF; + break; + case 2: + GM_GameStatus |= STATE_HIDE_LIFEBAR; + break; + } + } + + if (GCL_GetOption('r')) + { + switch (GCL_GetNextInt()) + { + case 0: + GM_GameStatus |= STATE_RADAR_OFF; + break; + case 1: + GM_GameStatus &= ~STATE_RADAR_OFF; + break; + case 2: + GM_GameStatus |= STATE_HIDE_RADAR; + break; + case 3: + GM_GameStatus |= STATE_SHOW_RADAR; + break; + } + } + + if (GCL_GetOption('p')) + { + if (GCL_GetNextInt() & 1) + { + GM_GameStatus |= STATE_PAUSE_OFF; + } + else + { + GM_GameStatus &= ~STATE_PAUSE_OFF; + } + } + + if (GCL_GetOption('s')) + { + MENU_SetRadarScale(GCL_GetNextInt()); + } + + if (GCL_GetOption('w')) // weapon + { + GM_DisableWeapon = GM_Command_menu_helper(); + } + + if (GCL_GetOption('i')) // item + { + GM_DisableItem = GM_Command_menu_helper(); + } + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_rand(char *top) +{ + int param; + int randValue; + + param = GCL_GetNextInt(); + randValue = rand(); + GM_LastResultFlag = randValue % param; + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_func(char *top) +{ + SVECTOR vec; + CONTROL *control; + int param; + MAP *map; + + if (GCL_GetOption('v')) // vector + { + GCL_StrToSV(GCL_NextStr(), (short *)&vec); + GM_LastResultFlag = DG_PointCheckOne((DVECTOR *)&vec); + } + if (GCL_GetOption('s')) + { + control = GM_PlayerControl; +#ifdef __psyz + /* "func -s" copies the player's position into the save variables. On + * the console the player always exists by the time a scenario asks; + * here a restart tears the actor list down and the scenario re-runs + * before Snake has been recreated, so this arrives with a null + * control and the null read reboots the board. Leaving the stored + * position untouched is the same answer the console would give, + * because there is nothing new to copy. */ + if (control) +#endif + { + GM_SnakePosX = control->mov.vx; + GM_SnakePosY = control->mov.vy; + GM_SnakePosZ = control->mov.vz; + GM_LastResultFlag = control->rot.vy; + } + } + if (GCL_GetOption('a')) // area + { + GM_LastResultFlag = GM_AreaHistory(GCL_GetNextInt()); + } + if (GCL_GetOption('p')) // photo (used for ghosts easter egg) + { + param = GCL_GetNextInt(); + GCL_StrToSV(GCL_NextStr(), (short *)&GM_PhotoViewPos); + if (GCL_GetNextInt() == HASH_LEAVE) + { + param = 0; + } + GM_Photocode = param; + } + if (GCL_GetOption('m')) // map + { + map = GM_FindMap(GCL_GetNextInt()); + if (map && map->used) + { + GM_LastResultFlag = 1; + } + else + { + GM_LastResultFlag = 0; + } + } + if (GCL_GetOption('c')) + { + GM_LastResultFlag = GM_StreamStatus(); + } + if (GCL_GetOption('n')) + { + GM_LastResultFlag = dword_8009F46C; + GM_SnakePosX = svector_8009F478.vx; + GM_SnakePosY = svector_8009F478.vy; + GM_SnakePosZ = svector_8009F478.vz; + } + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +int demodebug_finish_proc = -1; + +static int GM_Command_demodebug(char *top) +{ + int tmp, demo, flags, ivar; + char *filename; + + ivar = 0; + filename = NULL; + + flags = GCL_GetOption('e') != 0; + if (GCL_GetOption('a')) + { + flags |= 4; + } + if (GCL_GetOption('v')) + { + flags |= 2; + } + if (GCL_GetOption('s')) + { + ivar = GCL_StrToInt(GCL_NextStr()); + } + if (GCL_GetOption('f')) + { + filename = GCL_GetString(GCL_NextStr()); + } + if (GCL_GetOption('p')) + { + demodebug_finish_proc = GCL_GetNextInt(); + } + else + { + demodebug_finish_proc = -1; + } + tmp = GM_CurrentMap; + GM_CurrentMap = gBinds_800ABA60; + if (filename) + { + demo = DM_ThreadFile(flags, filename); + } + else + { + demo = DM_ThreadStream(flags, ivar); + } + GM_CurrentMap = tmp; + if (!demo) + { + printf("Error demo thread\n"); + } + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_print(char *top) +{ + int code; + int value; + + printf("print: "); + + while (top) + { + top = GCL_GetNextValue(top, &code, &value); + if (code == GCL_END) + break; + if (code == GCL_STRING) + printf("%s ", (char *)value); + else + printf("%d ", value); + } + + printf("\n"); + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +static int GM_Command_jimaku(char *top) +{ + NewJimaku(); + return GCL_OK; +} + +/*---------------------------------------------------------------------------*/ + +STATIC GCL_COMMANDLIST Commands[] = { + { 0x22ff, GM_Command_mesg }, // GV_StrCode("mesg") + { 0xd4cb, GM_Command_trap }, // GV_StrCode("trap") + + // BUG: Seems like earlier in development, GCL args were passed via + // main-style (int argc, char *argv[]) arguments. GM_Command_chara receives + // garbage arguments and passes them on to the actor callback. + { 0x9906, (GCL_COMMANDFUNC)GM_Command_chara }, // GV_StrCode("chara") + + { 0xc091, GM_Command_map }, // GV_StrCode("map") + { 0x7d50, GM_Command_mapdef }, // GV_StrCode("mapdef") + { 0xeee9, GM_Command_camera }, // GV_StrCode("camera") + { 0x306a, GM_Command_light }, // GV_StrCode("light") + { 0x9a1f, GM_Command_start }, // GV_StrCode("start") + { 0xc8bb, GM_Command_load }, // GV_StrCode("load") + { 0x24e1, GM_Command_radio }, // GV_StrCode("radio") + { 0xe43c, GM_Command_restart }, // GV_StrCode("restart") + { 0xa242, GM_Command_demo }, // GV_StrCode("demo") + { 0xdbab, GM_Command_ntrap }, // GV_StrCode("ntrap") + { 0x430d, GM_Command_delay }, // GV_StrCode("delay") + { 0xcc85, GM_Command_pad }, // GV_StrCode("pad") + { 0x5c9e, GM_Command_varsave }, // GV_StrCode("varsave") + { 0x4ad9, GM_Command_system }, // GV_StrCode("system") + { 0x698d, GM_Command_sound }, // GV_StrCode("sound") + { 0x226d, GM_Command_menu }, // GV_StrCode("menu") + { 0x925e, GM_Command_rand }, // GV_StrCode("rand") + { 0xe257, GM_Command_func }, // GV_StrCode("func") + { 0xa2bf, GM_Command_demodebug }, // GV_StrCode("demodebug") + { 0xb96e, GM_Command_print }, // GV_StrCode("print") + { 0xec9d, GM_Command_jimaku }, // GV_StrCode("jimaku") +}; + +STATIC GCL_COMMANDDEF script_commands = GCL_COMMANDS(Commands); + +int GM_ResetScript(void) +{ + gBinds_800ABA60 = 0; + gBindsCount_800ABA64 = 0; + HZD_SetBind(0, gBindsArray_800b58e0, 0); + return 0; +} + +void GM_InitScript(void) +{ + GM_ResetScript(); + GCL_AddCommMulti(&script_commands); +} diff --git a/components/mgs/mgs/source/game/second.c b/components/mgs/mgs/source/game/second.c new file mode 100644 index 00000000..bec8e01e --- /dev/null +++ b/components/mgs/mgs/source/game/second.c @@ -0,0 +1,56 @@ +#include +#include "common.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "game/game.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_USER + +typedef struct _Work +{ + GV_ACT actor; + int using_pad2; + int _unused1; + char *message; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void Act(Work *work) +{ + if (GV_PadData[1].status && work->using_pad2 == FALSE) + { + work->using_pad2 = TRUE; + printf("SECOND!!\n"); + MENU_JimakuWrite(work->message, 20000); + } + else if (work->using_pad2 == TRUE && GV_PadData[0].status) + { + work->using_pad2 = FALSE; + MENU_JimakuClear(); + } +} + +/*---------------------------------------------------------------------------*/ + +void *NewSecond(int name, int where, int argc, char **argv) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, NULL, "second.c"); + work->message = GCL_GetString(GCL_NextStr()); + work->using_pad2 = 0; + } + return (void *)work; +} + +int GM_SetSecondAvailable(void) +{ + GM_PlayerStatus |= PLAYER_SECOND_AVAILABLE; + return 1; +} diff --git a/components/mgs/mgs/source/game/second.h b/components/mgs/mgs/source/game/second.h new file mode 100644 index 00000000..d9dfa679 --- /dev/null +++ b/components/mgs/mgs/source/game/second.h @@ -0,0 +1,8 @@ +#ifndef __MGS_GAME_SECOND_H__ +#define __MGS_GAME_SECOND_H__ + +void *NewSecond(int name, int where, int argc, char **argv); + +int GM_SetSecondAvailable(void); + +#endif // __MGS_GAME_SECOND_H__ diff --git a/components/mgs/mgs/source/game/select.c b/components/mgs/mgs/source/game/select.c new file mode 100644 index 00000000..a8aac029 --- /dev/null +++ b/components/mgs/mgs/source/game/select.c @@ -0,0 +1,191 @@ +#include "select.h" + +#include +#include "common.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "menu/menuman.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_PREV2 + +typedef struct _Work +{ + GV_ACT actor; + unsigned char *gcl_menu_entries; + char *current_entry_name; + int current_entry_proc_id; + int current_idx; + + // when you are holding PAD_DOWN/PAD_UP + // it will continue moving through entries, + // this slows down the speed of this movement + int movement_delay_remaining; + + int previous_dir; +} Work; + +#ifdef DEV_EXE +int isStageSelectionMenu; // Used to print the extended info below only in stage selection menu. +char* extendedInfo[] = { + "%s [ %d ]", // TITLE + "%s: LOADING DOCK - CUTSCENE [ %d ]", // D00A + "%s: LOADING DOCK [ %d ]", // S00A + "%s: HELIPORT - CUTSCENE [ %d ]", // D01A + "%s: HELIPORT - USING SCOPE [ %d ]", // S01A + "%s: HELIPORT [ %d ]", // S01A1 + "%s: TANK HANGAR [ %d ]", // S02A + "%s: TANK HANGAR 2 [ %d ]", // S02B + "%s: TANK HANGAR - BEFORE CONTACTING MERYL [ %d ]", // S02C + "%s: TANK HANGAR - AFTER CONTACTING MERYL [ %d ]", // S02D + "%s: TANK HANGAR - AFTER TORTURE [ %d ]", // S02E + "%s: HOLDING CELL [ %d ]", // S03A + "%s: MEDICAL ROOM [ %d ]", // S03B + "%s: MEDICAL ROOM 2 [ %d ]", // S03C + "%s: ARMORY [ %d ]", // S04A + "%s: ARMORY SOUTH - VS OCELOT [ %d ]", // S04B + "%s [ %d ]" // RETURN +}; +#endif // DEV_EXE + +/*---------------------------------------------------------------------------*/ + +static void UpdateCurrentEntry(Work *work, int dir) +{ + int i; + int proc_id; + char *entry_name; + + work->current_idx += dir; +#ifdef DEV_EXE + // Circular scrolling (only in stage selection menu to keep things simple). + if (work->current_idx < 0) + { + work->current_idx = isStageSelectionMenu ? 16 : 0; + } + else if (isStageSelectionMenu && work->current_idx > 16) + { + work->current_idx = 0; + } +#else + if (work->current_idx < 0) + { + work->current_idx = 0; + } +#endif + + GCL_SetArgTop(work->gcl_menu_entries); + for (i = 0; i <= work->current_idx; i++) + { + if (GCL_NextStr() == 0) + { + work->current_idx = i; + break; + } + // entry_name = "s00a", "title", etc. + entry_name = GCL_GetString(GCL_NextStr()); + proc_id = GCL_StrToInt(GCL_NextStr()); + } + work->current_entry_name = entry_name; + work->current_entry_proc_id = proc_id; +} + +// See also menu_radio_do_file_mode_helper13_8004BCF8() in datasave.c +static void Act(Work *work) +{ + int dir; + GV_PAD *pPad; +#ifdef DEV_EXE + char stageInfo[55]; +#endif + + pPad = &GV_PadData[0]; + + if (pPad->status & (PAD_UP | PAD_DOWN)) + { + dir = 1; + if (pPad->status & PAD_UP) + { + dir = -1; + } + if (work->previous_dir == dir) + { + if (--work->movement_delay_remaining < 0) + { + UpdateCurrentEntry(work, dir); + work->movement_delay_remaining = 2; + } + } + else + { + UpdateCurrentEntry(work, dir); + work->movement_delay_remaining = 10; + work->previous_dir = dir; + } + } + else + { + work->previous_dir = 0; + } + if (pPad->press & PAD_CIRCLE) + { + GCL_ExecProc(work->current_entry_proc_id, NULL); + GV_DestroyActor(&work->actor); + } + MENU_ResetText(); + MENU_Locate(160, 120, 2); + +#ifdef DEV_EXE + if (isStageSelectionMenu) + { + sprintf(stageInfo, extendedInfo[work->current_idx], work->current_entry_name, work->current_entry_proc_id); + } + else + { + strcpy(stageInfo, work->current_entry_name); + } + MENU_Printf(stageInfo); +#else + MENU_Printf(work->current_entry_name); +#endif +} + +static int GetResources(Work *work, int where, int name) +{ + if (!GCL_GetOption('s')) + { + printf("NO MENU\n"); + return -1; + } + + work->gcl_menu_entries = GCL_NextStr(); + work->current_idx = 0; + work->previous_dir = 0; + UpdateCurrentEntry(work, 0); + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewSelect(int name, int where, int argc, char **argv) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, NULL, "select.c"); + if (GetResources(work, where, name) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + +#ifdef DEV_EXE + isStageSelectionMenu = name == 10524; +#endif + + return (void *)work; +} diff --git a/components/mgs/mgs/source/game/select.h b/components/mgs/mgs/source/game/select.h new file mode 100644 index 00000000..ba986706 --- /dev/null +++ b/components/mgs/mgs/source/game/select.h @@ -0,0 +1,6 @@ +#ifndef __MGS_GAME_SELECT_H__ +#define __MGS_GAME_SELECT_H__ + +void *NewSelect(int name, int where, int argc, char **argv); + +#endif // __MGS_GAME_SELECT_H__ diff --git a/components/mgs/mgs/source/game/sndtst.c b/components/mgs/mgs/source/game/sndtst.c new file mode 100644 index 00000000..19021388 --- /dev/null +++ b/components/mgs/mgs/source/game/sndtst.c @@ -0,0 +1,198 @@ +#include "sndtst.h" + +#include +#include "common.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "game/jimctrl.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_PREV2 + +typedef struct _Work +{ + GV_ACT actor; + int field_20; + char *field_24; + char *field_28_name; + int field_2C_code; + int field_30; + int field_34; + int field_38; + int field_3C[6]; +} Work; + +/*---------------------------------------------------------------------------*/ + +static void RunScripts( Work *work, int param_2 ) +{ + int i; + char *name; + int code; + + work->field_30 += param_2; + if ( work->field_30 < 0 ) + { + work->field_30 = 0; + } + + GCL_SetArgTop( work->field_24 ); + + for ( i = 0; i <= work->field_30; i++ ) + { + if ( !GCL_NextStr() ) + { + work->field_30 = i; + break; + } + + name = GCL_GetString( GCL_NextStr() ); + code = GCL_StrToInt( GCL_NextStr() ); + } + + work->field_28_name = name; + work->field_2C_code = code; +} + +/*---------------------------------------------------------------------------*/ + +static void Act( Work *work ) +{ + GV_PAD *pad; + int var_s0; + array_800B933C_child *pUnk; + char symbol; + int i; + + pad = &GV_PadData[0]; + + if ( work->field_20 == 0 ) + { + if ( pad->status & (PAD_DOWN | PAD_UP) ) + { + var_s0 = (pad->status & PAD_UP) ? -1 : 1; + + if ( pad->status & PAD_L1 ) + { + var_s0 *= 10; + } + + if ( work->field_38 == var_s0 ) + { + if ( --work->field_34 < 0 ) + { + RunScripts( work, var_s0 ); + work->field_34 = 2; + } + } + else + { + RunScripts( work, var_s0 ); + work->field_34 = 10; + work->field_38 = var_s0; + } + } + else + { + work->field_38 = 0; + } + + if ( pad->press & PAD_CIRCLE ) + { + work->field_20 = 1; + GM_VoxStream( work->field_2C_code, 0 ); + } + + MENU_Locate( 160, 120, 0x2 ); + MENU_Printf( work->field_28_name ); + } + else + { + MENU_Locate( 160, 120, 0x2 ); + MENU_Printf( "PLAYING" ); + + if ( pad->press & PAD_CROSS ) + { + GM_StreamPlayStop(); + } + + if ( GM_StreamStatus() == -1 ) + { + work->field_20 = 0; + GM_VoxStream( work->field_2C_code, 0 ); + } + + pUnk = jimctrl_80038698(); + + for ( i = 0; i < 6; i++ ) + { + if ( pUnk[i].field_2 != 0 ) + { + pUnk[i].field_2 = 0; + work->field_3C[i] = 1; + } + + if ( work->field_3C[i] > 0 ) + { + work->field_3C[i]++; + + symbol = "-=O"[(work->field_3C[i] / 2) - 1]; + + if ( work->field_3C[i] > 6 ) + { + work->field_3C[i] = 0; + } + } + else + { + symbol = '0'; + } + + MENU_Locate( 116, 32 + i * 10, 0 ); + MENU_Printf( "%c %c :%d", symbol, "-0=-"[pUnk[i].field_3], pUnk[i].field_0 ); + } + } +} + +/*---------------------------------------------------------------------------*/ + +static int GetResources( Work *work, int where, int name ) +{ + if ( !GCL_GetOption( 's' ) ) + { + printf( "NO MENU\n" ); + return -1; + } + + work->field_24 = GCL_NextStr(); + work->field_30 = 0; + work->field_38 = 0; + RunScripts( work, 0 ); + work->field_20 = 0; + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewSoundTest( int name, int where, int argc, char **argv ) +{ + Work *work; + + GM_GameStatus |= STATE_ALL_OFF; + + work = GV_NewActor( EXEC_LEVEL, sizeof( Work ) ); + if ( work != NULL ) + { + GV_SetNamedActor( &( work->actor ), Act, NULL, "sndtst.c" ); + if (GetResources( work, where, name ) < 0) + { + GV_DestroyActor( &work->actor ); + return NULL; + } + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/game/sndtst.h b/components/mgs/mgs/source/game/sndtst.h new file mode 100644 index 00000000..d4e311b8 --- /dev/null +++ b/components/mgs/mgs/source/game/sndtst.h @@ -0,0 +1,6 @@ +#ifndef __MGS_GAME_SNDTST_H__ +#define __MGS_GAME_SNDTST_H__ + +void *NewSoundTest( int name, int where, int argc, char **argv ); + +#endif // __MGS_GAME_SNDTST_H__ diff --git a/components/mgs/mgs/source/game/sound.c b/components/mgs/mgs/source/game/sound.c new file mode 100644 index 00000000..384fffdf --- /dev/null +++ b/components/mgs/mgs/source/game/sound.c @@ -0,0 +1,467 @@ +#include "game.h" + +#include "sound/sd_cli.h" +#include "sound/g_sound.h" +#include "common.h" +#include "libdg/libdg.h" +#include "libgv/libgv.h" + +typedef struct _SEPARAM +{ + short pan; + short vol; +} SEPARAM; + +extern GM_CameraSystemWork GM_Camera; +extern UnkCameraStruct2 gUnkCameraStruct2_800B7868; + +static inline void GM_Sound( int pan, int vol, int se_id, int exclude ) +{ + int se_id_; + + if (!(GM_GameStatus & exclude)) + { + pan &= 0xff; + vol &= 0xff; + vol = MIN(vol, 63); + se_id_ = se_id & 0xff; + sd_set_cli( (pan << 16 | vol << 8 | se_id_), SD_ASYNC ); + } +} + +STATIC void get_eye_pos(SVECTOR *pos, int *dir) +{ + MATRIX *eye; + + eye = &DG_Chanl(0)->eye; + pos->vx = eye->t[0]; + pos->vy = eye->t[1]; + pos->vz = eye->t[2]; + *dir = ratan2(eye->m[0][2], eye->m[2][2]); +} + +// Get the pan amount based on the angle from forward to the sound position +STATIC int get_pan(int angle, int scale) +{ + int pan; + + pan = (rcos(angle & 4095) * scale) / 4096; + if (pan < 0) + { + return pan + 255; + } + + return pan; +} + +// Get the pan & vol relative to the world direction +STATIC int get_pan_vol_world(SVECTOR *pos, int size, SEPARAM *param) +{ + SVECTOR diff; + int dist; + int vol; + int dir; + + dist = GV_DiffVec3(&GM_PlayerPosition, pos) - size; + if (dist < 0) + { + dist = 0; + vol = 63; + } + else + { + vol = 63; + } + + if (dist >= 1000) + { + if (dist < 7300) + { + vol = 63 - ((dist - 1000) / 100); + } + else + { + dist = 7300; + vol = 0; + } + } + + GV_SubVec3(pos, &GM_PlayerPosition, &diff); + dir = GV_VecDir2(&diff) + 1024; + + param->pan = get_pan(dir - gUnkCameraStruct2_800B7868.rotate.vy, (dist * 31) / 7300); + param->vol = vol; + + if (vol <= 0) + { + return -1; + } + + return 0; +} + +// Get the pan & vol relative to the camera direction +STATIC int get_pan_vol_camera(SVECTOR *pos, int size, SEPARAM *param) +{ + SVECTOR diff; + SVECTOR eye; + int eyedir; + int dist; + int vol; + int dir; + + get_eye_pos(&eye, &eyedir); + + dist = GV_DiffVec3(&eye, pos) - size; + if (dist < 0) + { + dist = 0; + vol = 63; + } + else + { + vol = 63; + } + + if (dist < 9500) + { + vol = 63 - (dist / 150); + } + else + { + dist = 9500; + vol = 0; + } + + dist *= 4; + + if (dist > 9500) + { + dist = 9500; + } + + GV_SubVec3(pos, &eye, &diff); + dir = GV_VecDir2(&diff) + 1024; + + param->pan = get_pan(dir - eyedir, (dist * 31) / 9500); + param->vol = vol; + + if (vol <= 0) + { + return -1; + } + + return 0; +} + +// Get the pan & vol relative to the world direction for explosions +STATIC int get_pan_vol_bomb_world(SVECTOR *pos, int size, SEPARAM *param) +{ + SVECTOR diff; + int dist; + int vol; + int dir; + + dist = GV_DiffVec3(&GM_PlayerPosition, pos) - size; + if (dist < 0) + { + dist = 0; + vol = 63; + } + else + { + vol = 63; + } + + if (dist >= 3000) + { + if (dist < 7700) + { + vol = 63 - ((dist - 3000) / 200); + } + else + { + dist = 7700; + vol = 39; + } + } + + GV_SubVec3(pos, &GM_PlayerPosition, &diff); + dir = GV_VecDir2(&diff) + 1024; + + param->pan = get_pan(dir - gUnkCameraStruct2_800B7868.rotate.vy, (dist * 31) / 7700); + param->vol = vol; + + if (vol <= 0) + { + return -1; + } + + return 0; +} + +// Get the pan & vol relative to the camera direction for explosions +STATIC int get_pan_vol_bomb_camera(SVECTOR *pos, int size, SEPARAM *param) +{ + SVECTOR diff; + SVECTOR eye; + int eyedir; + int dist; + int vol; + int dir; + + get_eye_pos(&eye, &eyedir); + + dist = GV_DiffVec3(&eye, pos) - size; + if (dist < 0) + { + dist = 0; + vol = 63; + } + else + { + vol = 63; + } + + if (dist >= 2000) + { + if (dist < 9050) + { + vol = 63 - ((dist - 2000) / 300); + } + else + { + dist = 9050; + vol = 39; + } + } + + dist *= 4; + + if (dist > 9050) + { + dist = 9050; + } + + GV_SubVec3(pos, &eye, &diff); + dir = GV_VecDir2(&diff) + 1024; + + param->pan = get_pan(dir - eyedir, (dist * 31) / 9050); + param->vol = vol; + + if (vol <= 0) + { + return -1; + } + + return 0; +} + +STATIC int get_pan_vol_size(SEPARAM *param, SVECTOR *pos, int size) +{ + if (!(GM_GameStatus & (STATE_BEHIND_CAMERA | GAME_FLAG_BIT_07)) && !GM_Camera.first_person) + { + if (get_pan_vol_world(pos, size, param) < 0) + { + return -1; + } + } + else + { + if (get_pan_vol_camera(pos, size, param) < 0) + { + return -1; + } + } + + return 0; +} + +STATIC int get_sound_normal(SEPARAM *param, SVECTOR *pos) +{ + if (!(GM_GameStatus & (STATE_BEHIND_CAMERA | GAME_FLAG_BIT_07)) && !GM_Camera.first_person) + { + if (get_pan_vol_world(pos, 0, param) < 0) + { + return -1; + } + } + else + { + if (get_pan_vol_camera(pos, 0, param) < 0) + { + return -1; + } + } + + return 0; +} + +STATIC int get_sound_bomb(SEPARAM *param, SVECTOR *pos) +{ + if (!(GM_GameStatus & (STATE_BEHIND_CAMERA | GAME_FLAG_BIT_07)) && !GM_Camera.first_person) + { + if (get_pan_vol_bomb_world(pos, 0, param) < 0) + { + return -1; + } + } + else + { + if (get_pan_vol_bomb_camera(pos, 0, param) < 0) + { + return -1; + } + } + + return 0; +} + + +STATIC int get_sound_real(SEPARAM *param, SVECTOR *pos) +{ + if (!(GM_GameStatus & (STATE_BEHIND_CAMERA | GAME_FLAG_BIT_07)) && !GM_Camera.first_person) + { + return -1; + } + + if (get_pan_vol_camera(pos, 0, param) < 0) + { + return -1; + } + + return 0; +} + +STATIC int get_sound_camera(SEPARAM *param, SVECTOR *pos) +{ + if (get_pan_vol_camera(pos, 0, param) < 0) + { + return -1; + } + + return 0; +} + +void GM_SeSet(SVECTOR *pos, int se_id) +{ + SEPARAM param; + + if (pos) + { + if (se_id == SE_EXPLOSION) + { + if (get_sound_bomb(¶m, pos) < 0) + { + return; + } + } + else if (se_id >= 160 && se_id <= 167) + { + if (get_sound_real(¶m, pos) < 0) + { + return; + } + } + else if (se_id == SE_MOUSE_STEP) + { + if (get_sound_camera(¶m, pos) < 0) + { + return; + } + } + else + { + if (get_sound_normal(¶m, pos) < 0) + { + return; + } + } + } + else + { + param.vol = 63; + param.pan = 0; + } + + GM_Sound(param.pan, param.vol, se_id, (STATE_GAME_OVER | STATE_DEMO)); +} + +void GM_SeSet2(int pan, int vol, int se_id) +{ + GM_Sound(pan, vol, se_id, (STATE_GAME_OVER | STATE_DEMO)); +} + +void GM_SeSetMode( SVECTOR *pos, int se_id, int mode ) +{ + SEPARAM param; + + if ( pos ) + { + switch ( mode ) + { + case GM_SEMODE_BOMB: + if ( get_sound_bomb( ¶m, pos ) < 0 ) + { + return; + } + break; + + case GM_SEMODE_REAL: + if ( get_sound_real( ¶m, pos ) < 0 ) + { + return; + } + break; + + case GM_SEMODE_CAMERA: + if ( get_sound_camera( ¶m, pos ) < 0 ) + { + return; + } + break; + + case GM_SEMODE_NORMAL: + if ( get_sound_normal( ¶m, pos ) < 0 ) + { + return; + } + break; + } + } + else + { + param.vol = 63; + param.pan = 0; + } + + GM_Sound(param.pan, param.vol, se_id, (STATE_GAME_OVER | STATE_DEMO)); +} + +void GM_SeSet3(int pan, int vol, int se_id) +{ + GM_Sound(pan, vol, se_id, STATE_DEMO); +} + +void GM_SeSetPan(SVECTOR *pos, int se_id, int vol) +{ + SEPARAM param; + + get_sound_normal(¶m, pos); + GM_Sound(param.pan, vol, se_id, (STATE_GAME_OVER | STATE_DEMO)); +} + +void GM_SeSetSize(SVECTOR *pos, int se_id, int size) +{ + SEPARAM param; + + if (get_pan_vol_size(¶m, pos, size) >= 0) + { + GM_Sound(param.pan, param.vol, se_id, (STATE_GAME_OVER | STATE_DEMO)); + } +} + +void GM_SetSound(int sound_code, int sync_mode) +{ + sd_set_cli(sound_code, sync_mode); +} diff --git a/components/mgs/mgs/source/game/strctrl.c b/components/mgs/mgs/source/game/strctrl.c new file mode 100644 index 00000000..f8c9255a --- /dev/null +++ b/components/mgs/mgs/source/game/strctrl.c @@ -0,0 +1,375 @@ +#include "strctrl.h" + +#include +#include +#include "common.h" +#include "sound/sd_cli.h" +#include "libdg/libdg.h" +#include "libfs/libfs.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "game/jimctrl.h" +#include "kojo/demo.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_MANAGER + +extern StreamCtrlWork strctrl_work; + +/*---------------------------------------------------------------------------*/ + +int str_sector_8009E280 = 0; +int str_gcl_proc_8009E284 = 0; +int str_8009E288 = 0; + +static void Act2( StreamCtrlWork *work ) +{ + if ( !FS_StreamTaskState() ) + { + GV_DestroyActor( &work->actor ); + } +} + +static void Act( StreamCtrlWork *work ) +{ + int sd_code; + int stream_data; + + GM_CurrentMap = work->map; + FS_StreamSync(); + +#ifdef __psyz + { + /* Watchdog: never let a stalled stream hold the game hostage. + * + * The stage machine will not change stage while a stream is running -- + * gamed.c:588 gates on GM_StreamStatus() == -1 -- and the screen is + * already blanked at that point (gamed.c:476, "keep it dark until the + * new stage is ready"). So a stream that starts and never finishes is + * not a missing cutscene: it is a permanently black screen with a + * perfectly healthy frame loop behind it. That is exactly what + * appeared the moment DEMO.DAT reached the card -- before that the + * file was absent, the stream was declined, and the game moved on. + * + * Give it ten seconds of real progress. Any state change counts as + * progress, so a slow stream is never punished; only a stuck one is. + * On expiry, hand it to the same Act2 that ends a finished stream, so + * the actor is destroyed, Die runs its callback, and the stage machine + * is released. */ + static int last_state; + static unsigned stuck; + + if ( work->field_20_state != last_state ) + { + last_state = work->field_20_state; + stuck = 0; + } + else if ( ++stuck > 600u ) + { + printf( "[stream] stalled in state %d -- abandoning so the game " + "can continue\n", work->field_20_state ); + stuck = 0; + /* Destroy outright rather than handing over to Act2: Act2 waits on + * FS_StreamTaskState() too, which is precisely what is stuck, so + * that just moves the deadlock. Die zeroes field_20_state, which + * is what GM_StreamStatus() reports and what the stage machine is + * waiting for. */ + GV_DestroyActor( &work->actor ); + return; + } + } +#endif + + switch ( work->field_20_state ) + { + case 1: + if ( FS_StreamTaskState() < 0 ) + { + return; + } + work->field_20_state = 2; + + case 2: + if ( !work->field_22_sub_state ) + { + work->field_20_state = 3; + GM_GameStatus |= STATE_VOX_STREAM; + work->field_34_pStreamData = ( int* )FS_StreamGetData( 0x10 ); + FS_StreamTickStart(); + work->field_22_sub_state = 1; + return; + } + break; + + case 3: +loop_case3: + if ( work->field_34_pStreamData || + ( work->field_34_pStreamData = ( int* )FS_StreamGetData( 0x10 ) ) ) + { + stream_data = *work->field_34_pStreamData; + if ( ( FS_StreamGetTick() >= ( stream_data >> 8 ) ) && + !FS_StreamIsForceStop() ) + { + switch ( stream_data & 0xFF ) + { + case 1: + if ( !sd_str_play() ) + { + FS_StreamClearType( work->field_34_pStreamData, 1 ); + FS_StreamSoundMode(); + sd_code = 0xE0000000; + if ( !work->field_26_flags ) + { + sd_code++; + } + GM_SetSound( sd_code, SD_ASYNC ); + break; + } + printf( "Double Pcm !!\n" ); + return; + case 5: + DG_UnDrawFrameCount = 3; + DM_ThreadStream( 1, 0 ); + work->field_24 = 1; + break; + case 3: + NewJimakuControl( work->field_26_flags ); + break; + case 6: + NewJimakuControl( work->field_26_flags | 0x80 ); + break; + default: + printf( "??? WRONG TYPE HEADER!!\n" ); + break; + } + FS_StreamClear( work->field_34_pStreamData ); + work->field_34_pStreamData = NULL; + work->field_22_sub_state = 2; + goto loop_case3; + } + } + if ( work->field_22_sub_state == 2 && !FS_StreamIsEnd() ) + { + work->field_22_sub_state = 0; + } + if ( ( !work->field_22_sub_state || FS_StreamIsForceStop() ) + && FS_StreamIsEnd() && !FS_StreamSync() ) + { + printf( "StreamPlay end\n" ); + if ( work->field_24 ) + { + DG_UnDrawFrameCount = 0x7FFF0000; + } + work->actor.act = ( GV_ACTFUNC )&Act2; + } + break; + } +} + +static void Die( StreamCtrlWork *work ) +{ + int cb_proc; + + cb_proc = work->field_38_proc; + work->field_20_state = 0; + GM_GameStatus &= ~STATE_VOX_STREAM; + if ( cb_proc >= 0 ) + { + work->field_38_proc = -1; + GCL_ExecProc( cb_proc, 0 ); + + } + if ( str_sector_8009E280 ) + { + NewStreamControl( str_sector_8009E280, str_gcl_proc_8009E284, str_8009E288 ); + str_sector_8009E280 = 0; + } +} + +void *NewStreamControl( int stream_code, int gcl_proc, int flags ) +{ + printf( "NewStream %d\n", stream_code ); + +#ifdef __psyz + /* Streams read from the big disc files -- DEMO.DAT for in-engine + * cutscenes, VOX.DAT for speech, ZMOVIE.STR for full-motion video. Those + * are half a gigabyte between them and are not on this board, so the + * virtual CD answers their sectors with nothing at all. + * + * On the console that cannot happen, so nobody checks: the stream actor + * simply waits for data that never arrives, and the script waits for the + * stream. That is a silent, permanent hang -- the game sat inside init + * with its frame loop running and an empty screen, because init opens with + * a movie. Same shape as a door leading to a stage that was never packed, + * and the same answer: refuse the request here, where the caller can still + * carry on, and run the completion callback so whatever was waiting on the + * stream gets its turn. */ + { + /* CD STREAMING IS NOT IMPLEMENTED ON THIS BOARD. + * + * The virtual CD serves ordinary sector reads, which is what stage + * loading needs, but the PSX's *streaming* path is a different + * mechanism: the drive delivers sectors continuously into a ring while + * the game consumes them, driven by a callback our layer never wires + * up. So fs_stream_read never clears, FS_StreamSync keeps returning + * "busy", fs_stream_task_state stays at -1, and strctrl's state + * machine sits in state 1 forever. + * + * That is not a harmless stall. gamed.c:588 will not change stage + * while a stream is running and gamed.c:476 has already blanked the + * screen for the load, so one stuck stream is a permanently black + * screen -- and a cutscene queues several, so a watchdog only turns it + * into ten seconds of black per stream. + * + * Decline up front and run the completion callback, so whatever waited + * on the stream proceeds immediately. The cost is the in-engine + * cutscenes and the voice-overs; the gain is a game that plays. Remove + * this once the streaming path exists -- the data is on the card and + * the rest of the machinery is intact. */ + printf( "[stream] sector %d declined: CD streaming not implemented on " + "this board\n", stream_code ); + if ( gcl_proc < 0 ) + { + GCL_ExecProc( gcl_proc & 0xFFFF, 0 ); + } + return (void *)&strctrl_work; + } +#endif + + if ( strctrl_work.field_20_state ) + { + printf( "pend!!\n" ); + if ( str_sector_8009E280 ) + { + if ( str_gcl_proc_8009E284 < 0 ) + { + GCL_ExecProc( str_gcl_proc_8009E284 & 0xFFFF, 0 ); + } + } + GM_StreamPlayStop(); + str_sector_8009E280 = stream_code; + str_gcl_proc_8009E284 = gcl_proc; + str_8009E288 = flags; + return (void *)&strctrl_work; + } + +#ifdef __psyz + /* the console put the stream buffer in the 128 KB above MEM_BOTTOM; + * same offset, real memory */ + { +#ifdef MGS_ESPBOX + extern unsigned char* mgs_main_ram; /* allocated at start (esp-box-emu) */ +#else + extern unsigned char mgs_main_ram[]; +#endif + /* 0xC9000 is MEM_BOTTOM within the reservation (libgv.h) */ + FS_StreamInit( mgs_main_ram + 0x80000 + 0xC9000 + 0x7800, FS_CDLOAD_BUF_SIZE ); + } +#else + FS_StreamInit( ( void * )0x801E7800, FS_CDLOAD_BUF_SIZE ); +#endif + GV_InitActor( EXEC_LEVEL, ( GV_ACT * )&strctrl_work, NULL ); + GV_SetNamedActor( ( GV_ACT * )&strctrl_work, &Act, &Die, "strctrl.c" ); + + strctrl_work.field_20_state = 1; + strctrl_work.field_38_proc = ( gcl_proc < 0 ) ? ( gcl_proc & 0xFFFF ) : -1; + if ( gcl_proc & 0x40000000 ) + { + strctrl_work.field_22_sub_state = 1; + } + else + { + strctrl_work.field_22_sub_state = 0; + } + strctrl_work.field_26_flags = flags; + strctrl_work.field_24 = 0; + strctrl_work.map = GM_CurrentMap; + + FS_StreamTaskStart( stream_code ); + return (void *)&strctrl_work; +} + +/*---------------------------------------------------------------------------*/ + +int GM_StreamStatus( void ) +{ + int state; + + state = strctrl_work.field_20_state - 1; + if ( !(strctrl_work.field_20_state || !FS_StreamTaskState()) ) + { + return 2; + } + return state; +} + +void GM_StreamPlayStart( void ) +{ + // TODO: Probably a switch + if ( (u_int)(u_short)strctrl_work.field_20_state - 1 < 2 ) + { + strctrl_work.field_22_sub_state = 0; + } + else + { + printf( "stream is not ready\n" ); + } +} + +void GM_StreamPlayStop( void ) +{ + printf( "GM_StreamPlayStop\n" ); + FS_StreamStop(); + + // TODO: Probably a switch + if ( (u_int)(u_short)strctrl_work.field_20_state - 1 < 2 ) + { + GV_DestroyOtherActor( &strctrl_work.actor ); + } +} + +void GM_StreamCancelCallback( void ) +{ + strctrl_work.field_38_proc = -1; +} + +int GM_StreamGetLastCode( void ) +{ + return strctrl_work.field_30_voxStream; +} + +void *GM_DemoStream( int base_sector, int gcl_proc ) +{ + int total_sector; + + strctrl_work.field_30_voxStream = base_sector; + GM_GameStatus |= STATE_VOX_STREAM; + total_sector = base_sector + FS_StreamGetTop( 1 ); + do {} while (0); + srand( 1 ); + return NewStreamControl( total_sector, gcl_proc, 2 ); +} + +void *GM_VoxStream( int vox_code, int proc ) +{ + strctrl_work.field_30_voxStream = vox_code; + vox_code++; vox_code--; + if ( GM_GameStatus & STATE_GAME_OVER ) + { + return 0; + } + + printf( "VoxStream %d\n", vox_code ); + if ( !(proc & 0x40000000) ) + { + GM_GameStatus |= STATE_VOX_STREAM; + } + return NewStreamControl( vox_code + FS_StreamGetTop(0), proc, 0 ); +} + +void *sub_80037EE0( int vox_stream, int gcl_proc ) +{ + strctrl_work.field_30_voxStream = vox_stream; + return NewStreamControl( vox_stream + FS_StreamGetTop(0), gcl_proc, 1 ); +} diff --git a/components/mgs/mgs/source/game/strctrl.h b/components/mgs/mgs/source/game/strctrl.h new file mode 100644 index 00000000..28e7e2ce --- /dev/null +++ b/components/mgs/mgs/source/game/strctrl.h @@ -0,0 +1,35 @@ +#ifndef __MGS_GAME_STRCTRL_H__ +#define __MGS_GAME_STRCTRL_H__ + +#include "libgv/libgv.h" + +// TODO: might be bigger if the jimctrl actually uses this +typedef struct StreamCtrlWork +{ + GV_ACT actor; + short field_20_state; + short field_22_sub_state; + short field_24; + short field_26_flags; + int field_28; + int map; + int field_30_voxStream; + int *field_34_pStreamData; + int field_38_proc; + int field_3C; +} StreamCtrlWork; + +/* strctrl.c */ +void *NewStreamControl( int stream_code, int gcl_proc, int flags ); + +int GM_StreamStatus( void ); +void GM_StreamPlayStart( void ); +void GM_StreamPlayStop( void ); +void GM_StreamCancelCallback( void ); +int GM_StreamGetLastCode( void ); + +void *GM_DemoStream( int base_sector, int gcl_proc ); +void *GM_VoxStream( int vox_code, int proc ); +void *sub_80037EE0( int vox_stream, int gcl_proc ); + +#endif // __MGS_GAME_STRCTRL_H__ diff --git a/components/mgs/mgs/source/game/target.c b/components/mgs/mgs/source/game/target.c new file mode 100644 index 00000000..1e6d2d85 --- /dev/null +++ b/components/mgs/mgs/source/game/target.c @@ -0,0 +1,685 @@ +#include "target.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "game.h" +#include "linkvar.h" + +// Instead of dynamically allocating TARGETs, +// the game uses the big TARGET array GM_TargetDatas. +// +// This also makes it easier to iterate over all TARGETs in use. +// +// Therefore each entry in this array will be either: +// - already used up (returned by GM_AllocTarget and not yet freed) +// - not used (target->class == 0) +// +// When allocating new TARGETs, the game will try to find a "free" slot in this array. +// It tries to do it efficiently (without having to scan the whole array too often). +// The game tries to ensure that slots [0, GM_TargetCount) +// are all actively used, while the latter part of the array is free. +// However, deallocating TARGETs can result in "holes" in that contiguous prefix. +// +// Two indicies are maintained: +// - GM_TargetCount: +// slots [0, GM_TargetCount) are potentially used +// slots [GM_TargetCount, TARGET_MAX) are definitely not used +// - GM_StaleTargetCount: +// how many slots [0, GM_TargetCount) are not used +// +// In most cases the game will just take GM_TargetCount+1 +// as a next used slot. If a TARGET is freed in the middle of the array +// (gets orphaned: GM_StaleTargetCount) the code will try +// to "plug" the hole when allocating the next target. +// +// Example: +// - [Target1, Target2, Target3, FREE, FREE, FREE, FREE...] +// GM_TargetCount = 3, GM_StaleTargetCount = 0 +// +// - GM_AllocTarget() +// +// - [Target1, Target2, Target3, Target4, FREE, FREE, FREE...] +// GM_TargetCount = 4, GM_StaleTargetCount = 0 +// +// - GM_FreeTarget(Target3) +// +// - [Target1, Target2, FREE, Target4, FREE, FREE, FREE...] +// GM_TargetCount = 4, GM_StaleTargetCount = 1 +// +// - GM_AllocTarget() +// +// - [Target1, Target2, Target5, Target4, FREE, FREE, FREE...] +// GM_TargetCount = 4, GM_StaleTargetCount = 0 +// +// - GM_FreeTarget(Target4) +// +// - [Target1, Target2, Target5, FREE, FREE, FREE, FREE...] +// GM_TargetCount = 3, GM_StaleTargetCount = 0 +// +extern TARGET GM_TargetDatas[TARGET_MAX]; + +STATIC int SECTION(".sbss") GM_TargetCount; +STATIC int SECTION(".sbss") GM_StaleTargetCount; + +static inline int Conflict(int center1, int size1, int center2, int size2) +{ + size1 += size2; + return center1 - size1 <= center2 && center2 - size1 <= center1; +} + +static inline int MapConflict(int map1, int map2) +{ + return (GM_PlayerMap & map1) && (GM_PlayerMap & map2); +} + +// Checks if two targets intersect. +// For an intersection, the two targets must be: +// 1. On opposing sides (PLAYER_SIDE & ENEMY_SIDE) +// 2. Contained within one's bounds +// 3. On the same map(s) +static int TargetConflict(TARGET *a, TARGET *b) +{ + return (((a->side & b->side) == 0) && + Conflict(a->center.vx, a->size.vx, b->center.vx, b->size.vx) && + Conflict(a->center.vz, a->size.vz, b->center.vz, b->size.vz) && + Conflict(a->center.vy, a->size.vy, b->center.vy, b->size.vy) && + MapConflict(a->map, b->map)); +} + +// Checks if a target with no side intersects with a given target. +// Similar to TargetConflict, but `a` must be on NO_SIDE. +static int TargetConflictMap(TARGET *a, TARGET *b) +{ + return (a->side == NO_SIDE && + Conflict(a->center.vx, a->size.vx, b->center.vx, b->size.vx) && + Conflict(a->center.vz, a->size.vz, b->center.vz, b->size.vz) && + Conflict(a->center.vy, a->size.vy, b->center.vy, b->size.vy) && + MapConflict(a->map, b->map)); +} + +void GM_InitTargetSystem(void) +{ + GM_TargetCount = 0; + GM_StaleTargetCount = 0; +} + +TARGET *GM_AllocTarget(void) +{ + TARGET *target; + int i; + + // The game tries to maintain that slots [0, GM_TargetCount) + // in GM_TargetDatas are all used up. However some "holes" could appear. + + // Are there no "holes" to plug in the GM_TargetDatas array? + if (GM_StaleTargetCount == 0) + { + // There are no "holes" in the GM_TargetDatas array + // so let's just take the next slot and increment GM_TargetCount + + if (GM_TargetCount >= TARGET_MAX) + { + // Out of memory... + return NULL; + } + + target = &GM_TargetDatas[GM_TargetCount]; + target->class = TARGET_AVAIL; + GM_TargetCount++; + return target; + } + + // There are "holes" in the GM_TargetDatas array, let's + // try to find one and use it up. + + target = GM_TargetDatas; + if (GM_TargetCount > 0) + { + for (i = GM_TargetCount; i > 0; i--, target++) + { + if (target->class == TARGET_STALE) + { + // Found unused slot, let's use it. + target->class = TARGET_AVAIL; + GM_StaleTargetCount--; + return target; + } + } + } + + // This should be unreachable, we should have found a slot above. + GM_StaleTargetCount = 0; + return NULL; +} + +void GM_FreeTarget(TARGET *target) +{ + // The game tries to maintain that slots [0, GM_TargetCount) + // in GM_TargetDatas are all used up. However some "holes" could appear + // due to freeing a TARGET here: + + if (target) + { + if (target == &GM_TargetDatas[GM_TargetCount - 1]) + { + // Freeing the last used TARGET doesn't result + // in a "hole", just adjust the lastSlotUsed. + GM_TargetCount--; + } + else + { + // Freeing this TARGET resulted in a "hole" in the array: + GM_StaleTargetCount++; + } + target->class = TARGET_STALE; // mark as a free slot + } +} + +void GM_MoveTarget(TARGET *target, SVECTOR *center) +{ + target->center = *center; + target->map = GM_CurrentMap; +} + +TARGET *GM_CaptureTarget(TARGET *target) +{ + TARGET *iter; + int i; + + iter = GM_TargetDatas; + for (i = GM_TargetCount; i > 0; iter++, i--) + { + // Skip if we are checking the current target + if (target == iter) + { + continue; + } + + // Skip if wrong class + if (!(iter->class & TARGET_CAPTURE)) + { + continue; + } + + // Return if this is the first intersection for the class + if (TargetConflict(iter, target) && !(iter->damaged & TARGET_CAPTURE)) + { + iter->damaged |= TARGET_CAPTURE; + iter->a_mode = target->a_mode; + iter->field_18 = target->field_18; + iter->field_1C = target->field_1C; + iter->faint -= target->faint; + iter->captured = 1; + + target->damaged |= TARGET_CAPTURE; + + return iter; + } + } + + return NULL; +} + +TARGET *GM_C4Target(TARGET *target) +{ + TARGET *iter; + int i; + + iter = GM_TargetDatas; + for (i = GM_TargetCount; i > 0; iter++, i--) + { + // Skip if we are checking the current target + if (target == iter) + { + continue; + } + + // Skip if wrong class + if (!(iter->class & TARGET_C4)) + { + continue; + } + + // Return if this is the first intersection for the class + if (TargetConflict(iter, target) && !(iter->damaged & TARGET_C4)) + { + iter->damaged |= TARGET_C4; + target->damaged |= TARGET_C4; + return iter; + } + } + + return NULL; +} + +int GM_TouchTarget(TARGET *target) +{ + TARGET *iter; + int i; + int hp, oldhp; + + if (!(target->class & TARGET_TOUCH)) + { + return 0; + } + + if (target->damaged & TARGET_TOUCH) + { + return 1; + } + + iter = GM_TargetDatas; + i = GM_TargetCount; + + hp = target->vital; + + iter = GM_TargetDatas; + for (i = GM_TargetCount; i > 0; iter++, i--) + { + // Skip if we are checking the current target + if (target == iter) + { + continue; + } + + // Skip if wrong class + if (!(iter->class & TARGET_TOUCH)) + { + continue; + } + + // Touch if there is an intersection for the class + if (TargetConflict(iter, target)) + { + oldhp = iter->vital; + iter->vital -= hp; + iter->damage += oldhp - iter->vital; + + iter->damaged |= TARGET_TOUCH; + target->damaged |= TARGET_TOUCH; + } + } + + // Return whether any target was touched + return (target->damaged & TARGET_TOUCH) >> 7; +} + +static inline int UpdatePowerParam( int type, int a, int b ) +{ + switch ( type & 0x3 ) + { + case POWER_ONCE: + return 0; + case POWER_DECREASE: + return a - b; + case POWER_THRESHOLD: + return ( a > b ) ? a : 0; + case POWER_CONST: + default: + return a; + } +} + +#define SCALE_VXZ(in, scale, out) \ +do \ +{ \ + int angle; \ + angle = GV_VecDir2(in); \ + GV_DirVec2(angle, scale, out); \ +} while (0) + +int GM_PowerTarget(TARGET *target) +{ + SVECTOR dist; + SVECTOR scaled; + int hp, hp2; + int p_mode; + TARGET *iter; + int i; + int hp_diff; + + hp = target->vital; + p_mode = target->p_mode; + + iter = GM_TargetDatas; + for (i = GM_TargetCount; i > 0; iter++, i--) + { + if (target == iter) + { + continue; + } + + if (!(iter->class & TARGET_POWER)) + { + continue; + } + + if (!TargetConflict(iter, target)) + { + continue; + } + + iter->damaged |= TARGET_POWER; + + hp2 = iter->vital; + iter->vital = UpdatePowerParam(iter->p_mode, hp2, hp); + hp = UpdatePowerParam(p_mode, hp, hp2); + + iter->damage += hp2 - iter->vital; + iter->faint -= target->faint; + iter->a_mode = target->a_mode; + iter->weapon = target->weapon; + + if (p_mode & POWER_EXPLODE) + { + GV_SubVec3(&iter->center, &target->center, &dist); + SCALE_VXZ(&dist, target->force.vx, &scaled); + GV_AddVec3(&iter->force, &scaled, &iter->force); + } + else + { + GV_AddVec3(&iter->force, &target->force, &iter->force); + } + + if (hp < 0) + { + break; + } + } + + hp_diff = target->vital - hp; + target->vital = hp; + + if (hp_diff > 0) + { + target->damaged |= TARGET_POWER; + } + + return hp_diff; +} + +static inline int Push(int center1, int size1, int center2, int size2) +{ + size1 += size2; + size2 = center2 - center1; + if (size2 >= 0) + { + size1 -= size2; + if (size1 < 0) size1 = 0; + return size1; + } + else + { + size2 += size1; + if (size2 < 0) size2 = 0; + return -size2; + } +} + +static inline int PushTarget(TARGET *off, TARGET *def) +{ + int x, z, x2, z2; + int dir; + + x = Push(def->center.vx, def->size.vx, off->center.vx, off->size.vx); + if (x == 0) return 0; + + z = Push(def->center.vz, def->size.vz, off->center.vz, off->size.vz); + if (z == 0) return 0; + + if (def->flag & 2) return 1; + + x2 = abs(x); + z2 = abs(z); + + if (x2 <= z2) + { + x /= 2; + off->offset.vx += x; + def->offset.vx -= x; + dir = (x > 0) ? 2 : 4; + } + else + { + z /= 2; + off->offset.vz += z; + def->offset.vz -= z; + dir = (z > 0) ? 1 : 3; + } + + if (def->flag & 1) off->offset.pad = dir; + return 1; +} + +int GM_PushTarget(TARGET *target) +{ + TARGET *iter; + int count; + + if (!(target->class & TARGET_PUSH)) + { + return 0; + } + + target->offset = DG_ZeroVector; + target->offset.pad = 0; + + iter = GM_TargetDatas; + + for (count = GM_TargetCount; count > 0; iter++, count--) + { + iter->push_side = NO_SIDE; + + if ((target == iter) || !(iter->class & TARGET_PUSH) || !TargetConflictMap(iter, target)) + { + continue; + } + + if (PushTarget(target, iter)) + { + iter->damaged |= TARGET_PUSH; + target->damaged |= TARGET_PUSH; + iter->push_side = target->side; + } + } + + return (short)(target->damaged & TARGET_PUSH) > 0; +} + +void GM_SetTarget(TARGET *target, int class, int side, SVECTOR *size) +{ + short cur_map = GM_CurrentMap; + target->class = class; + target->side = side; + target->damaged = 0; + target->map = cur_map; + target->size = *size; + target->flag = 0; +} + +void GM_SetCaptureTarget(TARGET *target, int a_mode, int faint, int *a4, SVECTOR *a5) +{ + target->field_18 = a4; + target->a_mode = a_mode; + target->faint = faint; + target->field_1C = a5; +} + +void GM_SetPowerTarget(TARGET *target, int p_mode, int a_mode, int vital, int faint, SVECTOR *force) +{ + target->p_mode = p_mode; + target->a_mode = a_mode; + target->vital = vital; + target->damage = 0; + target->faint = faint; + target->force = *force; + target->weapon = WP_None; +} + +void GM_TargetBody(TARGET *target, MATRIX *body) +{ + target->body = body; +} + +static void SetFromToTarget(SVECTOR *from, SVECTOR *to, TARGET *targ) +{ + int f, t, s; + + f = from->vx; t = to->vx; s = (f - t) / 2; + targ->center.vx = (f + t) / 2; + targ->size.vx = abs(s); + f = from->vy; t = to->vy; s = (f - t) / 2; + targ->center.vy = (f + t) / 2; + targ->size.vy = abs(s); + f = from->vz; t = to->vz; s = (f - t) / 2; + targ->center.vz = (f + t) / 2; + targ->size.vz = abs(s); +} + +static int OnlineTarget(SVECTOR *from, SVECTOR *to, TARGET *def, SVECTOR *hit) +{ + int cx, cy, cz, sx, sy, sz, df, f0; + int c1, c2; + + f0 = from->vx; + if ((df = to->vx - f0) == 0) goto cutx_failed; + cx = def->center.vx; sx = def->size.vx; + if (cx < f0) + { + cx += sx; + if (cx > f0) cx = f0; + } + else + { + cx -= sx; + if (cx < f0) cx = f0; + } + c1 = from->vy + (to->vy - from->vy) * (cx - f0) / df; + if (c1 < def->center.vy - def->size.vy || + c1 > def->center.vy + def->size.vy) goto cutx_failed; + c2 = from->vz + (to->vz - from->vz) * (cx - f0) / df; + if (c2 < def->center.vz - def->size.vz || + c2 > def->center.vz + def->size.vz) goto cutx_failed; + hit->vx = cx; + hit->vy = c1; + hit->vz = c2; + return 1; +cutx_failed: + f0 = from->vy; + if ((df = to->vy - f0) == 0) goto cuty_failed; + cy = def->center.vy; sy = def->size.vy; + if (cy < f0) + { + cy += sy; + if (cy > f0) cy = f0; + } + else + { + cy -= sy; + if (cy < f0) cy = f0; + } + c1 = from->vz + (to->vz - from->vz) * (cy - f0) / df; + if (c1 < def->center.vz - def->size.vz || + c1 > def->center.vz + def->size.vz) goto cuty_failed; + c2 = from->vx + (to->vx - from->vx) * (cy - f0) / df; + if (c2 < def->center.vx - def->size.vx || + c2 > def->center.vx + def->size.vx) goto cuty_failed; + hit->vy = cy; + hit->vz = c1; + hit->vx = c2; + return 1; +cuty_failed: + f0 = from->vz; + if ((df = to->vz - f0) == 0) goto cutz_failed; + cz = def->center.vz; sz = def->size.vz; + if (cz < f0) + { + cz += sz; + if (cz > f0) cz = f0; + } + else + { + cz -= sz; + if (cz < f0) cz = f0; + } + c1 = from->vx + (to->vx - from->vx) * (cz - f0) / df; + if (c1 < def->center.vx - def->size.vx || + c1 > def->center.vx + def->size.vx) goto cutz_failed; + c2 = from->vy + (to->vy - from->vy) * (cz - f0) / df; + if (c2 < def->center.vy - def->size.vy || + c2 > def->center.vy + def->size.vy) goto cutz_failed; + hit->vz = cz; + hit->vx = c1; + hit->vy = c2; + return 1; +cutz_failed: + return 0; +} + +int GM_OnlineTargetCheck(SVECTOR *from, SVECTOR *to, int map, SVECTOR *hit, int side) +{ + TARGET tmp; + TARGET *iter; + int found, i; + + tmp.map = map; + tmp.side = NO_SIDE; + + SetFromToTarget(from, to, &tmp); + + iter = GM_TargetDatas; + found = 0; + for (i = GM_TargetCount; i > 0; i--) + { + if (iter->side != side && (iter->class & TARGET_SEEK)) + { + if (TargetConflict(iter, &tmp) && OnlineTarget(from, to, iter, hit)) + { + SetFromToTarget(from, hit, &tmp); + found = 1; + } + } + + iter++; + } + + return found; +} + +int GM_OnlineTargetCheckAny(SVECTOR *from, SVECTOR *to, int map, SVECTOR *hit) +{ + TARGET tmp; + TARGET *iter; + int i; + + tmp.map = map; + tmp.side = NO_SIDE; + + SetFromToTarget(from, to, &tmp); + + iter = GM_TargetDatas; + for (i = GM_TargetCount; i > 0; i--) + { + if ((iter->flag & 0x1) && (iter->class & TARGET_SEEK)) + { + if (TargetConflict(iter, &tmp) && OnlineTarget(from, to, iter, hit)) + { + return 1; + } + } + + iter++; + } + + return 0; +} + +void GM_GetTargets(int *count, TARGET **targets) +{ + *count = GM_TargetCount; + *targets = GM_TargetDatas; +} diff --git a/components/mgs/mgs/source/game/target.h b/components/mgs/mgs/source/game/target.h new file mode 100644 index 00000000..308b796c --- /dev/null +++ b/components/mgs/mgs/source/game/target.h @@ -0,0 +1,101 @@ +#ifndef __MGS_GAME_TARGET_H__ +#define __MGS_GAME_TARGET_H__ + +#include +#include +#include + +typedef struct TARGET +{ + u_short class; + u_short side; + u_short map; + u_short damaged; + SVECTOR center; + SVECTOR size; + int *field_18; /* unused */ + SVECTOR *field_1C; /* unused */ + MATRIX *body; + short p_mode; + short vital; + short damage; + short faint; + SVECTOR force; + SVECTOR offset; + short flag; + short a_mode; + short push_side; + short captured; + int weapon; +} TARGET; + +enum +{ + TARGET_STALE = 0x0, + TARGET_AVAIL = 0x1, + TARGET_CAPTURE = 0x2, + TARGET_POWER = 0x4, + TARGET_PUSH = 0x8, + TARGET_SEEK = 0x10, + TARGET_DOWN = 0x20, + TARGET_C4 = 0x40, + TARGET_TOUCH = 0x80, + TARGET_PUSH_OVER = 0x100, + TARGET_NO_LOCKON = 0x200, + TARGET_C4_CLEAR = 0x400, + TARGET_DIE = 0x800, +}; + +enum +{ + POWER_ONCE = 0, + POWER_DECREASE = 1, + POWER_THRESHOLD = 2, + POWER_CONST = 3, + POWER_EXPLODE = 4, +}; + +enum +{ + ATK_BLAST = 0x2, +}; + +enum +{ + NO_SIDE = 0, + PLAYER_SIDE = 1, + ENEMY_SIDE = 2, + BOTH_SIDE = 3 +}; + +static inline void SetTargetClass( TARGET *target, unsigned int flag ) +{ + target->class |= ( flag ); +} + +static inline void UnsetTargetClass( TARGET *target, unsigned int flag ) +{ + target->class &= ~( flag ); +} + +#define TARGET_MAX 64 + +/* target.c */ +void GM_InitTargetSystem(void); +TARGET *GM_AllocTarget(void); +void GM_FreeTarget(TARGET *target); +void GM_MoveTarget(TARGET *target, SVECTOR *pVec); +TARGET *GM_CaptureTarget(TARGET *target); +TARGET *GM_C4Target(TARGET *target); +int GM_TouchTarget(TARGET *target); +int GM_PowerTarget(TARGET *target); +int GM_PushTarget(TARGET *target); +void GM_SetTarget(TARGET *target, int class, int side, SVECTOR *size); +void GM_SetCaptureTarget(TARGET *target, int a_mode, int faint, int *a4, SVECTOR *a5); +void GM_SetPowerTarget(TARGET *target, int p_mode, int a_mode, int vital, int faint, SVECTOR *force); +void GM_TargetBody(TARGET *target, MATRIX *body) ; +int GM_OnlineTargetCheck(SVECTOR *from, SVECTOR *to, int map, SVECTOR *hit, int side); +int GM_OnlineTargetCheckAny(SVECTOR *from, SVECTOR *to, int map, SVECTOR *hit); +void GM_GetTargets(int *count, TARGET **targets); + +#endif // __MGS_GAME_TARGET_H__ diff --git a/components/mgs/mgs/source/game/tobcnt.c b/components/mgs/mgs/source/game/tobcnt.c new file mode 100644 index 00000000..c3ef117a --- /dev/null +++ b/components/mgs/mgs/source/game/tobcnt.c @@ -0,0 +1,625 @@ +#include "tobcnt.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "strcode.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_DAEMON + +#define NUM_LINES 166 // ((countof(tobcnt_logo_data)-1)/4) + +#define PCX_COMING_SOON 0x0abb // GV_StrCode("COMING_SOON") + +#define FLARE_COLOR MAKE_RGB0(0,64,128) + +typedef struct _Work +{ + GV_ACT actor; + short state; + short time; + short timeout; + short gradient; + int vox; + POLY_G4 polys[2][12]; + LINE_G2 lines[2][NUM_LINES]; + DR_TPAGE anim_tpage[2]; + TILE tile[2]; + DR_TPAGE fade_tpage[2]; + DVECTOR directions[NUM_LINES]; +} Work; + +/*---------------------------------------------------------------------------*/ + +static short tobcnt_logo_data[] = { + NUM_LINES, +/*-- X0 Y0 X1 Y1 --*/ + 19, 93, 39, 93, /* 0 */ + 19, 93, 18, 98, /* 1 */ + 39, 93, 38, 98, /* 2 */ + 31, 98, 38, 98, /* 3 */ + 31, 98, 25, 129, /* 4 */ + 25, 129, 19, 129, /* 5 */ + 42, 93, 60, 93, /* 6 */ + 42, 93, 41, 98, /* 7 */ + 60, 93, 59, 98, /* 8 */ + 41, 98, 59, 98, /* 9 */ + 40, 102, 37, 117, /* 10 */ + 37, 117, 55, 117, /* 11 */ + 55, 117, 58, 102, /* 12 */ + 40, 102, 46, 102, /* 13 */ + 52, 102, 58, 102, /* 14 */ + 44, 112, 46, 102, /* 15 */ + 44, 112, 50, 112, /* 16 */ + 52, 102, 50, 112, /* 17 */ + 18, 98, 25, 98, /* 18 */ + 25, 98, 19, 129, /* 19 */ + 68, 98, 69, 93, /* 20 */ + 69, 93, 85, 93, /* 21 */ + 68, 98, 80, 98, /* 22 */ + 77, 102, 67, 102, /* 23 */ + 67, 102, 64, 117, /* 24 */ + 64, 117, 79, 117, /* 25 */ + 79, 117, 84, 113, /* 26 */ + 85, 109, 82, 105, /* 27 */ + 87, 98, 85, 93, /* 28 */ + 87, 98, 82, 105, /* 29 */ + 80, 98, 77, 102, /* 30 */ + 71, 112, 76, 112, /* 31 */ + 76, 112, 79, 110, /* 32 */ + 71, 112, 72, 107, /* 33 */ + 72, 107, 77, 107, /* 34 */ + 79, 110, 77, 107, /* 35 */ + 85, 109, 84, 113, /* 36 */ + 86, 117, 102, 117, /* 37 */ + 91, 93, 107, 93, /* 38 */ + 90, 98, 106, 98, /* 39 */ + 89, 102, 105, 102, /* 40 */ + 89, 102, 86, 117, /* 41 */ + 91, 93, 90, 98, /* 42 */ + 107, 93, 106, 98, /* 43 */ + 104, 107, 94, 107, /* 44 */ + 105, 102, 104, 107, /* 45 */ + 93, 112, 103, 112, /* 46 */ + 103, 112, 102, 117, /* 47 */ + 93, 112, 94, 107, /* 48 */ + 122, 93, 135, 93, /* 49 */ + 122, 93, 112, 105, /* 50 */ + 130, 117, 116, 117, /* 51 */ + 125, 98, 134, 98, /* 52 */ + 135, 93, 134, 98, /* 53 */ + 125, 98, 119, 105, /* 54 */ + 119, 105, 121, 112, /* 55 */ + 121, 112, 131, 112, /* 56 */ + 131, 112, 130, 117, /* 57 */ + 112, 105, 116, 117, /* 58 */ + 157, 93, 139, 93, /* 59 */ + 134, 117, 152, 117, /* 60 */ + 138, 98, 156, 98, /* 61 */ + 143, 102, 141, 112, /* 62 */ + 141, 112, 147, 112, /* 63 */ + 149, 102, 147, 112, /* 64 */ + 137, 102, 143, 102, /* 65 */ + 149, 102, 155, 102, /* 66 */ + 139, 93, 138, 98, /* 67 */ + 137, 102, 134, 117, /* 68 */ + 155, 102, 152, 117, /* 69 */ + 157, 93, 156, 98, /* 70 */ + 161, 93, 160, 98, /* 71 */ + 161, 93, 167, 93, /* 72 */ + 170, 102, 172, 106, /* 73 */ + 174, 98, 175, 93, /* 74 */ + 175, 93, 181, 93, /* 75 */ + 179, 102, 176, 117, /* 76 */ + 156, 117, 162, 117, /* 77 */ + 162, 117, 165, 103, /* 78 */ + 165, 103, 171, 117, /* 79 */ + 171, 117, 176, 117, /* 80 */ + 160, 98, 169, 98, /* 81 */ + 167, 93, 169, 98, /* 82 */ + 159, 102, 170, 102, /* 83 */ + 159, 102, 156, 117, /* 84 */ + 174, 98, 180, 98, /* 85 */ + 173, 102, 172, 106, /* 86 */ + 173, 102, 179, 102, /* 87 */ + 180, 98, 181, 93, /* 88 */ + 184, 93, 183, 98, /* 89 */ + 204, 93, 184, 93, /* 90 */ + 204, 93, 203, 98, /* 91 */ + 203, 98, 183, 98, /* 92 */ + 189, 102, 186, 117, /* 93 */ + 195, 102, 192, 117, /* 94 */ + 186, 117, 192, 117, /* 95 */ + 189, 102, 195, 102, /* 96 */ + 205, 102, 202, 117, /* 97 */ + 207, 93, 213, 93, /* 98 */ + 211, 102, 208, 117, /* 99 */ + 202, 117, 208, 117, /* 100 */ + 206, 98, 212, 98, /* 101 */ + 205, 102, 211, 102, /* 102 */ + 207, 93, 206, 98, /* 103 */ + 213, 93, 212, 98, /* 104 */ + 215, 102, 212, 117, /* 105 */ + 212, 117, 218, 117, /* 106 */ + 218, 117, 221, 103, /* 107 */ + 217, 93, 223, 93, /* 108 */ + 223, 93, 225, 98, /* 109 */ + 225, 98, 216, 98, /* 110 */ + 215, 102, 226, 102, /* 111 */ + 226, 102, 228, 106, /* 112 */ + 221, 103, 227, 117, /* 113 */ + 227, 117, 232, 117, /* 114 */ + 228, 106, 229, 102, /* 115 */ + 229, 102, 235, 102, /* 116 */ + 235, 102, 232, 117, /* 117 */ + 217, 93, 216, 98, /* 118 */ + 230, 98, 236, 98, /* 119 */ + 230, 98, 231, 93, /* 120 */ + 236, 98, 237, 93, /* 121 */ + 231, 93, 237, 93, /* 122 */ + 239, 102, 236, 117, /* 123 */ + 241, 93, 247, 93, /* 124 */ + 236, 117, 254, 117, /* 125 */ + 254, 117, 257, 102, /* 126 */ + 243, 112, 249, 112, /* 127 */ + 243, 112, 245, 102, /* 128 */ + 249, 112, 251, 102, /* 129 */ + 240, 98, 246, 98, /* 130 */ + 252, 98, 258, 98, /* 131 */ + 239, 102, 245, 102, /* 132 */ + 251, 102, 257, 102, /* 133 */ + 253, 93, 259, 93, /* 134 */ + 241, 93, 240, 98, /* 135 */ + 247, 93, 246, 98, /* 136 */ + 253, 93, 252, 98, /* 137 */ + 259, 93, 258, 98, /* 138 */ + 258, 117, 274, 117, /* 139 */ + 258, 117, 261, 102, /* 140 */ + 263, 93, 279, 93, /* 141 */ + 277, 102, 276, 107, /* 142 */ + 262, 98, 278, 98, /* 143 */ + 261, 102, 277, 102, /* 144 */ + 263, 93, 262, 98, /* 145 */ + 279, 93, 278, 98, /* 146 */ + 266, 107, 276, 107, /* 147 */ + 275, 112, 265, 112, /* 148 */ + 266, 107, 265, 112, /* 149 */ + 275, 112, 274, 117, /* 150 */ + 281, 102, 278, 117, /* 151 */ + 283, 93, 298, 93, /* 152 */ + 298, 93, 302, 104, /* 153 */ + 302, 104, 293, 117, /* 154 */ + 293, 117, 278, 117, /* 155 */ + 282, 98, 293, 98, /* 156 */ + 293, 98, 295, 104, /* 157 */ + 295, 104, 289, 112, /* 158 */ + 289, 112, 285, 112, /* 159 */ + 285, 112, 287, 102, /* 160 */ + 281, 102, 287, 102, /* 161 */ + 283, 93, 282, 98, /* 162 */ + 30, 120, 28, 129, /* 163 */ + 30, 120, 291, 120, /* 164 */ + 28, 129, 285, 129 /* 165 */ +}; + +/*---------------------------------------------------------------------------*/ + +static char *SetOptionPrim( char *buf, int x, int y, int name, unsigned int color, OT_TYPE *ot ) +{ + DG_TEX *tex; + SPRT *sprt; + DR_TPAGE *tpage; + + tex = DG_GetTexture( name ); + + sprt = (SPRT *)buf; + setXY0( sprt, x, y ); + setUV0( sprt, tex->off_x, tex->off_y ); + setWH( sprt, tex->w + 1, tex->h + 1 ); + sprt->clut = tex->clut; + LSTORE( color, &sprt->r0 ); + setSprt( sprt ); + addPrim( ot, sprt ); + + tpage = (DR_TPAGE *)(buf + sizeof(SPRT)); + setDrawTPage( tpage, 1, 0, tex->tpage ); + addPrim( ot, tpage ); + + return (buf + sizeof(SPRT) + sizeof(DR_TPAGE)); +} + +static int GetLineColor( int shade, int step ) +{ + int r, gb; + + if ( step < 8 ) + { + r = 128; + gb = 255; + } + else + { + step = MIN( step - 8, 8 ); + r = 128 - step * 16; + gb = 255 - ((255 - shade) * step) / 8; + } + + return MAKE_RGB(r, gb, gb); +} + +/*---------------------------------------------------------------------------*/ + +static void DrawAnimation( Work *work, OT_TYPE *ot ) +{ + int x0, y0; + int x1, y1; + int width, height; + int state; + int count; + int color1, color2, color3; + DR_TPAGE *tpage; + short *lines; + int flag; + DVECTOR *directions; + LINE_G2 *line; + POLY_G4 *poly; + int shade; + + state = work->state; + lines = tobcnt_logo_data; + count = tobcnt_logo_data[0]; + + directions = work->directions; + line = work->lines[GV_Clock]; + poly = work->polys[GV_Clock]; + + lines++; + + if ( state > 0 ) + { + if ( state < count + 16 ) + { + work->state = state + 2; + } + else + { + work->state = 0; + } + } + + flag = 1; + if ( state == 0 ) + { + flag = 0; + state = 16; + } + + for ( ; count > 0; count-- ) + { + height = 0; + width = 0; + + if ( flag ) + { + if ( state < 8 ) + { + width = directions->vx >> state; + height = directions->vy >> state; + + if ( (count % 4) == 2 ) + { + width = -width; + height = -height; + } + } + + if ( --state < 0 ) + { + break; + } + } + + x0 = lines[0]; + x1 = lines[2]; + + x0 += width; + x1 += width * 2; + + y0 = lines[1] + height; + y1 = lines[3] + height * 2; + + setXY2(line, x0, y0, x1, y1); + + color1 = x0 - 160; + color2 = x1 - 160; + + if (color1 < 0) + { + color1 = -color1; + } + if (color2 < 0) + { + color2 = -color2; + } + + color1 = 255 - color1; + color2 = 255 - color2; + + if (color1 > 255) + { + color1 = 255; + } + + if (color2 > 255) + { + color2 = 255; + } + + LSTORE(GetLineColor(color1, state), &line->r0); + + if (count < 3) + { + LSTORE(COLOR_BLACK, &line->r1); + } + else + { + LSTORE(GetLineColor(color2, state), &line->r1); + } + + setLineG2(line); + addPrim(ot, line); + line++; + + if (state >= 8 && state <= 13) + { + width = -(directions->vy >> (state - 6)); + height = directions->vx >> (state - 6); + + setXY4(poly, + x1 - width, y1 - height, + x0 - width, y0 - height, + (x0 + x1) / 2, + (y0 + y1) / 2, + x0 + width, y0 + height); + + LSTORE(COLOR_BLACK, &poly->r0); + LSTORE(COLOR_BLACK, &poly->r1); + LSTORE(FLARE_COLOR, &poly->r2); + LSTORE(COLOR_BLACK, &poly->r3); + + setPolyG4(poly); + setSemiTrans(poly, 1); + addPrim(ot, poly); + poly++; + + setXY4(poly, + x1 - width, y1 - height, + x1 + width, y1 + height, + (x0 + x1) / 2, + (y0 + y1) / 2, + x0 + width, y0 + height); + + LSTORE(COLOR_BLACK, &poly->r0); + LSTORE(COLOR_BLACK, &poly->r1); + LSTORE(FLARE_COLOR, &poly->r2); + LSTORE(COLOR_BLACK, &poly->r3); + + setPolyG4(poly); + setSemiTrans(poly, 1); + addPrim(ot, poly); + poly++; + } + + lines += 4; + directions++; + } + + tpage = &work->anim_tpage[GV_Clock]; + setDrawTPage( tpage, 1, 1, getTPage( 0, 1, 0, 0 ) ); + addPrim( ot, tpage ); + + if ( work->state == 0 ) + { + if (work->gradient > 32) + { + shade = (64 - work->gradient) * 2 + 100; + } + else + { + shade = work->gradient * 2 + 100; + } + + color3 = (shade << 8) | (shade << 16); + SetOptionPrim( (char *)work->polys[GV_Clock], 114, 121, PCX_COMING_SOON, color3, ot ); + } +} + +static void DrawBackgroundFade(Work *work, OT_TYPE *ot, int shade) +{ + TILE *tile; + DR_TPAGE *tpage; + + tile = &work->tile[GV_Clock]; + LSTORE((shade << 16) | (shade << 8) | shade, &tile->r0); + setTile(tile); + setSemiTrans(tile, 1); + setXY0(tile, 0, 0); + setWH(tile, SCREEN_WIDTH, SCREEN_HEIGHT); + addPrim(ot, tile); + + tpage = &work->fade_tpage[GV_Clock]; + setDrawTPage(tpage, 1, 1, getTPage(0, 2, 0, 0)); + addPrim(ot, tpage); +} + +/*---------------------------------------------------------------------------*/ + +static void Act( Work *work ) +{ + OT_TYPE *ot; + GV_PAD *pad; + int shade; + + ot = DG_ChanlOTag(1); + + if (work->time < 256) + { + DrawAnimation(work, ot); + DrawBackgroundFade(work, ot, work->time); + + if ((work->time == 120) && (work->vox >= 0)) + { + GM_StreamPlayStart(); + } + + work->time += 3; + if (work->time >= 256) + { + if (work->state > 0) + { + work->time = 255; + } + else + { + work->time = 256; + work->timeout = 300; + + GV_PauseLevel |= GV_PAUSE_STOP; + DG_FreeObjectQueue(); + GM_GameStatus |= STATE_ALL_OFF; + } + } + } + else if (work->time == 256) + { + DrawAnimation(work, ot); + + pad = &GV_PadData[0]; + GM_GameStatus &= ~(STATE_PADRELEASE | STATE_PADMASK); + + if ((pad->press & (PAD_START | PAD_CIRCLE | PAD_TRIANGLE)) || (--work->timeout < 0)) + { + /* did the user press skip? */ + if (work->timeout > 0) + { + GM_SeSet3(0, 63, SE_MENU_EXIT); + } + + work->time = 257; + work->timeout = 0; + work->gradient = 32; + } + else + { + work->gradient = (work->gradient + 1) % 64; + } + } + else + { + shade = MIN(work->time - 256, 255); + DrawBackgroundFade(work, ot, shade); + DrawAnimation(work, ot); + + work->time += 4; + if (work->time > 542) + { + work->time = 542; + + if (GM_StreamStatus() == -1) + { + GV_DestroyActor(&work->actor); + } + } + } +} + +static void Die( Work *work ) +{ + char *stage_name; + + stage_name = "title"; + + GV_PauseLevel &= ~GV_PAUSE_STOP; + DG_RestartMainChanlSystem(); + GM_StreamPlayStop(); + + GM_SetArea(GV_StrCode(stage_name), stage_name); + GM_LoadRequest = 0x81; + GM_GameOverTimer = 0; +} + +static void GetResources( Work *work ) +{ + int i; + short *lines; + DVECTOR *directions; + SVECTOR *scratch; + + i = tobcnt_logo_data[0]; + lines = tobcnt_logo_data + 1; + directions = work->directions; + + ((SVECTOR *)getScratchAddr(0))->vz = 0; + + for (; i > 0; lines += 4, directions++, i--) + { + scratch = (SVECTOR *)getScratchAddr(0); + scratch[0].vx = lines[0] - lines[2]; + scratch[0].vy = lines[1] - lines[3]; + + VectorNormalSS(&scratch[0], &scratch[1]); + + directions->vx = -scratch[1].vx / 8; + directions->vy = -scratch[1].vy / 8; + } +} + +/*---------------------------------------------------------------------------*/ + +void *NewToBeContinued(int name, int where, int argc, char **argv) +{ + Work *work; + + GM_GameStatus |= STATE_ALL_OFF; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "tobcnt.c"); + + work->state = 1; + work->time = 0; + + if (GCL_GetOption('v')) + { + work->vox = GCL_StrToInt(GCL_NextStr()); + } + else + { + work->vox = -1; + } + + GetResources(work); + } + + GM_SetSound(0xff0000fe, SD_ASYNC); + GM_SetSound(0x01ffff0b, SD_ASYNC); + GM_SeSet3(0, 63, SE_GAMEOVER); + + if (work->vox >= 0) + { + GM_VoxStream(work->vox, 0x40000000); + } + + GM_GameStatus |= STATE_GAME_OVER; + + return (void *)work; +} diff --git a/components/mgs/mgs/source/game/tobcnt.h b/components/mgs/mgs/source/game/tobcnt.h new file mode 100644 index 00000000..e08093cb --- /dev/null +++ b/components/mgs/mgs/source/game/tobcnt.h @@ -0,0 +1,6 @@ +#ifndef __MGS_GAME_TOBCNT_H__ +#define __MGS_GAME_TOBCNT_H__ + +void *NewToBeContinued(int name, int where, int argc, char **argv); + +#endif // __MGS_GAME_TOBCNT_H__ diff --git a/components/mgs/mgs/source/game/vibrate.c b/components/mgs/mgs/source/game/vibrate.c new file mode 100644 index 00000000..170c9aa8 --- /dev/null +++ b/components/mgs/mgs/source/game/vibrate.c @@ -0,0 +1,136 @@ +#include "vibrate.h" + +#include "common.h" +#include "game/game.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "strcode.h" + +extern unsigned char *next_str_ptr; + +/*---------------------------------------------------------------------------*/ + +typedef struct _Work +{ + GV_ACT actor; + char flags; + char increment; + short timer; + unsigned char *data; +} Work; + +/*---------------------------------------------------------------------------*/ + +static int vibrate_act_helper_8005D358(Work *work) +{ + unsigned char *data; + + data = work->data; + work->increment = data[0]; + work->timer = 2 * data[1]; + + if (!work->increment && !work->timer) + { + return 0; + } + + work->data = &data[2]; + return 1; +} + +static int vibrate_act_helper_8005D3A4(Work *work) +{ + GCL_SetArgTop(work->data); + + if (!work->data || !GCL_NextStr()) + { + return 0; + } + + work->increment = GCL_StrToInt(GCL_NextStr()); + work->timer = 2 * GCL_StrToInt(GCL_NextStr()); + work->data = GCL_NextStr(); + + return 1; +} + +static void Act(Work *work) +{ + int amount; + int result; + + amount = GV_PassageTime; + if (work->timer <= 0) + { + if (work->flags & 0x10) + { + result = vibrate_act_helper_8005D3A4(work); + } + else + { + result = vibrate_act_helper_8005D358(work); + } + + if (!result) + { + GV_DestroyActor(&work->actor); + } + } + + work->timer -= amount; + if ((GM_GameStatus & STATE_GAME_OVER) == 0) + { + if (work->flags & 1) + { + GM_PadVibration += work->increment; + } + else + { + GM_PadVibration2 += work->increment; + } + } +} + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_USER + +void *NewPadVibrationGcl(int pan) +{ + Work *work; + char flags; + unsigned char *data; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, NULL, "vibrate.c"); + + flags = 2; + if (pan == HASH_PAN2) + { + flags = 1; + } + data = next_str_ptr; + work->flags = flags | 0x10; + work->timer = 0; + work->data = data; + } + return (void *)work; +} + +void *NewPadVibration(unsigned char *data, int flags) +{ + Work *work; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work) + { + GV_SetNamedActor(&work->actor, Act, NULL, "vibrate.c"); + + work->data = data; + work->flags = flags | 0x20; + work->timer = 0; + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/game/vibrate.h b/components/mgs/mgs/source/game/vibrate.h new file mode 100644 index 00000000..90fce70f --- /dev/null +++ b/components/mgs/mgs/source/game/vibrate.h @@ -0,0 +1,7 @@ +#ifndef __MGS_GAME_VIBRATE_H__ +#define __MGS_GAME_VIBRATE_H__ + +void *NewPadVibrationGcl(int); +void *NewPadVibration(unsigned char *, int); + +#endif // __MGS_GAME_VIBRATE_H__ diff --git a/components/mgs/mgs/source/include/charadef.h b/components/mgs/mgs/source/include/charadef.h new file mode 100644 index 00000000..29ffa41d --- /dev/null +++ b/components/mgs/mgs/source/include/charadef.h @@ -0,0 +1,26 @@ +#ifndef __MGS_CHARADEF_H__ +#define __MGS_CHARADEF_H__ + +// XXX NOTE: The empty parameter list is widely regarded as one of old C's +// XXX anti-features. C++ and C23 have changed its meaning to (void). + +typedef void *NEWCHARA(); +// typedef void *NEWCHARA(int name, int map); <-- MGS2 version. + +// NOTE: Seems like earlier in development, GCL args were passed via main-style +// (int argc, char *argv[]) arguments. This was apparently replaced with libgcl's +// GCL_GetOption/GCL_Next* APIs. Some NewActor funcs are still declared with +// argc, argv parameters, but they go unused. +// +// XXX The "chara" command handler in game/script.c still calls NewActor funcs +// XXX with this outdated signature, so registers $a2 and $a3 are trashed and +// XXX passed garbage values every time a chara is instantiated. +// XXX +// XXX typedef void *NEWCHARA(int name, int map, int argc, char **argv); + +typedef struct { + unsigned short class_id; // strcode of the chara's GCL name + NEWCHARA *func; // NewActor callback +} CHARA; + +#endif // __MGS_CHARADEF_H__ diff --git a/components/mgs/mgs/source/include/charaid.h b/components/mgs/mgs/source/include/charaid.h new file mode 100644 index 00000000..4be49f9c --- /dev/null +++ b/components/mgs/mgs/source/include/charaid.h @@ -0,0 +1,369 @@ +#ifndef __MGS_CHARAID_H__ +#define __MGS_CHARAID_H__ + +#define CHARAID_10A_DEMO 0x40e9 // +#define CHARAID_S11_OBJS 0xe068 // GV_StrCode("コントロールS11物") +#define CHARAID_11B_DEMO 0xd53c // +#define CHARAID_11G_DEMO 0x153e // +#define CHARAID_9FFD_2ND 0x9ffd // +#define CHARAID_2D0A_2ND 0x2d0a // +#define CHARAID_ABST 0x566f // GV_StrCode("アブスト") +#define CHARAID_ABST_DEMO1 0x4974 // GV_StrCode("アブストデモ1") +#define CHARAID_ABST_DEMO2 0x4975 // GV_StrCode("アブストデモ2") +#define CHARAID_AIRSHOWER 0x05ef // GV_StrCode("エアシャワー") +#define CHARAID_ARRAY 0x2a90 // +#define CHARAID_ASIATOKUN 0x02c4 // GV_StrCode("足跡君") +#define CHARAID_ASIOTOKUN 0x92bc // GV_StrCode("足音君") +#define CHARAID_AT 0x9988 // GV_StrCode("AT") +#define CHARAID_BELONG 0xf59e // GV_StrCode("付随物") +#define CHARAID_BLACKSMOKE 0x6b6c // GV_StrCode("黒煙") +#define CHARAID_BLINKTEX 0x8185 // GV_StrCode("点滅テクスチャ") +#define CHARAID_BLOODY_MERYL 0x1158 // GV_StrCode("血まみれメリル") +#define CHARAID_BLOOD_POOL 0x4e95 // GV_StrCode("血溜り") +#define CHARAID_BLOOD_POOL2 0x6a4c // GV_StrCode("血溜り2") +#define CHARAID_BLUR 0x9093 // GV_StrCode("ブラー") +#define CHARAID_BOXALL 0xf74b // GV_StrCode("全部箱") +#define CHARAID_BREAK_OBJECT 0x32fc // +#define CHARAID_BUTTONCHECK 0x6471 // GV_StrCode("ボタンチェッカー") +#define CHARAID_BUBBLE 0x1a02 // GV_StrCode("泡") +#define CHARAID_B_SELECT 0x93b6 // +#define CHARAID_C4WIRE 0x7eca // GV_StrCode("ワイヤ") +#define CHARAID_CAMERA 0x6e90 // GV_StrCode("カメラ") +#define CHARAID_CAMERA2 0x56cc // GV_StrCode("カメラ2") +#define CHARAID_CAMERAGUN 0xa9c5 // GV_StrCode("カメラ付随ガン") +#define CHARAID_CAMERASHAKE 0x7bc2 // GV_StrCode("カメラ揺らし") +#define CHARAID_CAPE 0xb99f // +#define CHARAID_CAT_IN 0x51c6 // +#define CHARAID_CDCHANGE 0xf722 // GV_StrCode("CD交換") +#define CHARAID_CENSOR 0x43a0 // GV_StrCode("赤外線センサー") +#define CHARAID_CHAIR 0x788d // GV_StrCode("椅子") +#define CHARAID_CINEMA 0x7a05 // GV_StrCode("シネマスクリーン") +#define CHARAID_CLAYMORE 0x3c0c // GV_StrCode("クレイモア地雷") +#define CHARAID_COMMANDER 0xc6d7 // GV_StrCode("コマンダー") +#define CHARAID_CONTAINER 0xcc45 // GV_StrCode("コンテナ") +#define CHARAID_COUNTDOWN 0x22c6 // GV_StrCode("カウントダウン") +#define CHARAID_COUNTDOWN2 0xeced // GV_StrCode("カウントダウンタイマー") +#define CHARAID_CRANE 0xa3fb // GV_StrCode("クレーン") +#define CHARAID_CROW 0x8e60 // GV_StrCode("カラス") +#define CHARAID_DEATHSPARK 0xbe79 // GV_StrCode("送別火花") +#define CHARAID_DAMAGESMOKE 0x6a98 // GV_StrCode("ダメージ煙") +#define CHARAID_DAMAGESMOKE2 0x76bc // GV_StrCode("ダメージ煙2") +#define CHARAID_DEMOCANCEL 0xb4e6 // GV_StrCode("デモキャンセル") +#define CHARAID_DEMODOLL 0xe97e // GV_StrCode("デモ人形") +#define CHARAID_DEMOSEL 0x3686 // GV_StrCode("デモ劇場") +#define CHARAID_DISPLAY 0x9f7d // +#define CHARAID_DMYFLOOR 0x9d00 // GV_StrCode("落し穴") +#define CHARAID_DMYWALL 0x58f0 // GV_StrCode("塗り壁") +#define CHARAID_DOG 0x6c0e // GV_StrCode("狼犬") +#define CHARAID_DOOR 0xb997 // GV_StrCode("ドア") +#define CHARAID_DOOR2 0x73f8 // GV_StrCode("ドア2") +#define CHARAID_DRUMCAN 0xb58d // GV_StrCode("ドラム缶") +#define CHARAID_DRUMCAN2 0x4be8 // GV_StrCode("ドラム缶2") +#define CHARAID_DUCTMOUSE 0x3303 // +#define CHARAID_DYNFLOOR 0xaf6c // GV_StrCode("透明床") +#define CHARAID_DYNWALL 0xb103 // GV_StrCode("透明壁") +#define CHARAID_ED_TELOP 0xe75a // GV_StrCode("エンディングテロップ") +#define CHARAID_ELECDAMAGE 0x107c // GV_StrCode("電流床ダメージ") +#define CHARAID_ELECFLOOR 0xaef2 // GV_StrCode("電廊") +#define CHARAID_ELEVATOR 0x2abc // GV_StrCode("エレベータ") +#define CHARAID_ELEVCROW 0x9ab9 // GV_StrCode("エレベーターのカラス") +#define CHARAID_ELEVPANEL 0xe253 // GV_StrCode("エレベーターパネル") +#define CHARAID_EMITTER 0x32e5 // GV_StrCode("ジン発光") +#define CHARAID_EMITTER2 0xa9dd // GV_StrCode("ジン発光2") +#define CHARAID_ENDINGROLL 0xb789 // GV_StrCode("エンディングロール") +#define CHARAID_ENV_SOUND 0x3f9a // GV_StrCode("環境音") +#define CHARAID_ENV_TEST 0x76fe // GV_StrCode("環境マッピングテスト") +#define CHARAID_FADEIO 0xa12e // GV_StrCode("白黒フェド") +#define CHARAID_FALLSPLASH 0xc73e // GV_StrCode("滝しぶき") +#define CHARAID_FEWDAMAGE 0x5d64 // +#define CHARAID_FINDTRAP 0x118c // GV_StrCode("発見トラップ") +#define CHARAID_FOG 0xd6fb // GV_StrCode("黒フォグ") +#define CHARAID_FONTTEXT 0x84e1 // +#define CHARAID_MOTIONSE 0x0fad // +#define CHARAID_FURNACE 0xadd8 // GV_StrCode("溶鉱炉") +#define CHARAID_GASDAMAGE 0x8d5a // GV_StrCode("ガスダメージ") +#define CHARAID_GASEFFECT 0x5a50 // GV_StrCode("ガス効果") +#define CHARAID_JEEP_EMY 0x2477 // +#define CHARAID_GHOST 0xccd3 // GV_StrCode("ゴースト") +#define CHARAID_GLASS 0x8e70 // GV_StrCode("ガラス") +#define CHARAID_GOAL 0xdb1f // GV_StrCode("ゴール") +#define CHARAID_GODZCOM 0x9eb7 // GV_StrCode("ゴジラコマンダ") +#define CHARAID_GODZILA 0xcb1f // GV_StrCode("ゴジラ") +#define CHARAID_GRD_CAM 0x5f5a // GV_StrCode("監視カメラ主観") +#define CHARAID_BG_HEX 0xcbeb // +#define CHARAID_BG_HEX_S 0x0d9b // +#define CHARAID_HIYOKO 0x42e4 // GV_StrCode("ヒヨコ星") +#define CHARAID_INTRUDECAM 0xdd8b // GV_StrCode("イントルードカメラ") +#define CHARAID_ITEM 0x8767 // GV_StrCode("アイテム") +#define CHARAID_JEEPSCROLL 0x6a48 // GV_StrCode("ジープスクロール") +#define CHARAID_JEEPDRUM 0xa27e // GV_StrCode("ジープ戦ドラム") +#define CHARAID_B757_ED_TELOP 0xb757 // +#define CHARAID_JOHNNY 0x1ef9 // GV_StrCode("ジョニー") +#define CHARAID_JPEG 0x81c7 // GV_StrCode("JPEG") +#define CHARAID_JPEGCAMERA 0x4f02 // GV_StrCode("JPEGカメラ") +#define CHARAID_KEY_ITEM 0xc6ac // +#define CHARAID_KIKENKUN 0x52bf // GV_StrCode("危険君") +#define CHARAID_A6F6 0xa6f6 // +#define CHARAID_LANDING 0x8b1a // +#define CHARAID_LIFEUP 0x711f // GV_StrCode("ライフ増加") +#define CHARAID_LIFT 0x425f // GV_StrCode("リフト") +#define CHARAID_LIFT2 0x921b // GV_StrCode("リフト2") +#define CHARAID_LIQUID 0x7bf2 // GV_StrCode("リキッド") +#define CHARAID_AB_CH 0xcf72 // +#define CHARAID_LOAD_DATA 0x53c7 // GV_StrCode("ロードデータ") +#define CHARAID_M1E1 0x9d71 // +#define CHARAID_M1E1CAME 0xd5ec // +#define CHARAID_DOLL2 0xe448 // +#define CHARAID_MEILINGHAIR 0x450f // GV_StrCode("メイリン髪") +#define CHARAID_MEMPSY 0xaf72 // +#define CHARAID_MERYL3 0xc755 // +#define CHARAID_MERYL7 0x5078 // +#define CHARAID_MERYL72 0xe271 // +#define CHARAID_MGREX 0x4754 // +#define CHARAID_MG_DEMO1 0x954b // +#define CHARAID_1787_HIND 0x1787 // +#define CHARAID_MINENE 0xb30a // +#define CHARAID_MIRROR 0xc218 // +#define CHARAID_SAFETY 0xa2b5 // +#define CHARAID_MODELDOT 0x7acf // GV_StrCode("モデルドット") +#define CHARAID_MONITOR1 0x6d78 // GV_StrCode("モニタ1") +#define CHARAID_MOSAIC 0x0065 // GV_StrCode("モザイク") +#define CHARAID_MOUSE 0xd4a5 // GV_StrCode("ネズミ") +#define CHARAID_MOVIE 0x3453 // GV_StrCode("ムービー") +#define CHARAID_5345_MOVIE 0x5345 // +#define CHARAID_MYSTERYDOOR 0xb98c // GV_StrCode("ミステリードア") +#define CHARAID_MYSTERYHEI 0x173c // GV_StrCode("ミステリー兵") +#define CHARAID_D3C0 0xd3c0 // +#define CHARAID_NAOMIHAIR 0xf002 // GV_StrCode("ナオミ髪") +#define CHARAID_NINJA 0x30ba // GV_StrCode("忍者") +#define CHARAID_OBJECT 0x4811 // GV_StrCode("移動物") +#define CHARAID_OPEN 0xcf79 // GV_StrCode("タイトル") +#define CHARAID_OPENA 0x3ac3 // +#define CHARAID_OPENP 0xcfef // +#define CHARAID_OPT 0x976c // +#define CHARAID_OPTA 0x8d31 // +#define CHARAID_OPTP 0xb916 // +#define CHARAID_PADCONTROL 0xcbf8 // GV_StrCode("パッドコントロール") +#define CHARAID_PADDEMO 0x3ed7 // GV_StrCode("パッドデモ") +#define CHARAID_PADDEMO2 0x720d // GV_StrCode("パッドデモ2") +#define CHARAID_PADVIBRATE 0xfed1 // GV_StrCode("パッド振動") +#define CHARAID_PANEL 0x20f2 // GV_StrCode("パネル") +#define CHARAID_PANEL2 0xdd95 // GV_StrCode("パネル2") +#define CHARAID_PAPER 0x5f02 // GV_StrCode("書類") +#define CHARAID_PATOLAMP 0x30ce // GV_StrCode("パトランプ") +#define CHARAID_FDB6_POCKET 0xfdb6 // +#define CHARAID_8FDC_POCKET 0x8fdc // +#define CHARAID_6414_POCKET 0x6414 // +#define CHARAID_AA13 0xaa13 // +#define CHARAID_CF00 0xcf00 // +#define CHARAID_PERAPICT 0x10ff // GV_StrCode("ぺら絵") +#define CHARAID_PHOTOSEL 0xedeb // +#define CHARAID_SCN_ANIM 0x8427 // +#define CHARAID_BG_SP 0x17f5 // +#define CHARAID_BG_STAGE 0x1fa5 // +#define CHARAID_BG_ST1 0xb032 // +#define CHARAID_BG_ST2 0xb033 // +#define CHARAID_BG_SP_CM 0xb7ae // +#define CHARAID_BG_SP_ST 0xbc76 // +#define CHARAID_PK_GATE 0x9c6c // GV_StrCode("駐車場ゲート") +#define CHARAID_PK_JEEP 0xb47a // GV_StrCode("駐車ジープ") +#define CHARAID_PLASMA 0x9bc2 // GV_StrCode("プラズマ") +#define CHARAID_FLR_SPA 0xfc0b // +#define CHARAID_3AD2 0x3ad2 // +#define CHARAID_BG_HEX_P 0xcc89 // +#define CHARAID_PREOPE 0x6d1b // +#define CHARAID_PRISONNINJA 0xd8dd // GV_StrCode("独房忍者") +#define CHARAID_PRISONOTACON 0xbf66 // GV_StrCode("独房オタコン") +#define CHARAID_PRISONSNAKE 0xa404 // GV_StrCode("独房スネーク") +#define CHARAID_PRISONSNAKE2 0x27a2 // GV_StrCode("独房スネーク2") +#define CHARAID_PSYCHOMANTIS 0xa76f // GV_StrCode("サイコマンティス") +#define CHARAID_PSYCHOMERYL 0xf4b0 // GV_StrCode("サイコメリル") +#define CHARAID_PSYOBJ 0x710d // GV_StrCode("サイコ物体") +#define CHARAID_PUTHZD 0x91aa // +#define CHARAID_PILOTLAMP 0x169c // GV_StrCode("パイロットランプ") +#define CHARAID_RADARPOINT 0x5147 // GV_StrCode("レーダーポイント") +#define CHARAID_ITEM_DOT 0x917b // +#define CHARAID_RANKING 0x04f2 // GV_StrCode("ランキング") +#define CHARAID_RASEN 0x5fd9 // GV_StrCode("螺旋階段") +#define CHARAID_RASEN_EL 0xaa21 // GV_StrCode("螺旋階段エレベータ") +#define CHARAID_REDALERT 0xe397 // GV_StrCode("レッドアラート") +#define CHARAID_REVOLVER04 0x05af // GV_StrCode("ボス") +#define CHARAID_RSURFACE 0x81ea // +#define CHARAID_RIPPLES 0x63aa // +#define CHARAID_ROPE 0xbda8 // +#define CHARAID_PIPE 0xc35f // +#define CHARAID_11E9_HIND 0x11e9 // +#define CHARAID_HIND2 0xb959 // +#define CHARAID_SAVE_DATA 0x9302 // GV_StrCode("セーブデータ") +#define CHARAID_SCN_BOMB 0x600d // +#define CHARAID_SCN_MARK 0xee63 // +#define CHARAID_SEARCHLIGHT 0xf50f // GV_StrCode("サーチライト") +#define CHARAID_SHAKEMODEL 0xba52 // +#define CHARAID_SHUTTER 0x03d9 // GV_StrCode("シャッター") +#define CHARAID_SMOKE 0x170c // GV_StrCode("煙") +#define CHARAID_SNAKE 0x21ca // GV_StrCode("スネーク") +#define CHARAID_SNAKE18 0x760e // GV_StrCode("スネーク18") +#define CHARAID_SNAKEWARP 0x672e // GV_StrCode("スネークワープ") +#define CHARAID_BUB_D_SN 0xc0fe // +#define CHARAID_SNE17A 0xa791 // +#define CHARAID_SNEBREATH 0x4170 // GV_StrCode("スネーク息") +#define CHARAID_SNOW 0x18e3 // GV_StrCode("雪") +#define CHARAID_SNOWSTORM 0xa6f5 // GV_StrCode("雪嵐") +#define CHARAID_SNOWAREA 0x901e // +#define CHARAID_SOUNDTEST 0x4efc // GV_StrCode("サウンドテスト") +#define CHARAID_FAA8_MOVIE 0xfaa8 // +#define CHARAID_SPHERE 0x73ea // GV_StrCode("天球") +#define CHARAID_SPHERE2 0xbee1 // GV_StrCode("天球2") +#define CHARAID_STAGESELECT 0xd2f6 // GV_StrCode("ステージセレクト") +#define CHARAID_SMKTRGT 0x175b // +#define CHARAID_TELOP 0x7ff7 // GV_StrCode("テロップ") +#define CHARAID_TEXANIME 0xdcac // GV_StrCode("テクスチャアニメ") +#define CHARAID_TEXSCROLL 0x6865 // GV_StrCode("テクスチャスクロール") +#define CHARAID_TEXTURE 0x1ad3 // GV_StrCode("テクスチャ") +#define CHARAID_TOBCNT 0xaefb // +#define CHARAID_TORTURE 0xe608 // +#define CHARAID_TRUCKTRAP 0xcb3a // GV_StrCode("トラック移動トラップ") +#define CHARAID_TR_BED 0x2a21 // GV_StrCode("拷問台") +#define CHARAID_TR_OCELOT 0x050c // GV_StrCode("拷問オセロット") +#define CHARAID_UJI 0xf5c5 // GV_StrCode("蛆虫") +#define CHARAID_VALCAN 0x9ff5 // GV_StrCode("バルカン") +#define CHARAID_VIBEDITOR 0x6c66 // +#define CHARAID_VMODEL 0x0a02 // GV_StrCode("モデル") +#define CHARAID_VOICESYS 0x0dc9 // GV_StrCode("音声制御") +#define CHARAID_VR 0xed86 // GV_StrCode("VR") +#define CHARAID_VR2 0x30c8 // GV_StrCode("VR2") +#define CHARAID_VRBG 0xd07f // GV_StrCode("VR背景") +#define CHARAID_VRBG2 0x1454 // GV_StrCode("VR背景2") +#define CHARAID_VRBG3 0x1455 // GV_StrCode("VR背景3") +#define CHARAID_VRBOX 0xdf0f // GV_StrCode("VRBOX") +#define CHARAID_VRBOX2 0x548e // GV_StrCode("VRBOX2") +#define CHARAID_VRBOX3 0x548f // GV_StrCode("VRBOX3") +#define CHARAID_VRBOX4 0x5490 // GV_StrCode("VRBOX4") +#define CHARAID_VRCLEAR 0x4deb // GV_StrCode("VRクリア") +#define CHARAID_VRCLEAR2 0xc249 // GV_StrCode("VRクリア2") +#define CHARAID_VRCLEAR3 0xc24a // GV_StrCode("VRクリア3") +#define CHARAID_VRCLUTER 0xb95f // +#define CHARAID_VRDEMO 0x8a46 // GV_StrCode("VRデモ") +#define CHARAID_VRGLASS 0x49d2 // GV_StrCode("VRガラス") +#define CHARAID_VRRESET 0x03bf // GV_StrCode("VRリセット") +#define CHARAID_VRSPARK 0x0c09 // +#define CHARAID_VRTITLE 0x5667 // GV_StrCode("VRタイトル") +#define CHARAID_VRWINDOW 0xd44e // GV_StrCode("VRウィンドウ") +#define CHARAID_VRWINMNGR 0x3d26 // GV_StrCode("VRマネージャ") +#define CHARAID_VR_PAUSEMENU 0xa5dc // GV_StrCode("VRポーズメニュー") +#define CHARAID_VR_SNAKE 0xb162 // +#define CHARAID_WAKE 0x41a3 // GV_StrCode("覚醒スネーク") +#define CHARAID_WALL 0xec77 // GV_StrCode("障害物") +#define CHARAID_PUT_OBJECT 0xf4c3 // +#define CHARAID_WALLSPARK 0x2b24 // GV_StrCode("壁スパーク") +#define CHARAID_WATCHER 0x6e9a // GV_StrCode("巡回兵") +#define CHARAID_WATEREFFECT 0x96b5 // GV_StrCode("水エフェクト") +#define CHARAID_WSURFACE 0xeea7 // +#define CHARAID_WOLF 0x962c // GV_StrCode("ウルフ") +#define CHARAID_WT_AREA 0xdba3 // GV_StrCode("水領域") +#define CHARAID_WT_AREA2 0xa480 // GV_StrCode("水領域2") +#define CHARAID_WT_OBJ 0x69ad // +#define CHARAID_WT_VIEW 0x8e45 // GV_StrCode("水中主観") +#define CHARAID_ZAKO 0xed87 // GV_StrCode("ざこ") +#define CHARAID_ZAKO10 0x31e3 // GV_StrCode("ざこ10") +#define CHARAID_ZAKO10COM 0x8e64 // GV_StrCode("ざこ10コマンダー") +#define CHARAID_ZAKO11A 0xa608 // GV_StrCode("ざこ11a") +#define CHARAID_ZAKO11ACOM 0x5efa // GV_StrCode("ざこ11aコマンダー") +#define CHARAID_ZAKO11E 0xa60c // GV_StrCode("ざこ11e") +#define CHARAID_ZAKO11ECOM 0x5f0a // GV_StrCode("ざこ11eコマンダー") +#define CHARAID_ZAKO11F 0xa60d // GV_StrCode("ざこ11f") +#define CHARAID_ZAKO11FCOM 0x5f0e // GV_StrCode("ざこ11fコマンダー") +#define CHARAID_ZAKO14 0x31e7 // GV_StrCode("ざこ14") +#define CHARAID_ZAKO14COM 0x8e74 // GV_StrCode("ざこ14コマンダー") +#define CHARAID_ZAKO19 0x31ec // GV_StrCode("ざこ19") +#define CHARAID_ZAKO19COM 0x8e88 // GV_StrCode("ざこ19コマンダー") +#define CHARAID_ZAKOCOM 0x7cf7 // GV_StrCode("ざこコマンダー") +#define CHARAID_NINJAPLAY 0xb8d4 // +#define CHARAID_NOBU_WINMNGR 0xae06 // +#define CHARAID_LOADREP 0xc8ca // +#define CHARAID_SAVEMANAGER 0xc5b7 // +#define CHARAID_PLAYDEMO 0x3f9d // +#define CHARAID_REC_DEMO 0xf364 // +#define CHARAID_VR_CHECK 0x26bc // +#define CHARAID_PRESENT 0xaf75 // +#define CHARAID_3D78 0x3d78 // + +/*--- Polygon Demo Charas ---*/ +#define DEMOID_EXPLOSION 0x0001 +#define DEMOID_SMOKE 0x0002 +#define DEMOID_FADEIN 0x0003 +#define DEMOID_FADEOUT 0x0004 +#define DEMOID_TEXT 0x0005 +#define DEMOID_SURPRISEDMARK 0x0006 +#define DEMOID_QUESTIONMARK 0x0007 +#define DEMOID_SLEEPMARK 0x0008 +#define DEMOID_BLOOD 0x0009 +#define DEMOID_BLOOD2 0x000a +#define DEMOID_BULLET 0x000b +#define DEMOID_MISSILESMOKE 0x000c +#define DEMOID_BLOODCIRCLE 0x000d +#define DEMOID_BREATH 0x000e +#define DEMOID_SHADOW 0x000f +#define DEMOID_FOOTPRINTS 0x0010 +#define DEMOID_NINJAEYE 0x0011 +#define DEMOID_BUBBLE 0x0012 +#define DEMOID_BUBBLE2 0x0013 +#define DEMOID_SCOPE 0x0014 +#define DEMOID_DARKVISIBLEGOGGLE 0x0015 +#define DEMOID_DARKVISIBLEGOGGLE2 0x0016 +#define DEMOID_IRRAYSGOGGLE 0x0017 +#define DEMOID_IRRAYSGOGGLE2 0x0018 +#define DEMOID_GUNSMOKE 0x0019 +#define DEMOID_OPTICSCAMOUFLAGE 0x001a +#define DEMOID_OPTICSCAMOUFLAGE2 0x001b +#define DEMOID_ENVIRONMENTMAPPING 0x001c +#define DEMOID_PLASMA 0x001d +#define DEMOID_WINDCIRCLE 0x001e +#define DEMOID_SEPIA 0x001f +#define DEMOID_METALGEARLASER 0x0020 +#define DEMOID_UNSHAPEVIEW 0x0021 +#define DEMOID_GUSMASK 0x0022 +#define DEMOID_URINATIONCIRCLE 0x0023 +#define DEMOID_PLASMA2 0x0024 +#define DEMOID_BLUR 0x0025 +#define DEMOID_STEAMSMOKE 0x0026 +#define DEMOID_STEAMSMOKE2 0x0027 +#define DEMOID_MONOTONE 0x0028 +#define DEMOID_RIFLESIGHT 0x0029 +#define DEMOID_BLACKSMOKE 0x002a +#define DEMOID_URINATIONCIRCLE2 0x002b +#define DEMOID_SIGHT 0x002c +#define DEMOID_NINJASWORD 0x002d +#define DEMOID_SUBMARINEROOM 0x002e +#define DEMOID_BLACKSMOKE2 0x002f +#define DEMOID_BLASTLINE 0x0030 +#define DEMOID_SMOKELINE 0x0031 +#define DEMOID_SHELLSMOKE 0x0032 +#define DEMOID_CATERPILLERSMOKE 0x0033 +#define DEMOID_CROWEYE 0x0034 +#define DEMOID_OCEROTTEBULLETFIRE 0x0035 +#define DEMOID_OCEROTTEBULLETSMOKE 0x0036 +#define DEMOID_ENVIRONMENTLIGHT 0x0037 +#define DEMOID_ENVIRONMENTLIGHT2 0x0038 +#define DEMOID_CRASHSMOKE 0x0039 +#define DEMOID_CELOFAN 0x003a +#define DEMOID_INVERSLIGHT 0x003b +#define DEMOID_SPACK 0x003c +#define DEMOID_SPACK2 0x003d +#define DEMOID_STEAMEXPLOSION 0x003e +#define DEMOID_INVERSLIGHT2 0x003f +#define DEMOID_CELOFAN2 0x0040 +#define DEMOID_NINJALASER 0x0041 +#define DEMOID_GUSEFFECT 0x0042 +#define DEMOID_SUBMARINEWATER 0x0043 +#define DEMOID_GHOST 0x0044 +#define DEMOID_FAMASLIGHT 0x0045 +#define DEMOID_HUMANSHADOW 0x0046 +#define DEMOID_BLOODHAZARD 0x0047 +#define DEMOID_BLOODDRIP 0x0048 +#define DEMOID_NINJAGROUND 0x0049 +#define DEMOID_BOMBLIGHT 0x004a +#define DEMOID_MGCROOMDISPLAY 0x004b +#define DEMOID_TEXT2 0x004c + +#endif // __MGS_CHARAID_H__ diff --git a/components/mgs/mgs/source/include/charalst.h b/components/mgs/mgs/source/include/charalst.h new file mode 100644 index 00000000..3a5fc7f1 --- /dev/null +++ b/components/mgs/mgs/source/include/charalst.h @@ -0,0 +1,603 @@ +#ifndef __MGS_CHARALST_H__ +#define __MGS_CHARALST_H__ + +#include // for NULL +#include "charadef.h" // for NEWCHARA, CHARA + +/*---------------------------------------------------------------------------*/ +/* CHARA table entry defintions */ +/*---------------------------------------------------------------------------*/ + +/* end-of-table marker */ +#define CHARA_END { 0, NULL } + +// NOTE: These names should be kept in sync with the CHARAID_xxx defines +// in charaid.h as those are aliases for the 'class_id' field constants. + +// TODO: We don't really need the source path listed for CHARA entries +// with decompiled functions (listing it next to the extern declaration +// is enough). Please don't remove the GV_StrCode("...") part, though. + +#define CHARA_10A_DEMO { 0x40e9, ? } // okajima/10a_demo.c +#define CHARA_S11_OBJS { 0xe068, NewStage11Objects } // GV_StrCode("コントロールS11物") okajima/s11_objs.c +#define CHARA_11B_DEMO { 0xd53c, ? } // okajima/11b_demo.c +#define CHARA_11G_DEMO { 0x153e, NewStage11GDemo } // okajima/11g_demo.c +#define CHARA_9FFD_2ND { 0x9ffd, GM_SetSecondAvailable } // game/second.c +#define CHARA_2D0A_2ND { 0x2d0a, ? } // game/second.c +#define CHARA_ABST { 0x566f, NewAbstract } // GV_StrCode("アブスト") onoda/abst/abst.c +#define CHARA_ABST_DEMO1 { 0x4974, NewAbstractDemo1 } // GV_StrCode("アブストデモ1") onoda/abst/ab_demo1.c +#define CHARA_ABST_DEMO2 { 0x4975, NewAbstractDemo2 } // GV_StrCode("アブストデモ2") onoda/abst/ab_demo2.c +#define CHARA_AIRSHOWER { 0x05ef, ? } // GV_StrCode("エアシャワー") okajima/a_shower.c (PC-ONLY) +#define CHARA_ARRAY { 0x2a90, ? } // takabe/gclarray.c +#define CHARA_ASIATOKUN { 0x02c4, NewAsiatoKun } // GV_StrCode("足跡君") enemy/asiato.c +#define CHARA_ASIOTOKUN { 0x92bc, NewAsiotoKun } // GV_StrCode("足音君") enemy/asioto.c +#define CHARA_AT { 0x9988, NewArmsTechPresident } // GV_StrCode("AT") onoda/s04b/at.c +#define CHARA_BELONG { 0xf59e, NewBelong } // GV_StrCode("付随物") chara/others/belong.c +#define CHARA_BLACKSMOKE { 0x6b6c, ? } // GV_StrCode("黒煙") animal/liquid/b_smoke.c +#define CHARA_BLINKTEX { 0x8185, NewBlinkTexture } // GV_StrCode("点滅テクスチャ") okajima/blink_tx.c +#define CHARA_BLOODY_MERYL { 0x1158, ? } // GV_StrCode("血まみれメリル") okajima/meryl10/mel10.c +#define CHARA_BLOOD_POOL { 0x4e95, NewBloodCl } // GV_StrCode("血溜り") okajima/blood_cl.c +#define CHARA_BLOOD_POOL2 { 0x6a4c, NewBloodBl } // GV_StrCode("血溜り2") okajima/blood_bl.c +#define CHARA_BLUR { 0x9093, ? } // GV_StrCode("ブラー") ??? +#define CHARA_BOXALL { 0xf74b, NewAllItemBox } // GV_StrCode("全部箱") chara/torture/boxall.c +#define CHARA_BREAK_OBJECT { 0x32fc, NewBreakObject } // takabe/breakobj.c +#define CHARA_BUTTONCHECK { 0x6471, ? } // GV_StrCode("ボタンチェッカー") takabe/btn_chk.c +#define CHARA_BUBBLE { 0x1a02, NewBubbleS } // GV_StrCode("泡") okajima/bubble_s.c +#define CHARA_B_SELECT { 0x93b6, ? } // onoda/brf/b_select.c +#define CHARA_C4WIRE { 0x7eca, NewWire } // GV_StrCode("ワイヤ") onoda/s04b/wire.c +#define CHARA_CAMERA { 0x6e90, NewCamera_800D67F8 } // GV_StrCode("カメラ") enemy/camera.c +#define CHARA_CAMERA2 { 0x56cc, ? } // GV_StrCode("カメラ2") enemy/camera2.c +#define CHARA_CAMERAGUN { 0xa9c5, NewGunCamera } // GV_StrCode("カメラ付随ガン") okajima/guncame.c +#define CHARA_CAMERASHAKE { 0x7bc2, NewCameraShake } // GV_StrCode("カメラ揺らし") takabe/camshake.c +#define CHARA_CAPE { 0xb99f, NewCape } // onoda/s04b/cape.c +#define CHARA_CAT_IN { 0x51c6, NewZoom_800DFA88 } // takabe/cat_in.c +#define CHARA_CDCHANGE { 0xf722, NewCdChange } // GV_StrCode("CD交換") onoda/change/change.c +#define CHARA_CENSOR { 0x43a0, NewIrSensor } // GV_StrCode("赤外線センサー") takabe/ir_cens.c +#define CHARA_CHAIR { 0x788d, NewChair } // GV_StrCode("椅子") takabe/chair.c +#define CHARA_CINEMA { 0x7a05, NewCinemaScreenSet } // GV_StrCode("シネマスクリーン") takabe/cinema.c +#define CHARA_CLAYMORE { 0x3c0c, NewScenarioJirai } // GV_StrCode("クレイモア地雷") bullet/jirai.c +#define CHARA_COMMANDER { 0xc6d7, NewCommander } // GV_StrCode("コマンダー") enemy/command.c +#define CHARA_CONTAINER { 0xcc45, s15c_dyncon_800D8C9C } // GV_StrCode("コンテナ") okajima/valcan/dyncon.c +#define CHARA_COUNTDOWN { 0x22c6, ? } // GV_StrCode("カウントダウン") enemy/timer.c +#define CHARA_COUNTDOWN2 { 0xeced, NewCountdownGcl } // GV_StrCode("カウントダウンタイマー") --> menu/countdwn.c +#define CHARA_CRANE { 0xa3fb, NewCrane } // GV_StrCode("クレーン") okajima/crane.c +#define CHARA_CROW { 0x8e60, NewCrow } // GV_StrCode("カラス") okajima/valcan/crow.c +#define CHARA_DEATHSPARK { 0xbe79, NewDeathSpark } // GV_StrCode("送別火花") okajima/death_sp.c +#define CHARA_DAMAGESMOKE { 0x6a98, ? } // GV_StrCode("ダメージ煙") chara/rope/dsmoke.c (PC-ONLY) +#define CHARA_DAMAGESMOKE2 { 0x76bc, NewDamageSmoke2 } // GV_StrCode("ダメージ煙2") chara/rope/dsmoke2.c +#define CHARA_DEMOCANCEL { 0xb4e6, NewDemoCancel } // GV_StrCode("デモキャンセル") game/cancel.c +#define CHARA_DEMODOLL { 0xe97e, NewDemoDoll } // GV_StrCode("デモ人形") animal/doll/doll.c +#define CHARA_DEMOSEL { 0x3686, NewDemoSelect } // GV_StrCode("デモ劇場") onoda/demosel/demosel.c +#define CHARA_DISPLAY { 0x9f7d, ? } // chara/pocket/display/display.c +#define CHARA_DMYFLOOR { 0x9d00, NewDummyFloor } // GV_StrCode("落し穴") takabe/dummy_fl.c +#define CHARA_DMYWALL { 0x58f0, NewDummyWall } // GV_StrCode("塗り壁") takabe/dummy_wl.c +#define CHARA_DOG { 0x6c0e, NewDog } // GV_StrCode("狼犬") okajima/dog/dog.c +#define CHARA_DOOR { 0xb997, NewDoor } // GV_StrCode("ドア") thing/door.c +#define CHARA_DOOR2 { 0x73f8, NewDoor2 } // GV_StrCode("ドア2") takabe/door2.c +#define CHARA_DRUMCAN { 0xb58d, ? } // GV_StrCode("ドラム缶") takabe/drum.c +#define CHARA_DRUMCAN2 { 0x4be8, ? } // GV_StrCode("ドラム缶2") takabe/drum2.c +#define CHARA_DUCTMOUSE { 0x3303, NewDuctMouse } // okajima/ductmous.c +#define CHARA_DYNFLOOR { 0xaf6c, NewDynamicFloorSet } // GV_StrCode("透明床") takabe/dymc_flr.c +#define CHARA_DYNWALL { 0xb103, NewDynamicWallSet } // GV_StrCode("透明壁") takabe/dymc_seg.c +#define CHARA_ED_TELOP { 0xe75a, NewEndingTelop } // GV_StrCode("エンディングテロップ") takabe/ed_telop.c +#define CHARA_ELECDAMAGE { 0x107c, NewElectricDamage } // GV_StrCode("電流床ダメージ") takabe/elc_damg.c +#define CHARA_ELECFLOOR { 0xaef2, NewElectricFloor } // GV_StrCode("電廊") takabe/elc_flr.c +#define CHARA_ELEVATOR { 0x2abc, NewElevator } // GV_StrCode("エレベータ") takabe/elevator.c +#define CHARA_ELEVCROW { 0x9ab9, ? } // GV_StrCode("エレベーターのカラス") okajima/valcan/ele_crow.c +#define CHARA_ELEVPANEL { 0xe253, NewElevatorPanel } // GV_StrCode("エレベーターパネル") game/evpanel.c +#define CHARA_EMITTER { 0x32e5, NewEmitter } // GV_StrCode("ジン発光") thing/emitter.c +#define CHARA_EMITTER2 { 0xa9dd, ? } // GV_StrCode("ジン発光2") thing/emitter2.c (PC-ONLY) +#define CHARA_ENDINGROLL { 0xb789, NewEndingRoll } // GV_StrCode("エンディングロール") takabe/ending2.c +#define CHARA_ENV_SOUND { 0x3f9a, NewEnvSound } // GV_StrCode("環境音") takabe/env_snd.c +#define CHARA_ENV_TEST { 0x76fe, ? } // GV_StrCode("環境マッピングテスト") takabe/env_test.c (PC-ONLY) +#define CHARA_FADEIO { 0xa12e, NewFadeInOutSet } // GV_StrCode("白黒フェド") takabe/fadeio.c +#define CHARA_FALLSPLASH { 0xc73e, NewFallSplash } // GV_StrCode("滝しぶき") okajima/fall_spl.c +#define CHARA_FEWDAMAGE { 0x5d64, NewFewDamageSet } // takabe/o2_damge.c +#define CHARA_FINDTRAP { 0x118c, NewFindTrap } // GV_StrCode("発見トラップ") takabe/findtrap.c +#define CHARA_FOG { 0xd6fb, NewFog } // GV_StrCode("黒フォグ") takabe/fog.c +#define CHARA_FONTTEXT { 0x84e1, NewFontText } // chara/others/fonttext.c +#define CHARA_MOTIONSE { 0x0fad, NewMotionSoundEffect } // chara/others/motse.c +#define CHARA_FURNACE { 0xadd8, NewFurnace } // GV_StrCode("溶鉱炉") takabe/furnace.c +#define CHARA_GASDAMAGE { 0x8d5a, NewGasDamage } // GV_StrCode("ガスダメージ") takabe/gasdamge.c +#define CHARA_GASEFFECT { 0x5a50, NewGasEffectSet } // GV_StrCode("ガス効果") takabe/gas_efct.c +#define CHARA_JEEP_EMY { 0x2477, NewJeepEnemy } // takabe/jeep_emy/jeep_emy.c +#define CHARA_GHOST { 0xccd3, ? } // GV_StrCode("ゴースト") ??? (PC-ONLY) +#define CHARA_GLASS { 0x8e70, NewGlassSet } // GV_StrCode("ガラス") takabe/glass.c +#define CHARA_GOAL { 0xdb1f, ? } // GV_StrCode("ゴール") koba/vr/goal.c +#define CHARA_GODZCOM { 0x9eb7, ? } // GV_StrCode("ゴジラコマンダ") animal/godzila/godzcom.c +#define CHARA_GODZILA { 0xcb1f, ? } // GV_StrCode("ゴジラ") animal/godzila/godzila.c +#define CHARA_GRD_CAM { 0x5f5a, ? } // GV_StrCode("監視カメラ主観") thing/grd_cam.c (PC-ONLY) +#define CHARA_BG_HEX { 0xcbeb, ? } // okajima/photo/bg_hex.c (PC-ONLY) +#define CHARA_BG_HEX_S { 0x0d9b, ? } // okajima/photo/bg_hex_s.c +#define CHARA_HIYOKO { 0x42e4, NewHiyokoGcl } // GV_StrCode("ヒヨコ星") okajima/hiyoko.c +#define CHARA_INTRUDECAM { 0xdd8b, NewIntrudeCamera } // GV_StrCode("イントルードカメラ") chara/others/intr_cam.c +#define CHARA_ITEM { 0x8767, NewItem } // GV_StrCode("アイテム") game/item.c +#define CHARA_JEEPSCROLL { 0x6a48, NewJeepScroll } // GV_StrCode("ジープスクロール") takabe/jeep_srl.c +#define CHARA_JEEPDRUM { 0xa27e, NewJeepDrum } // GV_StrCode("ジープ戦ドラム") takabe/jeep/jdrum.c +#define CHARA_B757_ED_TELOP { 0xb757, EdTelop_800C4F18 } // takabe/ed_telop.c (?) +#define CHARA_JOHNNY { 0x1ef9, NewJohnny } // GV_StrCode("ジョニー") chara/torture/johnny.c +#define CHARA_JPEG { 0x81c7, NewCamera_800CF388 } // GV_StrCode("JPEG") camera.c +#define CHARA_JPEGCAMERA { 0x4f02, ? } // GV_StrCode("JPEGカメラ") +#define CHARA_KEY_ITEM { 0xc6ac, NewKeyItem } // okajima/key_item.c +#define CHARA_KIKENKUN { 0x52bf, NewKikenKun } // GV_StrCode("危険君") enemy/kiken.c +#define CHARA_A6F6 { 0xa6f6, ? } // --> kmdarutl.c +#define CHARA_LANDING { 0x8b1a, NewLanding } // chara/rope/landing.c +#define CHARA_LIFEUP { 0x711f, NewLifeUp } // GV_StrCode("ライフ増加") takabe/life_up.c +#define CHARA_LIFT { 0x425f, NewLift } // GV_StrCode("リフト") takabe/lift.c +#define CHARA_LIFT2 { 0x921b, ? } // GV_StrCode("リフト2") takabe/lift2.c (PC-ONLY) +#define CHARA_LIQUID { 0x7bf2, ? } // GV_StrCode("リキッド") animal/liquid/liquid.c +#define CHARA_AB_CH { 0xcf72, NewAbstractChange } // onoda/abst/ab_ch.c +#define CHARA_LOAD_DATA { 0x53c7, NewLoadData } // GV_StrCode("ロードデータ") menu/mload.c +#define CHARA_M1E1 { 0x9d71, ? } // kojo/m1e1.c +#define CHARA_M1E1CAME { 0xd5ec, ? } // kojo/m1e1came.c +#define CHARA_DOLL2 { 0xe448, ? } // takabe/doll2.c +#define CHARA_MEILINGHAIR { 0x450f, ? } // GV_StrCode("メイリン髪") chara/others/hair_m.c +#define CHARA_MEMPSY { 0xaf72, ? } // chara/psyco/mempsy.c +#define CHARA_MERYL3 { 0xc755, ? } // animal/meryl3/meryl3.c +#define CHARA_MERYL7 { 0x5078, NewEnemyMeryl_800D63A4 } // enemy/meryl7.c +#define CHARA_MERYL72 { 0xe271, NewMeryl72_800C7BC4 } // animal/meryl72/meryl72.c +#define CHARA_MGREX { 0x4754, ? } // kojo/mgrex.c +#define CHARA_MG_DEMO1 { 0x954b, ? } // takabe/mg_demo1.c +#define CHARA_1787_HIND { 0x1787, NewHindBoss } // kojo/hind.c +#define CHARA_MINENE { 0xb30a, ? } // enemy/minene.c +#define CHARA_MIRROR { 0xc218, NewMirror } // takabe/mirror.c +#define CHARA_SAFETY { 0xa2b5, Safety_800C47A0 } // safety.c +#define CHARA_MODELDOT { 0x7acf, ? } // GV_StrCode("モデルドット") okajima/photo/model_dt.c (PC-ONLY) +#define CHARA_MONITOR1 { 0x6d78, NewMonitor1 } // GV_StrCode("モニタ1") takabe/monitor1.c +#define CHARA_MOSAIC { 0x0065, NewMosaicSet } // GV_StrCode("モザイク") takabe/mosaic.c +#define CHARA_MOUSE { 0xd4a5, NewMouse } // GV_StrCode("ネズミ") okajima/mouse.c +#define CHARA_MOVIE { 0x3453, NewMovieGCL } // GV_StrCode("ムービー") game/movie.c +#define CHARA_5345_MOVIE { 0x5345, ? } // --> game/movie.c +#define CHARA_MYSTERYDOOR { 0xb98c, ? } // GV_StrCode("ミステリードア") enemy/m_door.c +#define CHARA_MYSTERYHEI { 0x173c, ? } // GV_StrCode("ミステリー兵") enemy/mystery.c +#define CHARA_D3C0 { 0xd3c0, ? } // ??? +#define CHARA_NAOMIHAIR { 0xf002, ? } // GV_StrCode("ナオミ髪") chara/others/hair_n.c +#define CHARA_NINJA { 0x30ba, NewNinjaBoss } // GV_StrCode("忍者") animal/ninja/ninja.c +#define CHARA_OBJECT { 0x4811, NewMovingObject } // GV_StrCode("移動物") enemy/object.c +#define CHARA_OPEN { 0xcf79, NewOpen } // GV_StrCode("タイトル") onoda/open/open.c +#define CHARA_OPENA { 0x3ac3, ? } // onoda/opena/opena.c +#define CHARA_OPENP { 0xcfef, ? } // onoda/openp/openp.c +#define CHARA_OPT { 0x976c, NewOption } // onoda/option/opt.c +#define CHARA_OPTA { 0x8d31, ? } // onoda/optiona/opta.c +#define CHARA_OPTP { 0xb916, ? } // onoda/optionp/optp.c +#define CHARA_PADCONTROL { 0xcbf8, NewPadControl } // GV_StrCode("パッドコントロール") game/pad.c +#define CHARA_PADDEMO { 0x3ed7, NewPadDemo } // GV_StrCode("パッドデモ") takabe/pad_demo.c +#define CHARA_PADDEMO2 { 0x720d, ? } // GV_StrCode("パッドデモ2") nobu/vr_demo/paddemo2.c +#define CHARA_PADVIBRATE { 0xfed1, NewPadVibrationGcl } // GV_StrCode("パッド振動") game/vibrate.c +#define CHARA_PANEL { 0x20f2, NewPanel } // GV_StrCode("パネル") takabe/panel.c +#define CHARA_PANEL2 { 0xdd95, NewPanel2 } // GV_StrCode("パネル2") takabe/panel2.c +#define CHARA_PAPER { 0x5f02, NewPaper } // GV_StrCode("書類") takabe/paper.c +#define CHARA_PATOLAMP { 0x30ce, NewPatrolLamp } // GV_StrCode("パトランプ") okajima/pato_lmp.c +#define CHARA_FDB6_POCKET { 0xfdb6, ? } // menu/pocket.c +#define CHARA_8FDC_POCKET { 0x8fdc, ? } // --> menu/pocket.c +#define CHARA_6414_POCKET { 0x6414, ? } // --> menu/pocket.c +#define CHARA_AA13 { 0xaa13, ? } // ??? +#define CHARA_CF00 { 0xcf00, ? } // ??? +#define CHARA_PERAPICT { 0x10ff, ? } // GV_StrCode("ぺら絵") chara/others/perapict.c +#define CHARA_PHOTOSEL { 0xedeb, ? } // chara/photosel/photosel.c +#define CHARA_SCN_ANIM { 0x8427, ? } // okajima/scn_anim.c +#define CHARA_BG_SP { 0x17f5, ? } // okajima/photo/bg_sp.c (PC-ONLY) +#define CHARA_BG_STAGE { 0x1fa5, ? } // okajima/photo/bg_stage.c +#define CHARA_BG_ST1 { 0xb032, ? } // okajima/photo/bg_st1.c +#define CHARA_BG_ST2 { 0xb033, ? } // okajima/photo/bg_st2.c (PC-ONLY) +#define CHARA_BG_SP_CM { 0xb7ae, ? } // okajima/photo/bg_sp_cm.c (PC-ONLY) +#define CHARA_BG_SP_ST { 0xbc76, ? } // okajima/photo/bg_sp_st.c (PC-ONLY) +#define CHARA_PK_GATE { 0x9c6c, ? } // GV_StrCode("駐車場ゲート") takabe/pk_gate.c +#define CHARA_PK_JEEP { 0xb47a, ? } // GV_StrCode("駐車ジープ") takabe/pjeep.c +#define CHARA_PLASMA { 0x9bc2, NewPlasmaGcl } // GV_StrCode("プラズマ") okajima/plasma.c +#define CHARA_FLR_SPA { 0xfc0b, NewFloorSpark } // okajima/flr_spa.c +#define CHARA_3AD2 { 0x3ad2, ? } // ??? +#define CHARA_BG_HEX_P { 0xcc89, ? } // okajima/photo/bg_hex_p.c (PC-ONLY) +#define CHARA_PREOPE { 0x6d1b, NewPreviousOperation } // onoda/preope/preope.c +#define CHARA_PRISONNINJA { 0xd8dd, NewPrisonNinja } // GV_StrCode("独房忍者") chara/torture/ninja.c +#define CHARA_PRISONOTACON { 0xbf66, NewPrisonOtacon } // GV_StrCode("独房オタコン") chara/torture/otacom.c +#define CHARA_PRISONSNAKE { 0xa404, NewPrisonSnake } // GV_StrCode("独房スネーク") chara/torture/sne_03c.c +#define CHARA_PRISONSNAKE2 { 0x27a2, NewPrisonSnake2 } // GV_StrCode("独房スネーク2") chara/torture/sne_03c.c +#define CHARA_PSYCHOMANTIS { 0xa76f, ? } // GV_StrCode("サイコマンティス") chara/psyco/psyco.c +#define CHARA_PSYCHOMERYL { 0xf4b0, ? } // GV_StrCode("サイコメリル") chara/meryl07b/meryl07b.c +#define CHARA_PSYOBJ { 0x710d, ? } // GV_StrCode("サイコ物体") chara/psyco/psyobj.c +#define CHARA_PUTHZD { 0x91aa, ? } // takabe/puthzd.c +#define CHARA_PILOTLAMP { 0x169c, NewPilotLamp } // GV_StrCode("パイロットランプ") okajima/p_lamp.c +#define CHARA_RADARPOINT { 0x5147, NewPoint } // GV_StrCode("レーダーポイント") game/point.c +#define CHARA_ITEM_DOT { 0x917b, NewItemDot } // okajima/item_dot.c +#define CHARA_RANKING { 0x04f2, ? } // GV_StrCode("ランキング") onoda/rank/rank.c +#define CHARA_RASEN { 0x5fd9, NewRasen2 } // GV_StrCode("螺旋階段") takabe/rasen.c +#define CHARA_RASEN_EL { 0xaa21, NewRasenElevator } // GV_StrCode("螺旋階段エレベータ") takabe/rasen_el.c +#define CHARA_REDALERT { 0xe397, NewRedAlert } // GV_StrCode("レッドアラート") okajima/red_alrt.c +#define CHARA_REVOLVER04 { 0x05af, NewOcelotBoss } // GV_StrCode("ボス") onoda/s04b/revolver.c +#define CHARA_RSURFACE { 0x81ea, NewRippleSurface } // takabe/rsurface.c +#define CHARA_RIPPLES { 0x63aa, NewRipples } // takabe/ripples.c +#define CHARA_ROPE { 0xbda8, NewRope } // --> chara/rope/rope.c +#define CHARA_PIPE { 0xc35f, NewPipe } // chara/rope/pipe.c +#define CHARA_11E9_HIND { 0x11e9, NewHind } // chara/hind/hind.c +#define CHARA_HIND2 { 0xb959, NewHind2 } // chara/hind2/hind2.c +#define CHARA_SAVE_DATA { 0x9302, ? } // GV_StrCode("セーブデータ") menu/mload.c +#define CHARA_SCN_BOMB { 0x600d, ? } // okajima/scn_bomb.c (PC-ONLY) +#define CHARA_SCN_MARK { 0xee63, NewScreenMark } // okajima/scn_mark.c +#define CHARA_SEARCHLIGHT { 0xf50f, NewSearchlight } // GV_StrCode("サーチライト") enemy/searchli.c +#define CHARA_SHAKEMODEL { 0xba52, NewShakeModelGCL } // takabe/shakemdl.c +#define CHARA_SHUTTER { 0x03d9, NewShutter } // GV_StrCode("シャッター") takabe/shuter.c +#define CHARA_SMOKE { 0x170c, NewSmoke } // GV_StrCode("煙") enemy/smoke.c +#define CHARA_SNAKE { 0x21ca, NewSnake } // GV_StrCode("スネーク") chara/snake/sna_init.c +#define CHARA_SNAKE18 { 0x760e, d18a_snake18_800D4E94 } // GV_StrCode("スネーク18") animal/snake18/snake18.c +#define CHARA_SNAKEWARP { 0x672e, ? } // GV_StrCode("スネークワープ") enemy/snwarp.c +#define CHARA_BUB_D_SN { 0xc0fe, NewBubbleDSn } // okajima/bub_d_sn.c +#define CHARA_SNE17A { 0xa791, ? } // chara/sne17a/sne17a.c (PC-ONLY) +#define CHARA_SNEBREATH { 0x4170, NewSnakeBreath } // GV_StrCode("スネーク息") chara/snake/breath.c +#define CHARA_SNOW { 0x18e3, NewSnow } // GV_StrCode("雪") thing/snow.c +#define CHARA_SNOWSTORM { 0xa6f5, NewSnowStorm } // GV_StrCode("雪嵐") kojo/sstorm.c +#define CHARA_SNOWAREA { 0x901e, ? } // okajima/snowarea.c +#define CHARA_SOUNDTEST { 0x4efc, NewSoundTest } // GV_StrCode("サウンドテスト") game/sndtst.c +#define CHARA_FAA8_MOVIE { 0xfaa8, ? } // chara/movie/movie.c +#define CHARA_SPHERE { 0x73ea, NewSphere } // GV_StrCode("天球") thing/sphere.c +#define CHARA_SPHERE2 { 0xbee1, NewSphere2 } // GV_StrCode("天球2") thing/sphere2.c +#define CHARA_STAGESELECT { 0xd2f6, NewSelect } // GV_StrCode("ステージセレクト") game/select.c +#define CHARA_SMKTRGT { 0x175b, NewSmokeTarget } // okajima/smktrgt.c +#define CHARA_TELOP { 0x7ff7, NewTelopSet } // GV_StrCode("テロップ") takabe/telop.c +#define CHARA_TEXANIME { 0xdcac, ? } // GV_StrCode("テクスチャアニメ") enemy/texanime.c (PC-ONLY) +#define CHARA_TEXSCROLL { 0x6865, NewTexScroll } // GV_StrCode("テクスチャスクロール") takabe/tex_scrl.c +#define CHARA_TEXTURE { 0x1ad3, NewTextureLamp } // GV_StrCode("テクスチャ") game/lamp.c +#define CHARA_TOBCNT { 0xaefb, NewToBeContinued } // game/tobcnt.c +#define CHARA_TORTURE { 0xe608, NewTorture } // chara/torture/torture.c +#define CHARA_TRUCKTRAP { 0xcb3a, NewTruckTrap } // GV_StrCode("トラック移動トラップ") takabe/tracktrp.c +#define CHARA_TR_BED { 0x2a21, NewTortureBed } // GV_StrCode("拷問台") chara/torture/bed.c +#define CHARA_TR_OCELOT { 0x050c, NewTortureOcelot } // GV_StrCode("拷問オセロット") chara/torture/revolver.c +#define CHARA_UJI { 0xf5c5, NewUji } // GV_StrCode("蛆虫") okajima/uji.c +#define CHARA_VALCAN { 0x9ff5, ? } // GV_StrCode("バルカン") okajima/valcan/valcan.c +#define CHARA_VIBEDITOR { 0x6c66, NewVibrationEditor } // takabe/vib_edit.c +#define CHARA_VMODEL { 0x0a02, ? } // GV_StrCode("モデル") thing/vmodel.c (PC-ONLY) +#define CHARA_VOICESYS { 0x0dc9, NewVoiceSystem } // GV_StrCode("音声制御") takabe/voicesys.c +#define CHARA_VR { 0xed86, ? } // GV_StrCode("VR") koba/vr/vr.c +#define CHARA_VR2 { 0x30c8, ? } // GV_StrCode("VR2") koba/vr/vr2.c +#define CHARA_VRBG { 0xd07f, ? } // GV_StrCode("VR背景") koba/vr/backgrnd.c +#define CHARA_VRBG2 { 0x1454, ? } // GV_StrCode("VR背景2") koba/vr/bg2.c +#define CHARA_VRBG3 { 0x1455, ? } // GV_StrCode("VR背景3") koba/vr/bg3.c +#define CHARA_VRBOX { 0xdf0f, ? } // GV_StrCode("VRBOX") koba/vr/vrbox.c +#define CHARA_VRBOX2 { 0x548e, ? } // GV_StrCode("VRBOX2") koba/vr/vrbox2.c +#define CHARA_VRBOX3 { 0x548f, ? } // GV_StrCode("VRBOX3") koba/vr/vrbox2.c +#define CHARA_VRBOX4 { 0x5490, ? } // GV_StrCode("VRBOX4") koba/vr/vrbox.c +#define CHARA_VRCLEAR { 0x4deb, ? } // GV_StrCode("VRクリア") koba/vr/vrclear.c +#define CHARA_VRCLEAR2 { 0xc249, ? } // GV_StrCode("VRクリア2") koba/vr/vrclear2.c +#define CHARA_VRCLEAR3 { 0xc24a, ? } // GV_StrCode("VRクリア3") koba/vr/vrclear3.c (PC-ONLY) +#define CHARA_VRCLUTER { 0xb95f, ? } // koba/vr/cluter.c (PC-ONLY) +#define CHARA_VRDEMO { 0x8a46, ? } // GV_StrCode("VRデモ") koba/demo/demomngr.c +#define CHARA_VRGLASS { 0x49d2, ? } // GV_StrCode("VRガラス") koba/vr/vrglass.c +#define CHARA_VRRESET { 0x03bf, ? } // GV_StrCode("VRリセット") (PC-ONLY) +#define CHARA_VRSPARK { 0x0c09, ? } // koba/vr/vrspark.c +#define CHARA_VRTITLE { 0x5667, ? } // GV_StrCode("VRタイトル") chara/vrtitle/vrtitle.c +#define CHARA_VRWINDOW { 0xd44e, NewVrWindow } // GV_StrCode("VRウィンドウ") koba/vr/vrwindow.c +#define CHARA_VRWINMNGR { 0x3d26, ? } // GV_StrCode("VRマネージャ") koba/vr_slct/winmngr.c +#define CHARA_VR_PAUSEMENU { 0xa5dc, ? } // GV_StrCode("VRポーズメニュー") koba/vr/pausmenu.c +#define CHARA_VR_SNAKE { 0xb162, ? } // okajima/snake_e1.c +#define CHARA_WAKE { 0x41a3, NewWake } // GV_StrCode("覚醒スネーク") chara/wake/wake.c +#define CHARA_WALL { 0xec77, NewWallGcl } // GV_StrCode("障害物") enemy/wall.c +#define CHARA_PUT_OBJECT { 0xf4c3, NewPutObject } // takabe/put_obj.c +#define CHARA_WALLSPARK { 0x2b24, NewWallSpark } // GV_StrCode("壁スパーク") okajima/wall_spa.c +#define CHARA_WATCHER { 0x6e9a, NewSnakeWatcher } // GV_StrCode("巡回兵") enemy/watcher.c +#define CHARA_WATEREFFECT { 0x96b5, ? } // GV_StrCode("水エフェクト") takabe/water.c (PC-ONLY) +#define CHARA_WSURFACE { 0xeea7, NewWaterSurface } // takabe/wsurface.c +#define CHARA_WOLF { 0x962c, NewWolf2 } // GV_StrCode("ウルフ") okajima/wolf/wolf2.c +#define CHARA_WT_AREA { 0xdba3, NewWaterArea } // GV_StrCode("水領域") takabe/wt_area.c +#define CHARA_WT_AREA2 { 0xa480, NewWaterArea2 } // GV_StrCode("水領域2") takabe/wt_area2.c +#define CHARA_WT_OBJ { 0x69ad, ? } // takabe/wt_obj.c (PC-ONLY) +#define CHARA_WT_VIEW { 0x8e45, NewWaterView } // GV_StrCode("水中主観") takabe/wt_view.c +#define CHARA_ZAKO { 0xed87, ? } // GV_StrCode("ざこ") animal/zako/zako.c +#define CHARA_ZAKO10 { 0x31e3, ? } // GV_StrCode("ざこ10") animal/zako10/zako10.c +#define CHARA_ZAKO10COM { 0x8e64, ? } // GV_StrCode("ざこ10コマンダー") animal/zako10/zk10com.c +#define CHARA_ZAKO11A { 0xa608, ? } // GV_StrCode("ざこ11a") animal/zako11a/zako11a.c +#define CHARA_ZAKO11ACOM { 0x5efa, ? } // GV_StrCode("ざこ11aコマンダー") animal/zako11a/zk11acom.c +#define CHARA_ZAKO11E { 0xa60c, NewZako11E } // GV_StrCode("ざこ11e") animal/zako11e/zako11e.c +#define CHARA_ZAKO11ECOM { 0x5f0a, NewZako11ECommander } // GV_StrCode("ざこ11eコマンダー") animal/zako11e/zk11ecom.c +#define CHARA_ZAKO11F { 0xa60d, NewZako11F } // GV_StrCode("ざこ11f") animal/zako11f/zako11f.c +#define CHARA_ZAKO11FCOM { 0x5f0e, NewZako11FCommander } // GV_StrCode("ざこ11fコマンダー") animal/zako11f/zk11fcom.c +#define CHARA_ZAKO14 { 0x31e7, ? } // GV_StrCode("ざこ14") animal/zako14/zako14.c +#define CHARA_ZAKO14COM { 0x8e74, ? } // GV_StrCode("ざこ14コマンダー") animal/zako14/zk14com.c +#define CHARA_ZAKO19 { 0x31ec, ? } // GV_StrCode("ざこ19") animal/zako19/zako19.c +#define CHARA_ZAKO19COM { 0x8e88, ? } // GV_StrCode("ざこ19コマンダー") animal/zako19/zk19com.c +#define CHARA_ZAKOCOM { 0x7cf7, ? } // GV_StrCode("ざこコマンダー") animal/zako/zakocom.c +#define CHARA_NINJAPLAY { 0xb8d4, ? } // chara/njaplay/njaplay.c +#define CHARA_NOBU_WINMNGR { 0xae06, ? } // nobu/vr_slct/winmngr.c +#define CHARA_LOADREP { 0xc8ca, ? } // nobu/vr/loadrep.c +#define CHARA_SAVEMANAGER { 0xc5b7, ? } // nobu/vr_save/savemngr.c +#define CHARA_PLAYDEMO { 0x3f9d, ? } // nobu/vr_demo/playdemo.c (PC-ONLY) +#define CHARA_REC_DEMO { 0xf364, ? } // nobu/vr_demo/recdemo.c (PC-ONLY) +#define CHARA_VR_CHECK { 0x26bc, ? } // nobu/vr_slct/vr_check.c +#define CHARA_PRESENT { 0xaf75, ? } // nobu/present/present.c (PC-ONLY) +#define CHARA_3D78 { 0x3d78, ? } // (PSX-ONLY) + +/*--- Polygon Demo Charas ---*/ +#define DEMO_EXPLOSION { 0x0001, ? } // bullet/blast.c +#define DEMO_SMOKE { 0x0002, ? } // --> anime/animconv/anime.c +#define DEMO_FADEIN { 0x0003, NewFadeInOut } // takabe/fadeio.c +#define DEMO_FADEOUT { 0x0004, NewFadeInOut } // takabe/fadeio.c +#define DEMO_TEXT { 0x0005, NewTelop } // takabe/telop.c +#define DEMO_SURPRISEDMARK { 0x0006, ? } // --> anime/animconv/anime.c (PC-ONLY) +#define DEMO_QUESTIONMARK { 0x0007, ? } // --> anime/animconv/anime.c (PC-ONLY) +#define DEMO_SLEEPMARK { 0x0008, ? } // --> anime/animconv/anime.c (PC-ONLY) +#define DEMO_BLOOD { 0x0009, NewBlood } // okajima/blood.c +#define DEMO_BLOOD2 { 0x000a, NewSplash_800C8D6C } // okajima/splash.c +#define DEMO_BULLET { 0x000b, NewBulletEx } // okajima/bullet.c +#define DEMO_MISSILESMOKE { 0x000c, ? } // --> anime/animconv/anime.c +#define DEMO_BLOODCIRCLE { 0x000d, NewKetchap_s } // okajima/d_bloods.c +#define DEMO_BREATH { 0x000e, AN_Breath } // --> anime/animconv/anime.c +#define DEMO_SHADOW { 0x000f, NewDemoKage } // enemy/demokage.c +#define DEMO_FOOTPRINTS { 0x0010, NewDemoAsiato } // enemy/demoasi.c +#define DEMO_NINJAEYE { 0x0011, s08c_800C4194 } // --> anime/animconv/anime.c +#define DEMO_BUBBLE { 0x0012, NewBubbleT } // okajima/bubble_t.c +#define DEMO_BUBBLE2 { 0x0013, NewBubbleP_800D9D94 } // okajima/bubble_p.c +#define DEMO_SCOPE { 0x0014, NewScope } // equip/scope.c +#define DEMO_DARKVISIBLEGOGGLE { 0x0015, NewGoggle } // takabe/goggle.c +#define DEMO_DARKVISIBLEGOGGLE2 { 0x0016, NewGoggleSight } // equip/gglsight.c +#define DEMO_IRRAYSGOGGLE { 0x0017, NewGoggleIr } // takabe/goggleir.c +#define DEMO_IRRAYSGOGGLE2 { 0x0018, NewGoggleSight } // equip/gglsight.c +#define DEMO_GUNSMOKE { 0x0019, ? } // --> anime/animconv/anime.c (PC-ONLY) +#define DEMO_OPTICSCAMOUFLAGE { 0x001a, NewKogaku2 } // equip/kogaku2.c +#define DEMO_OPTICSCAMOUFLAGE2 { 0x001b, NewKogaku3 } // equip/kogaku2.c +#define DEMO_ENVIRONMENTMAPPING { 0x001c, NewEnvmap3_800CA3A4 } // takabe/envmap3.c +#define DEMO_PLASMA { 0x001d, ? } // okajima/plasma.c +#define DEMO_WINDCIRCLE { 0x001e, NewWindcrcl_800CF6BC } // takabe/windcrcl.c +#define DEMO_SEPIA { 0x001f, NewSepia_800C4F9C } // takabe/sepia.c +#define DEMO_METALGEARLASER { 0x0020, ? } // kojo/mgrexlzr.c +#define DEMO_UNSHAPEVIEW { 0x0021, NewFocusView } // takabe/focus.c +#define DEMO_GUSMASK { 0x0022, ? } // equip/gmsight.c +#define DEMO_URINATIONCIRCLE { 0x0023, ? } // okajima/d_bloods.c +#define DEMO_PLASMA2 { 0x0024, ? } // okajima/plasma.c +#define DEMO_BLUR { 0x0025, NewBlurSet } // okajima/blur.c +#define DEMO_STEAMSMOKE { 0x0026, ? } // --> anime/animconv/anime.c (PC-ONLY) +#define DEMO_STEAMSMOKE2 { 0x0027, ? } // --> anime/animconv/anime.c +#define DEMO_MONOTONE { 0x0028, NewSepia_800C5214 } // takabe/sepia.c +#define DEMO_RIFLESIGHT { 0x0029, ? } // weapon/rfsight.c (PC-ONLY) +#define DEMO_BLACKSMOKE { 0x002a, ? } // ??? +#define DEMO_URINATIONCIRCLE2 { 0x002b, NewKetchap_s } // okajima/d_bloods.c +#define DEMO_SIGHT { 0x002c, ? } // thing/sight.c (PC-ONLY) +#define DEMO_NINJASWORD { 0x002d, NewDemoKatana } // enemy/katana.c +#define DEMO_SUBMARINEROOM { 0x002e, NewSubRoom_800C815C } // okajima/sub_room.c +#define DEMO_BLACKSMOKE2 { 0x002f, AN_Smoke_800CE164 } // ??? +#define DEMO_BLASTLINE { 0x0030, ? } // okajima/blst_ln.c +#define DEMO_SMOKELINE { 0x0031, NewSmokeLn_800CDFA4 } // okajima/smke_ln.c +#define DEMO_SHELLSMOKE { 0x0032, ? } // kojo/m1e1smke.c +#define DEMO_CATERPILLERSMOKE { 0x0033, demothrd_2_8007DA94 } // --> anime/animconv/anime.c +#define DEMO_CROWEYE { 0x0034, ? } // --> anime/animconv/anime.c (PC-ONLY) +#define DEMO_OCEROTTEBULLETFIRE { 0x0035, NewAnime_8005E574 } // --> anime/animconv/anime.c +#define DEMO_OCEROTTEBULLETSMOKE { 0x0036, NewAnime_8005E6A4 } // --> anime/animconv/anime.c +#define DEMO_ENVIRONMENTLIGHT { 0x0037, NewRedAlert2 } // okajima/red_alrt.c +#define DEMO_ENVIRONMENTLIGHT2 { 0x0038, RedAlert_800C4F48 } // okajima/red_alrt.c +#define DEMO_CRASHSMOKE { 0x0039, ? } // okajima/crsh_smk.c +#define DEMO_CELOFAN { 0x003a, ? } // kojo/celofan.c (PC-ONLY) +#define DEMO_INVERSLIGHT { 0x003b, ? } // kojo/inverlt.c +#define DEMO_SPACK { 0x003c, NewSpark2_800CA714 } // takabe/spark2.c +#define DEMO_SPACK2 { 0x003d, NewSpark } // okajima/spark.c +#define DEMO_STEAMEXPLOSION { 0x003e, ? } // --> anime/animconv/anime.c +#define DEMO_INVERSLIGHT2 { 0x003f, NewInverseLight2 } // kojo/inverlt2.c +#define DEMO_CELOFAN2 { 0x0040, ? } // kojo/celofan2.c (PC-ONLY) +#define DEMO_NINJALASER { 0x0041, ? } // kojo/ninjalzr.c +#define DEMO_GUSEFFECT { 0x0042, NewGasEffect } // takabe/gas_efct.c +#define DEMO_SUBMARINEWATER { 0x0043, NewSubEfct_800CCB10 } // takabe/sub_efct.c +#define DEMO_GHOST { 0x0044, NewBlurPure } // okajima/blurpure.c +#define DEMO_FAMASLIGHT { 0x0045, NewFamasLight } // kojo/famaslit.c +#define DEMO_HUMANSHADOW { 0x0046, ? } // takabe/realshdw.c (PC-ONLY) +#define DEMO_BLOODHAZARD { 0x0047, ? } // kojo/bloodhzd.c +#define DEMO_BLOODDRIP { 0x0048, ? } // kojo/blooddrp.c +#define DEMO_NINJAGROUND { 0x0049, ? } // takabe/windcrcl.c +#define DEMO_BOMBLIGHT { 0x004a, NewBombLed } // animal/liquid/bombled.c +#define DEMO_MGCROOMDISPLAY { 0x004b, NewMGRoom } // okajima/mg_room.c +#define DEMO_TEXT2 { 0x004c, NewTelop2 } // takabe/telop.c + +/*---------------------------------------------------------------------------*/ +/* NEWCHARA external declarations */ +/*---------------------------------------------------------------------------*/ +#ifdef DECLARE_NEWCHARA_PROTOS + +// XXX You CAN NOT let any code that knows the real signatures of these +// XXX functions see these prototypes!! They're declared 'extern NEWCHARA' +// XXX so we can compile CHARA tables without warnings about incompatible +// XXX pointer types. + +extern NEWCHARA AN_Breath; /* --> anime/animconv/anime.c */ +extern NEWCHARA AN_Smoke_800CE164; /* --> anime/animconv/anime.c */ +extern NEWCHARA NewAnime_8005E574; /* --> anime/animconv/anime.c */ +extern NEWCHARA NewAnime_8005E6A4; /* --> anime/animconv/anime.c */ +extern NEWCHARA demothrd_2_8007DA94; /* --> anime/animconv/anime.c */ +extern NEWCHARA s08c_800C4194; /* --> anime/animconv/anime.c */ +extern NEWCHARA NewRope; /* --> chara/rope/rope.c */ +extern NEWCHARA NewCountdownGcl; /* --> menu/countdwn.c */ +extern NEWCHARA NewCamera_800CF388; /* ???/camera.c */ +extern NEWCHARA NewDemoDoll; /* animal/doll/doll.c */ +extern NEWCHARA NewBombLed; /* animal/liquid/bombled.c */ +extern NEWCHARA NewMeryl72_800C7BC4; /* animal/meryl72/meryl72.c */ +extern NEWCHARA NewNinjaBoss; /* animal/ninja/ninja.c */ +extern NEWCHARA d18a_snake18_800D4E94; /* animal/snake18/snake18.c */ +extern NEWCHARA NewZako11E; /* animal/zako11e/zako11e.c */ +extern NEWCHARA NewZako11ECommander; /* animal/zako11e/zk11ecom.c */ +extern NEWCHARA NewZako11F; /* animal/zako11f/zako11f.c */ +extern NEWCHARA NewZako11FCommander; /* animal/zako11f/zk11fcom.c */ +extern NEWCHARA NewScenarioJirai; /* bullet/jirai.c */ +extern NEWCHARA NewHindBoss; /* kojo/hind.c */ +extern NEWCHARA NewHind; /* chara/hind/hind.c */ +extern NEWCHARA NewHind2; /* chara/hind2/hind2.c */ +extern NEWCHARA NewBelong; /* chara/others/belong.c */ +extern NEWCHARA NewFontText; /* chara/others/fonttext.c */ +extern NEWCHARA NewIntrudeCamera; /* chara/others/intr_cam.c */ +extern NEWCHARA NewMotionSoundEffect; /* chara/others/motse.c */ +extern NEWCHARA NewDamageSmoke2; /* chara/rope/dsmoke2.c */ +extern NEWCHARA NewLanding; /* chara/rope/landing.c */ +extern NEWCHARA NewPipe; /* chara/rope/pipe.c */ +extern NEWCHARA NewSnakeBreath; /* chara/snake/breath.c */ +extern NEWCHARA NewSnake; /* chara/snake/sna_init.c */ +extern NEWCHARA NewTortureBed; /* chara/torture/bed.c */ +extern NEWCHARA NewAllItemBox; /* chara/torture/boxall.c */ +extern NEWCHARA NewJohnny; /* chara/torture/johnny.c */ +extern NEWCHARA NewPrisonNinja; /* chara/torture/ninja.c */ +extern NEWCHARA NewPrisonOtacon; /* chara/torture/otacom.c */ +extern NEWCHARA NewTortureOcelot; /* chara/torture/revolver.c */ +extern NEWCHARA NewPrisonSnake; /* chara/torture/sne_03c.c */ +extern NEWCHARA NewPrisonSnake2; /* chara/torture/sne_03c.c */ +extern NEWCHARA NewTorture; /* chara/torture/torture.c */ +extern NEWCHARA NewWake; /* chara/wake/wake.c */ +extern NEWCHARA NewAsiatoKun; /* enemy/asiato.c */ +extern NEWCHARA NewAsiotoKun; /* enemy/asioto.c */ +extern NEWCHARA NewCamera_800D67F8; /* enemy/camera.c */ +extern NEWCHARA NewCommander; /* enemy/command.c */ +extern NEWCHARA NewDemoAsiato; /* enemy/demoasi.c */ +extern NEWCHARA NewDemoKage; /* enemy/demokage.c */ +extern NEWCHARA NewDemoKatana; /* enemy/katana.c */ +extern NEWCHARA NewKikenKun; /* enemy/kiken.c */ +extern NEWCHARA NewEnemyMeryl_800D63A4; /* enemy/meryl7.c */ +extern NEWCHARA NewMovingObject; /* enemy/object.c */ +extern NEWCHARA NewSearchlight; /* enemy/searchli.c */ +extern NEWCHARA NewSmoke; /* enemy/smoke.c */ +extern NEWCHARA NewWallGcl; /* enemy/wall.c */ +extern NEWCHARA NewSnakeWatcher; /* enemy/watcher.c */ +extern NEWCHARA NewGoggleSight; /* equip/gglsight.c */ +extern NEWCHARA NewKogaku2; /* equip/kogaku2.c */ +extern NEWCHARA NewKogaku3; /* equip/kogaku2.c */ +extern NEWCHARA NewScope; /* equip/scope.c */ +extern NEWCHARA NewDemoCancel; /* game/cancel.c */ +extern NEWCHARA NewElevatorPanel; /* game/evpanel.c */ +extern NEWCHARA NewItem; /* game/item.c */ +extern NEWCHARA NewTextureLamp; /* game/lamp.c */ +extern NEWCHARA NewMovieGCL; /* game/movie.c */ +extern NEWCHARA NewPadControl; /* game/pad.c */ +extern NEWCHARA NewPoint; /* game/point.c */ +extern NEWCHARA GM_SetSecondAvailable; /* game/second.c */ +extern NEWCHARA NewSelect; /* game/select.c */ +extern NEWCHARA NewSoundTest; /* game/sndtst.c */ +extern NEWCHARA NewToBeContinued; /* game/tobcnt.c */ +extern NEWCHARA NewPadVibrationGcl; /* game/vibrate.c */ +extern NEWCHARA NewVrWindow; /* koba/vr/vrwindow.c */ +extern NEWCHARA NewFamasLight; /* kojo/famaslit.c */ +extern NEWCHARA NewInverseLight2; /* kojo/inverlt2.c */ +extern NEWCHARA NewSnowStorm; /* kojo/sstorm.c */ +extern NEWCHARA NewLoadData; /* menu/mload.c */ +extern NEWCHARA NewStage11GDemo; /* okajima/11g_demo.c */ +extern NEWCHARA NewBlinkTexture; /* okajima/blink_tx.c */ +extern NEWCHARA NewBlood; /* okajima/blood.c */ +extern NEWCHARA NewBloodBl; /* okajima/blood_bl.c */ +extern NEWCHARA NewBloodCl; /* okajima/blood_cl.c */ +extern NEWCHARA NewBlurSet; /* okajima/blur.c */ +extern NEWCHARA NewBlurPure; /* okajima/blurpure.c */ +extern NEWCHARA NewBubbleDSn; /* okajima/bub_d_sn.c */ +extern NEWCHARA NewBubbleP_800D9D94; /* okajima/bubble_p.c */ +extern NEWCHARA NewBubbleS; /* okajima/bubble_s.c */ +extern NEWCHARA NewBubbleT; /* okajima/bubble_t.c */ +extern NEWCHARA NewBulletEx; /* okajima/bullet.c */ +extern NEWCHARA NewCrane; /* okajima/crane.c */ +extern NEWCHARA NewKetchap_s; /* okajima/d_bloods.c */ +extern NEWCHARA NewDeathSpark; /* okajima/death_sp.c */ +extern NEWCHARA NewDog; /* okajima/dog/dog.c */ +extern NEWCHARA NewDuctMouse; /* okajima/ductmous.c */ +extern NEWCHARA NewFallSplash; /* okajima/fall_spl.c */ +extern NEWCHARA NewFloorSpark; /* okajima/flr_spa.c */ +extern NEWCHARA NewGunCamera; /* okajima/guncame.c */ +extern NEWCHARA NewHiyokoGcl; /* okajima/hiyoko.c */ +extern NEWCHARA NewItemDot; /* okajima/item_dot.c */ +extern NEWCHARA NewKeyItem; /* okajima/key_item.c */ +extern NEWCHARA NewMGRoom; /* okajima/mg_room.c */ +extern NEWCHARA NewMouse; /* okajima/mouse.c */ +extern NEWCHARA NewPilotLamp; /* okajima/p_lamp.c */ +extern NEWCHARA NewPatrolLamp; /* okajima/pato_lmp.c */ +extern NEWCHARA NewPlasmaGcl; /* okajima/plasma.c */ +extern NEWCHARA NewRedAlert; /* okajima/red_alrt.c */ +extern NEWCHARA NewRedAlert2; /* okajima/red_alrt.c */ +extern NEWCHARA RedAlert_800C4F48; /* okajima/red_alrt.c */ +extern NEWCHARA NewStage11Objects; /* okajima/s11_objs.c */ +extern NEWCHARA NewScreenMark; /* okajima/scn_mark.c */ +extern NEWCHARA NewSmokeLn_800CDFA4; /* okajima/smke_ln.c */ +extern NEWCHARA NewSmokeTarget; /* okajima/smktrgt.c */ +extern NEWCHARA NewSpark; /* okajima/spark.c */ +extern NEWCHARA NewSplash_800C8D6C; /* okajima/splash.c */ +extern NEWCHARA NewSubRoom_800C815C; /* okajima/sub_room.c */ +extern NEWCHARA NewUji; /* okajima/uji.c */ +extern NEWCHARA NewCrow; /* okajima/valcan/crow.c */ +extern NEWCHARA s15c_dyncon_800D8C9C; /* okajima/valcan/dyncon.c */ +extern NEWCHARA NewWallSpark; /* okajima/wall_spa.c */ +extern NEWCHARA NewWolf2; /* okajima/wolf/wolf2.c */ +extern NEWCHARA NewAbstractChange; /* onoda/abst/ab_ch.c */ +extern NEWCHARA NewAbstractDemo1; /* onoda/abst/ab_demo1.c */ +extern NEWCHARA NewAbstractDemo2; /* onoda/abst/ab_demo2.c */ +extern NEWCHARA NewAbstract; /* onoda/abst/abst.c */ +extern NEWCHARA NewCdChange; /* onoda/change/change.c */ +extern NEWCHARA Safety_800C47A0; /* onoda/change/safety.c (?) */ +extern NEWCHARA NewDemoSelect; /* onoda/demosel/demosel.c */ +extern NEWCHARA NewOpen; /* onoda/open/open.c */ +extern NEWCHARA NewOption; /* onoda/option/opt.c */ +extern NEWCHARA NewPreviousOperation; /* onoda/preope/preope.c */ +extern NEWCHARA NewArmsTechPresident; /* onoda/s04b/at.c */ +extern NEWCHARA NewCape; /* onoda/s04b/cape.c */ +extern NEWCHARA NewOcelotBoss; /* onoda/s04b/revolver.c */ +extern NEWCHARA NewWire; /* onoda/s04b/wire.c */ +extern NEWCHARA NewBreakObject; /* takabe/breakobj.c */ +extern NEWCHARA NewCameraShake; /* takabe/camshake.c */ +extern NEWCHARA NewZoom_800DFA88; /* takabe/cat_in.c */ +extern NEWCHARA NewChair; /* takabe/chair.c */ +extern NEWCHARA NewCinemaScreenSet; /* takabe/cinema.c */ +extern NEWCHARA NewDoor2; /* takabe/door2.c */ +extern NEWCHARA NewDummyFloor; /* takabe/dummy_fl.c */ +extern NEWCHARA NewDummyWall; /* takabe/dummy_wl.c */ +extern NEWCHARA NewDynamicFloorSet; /* takabe/dymc_flr.c */ +extern NEWCHARA NewDynamicWallSet; /* takabe/dymc_seg.c */ +extern NEWCHARA NewEndingTelop; /* takabe/ed_telop.c */ +extern NEWCHARA EdTelop_800C4F18; /* takabe/ed_telop.c (?) */ +extern NEWCHARA NewElectricDamage; /* takabe/elc_damg.c */ +extern NEWCHARA NewElectricFloor; /* takabe/elc_flr.c */ +extern NEWCHARA NewElevator; /* takabe/elevator.c */ +extern NEWCHARA NewEndingRoll; /* takabe/ending2.c */ +extern NEWCHARA NewEnvSound; /* takabe/env_snd.c */ +extern NEWCHARA NewEnvmap3_800CA3A4; /* takabe/envmap3.c */ +extern NEWCHARA NewFadeInOut; /* takabe/fadeio.c */ +extern NEWCHARA NewFadeInOutSet; /* takabe/fadeio.c */ +extern NEWCHARA NewFindTrap; /* takabe/findtrap.c */ +extern NEWCHARA NewFocusView; /* takabe/focus.c */ +extern NEWCHARA NewFog; /* takabe/fog.c */ +extern NEWCHARA NewFurnace; /* takabe/furnace.c */ +extern NEWCHARA NewGasEffectSet; /* takabe/gas_efct.c */ +extern NEWCHARA NewGasEffect; /* takabe/gas_efct.c */ +extern NEWCHARA NewGasDamage; /* takabe/gasdamge.c */ +extern NEWCHARA NewGlassSet; /* takabe/glass.c */ +extern NEWCHARA NewGoggle; /* takabe/goggle.c */ +extern NEWCHARA NewGoggleIr; /* takabe/goggleir.c */ +extern NEWCHARA NewIrSensor; /* takabe/ir_cens.c */ +extern NEWCHARA NewJeepDrum; /* takabe/jeep/jdrum.c */ +extern NEWCHARA NewJeepEnemy; /* takabe/jeep_emy/jeep_emy.c */ +extern NEWCHARA NewJeepScroll; /* takabe/jeep_srl.c */ +extern NEWCHARA NewLifeUp; /* takabe/life_up.c */ +extern NEWCHARA NewLift; /* takabe/lift.c */ +extern NEWCHARA NewMirror; /* takabe/mirror.c */ +extern NEWCHARA NewMonitor1; /* takabe/monitor1.c */ +extern NEWCHARA NewMosaicSet; /* takabe/mosaic.c */ +extern NEWCHARA NewFewDamageSet; /* takabe/o2_damge.c */ +extern NEWCHARA NewPadDemo; /* takabe/pad_demo.c */ +extern NEWCHARA NewPanel; /* takabe/panel.c */ +extern NEWCHARA NewPanel2; /* takabe/panel2.c */ +extern NEWCHARA NewPaper; /* takabe/paper.c */ +extern NEWCHARA NewPutObject; /* takabe/put_obj.c */ +extern NEWCHARA NewRasen2; /* takabe/rasen.c */ +extern NEWCHARA NewRasenElevator; /* takabe/rasen_el.c */ +extern NEWCHARA NewRipples; /* takabe/ripples.c */ +extern NEWCHARA NewRippleSurface; /* takabe/rsurface.c */ +extern NEWCHARA NewSepia_800C4F9C; /* takabe/sepia.c */ +extern NEWCHARA NewSepia_800C5214; /* takabe/sepia.c */ +extern NEWCHARA NewShakeModelGCL; /* takabe/shakemdl.c */ +extern NEWCHARA NewShutter; /* takabe/shuter.c */ +extern NEWCHARA NewSpark2_800CA714; /* takabe/spark2.c */ +extern NEWCHARA NewSubEfct_800CCB10; /* takabe/sub_efct.c */ +extern NEWCHARA NewTelop2; /* takabe/telop.c */ +extern NEWCHARA NewTelopSet; /* takabe/telop.c */ +extern NEWCHARA NewTelop; /* takabe/telop.c */ +extern NEWCHARA NewTexScroll; /* takabe/tex_scrl.c */ +extern NEWCHARA NewTruckTrap; /* takabe/tracktrp.c */ +extern NEWCHARA NewVibrationEditor; /* takabe/vib_edit.c */ +extern NEWCHARA NewVoiceSystem; /* takabe/voicesys.c */ +extern NEWCHARA NewWindcrcl_800CF6BC; /* takabe/windcrcl.c */ +extern NEWCHARA NewWaterSurface; /* takabe/wsurface.c */ +extern NEWCHARA NewWaterArea; /* takabe/wt_area.c */ +extern NEWCHARA NewWaterArea2; /* takabe/wt_area2.c */ +extern NEWCHARA NewWaterView; /* takabe/wt_view.c */ +extern NEWCHARA NewDoor; /* thing/door.c */ +extern NEWCHARA NewEmitter; /* thing/emitter.c */ +extern NEWCHARA NewSnow; /* thing/snow.c */ +extern NEWCHARA NewSphere; /* thing/sphere.c */ +extern NEWCHARA NewSphere2; /* thing/sphere2.c */ + +#endif // DECLARE_NEWCHARA_PROTOS +#endif // __MGS_CHARALST_H__ diff --git a/components/mgs/mgs/source/include/common.h b/components/mgs/mgs/source/include/common.h new file mode 100644 index 00000000..71be1eef --- /dev/null +++ b/components/mgs/mgs/source/include/common.h @@ -0,0 +1,207 @@ +#ifndef __MGS_COMMON_H__ +#define __MGS_COMMON_H__ + +#include // for NULL +#include +#include "psxdefs.h" + +/* omitted from the PSX Runtime Library's stddef.h */ +#ifndef offsetof +#define offsetof(type, member) ((size_t)&(((type *)0)->member)) +#endif + +/* MSVC defines _countof as an extension to stdlib.h */ +#ifndef _countof +#define _countof(array) (sizeof(array)/sizeof(array[0])) +#endif +/* alternate names without the leading underscore */ +#define countof(array) _countof(array) +#define COUNTOF(array) _countof(array) + +/*---------------------------------------------------------------------------*/ + +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif +#ifndef MAX +#define MAX(x, y) (((x) > (y)) ? (x) : (y)) +#endif +#ifndef ABS +#define ABS(x) (((x) >= 0) ? (x) : -(x)) +#endif + +#ifndef CLAMP +#define CLAMP(x, min, max) (MAX(MIN(x, max), min)) +#endif + +#ifndef SWAP +#define SWAP(a, b) \ + do { \ + __typeof__(a) _temp = (a); \ + (a) = (b); (b) = _temp; \ + } while (0) +#endif + +/*---------------------------------------------------------------------------*/ +// NOTE: (bool)0.5 evaluates to true, whereas (int)0.5 evaluates to 0. + +typedef int BOOL; + +#ifndef FALSE +#define FALSE (0) +#endif +#ifndef TRUE +#define TRUE (!FALSE) +#endif + +/*---------------------------------------------------------------------------*/ + +// NOTE: Within the PSX primitive structures, RGB color values are followed +// by either a GPU primitive code or padding. We'll still consider the fourth +// byte to be the "alpha" channel here. +// +// The GPU primitives store the RGB values in big-endian order (see P_TAG/P_CODE +// in libgpu.h), but MGS handles the values by reading/writing the whole row as +// a single 32-bit word, therefore R becomes the LSB, etc. + +/*----- RGBA8888 format -----*/ + +#ifdef WORDS_BIGENDIAN +#define RGBA_R_SHIFT (24) +#define RGBA_G_SHIFT (16) +#define RGBA_B_SHIFT ( 8) +#define RGBA_A_SHIFT ( 0) +#else +#define RGBA_R_SHIFT ( 0) +#define RGBA_G_SHIFT ( 8) +#define RGBA_B_SHIFT (16) +#define RGBA_A_SHIFT (24) +#endif + +#define RGBA_R_MASK (0xff << RGBA_R_SHIFT) +#define RGBA_G_MASK (0xff << RGBA_G_SHIFT) +#define RGBA_B_MASK (0xff << RGBA_B_SHIFT) +#define RGBA_A_MASK (0xff << RGBA_A_SHIFT) + +#define MAKE_RGB_WITHOUT_BITMASK +#ifdef MAKE_RGB_WITHOUT_BITMASK +/* simple version without setting the alpha channel */ +#define MAKE_RGB(_r, _g, _b) \ + ((unsigned int)(((_r) << RGBA_R_SHIFT) | \ + ((_g) << RGBA_G_SHIFT) | \ + ((_b) << RGBA_B_SHIFT))) +#else +#define MAKE_RGB(_r, _g, _b) \ + ((unsigned int)((((_r) & 0xff) << RGBA_R_SHIFT) | \ + (((_g) & 0xff) << RGBA_G_SHIFT) | \ + (((_b) & 0xff) << RGBA_B_SHIFT))) +#endif + +//#define MAKE_RGBA_WITHOUT_BITMASK +#ifdef MAKE_RGBA_WITHOUT_BITMASK +#define MAKE_RGBA(_r,_g,_b,_a) \ + ((unsigned int)(((_r) << RGBA_R_SHIFT) | \ + ((_g) << RGBA_G_SHIFT) | \ + ((_b) << RGBA_B_SHIFT) | \ + ((_a) << RGBA_A_SHIFT))) +#else +#define MAKE_RGBA(_r,_g,_b,_a) \ + ((unsigned int)((((_r) & 0xff) << RGBA_R_SHIFT) | \ + (((_g) & 0xff) << RGBA_G_SHIFT) | \ + (((_b) & 0xff) << RGBA_B_SHIFT) | \ + (((_a) & 0xff) << RGBA_A_SHIFT))) +#endif + +#define MAKE_RGB0(_r,_g,_b) MAKE_RGBA(_r,_g,_b,0x00) +#define MAKE_RGBX(_r,_g,_b) MAKE_RGBA(_r,_g,_b,0xff) /* full-alpha */ +#define MAKE_RGBH(_r,_g,_b) MAKE_RGBA(_r,_g,_b,0x80) /* half-alpha */ + +#define GET_R_FROM_RGBA(_rgba) (((_rgba) >> RGBA_R_SHIFT) & 0xff) +#define GET_G_FROM_RGBA(_rgba) (((_rgba) >> RGBA_G_SHIFT) & 0xff) +#define GET_B_FROM_RGBA(_rgba) (((_rgba) >> RGBA_B_SHIFT) & 0xff) +#define GET_A_FROM_RGBA(_rgba) (((_rgba) >> RGBA_A_SHIFT) & 0xff) + +/*----- Common Colors -----*/ + +#define COLOR_BLACK MAKE_RGB0( 0, 0, 0) // 0x00000000 +#define COLOR_WHITE MAKE_RGB0(255,255,255) // 0x00ffffff +#define COLOR_GRAY MAKE_RGB0(128,128,128) // 0x00808080 +#define COLOR_RED MAKE_RGB0(255, 0, 0) // 0x000000ff +#define COLOR_GREEN MAKE_RGB0( 0,255, 0) // 0x0000ff00 +#define COLOR_BLUE MAKE_RGB0( 0, 0,255) // 0x00ff0000 +#define COLOR_CYAN MAKE_RGB0( 0,255,255) // 0x00ffff00 +#define COLOR_MAGENTA MAKE_RGB0(255, 0,255) // 0x00ff00ff +#define COLOR_YELLOW MAKE_RGB0(255,255, 0) // 0x0000ffff + +/*---------------------------------------------------------------------------*/ + +// #define USE_STATIC_KEYWORD +#ifdef USE_STATIC_KEYWORD +#define STATIC static +#else +#define STATIC /* fake keyword for documentation */ +#endif + +#define SECTION(x) __attribute__((section(x))) + +/*---------------------------------------------------------------------------*/ + +#define SCREEN_WIDTH (320) +#define SCREEN_HEIGHT (240) + +#define FRAME_WIDTH (320) +#define FRAME_HEIGHT (224) /* 240 minus 16-pixel overscan area */ + +/*---------------------------------------------------------------------------*/ + +static inline u_long LLOAD( void *from ) +{ + return *(u_long *)from; +} + +static inline void LSTORE( u_long from, void *to ) +{ + *(u_long *)to = from; +} + +static inline u_short SLOADL( void *from ) +{ + return *(u_short *)from; +} + +static inline void SSTOREL( u_short from, void *to ) +{ + *(u_short *)to = from; +} + +static inline void LCOPY( void *s1, void *d1 ) +{ + *(u_long *)d1 = *(u_long *)s1; +} + +static inline void LCOPY2( void *s1, void *d1, void *s2, void *d2 ) +{ + u_long r1, r2; + + r1 = *(u_long *)s1; + r2 = *(u_long *)s2; + *(u_long *)d1 = r1; + *(u_long *)d2 = r2; +} + +static inline void SCOPYL( void *s1, void *d1 ) +{ + *(u_short *)d1 = *(u_short *)s1; +} + +static inline void SCOPYL2( void *s1, void *d1, void *s2, void *d2 ) +{ + u_short r1, r2; + + r1 = *(u_short *)s1; + r2 = *(u_short *)s2; + *(u_short *)d1 = r1; + *(u_short *)d2 = r2; +} + +#endif // __MGS_COMMON_H__ diff --git a/components/mgs/mgs/source/include/fmt_dmo.h b/components/mgs/mgs/source/include/fmt_dmo.h new file mode 100644 index 00000000..769e0910 --- /dev/null +++ b/components/mgs/mgs/source/include/fmt_dmo.h @@ -0,0 +1,86 @@ +#ifndef __MGS_FMT_DMO_H__ +#define __MGS_FMT_DMO_H__ +// clang-format off + +#include + +/* might be a union, same as 0x36 */ +typedef struct { + int field_0; + int field_4_type; + short field_8_x; + short field_A_y; + short field_C_z; + short field_E_x; + short field_10_y; + short field_12_z; + int field_14_type; + int field_18_xy; + int field_1C_zpad; + int field_20; + char field_24; + char field_25; + char field_26; + char field_27; + short field_28; + short field_2A; + int field_2C; + short field_30; + short field_32; +} DMO_CHA; + +typedef struct { + int type; + short visible; + short rot_x; + short rot_y; + short rot_z; + short pos_x; + short pos_y; + short pos_z; + short n_rots; + short *rots; +} DMO_ADJ; + +typedef struct { + u_int tag; + int frame; + short eye_x; + short eye_y; + short eye_z; + short center_x; + short center_y; + short center_z; + short roll; + short clip_dist; + short n_charas; + DMO_CHA *chara; + short n_adjusts; + DMO_ADJ *adjust; +} DMO_DAT; + +typedef struct { + int cache_id; + int filename; +} DMO_MAP; + +typedef struct { + int type; + int flag; + int cache_id; + int filename; + int name; +} DMO_MDL; + +typedef struct { + u_int tag; + int frame; + int n_frames; + int n_maps; + int n_models; + DMO_MAP *maps; + DMO_MDL *models; +} DMO_DEF; + +// clang-format on +#endif // __MGS_FMT_DMO_H__ diff --git a/components/mgs/mgs/source/include/fmt_hzd.h b/components/mgs/mgs/source/include/fmt_hzd.h new file mode 100644 index 00000000..8177443d --- /dev/null +++ b/components/mgs/mgs/source/include/fmt_hzd.h @@ -0,0 +1,151 @@ +#ifndef __MGS_FMT_HZD_H__ +#define __MGS_FMT_HZD_H__ +// clang-format off + +#include +#include + +/** + * Hazard 16-bit vector + */ +typedef struct { + long long_access[0]; + short x, z, y, h; +} HZD_VEC; + +/** + * Hazard segment (a.k.a "wall") + * + * Walls are always pefectly vertical. + * The soliton radar is drawn from this data. + */ +typedef struct { + HZD_VEC p1, p2; +} HZD_SEG; + +/** + * Hazard floor + * + * Used for every collidable surface that's not considered a wall. + */ +typedef struct { + HZD_VEC b1, b2; + HZD_VEC p1, p2, p3, p4; +} HZD_FLR; + +/** + * Hazard trap (a.k.a "trigger volume") + * + * Named bounding box that triggers an event when an actor enters + * or exits its dimensions. + */ +typedef struct { + HZD_VEC b1, b2; + char name[12]; + u_char id1, id2; + u_short name_id; +} HZD_TRP; + +/* +// Old version(?) from memleak + +typedef struct { + HZD_VEC b1, b2 ; + char name[ 12 ] ; + u_short name_id ; +} HZD_TRP ; +*/ + +/** + * Hazard camera + * + * Camera angle data for "behind mode" + */ +typedef struct { + HZD_VEC b1, b2; + HZD_VEC cam; + HZD_VEC orient; +} HZD_CAM; + +/* +typedef struct { + HZD_VEC b1, b2; + HZD_VEC c1, c2; + HZD_VEC orient; + u_short id; + u_short name_id; +} HZD_CAM; +*/ + +/** + * Hazard trigger + * + * Union of the trap and camera data definitions. + */ +typedef union { + HZD_CAM cam; + HZD_TRP trap; +} HZD_TRG; + +/** + * Hazard zone + * + * Individual sector of the hazard map's navmesh. + */ +typedef struct { + short x, z, y; + short w, h; + u_char nears[6]; + u_char dists[6]; + short padding; +} HZD_ZON; + +/** + * Hazard patrol point + * + * Individual point of an enemy soldier's patrol route. + * Includes behavior command data (which direction to face, etc.) + */ +typedef struct { + short x, z, y; + short command; // act, time, dir, unk +} HZD_PTP; + +/** + * Hazard patrol route + * + * Complete enemy soldier patrol route data. + * Defines the number of points and starting point for the soldier. + */ +typedef struct { + short n_points; + short init_point; + HZD_PTP *points; +} HZD_PAT; + +typedef struct { + short n_triggers; + short n_walls; + short n_floors; + short n_flat_walls; + HZD_SEG *walls; + HZD_FLR *floors; + HZD_TRG *triggers; + char *wallsFlags; +} HZD_GRP; + +typedef struct { + void *ptr_access[0]; + short version; + short min_x, min_y; + short max_x, max_y; + short n_groups; + short n_zones; + short n_routes; + HZD_GRP *groups; + HZD_ZON *zones; + HZD_PAT *routes; +} HZD_DEF; + +// clang-format on +#endif // __MGS_FMT_HZD_H__ diff --git a/components/mgs/mgs/source/include/fmt_mot.h b/components/mgs/mgs/source/include/fmt_mot.h new file mode 100644 index 00000000..f6711c5b --- /dev/null +++ b/components/mgs/mgs/source/include/fmt_mot.h @@ -0,0 +1,25 @@ +#ifndef __MGS_FMT_MOT_H__ +#define __MGS_FMT_MOT_H__ + +#include +#include + +typedef u_short MOTION_ARCHIVE; +typedef u_short MOTION_TABLE; + +typedef struct MOTION_SEGMENT +{ + SVECTOR base; + SVECTOR delta; + int interp; + u_short *stream; + short field_18; + short field_1A; + unsigned char field_1C; + char bit_offset; + char x_size; + char y_size; + char z_size; +} MOTION_SEGMENT; + +#endif // __MGS_FMT_MOT_H__ diff --git a/components/mgs/mgs/source/include/fmt_sgt.h b/components/mgs/mgs/source/include/fmt_sgt.h new file mode 100644 index 00000000..92c43903 --- /dev/null +++ b/components/mgs/mgs/source/include/fmt_sgt.h @@ -0,0 +1,16 @@ +#ifndef __MGS_FMT_SGT_H__ +#define __MGS_FMT_SGT_H__ + +#include + +typedef struct SgtFile +{ + unsigned int unknown0; + unsigned char *unknown1; + unsigned char *unknown2; + unsigned char *unknown3; + unsigned char *unknown4; + unsigned char *unknown5; +} SgtFile; + +#endif // __MGS_FMT_SGT_H__ diff --git a/components/mgs/mgs/source/include/fmt_tex.h b/components/mgs/mgs/source/include/fmt_tex.h new file mode 100644 index 00000000..6db5d5ed --- /dev/null +++ b/components/mgs/mgs/source/include/fmt_tex.h @@ -0,0 +1,47 @@ +#ifndef __MGS_FMT_TEX_H__ +#define __MGS_FMT_TEX_H__ +// clang-format off + +#include + +typedef struct PCXINFO { + unsigned short magic; /* always 12345 */ + unsigned short flags; + unsigned short px, py; /* pixel X/Y coords */ + unsigned short cx, cy; /* CLUT X/Y coords */ + unsigned short n_colors; +} PCXINFO; + +typedef struct PCXDATA { + unsigned char manufacturer; + unsigned char version; + unsigned char encoding; + unsigned char bits_per_pixel; + unsigned short min_x, min_y; + unsigned short max_x, max_y; + unsigned short dpi_x, dpi_y; + unsigned char header_palette[ 48 ]; + unsigned char reserved; + unsigned char n_planes; + unsigned short bytes_per_line; + unsigned short header_palette_class; + unsigned short screen_width, screen_height; + PCXINFO info; + unsigned char pad[ 54 - sizeof(PCXINFO) ]; + unsigned char data[ 0 ]; /* image data */ +} PCXDATA; + +typedef struct { + u_char mode; + u_char bit; + u_short color; + u_short width; + u_short height; + u_short flag; + u_short px, py; + u_short cx, cy; + u_short n_data; +} PLL_HEADER; + +// clang-format on +#endif // __MGS_FMT_TEX_H__ diff --git a/components/mgs/mgs/source/include/inline_n.h b/components/mgs/mgs/source/include/inline_n.h new file mode 100644 index 00000000..b36bad81 --- /dev/null +++ b/components/mgs/mgs/source/include/inline_n.h @@ -0,0 +1,1457 @@ +/* clang-format off */ + +/* + * Macro definitions w/o DMPSX for Nugget + * inline_n.h + */ + +#ifdef __psyz +/* These macros are raw MIPS COP2 assembly and cannot be assembled for any other + * target. PSY-Z implements the same GTE as portable C and exposes it through + * , so on a non-PSX build we defer to that instead of emitting asm. + * Anything PSY-Z does not yet cover shows up as an undefined symbol at link + * time, which is the honest way to find the remaining gap. */ +#include +#else +/* + * Type 1 functions + */ + +#define gte_ldv0( r0 ) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 )" \ + : \ + : "r"( r0 ) ) + +#define gte_ldv1( r0 ) __asm__ volatile ( \ + "lwc2 $2, 0( %0 );" \ + "lwc2 $3, 4( %0 )" \ + : \ + : "r"( r0 ) ) + +#define gte_ldv2( r0 ) __asm__ volatile ( \ + "lwc2 $4, 0( %0 );" \ + "lwc2 $5, 4( %0 )" \ + : \ + : "r"( r0 ) ) + +#define gte_ldv3( r0, r1, r2 ) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 );" \ + "lwc2 $2, 0( %1 );" \ + "lwc2 $3, 4( %1 );" \ + "lwc2 $4, 0( %2 );" \ + "lwc2 $5, 4( %2 )" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) ) + +#define gte_ldv3c( r0 ) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 );" \ + "lwc2 $2, 8( %0 );" \ + "lwc2 $3, 12( %0 );" \ + "lwc2 $4, 16( %0 );" \ + "lwc2 $5, 20( %0 )" \ + : \ + : "r"( r0 ) ) + +#define gte_ldv3c_vertc( r0 ) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 );" \ + "lwc2 $2, 12( %0 );" \ + "lwc2 $3, 16( %0 );" \ + "lwc2 $4, 24( %0 );" \ + "lwc2 $5, 28( %0 )" \ + : \ + : "r"( r0 ) ) + +#define gte_ldv01( r0, r1 ) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 );" \ + "lwc2 $2, 0( %1 );" \ + "lwc2 $3, 4( %1 )" \ + : \ + : "r"( r0 ), "r"( r1 ) ) + +#define gte_ldv01c( r0 ) __asm__ volatile ( \ + "lwc2 $0, 0( %0 );" \ + "lwc2 $1, 4( %0 );" \ + "lwc2 $2, 8( %0 );" \ + "lwc2 $3, 12( %0 )" \ + : \ + : "r"( r0 ) ) + +#define gte_ldrgb( r0 ) __asm__ volatile ( \ + "lwc2 $6, 0( %0 )" \ + : \ + : "r"( r0 ) ) + +#define gte_ldrgb3( r0, r1, r2 ) __asm__ volatile ( \ + "lwc2 $20, 0( %0 );" \ + "lwc2 $21, 0( %1 );" \ + "lwc2 $22, 0( %2 );" \ + "lwc2 $6, 0( %2 )" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) ) + +#define gte_ldrgb3c( r0 ) __asm__ volatile ( \ + "lwc2 $20, 0( %0 );" \ + "lwc2 $21, 4( %0 );" \ + "lwc2 $22, 8( %0 );" \ + "lwc2 $6, 8( %0 )" \ + : \ + : "r"( r0 ) ) + +#define gte_ldlv0( r0 ) __asm__ volatile ( \ + "lhu $13, 4( %0 );" \ + "lhu $12, 0( %0 );" \ + "sll $13, $13, 16;" \ + "or $12, $12, $13;" \ + "mtc2 $12, $0;" \ + "lwc2 $1, 8( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "$13" ) + +#define gte_ldlvl( r0 ) __asm__ volatile ( \ + "lwc2 $9, 0( %0 );" \ + "lwc2 $10, 4( %0 );" \ + "lwc2 $11, 8( %0 )" \ + : \ + : "r"( r0 ) ) + +#define gte_ldsv( r0 ) __asm__ volatile ( \ + "lhu $12, 0( %0 );" \ + "lhu $13, 2( %0 );" \ + "lhu $14, 4( %0 );" \ + "mtc2 $12, $9;" \ + "mtc2 $13, $10;" \ + "mtc2 $14, $11" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) + +#define gte_ldbv( r0 ) __asm__ volatile ( \ + "lbu $12, 0( %0 );" \ + "lbu $13, 1( %0 );" \ + "mtc2 $12, $9;" \ + "mtc2 $13, $10" \ + : \ + : "r"( r0 ) \ + : "$12", "$13" ) + +#define gte_ldcv( r0 ) __asm__ volatile ( \ + "lbu $12, 0( %0 );" \ + "lbu $13, 1( %0 );" \ + "lbu $14, 2( %0 );" \ + "mtc2 $12, $9;" \ + "mtc2 $13, $10;" \ + "mtc2 $14, $11" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) + +#define gte_ldclmv( r0 ) __asm__ volatile ( \ + "lhu $12, 0( %0 );" \ + "lhu $13, 6( %0 );" \ + "lhu $14, 12( %0 );" \ + "mtc2 $12, $9;" \ + "mtc2 $13, $10;" \ + "mtc2 $14, $11" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) + +#define gte_lddp( r0 ) __asm__ volatile ( \ + "mtc2 %0, $8" \ + : \ + : "r"( r0 ) ) + +#define gte_ldsxy0( r0 ) __asm__ volatile ( \ + "mtc2 %0, $12" \ + : \ + : "r"( r0 ) ) + +#define gte_ldsxy1( r0 ) __asm__ volatile ( \ + "mtc2 %0, $13" \ + : \ + : "r"( r0 ) ) + +#define gte_ldsxy2( r0 ) __asm__ volatile ( \ + "mtc2 %0, $14" \ + : \ + : "r"( r0 ) ) + +#define gte_ldsxy3( r0, r1, r2 ) __asm__ volatile ( \ + "mtc2 %0, $12;" \ + "mtc2 %2, $14;" \ + "mtc2 %1, $13" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) ) + +#define gte_ldsxy3c( r0 ) __asm__ volatile ( \ + "lwc2 $12, 0( %0 );" \ + "lwc2 $13, 4( %0 );" \ + "lwc2 $14, 8( %0 )" \ + : \ + : "r"( r0 ) ) + +#define gte_ldsz3( r0, r1, r2 ) __asm__ volatile ( \ + "mtc2 %0, $17;" \ + "mtc2 %1, $18;" \ + "mtc2 %2, $19" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) ) + +#define gte_ldsz4( r0, r1, r2, r3 ) __asm__ volatile ( \ + "mtc2 %0, $16;" \ + "mtc2 %1, $17;" \ + "mtc2 %2, $18;" \ + "mtc2 %3, $19" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ), "r"( r3 ) ) + +#define gte_ldopv1( r0 ) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "lw $14, 8( %0 );" \ + "ctc2 $13, $2;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) + +#define gte_ldopv2( r0 ) __asm__ volatile ( \ + "lwc2 $11, 8( %0 );" \ + "lwc2 $9, 0( %0 );" \ + "lwc2 $10, 4( %0 )" \ + : \ + : "r"( r0 ) ) + +#define gte_ldlzc( r0 ) __asm__ volatile ( \ + "mtc2 %0, $30" \ + : \ + : "r"( r0 ) ) + +#define gte_SetRGBcd( r0 ) __asm__ volatile ( \ + "lwc2 $6, 0( %0 )" \ + : \ + : "r"( r0 ) ) + +#define gte_ldbkdir( r0, r1, r2 ) __asm__ volatile ( \ + "ctc2 %0, $13;" \ + "ctc2 %1, $14;" \ + "ctc2 %2, $15" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) ) + +#define gte_SetBackColor( r0, r1, r2 ) __asm__ volatile ( \ + "sll $12, %0, 4;" \ + "sll $13, %1, 4;" \ + "sll $14, %2, 4;" \ + "ctc2 $12, $13;" \ + "ctc2 $13, $14;" \ + "ctc2 $14, $15" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) \ + : "$12", "$13", "$14" ) + +#define gte_ldfcdir( r0, r1, r2 ) __asm__ volatile ( \ + "ctc2 %0, $21;" \ + "ctc2 %1, $22;" \ + "ctc2 %2, $23" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) ) + +#define gte_SetFarColor( r0, r1, r2 ) __asm__ volatile ( \ + "sll $12, %0, 4;" \ + "sll $13, %1, 4;" \ + "sll $14, %2, 4;" \ + "ctc2 $12, $21;" \ + "ctc2 $13, $22;" \ + "ctc2 $14, $23" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) \ + : "$12", "$13", "$14" ) + +#define gte_SetGeomOffset( r0, r1 ) __asm__ volatile ( \ + "sll $12, %0, 16;" \ + "sll $13, %1, 16;" \ + "ctc2 $12, $24;" \ + "ctc2 $13, $25" \ + : \ + : "r"( r0 ), "r"( r1 ) \ + : "$12", "$13" ) + +#define gte_SetGeomScreen( r0 ) __asm__ volatile ( \ + "ctc2 %0, $26" \ + : \ + : "r"( r0 ) ) + +#define gte_ldsvrtrow0( r0 ) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1" \ + : \ + : "r"( r0 ) \ + : "$12", "$13" ) + +#define gte_SetRotMatrix( r0 ) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $0;" \ + "ctc2 $13, $1;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $2;" \ + "ctc2 $13, $3;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) + +#define gte_ldsvllrow0( r0 ) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $8;" \ + "ctc2 $13, $9" \ + : \ + : "r"( r0 ) \ + : "$12", "$13" ) + +#define gte_SetLightMatrix( r0 ) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $8;" \ + "ctc2 $13, $9;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $10;" \ + "ctc2 $13, $11;" \ + "ctc2 $14, $12" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) + +#define gte_ldsvlcrow0( r0 ) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $16;" \ + "ctc2 $13, $17" \ + : \ + : "r"( r0 ) \ + : "$12", "$13" ) + +#define gte_SetColorMatrix( r0 ) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "ctc2 $12, $16;" \ + "ctc2 $13, $17;" \ + "lw $12, 8( %0 );" \ + "lw $13, 12( %0 );" \ + "lw $14, 16( %0 );" \ + "ctc2 $12, $18;" \ + "ctc2 $13, $19;" \ + "ctc2 $14, $20" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) + +#define gte_SetTransMatrix( r0 ) __asm__ volatile ( \ + "lw $12, 20( %0 );" \ + "lw $13, 24( %0 );" \ + "ctc2 $12, $5;" \ + "lw $14, 28( %0 );" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) + +#define gte_ldtr( r0, r1, r2 ) __asm__ volatile ( \ + "ctc2 %0, $5;" \ + "ctc2 %1, $6;" \ + "ctc2 %2, $7" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) ) + +#define gte_SetTransVector( r0 ) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "lw $14, 8( %0 );" \ + "ctc2 $12, $5;" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) + +#define gte_ld_intpol_uv0( r0 ) __asm__ volatile ( \ + "lbu $12, 0( %0 );" \ + "lbu $13, 1( %0 );" \ + "ctc2 $12, $21;" \ + "ctc2 $13, $22" \ + : \ + : "r"( r0 ) \ + : "$12", "$13" ) + +#define gte_ld_intpol_uv1( r0 ) __asm__ volatile ( \ + "lbu $12, 0( %0 );" \ + "lbu $13, 1( %0 );" \ + "mtc2 $12, $9;" \ + "mtc2 $13, $10" \ + : \ + : "r"( r0 ) \ + : "$12", "$13" ) + +#define gte_ld_intpol_bv0( r0 ) __asm__ volatile ( \ + "lbu $12, 0( %0 );" \ + "lbu $13, 1( %0 );" \ + "ctc2 $12, $21;" \ + "ctc2 $13, $22" \ + : \ + : "r"( r0 ) \ + : "$12", "$13" ) + +#define gte_ld_intpol_bv1( r0 ) __asm__ volatile ( \ + "lbu $12, 0( %0 );" \ + "lbu $13, 1( %0 );" \ + "mtc2 $12, $9;" \ + "mtc2 $13, $10" \ + : \ + : "r"( r0 ) \ + : "$12", "$13" ) + +#define gte_ld_intpol_sv0( r0 ) __asm__ volatile ( \ + "lh $12, 0( %0 );" \ + "lh $13, 2( %0 );" \ + "lh $14, 4( %0 );" \ + "ctc2 $12, $21;" \ + "ctc2 $13, $22;" \ + "ctc2 $14, $23" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) + +#define gte_ld_intpol_sv1( r0 ) __asm__ volatile ( \ + "lh $12, 0( %0 );" \ + "lh $13, 2( %0 );" \ + "lh $14, 4( %0 );" \ + "mtc2 $12, $9;" \ + "mtc2 $13, $10;" \ + "mtc2 $14, $11" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) + +#define gte_ldfc( r0 ) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 4( %0 );" \ + "lw $14, 8( %0 );" \ + "ctc2 $12, $21;" \ + "ctc2 $13, $22;" \ + "ctc2 $14, $23" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) + +#define gte_ldopv2SV( r0 ) __asm__ volatile ( \ + "lh $12, 0( %0 );" \ + "lh $13, 2( %0 );" \ + "lh $14, 4( %0 );" \ + "mtc2 $12, $9;" \ + "mtc2 $13, $10;" \ + "mtc2 $14, $11" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) + +#define gte_ldopv1SV( r0 ) __asm__ volatile ( \ + "lh $12, 0( %0 );" \ + "lh $13, 2( %0 );" \ + "ctc2 $12, $0;" \ + "lh $14, 4( %0 );" \ + "ctc2 $13, $2;" \ + "ctc2 $14, $4" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14" ) + +/* + * Type 2 functions + */ + +#define gte_rtps() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00180001;" ) + +#define gte_rtpt() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00280030;" ) + +#define gte_rt() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00480012;" ) + +#define gte_rtv0() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00486012;" ) + +#define gte_rtv1() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x0048e012;" ) + +#define gte_rtv2() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00496012;" ) + +#define gte_rtir() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x0049e012;" ) + +#define gte_rtir_sf0() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x0041e012;" ) + +#define gte_rtv0tr() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00480012;" ) + +#define gte_rtv1tr() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00488012;" ) + +#define gte_rtv2tr() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00490012;" ) + +#define gte_rtirtr() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00498012;" ) + +#define gte_rtv0bk() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00482012;" ) + +#define gte_rtv1bk() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x0048a012;" ) + +#define gte_rtv2bk() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00492012;" ) + +#define gte_rtirbk() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x0049a012;" ) + +#define gte_ll() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004a6412;" ) + +#define gte_llv0() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004a6012;" ) + +#define gte_llv1() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004ae012;" ) + +#define gte_llv2() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004b6012;" ) + +#define gte_llir() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004be012;" ) + +#define gte_llv0tr() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004a0012;" ) + +#define gte_llv1tr() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004a8012;" ) + +#define gte_llv2tr() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004b0012;" ) + +#define gte_llirtr() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004b8012;" ) + +#define gte_llv0bk() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004a2012;" ) + +#define gte_llv1bk() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004aa012;" ) + +#define gte_llv2bk() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004b2012;" ) + +#define gte_llirbk() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004ba012;" ) + +#define gte_lc() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004da412;" ) + +#define gte_lcv0() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004c6012;" ) + +#define gte_lcv1() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004ce012;" ) + +#define gte_lcv2() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004d6012;" ) + +#define gte_lcir() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004de012;" ) + +#define gte_lcv0tr() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004c0012;" ) + +#define gte_lcv1tr() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004c8012;" ) + +#define gte_lcv2tr() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004d0012;" ) + +#define gte_lcirtr() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004d8012;" ) + +#define gte_lcv0bk() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004c2012;" ) + +#define gte_lcv1bk() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004ca012;" ) + +#define gte_lcv2bk() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004d2012;" ) + +#define gte_lcirbk() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x004da012;" ) + +#define gte_dpcl() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00680029;" ) + +#define gte_dpcs() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00780010;" ) + +#define gte_dpct() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00f8002a;" ) + +#define gte_intpl() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00980011;" ) + +#define gte_sqr12() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00a80428;" ) + +#define gte_sqr0() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00a00428;" ) + +#define gte_ncs() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00c8041e;" ) + +#define gte_nct() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00d80420;" ) + +#define gte_ncds() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00e80413;" ) + +#define gte_ncdt() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x00f80416;" ) + +#define gte_nccs() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x0108041b;" ) + +#define gte_ncct() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x0118043f;" ) + +#define gte_cdp() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x01280414;" ) + +#define gte_cc() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x0138041c;" ) + +#define gte_nclip() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x01400006;" ) + +#define gte_avsz3() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x0158002d;" ) + +#define gte_avsz4() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x0168002e;" ) + +#define gte_op12() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x0178000c;" ) + +#define gte_op0() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x0170000c;" ) + +#define gte_gpf12() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x0198003d;" ) + +#define gte_gpf0() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x0190003d;" ) + +#define gte_gpl12() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x01a8003e;" ) + +#define gte_gpl0() __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 0x01a0003e" ) + +#define gte_mvmva_core( r0 ) __asm__ volatile ( \ + "nop;" \ + "nop;" \ + "cop2 %0" \ + : \ + : "g"( r0 ) ) + +#define gte_mvmva(sf,mx,v,cv,lm) gte_mvmva_core( 0x0400012 | \ + ((sf)<<19) | ((mx)<<17) | ((v)<<15) | ((cv)<<13) | ((lm)<<10)) + +/* + * Type 2 functions without nop + */ + +#define gte_rtps_b() __asm__ volatile ( "cop2 0x00180001;" ) +#define gte_rtpt_b() __asm__ volatile ( "cop2 0x00280030;" ) +#define gte_rt_b() __asm__ volatile ( "cop2 0x00480012;" ) +#define gte_rtv0_b() __asm__ volatile ( "cop2 0x00486012;" ) +#define gte_rtv1_b() __asm__ volatile ( "cop2 0x0048e012;" ) +#define gte_rtv2_b() __asm__ volatile ( "cop2 0x00496012;" ) +#define gte_rtir_b() __asm__ volatile ( "cop2 0x0049e012;" ) +#define gte_rtir_sf0_b() __asm__ volatile ( "cop2 0x0041e012;" ) +#define gte_rtv0tr_b() __asm__ volatile ( "cop2 0x00480012;" ) +#define gte_rtv1tr_b() __asm__ volatile ( "cop2 0x00488012;" ) +#define gte_rtv2tr_b() __asm__ volatile ( "cop2 0x00490012;" ) +#define gte_rtirtr_b() __asm__ volatile ( "cop2 0x00498012;" ) +#define gte_rtv0bk_b() __asm__ volatile ( "cop2 0x00482012;" ) +#define gte_rtv1bk_b() __asm__ volatile ( "cop2 0x0048a012;" ) +#define gte_rtv2bk_b() __asm__ volatile ( "cop2 0x00492012;" ) +#define gte_rtirbk_b() __asm__ volatile ( "cop2 0x0049a012;" ) +#define gte_ll_b() __asm__ volatile ( "cop2 0x004a6412;" ) +#define gte_llv0_b() __asm__ volatile ( "cop2 0x004a6012;" ) +#define gte_llv1_b() __asm__ volatile ( "cop2 0x004ae012;" ) +#define gte_llv2_b() __asm__ volatile ( "cop2 0x004b6012;" ) +#define gte_llir_b() __asm__ volatile ( "cop2 0x004be012;" ) +#define gte_llv0tr_b() __asm__ volatile ( "cop2 0x004a0012;" ) +#define gte_llv1tr_b() __asm__ volatile ( "cop2 0x004a8012;" ) +#define gte_llv2tr_b() __asm__ volatile ( "cop2 0x004b0012;" ) +#define gte_llirtr_b() __asm__ volatile ( "cop2 0x004b8012;" ) +#define gte_llv0bk_b() __asm__ volatile ( "cop2 0x004a2012;" ) +#define gte_llv1bk_b() __asm__ volatile ( "cop2 0x004aa012;" ) +#define gte_llv2bk_b() __asm__ volatile ( "cop2 0x004b2012;" ) +#define gte_llirbk_b() __asm__ volatile ( "cop2 0x004ba012;" ) +#define gte_lc_b() __asm__ volatile ( "cop2 0x004da412;" ) +#define gte_lcv0_b() __asm__ volatile ( "cop2 0x004c6012;" ) +#define gte_lcv1_b() __asm__ volatile ( "cop2 0x004ce012;" ) +#define gte_lcv2_b() __asm__ volatile ( "cop2 0x004d6012;" ) +#define gte_lcir_b() __asm__ volatile ( "cop2 0x004de012;" ) +#define gte_lcv0tr_b() __asm__ volatile ( "cop2 0x004c0012;" ) +#define gte_lcv1tr_b() __asm__ volatile ( "cop2 0x004c8012;" ) +#define gte_lcv2tr_b() __asm__ volatile ( "cop2 0x004d0012;" ) +#define gte_lcirtr_b() __asm__ volatile ( "cop2 0x004d8012;" ) +#define gte_lcv0bk_b() __asm__ volatile ( "cop2 0x004c2012;" ) +#define gte_lcv1bk_b() __asm__ volatile ( "cop2 0x004ca012;" ) +#define gte_lcv2bk_b() __asm__ volatile ( "cop2 0x004d2012;" ) +#define gte_lcirbk_b() __asm__ volatile ( "cop2 0x004da012;" ) +#define gte_dpcl_b() __asm__ volatile ( "cop2 0x00680029;" ) +#define gte_dpcs_b() __asm__ volatile ( "cop2 0x00780010;" ) +#define gte_dpct_b() __asm__ volatile ( "cop2 0x00f8002a;" ) +#define gte_intpl_b() __asm__ volatile ( "cop2 0x00980011;" ) +#define gte_sqr12_b() __asm__ volatile ( "cop2 0x00a80428;" ) +#define gte_sqr0_b() __asm__ volatile ( "cop2 0x00a00428;" ) +#define gte_ncs_b() __asm__ volatile ( "cop2 0x00c8041e;" ) +#define gte_nct_b() __asm__ volatile ( "cop2 0x00d80420;" ) +#define gte_ncds_b() __asm__ volatile ( "cop2 0x00e80413;" ) +#define gte_ncdt_b() __asm__ volatile ( "cop2 0x00f80416;" ) +#define gte_nccs_b() __asm__ volatile ( "cop2 0x0108041b;" ) +#define gte_ncct_b() __asm__ volatile ( "cop2 0x0118043f;" ) +#define gte_cdp_b() __asm__ volatile ( "cop2 0x01280414;" ) +#define gte_cc_b() __asm__ volatile ( "cop2 0x0138041c;" ) +#define gte_nclip_b() __asm__ volatile ( "cop2 0x01400006;" ) +#define gte_avsz3_b() __asm__ volatile ( "cop2 0x0158002d;" ) +#define gte_avsz4_b() __asm__ volatile ( "cop2 0x0168002e;" ) +#define gte_op12_b() __asm__ volatile ( "cop2 0x0178000c;" ) +#define gte_op0_b() __asm__ volatile ( "cop2 0x0170000c;" ) +#define gte_gpf12_b() __asm__ volatile ( "cop2 0x0198003d;" ) +#define gte_gpf0_b() __asm__ volatile ( "cop2 0x0190003d;" ) +#define gte_gpl12_b() __asm__ volatile ( "cop2 0x01a8003e;" ) +#define gte_gpl0_b() __asm__ volatile ( "cop2 0x01a0003e;" ) +#define gte_mvmva_core_b( r0 ) __asm__ volatile ( \ + "cop2 %0" \ + : \ + : "g"( r0 ) ) +#define gte_mvmva_b(sf,mx,v,cv,lm) gte_mvmva_core_b( 0x0400012 |\ + ((sf)<<19) | ((mx)<<17) | ((v)<<15) | ((cv)<<13) | ((lm)<<10)) + +/* + * Type 3 functions + */ + +#define gte_stsxy( r0 ) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsxy3( r0, r1, r2 ) __asm__ volatile ( \ + "swc2 $12, 0( %0 );" \ + "swc2 $13, 0( %1 );" \ + "swc2 $14, 0( %2 )" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) \ + : "memory" ) + +#define gte_stsxy3c( r0 ) __asm__ volatile ( \ + "swc2 $12, 0( %0 );" \ + "swc2 $13, 4( %0 );" \ + "swc2 $14, 8( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsxy2( r0 ) __asm__ volatile ( \ + "swc2 $14, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsxy1( r0 ) __asm__ volatile ( \ + "swc2 $13, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsxy0( r0 ) __asm__ volatile ( \ + "swc2 $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsxy01( r0, r1 ) __asm__ volatile ( \ + "swc2 $12, 0( %0 );" \ + "swc2 $13, 0( %1 )" \ + : \ + : "r"( r0 ), "r"( r1 ) \ + : "memory" ) + +#define gte_stsxy01c( r0 ) __asm__ volatile ( \ + "swc2 $12, 0( %0 );" \ + "swc2 $13, 4( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsxy3_f3( r0 ) __asm__ volatile ( \ + "swc2 $12, 8( %0 );" \ + "swc2 $13, 12( %0 );" \ + "swc2 $14, 16( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsxy3_g3( r0 ) __asm__ volatile ( \ + "swc2 $12, 8( %0 );" \ + "swc2 $13, 16( %0 );" \ + "swc2 $14, 24( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsxy3_ft3( r0 ) __asm__ volatile ( \ + "swc2 $12, 8( %0 );" \ + "swc2 $13, 16( %0 );" \ + "swc2 $14, 24( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsxy3_gt3( r0 ) __asm__ volatile ( \ + "swc2 $12, 8( %0 );" \ + "swc2 $13, 20( %0 );" \ + "swc2 $14, 32( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsxy3_f4( r0 ) __asm__ volatile ( \ + "swc2 $12, 8( %0 );" \ + "swc2 $13, 12( %0 );" \ + "swc2 $14, 16( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsxy3_g4( r0 ) __asm__ volatile ( \ + "swc2 $12, 8( %0 );" \ + "swc2 $13, 16( %0 );" \ + "swc2 $14, 24( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsxy3_ft4( r0 ) __asm__ volatile ( \ + "swc2 $12, 8( %0 );" \ + "swc2 $13, 16( %0 );" \ + "swc2 $14, 24( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsxy3_gt4( r0 ) __asm__ volatile ( \ + "swc2 $12, 8( %0 );" \ + "swc2 $13, 20( %0 );" \ + "swc2 $14, 32( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stdp( r0 ) __asm__ volatile ( \ + "swc2 $8, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stflg( r0 ) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +#define gte_stflg_4( r0 ) __asm__ volatile ( \ + "cfc2 $12, $31;" \ + "addi $13, $0, 4;" \ + "sll $13, $13, 16;" \ + "and $12, $12, $13;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "memory" ) + +#define gte_stsz( r0 ) __asm__ volatile ( \ + "swc2 $19, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsz3( r0, r1, r2 ) __asm__ volatile ( \ + "swc2 $17, 0( %0 );" \ + "swc2 $18, 0( %1 );" \ + "swc2 $19, 0( %2 )" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) \ + : "memory" ) + +#define gte_stsz4( r0, r1, r2, r3 ) __asm__ volatile ( \ + "swc2 $16, 0( %0 );" \ + "swc2 $17, 0( %1 );" \ + "swc2 $18, 0( %2 );" \ + "swc2 $19, 0( %3 )" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ), "r"( r3 ) \ + : "memory" ) + +#define gte_stsz3c( r0 ) __asm__ volatile ( \ + "swc2 $17, 0( %0 );" \ + "swc2 $18, 4( %0 );" \ + "swc2 $19, 8( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsz4c( r0 ) __asm__ volatile ( \ + "swc2 $16, 0( %0 );" \ + "swc2 $17, 4( %0 );" \ + "swc2 $18, 8( %0 );" \ + "swc2 $19, 12( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stszotz( r0 ) __asm__ volatile ( \ + "mfc2 $12, $19;" \ + "nop;" \ + "sra $12, $12, 2;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +#define gte_stotz( r0 ) __asm__ volatile ( \ + "swc2 $7, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stopz( r0 ) __asm__ volatile ( \ + "swc2 $24, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stlvl( r0 ) __asm__ volatile ( \ + "swc2 $9, 0( %0 );" \ + "swc2 $10, 4( %0 );" \ + "swc2 $11, 8( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stlvnl( r0 ) __asm__ volatile ( \ + "swc2 $25, 0( %0 );" \ + "swc2 $26, 4( %0 );" \ + "swc2 $27, 8( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stlvnl0( r0 ) __asm__ volatile ( \ + "swc2 $25, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stlvnl1( r0 ) __asm__ volatile ( \ + "swc2 $26, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stlvnl2( r0 ) __asm__ volatile ( \ + "swc2 $27, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stsv( r0 ) __asm__ volatile ( \ + "mfc2 $12, $9;" \ + "mfc2 $13, $10;" \ + "mfc2 $14, $11;" \ + "sh $12, 0( %0 );" \ + "sh $13, 2( %0 );" \ + "sh $14, 4( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14", "memory" ) + +#define gte_stclmv( r0 ) __asm__ volatile ( \ + "mfc2 $12, $9;" \ + "mfc2 $13, $10;" \ + "mfc2 $14, $11;" \ + "sh $12, 0( %0 );" \ + "sh $13, 6( %0 );" \ + "sh $14, 12( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14", "memory" ) + +#define gte_stbv( r0 ) __asm__ volatile ( \ + "mfc2 $12, $9;" \ + "mfc2 $13, $10;" \ + "sb $12, 0( %0 );" \ + "sb $13, 1( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "memory" ) + +#define gte_stcv( r0 ) __asm__ volatile ( \ + "mfc2 $12, $9;" \ + "mfc2 $13, $10;" \ + "mfc2 $14, $11;" \ + "sb $12, 0( %0 );" \ + "sb $13, 1( %0 );" \ + "sb $14, 2( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14", "memory" ) + +#define gte_strgb( r0 ) __asm__ volatile ( \ + "swc2 $22, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_strgb3( r0, r1, r2 ) __asm__ volatile ( \ + "swc2 $20, 0( %0 );" \ + "swc2 $21, 0( %1 );" \ + "swc2 $22, 0( %2 )" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) \ + : "memory" ) + +#define gte_strgb3_g3( r0 ) __asm__ volatile ( \ + "swc2 $20, 4( %0 );" \ + "swc2 $21, 12( %0 );" \ + "swc2 $22, 20( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_strgb3_gt3( r0 ) __asm__ volatile ( \ + "swc2 $20, 4( %0 );" \ + "swc2 $21, 16( %0 );" \ + "swc2 $22, 28( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_strgb3_g4( r0 ) __asm__ volatile ( \ + "swc2 $20, 4( %0 );" \ + "swc2 $21, 12( %0 );" \ + "swc2 $22, 20( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_strgb3_gt4( r0 ) __asm__ volatile ( \ + "swc2 $20, 4( %0 );" \ + "swc2 $21, 16( %0 );" \ + "swc2 $22, 28( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_ReadGeomOffset( r0, r1 ) __asm__ volatile ( \ + "cfc2 $12, $24;" \ + "cfc2 $13, $25;" \ + "sra $12, $12, 16;" \ + "sra $13, $13, 16;" \ + "sw $12, 0( %0 );" \ + "sw $13, 0( %1 )" \ + : \ + : "r"( r0 ), "r"( r1 ) \ + : "$12", "$13", "memory" ) + +#define gte_ReadGeomScreen( r0 ) __asm__ volatile ( \ + "cfc2 $12, $26;" \ + "nop;" \ + "sw $12, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "memory" ) + +#define gte_ReadRotMatrix( r0 ) __asm__ volatile ( \ + "cfc2 $12, $0;" \ + "cfc2 $13, $1;" \ + "sw $12, 0( %0 );" \ + "sw $13, 4( %0 );" \ + "cfc2 $12, $2;" \ + "cfc2 $13, $3;" \ + "cfc2 $14, $4;" \ + "sw $12, 8( %0 );" \ + "sw $13, 12( %0 );" \ + "sw $14, 16( %0 );" \ + "cfc2 $12, $5;" \ + "cfc2 $13, $6;" \ + "cfc2 $14, $7;" \ + "sw $12, 20( %0 );" \ + "sw $13, 24( %0 );" \ + "sw $14, 28( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14", "memory" ) + +#define gte_sttr( r0 ) __asm__ volatile ( \ + "cfc2 $12, $5;" \ + "cfc2 $13, $6;" \ + "cfc2 $14, $7;" \ + "sw $12, 0( %0 );" \ + "sw $13, 4( %0 );" \ + "sw $14, 8( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14", "memory" ) + +#define gte_ReadLightMatrix( r0 ) __asm__ volatile ( \ + "cfc2 $12, $8;" \ + "cfc2 $13, $9;" \ + "sw $12, 0( %0 );" \ + "sw $13, 4( %0 );" \ + "cfc2 $12, $10;" \ + "cfc2 $13, $11;" \ + "cfc2 $14, $12;" \ + "sw $12, 8( %0 );" \ + "sw $13, 12( %0 );" \ + "sw $14, 16( %0 );" \ + "cfc2 $12, $13;" \ + "cfc2 $13, $14;" \ + "cfc2 $14, $15;" \ + "sw $12, 20( %0 );" \ + "sw $13, 24( %0 );" \ + "sw $14, 28( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14", "memory" ) + +#define gte_ReadColorMatrix( r0 ) __asm__ volatile ( \ + "cfc2 $12, $16;" \ + "cfc2 $13, $17;" \ + "sw $12, 0( %0 );" \ + "sw $13, 4( %0 );" \ + "cfc2 $12, $18;" \ + "cfc2 $13, $19;" \ + "cfc2 $14, $20;" \ + "sw $12, 8( %0 );" \ + "sw $13, 12( %0 );" \ + "sw $14, 16( %0 );" \ + "cfc2 $12, $21;" \ + "cfc2 $13, $22;" \ + "cfc2 $14, $23;" \ + "sw $12, 20( %0 );" \ + "sw $13, 24( %0 );" \ + "sw $14, 28( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14", "memory" ) + +#define gte_stlzc( r0 ) __asm__ volatile ( \ + "swc2 $31, 0( %0 )" \ + : \ + : "r"( r0 ) \ + : "memory" ) + +#define gte_stfc( r0 ) __asm__ volatile ( \ + "cfc2 $12, $21;" \ + "cfc2 $13, $22;" \ + "cfc2 $14, $23;" \ + "sw $12, 0( %0 );" \ + "sw $13, 4( %0 );" \ + "sw $14, 8( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "$14", "memory" ) + +#define gte_mvlvtr() __asm__ volatile ( \ + "mfc2 $12, $25;" \ + "mfc2 $13, $26;" \ + "mfc2 $14, $27;" \ + "ctc2 $12, $5;" \ + "ctc2 $13, $6;" \ + "ctc2 $14, $7" \ + : \ + : \ + : "$12", "$13", "$14" ) + +#define gte_nop() __asm__ volatile ( \ + "nop" ) + +#define gte_subdvl( r0, r1, r2 ) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 0( %1 );" \ + "mtc2 $12, $9;" \ + "mtc2 $13, $10;" \ + "sra $12, $12, 16;" \ + "sra $13, $13, 16;" \ + "subu $15, $12, $13;" \ + "mfc2 $12, $9;" \ + "mfc2 $13, $10;" \ + "sw $15, 4( %2 );" \ + "subu $12, $12, $13;" \ + "sw $12, 0( %2 )" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) \ + : "$12", "$13", "$14", "$15", "memory" ) + +#define gte_subdvd( r0, r1, r2 ) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 0( %1 );" \ + "mtc2 $12, $9;" \ + "mtc2 $13, $10;" \ + "sra $12, $12, 16;" \ + "sra $13, $13, 16;" \ + "subu $15, $12, $13;" \ + "mfc2 $12, $9;" \ + "mfc2 $13, $10;" \ + "sh $15, 2( %2 );" \ + "subu $12, $12, $13;" \ + "sh $12, 0( %2 )" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) \ + : "$12", "$13", "$14", "$15", "memory" ) + +#define gte_adddvl( r0, r1, r2 ) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 0( %1 );" \ + "mtc2 $12, $9;" \ + "mtc2 $13, $10;" \ + "sra $12, $12, 16;" \ + "sra $13, $13, 16;" \ + "addu $15, $12, $13;" \ + "mfc2 $12, $9;" \ + "mfc2 $13, $10;" \ + "sw $15, 4( %2 );" \ + "addu $12, $12, $13;" \ + "sw $12, 0( %2 )" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) \ + : "$12", "$13", "$14", "$15", "memory" ) + +#define gte_adddvd( r0, r1, r2 ) __asm__ volatile ( \ + "lw $12, 0( %0 );" \ + "lw $13, 0( %1 );" \ + "mtc2 $12, $9;" \ + "mtc2 $13, $10;" \ + "sra $12, $12, 16;" \ + "sra $13, $13, 16;" \ + "addu $15, $12, $13;" \ + "mfc2 $12, $9;" \ + "mfc2 $13, $10;" \ + "sh $15, 2( %2 );" \ + "addu $12, $12, $13;" \ + "sh $12, 0( %2 )" \ + : \ + : "r"( r0 ), "r"( r1 ), "r"( r2 ) \ + : "$12", "$13", "$14", "$15", "memory" ) + +#define gte_FlipRotMatrixX() __asm__ volatile ( \ + "cfc2 $12, $0;" \ + "cfc2 $13, $1;" \ + "sll $14, $12, 16;" \ + "sra $14, $14, 16;" \ + "subu $14, $0, $14;" \ + "sra $15, $12, 16;" \ + "subu $15, $0, $15;" \ + "sll $15, $15, 16;" \ + "sll $14, $14, 16;" \ + "srl $14, $14, 16;" \ + "or $14, $14, $15;" \ + "ctc2 $14, $0;" \ + "sll $14, $13, 16;" \ + "sra $14, $14, 16;" \ + "subu $14, $0, $14;" \ + "sra $15, $13, 16;" \ + "sll $15, $15, 16;" \ + "sll $14, $14, 16;" \ + "srl $14, $14, 16;" \ + "or $14, $14, $15;" \ + "ctc2 $14, $1" \ + : \ + : \ + : "$12", "$13", "$14", "$15" ) + +#define gte_FlipTRX() __asm__ volatile ( \ + "cfc2 $12, $5;" \ + "nop;" \ + "subu $12, $0, $12;" \ + "ctc2 $12, $5" \ + : \ + : \ + : "$12" ) + +/* clang-format on */ + +#endif /* __psyz */ diff --git a/components/mgs/mgs/source/include/inline_x.h b/components/mgs/mgs/source/include/inline_x.h new file mode 100644 index 00000000..705e0d13 --- /dev/null +++ b/components/mgs/mgs/source/include/inline_x.h @@ -0,0 +1,67 @@ +/* clang-format off */ + +/* + * Macro definitions w/o DMPSX for METAL GEAR SOLID + * inline_x.h + */ + +/* additional functions */ + +#ifdef __psyz +#include +#else +#define gte_read_opz( r0 ) __asm__ volatile ( \ + "mfc2 %0, $24" \ + : "=r"( r0 ) ) + +#define gte_ldVXY0( r0 ) __asm__ volatile ( \ + "mtc2 %0, $0" \ + : \ + : "r"( r0 ) ) + +#define gte_ldVZ0( r0 ) __asm__ volatile ( \ + "mtc2 %0, $1" \ + : \ + : "r"( r0 ) ) + +#define gte_ldVXY1( r0 ) __asm__ volatile ( \ + "mtc2 %0, $2" \ + : \ + : "r"( r0 ) ) + +#define gte_ldVZ1( r0 ) __asm__ volatile ( \ + "mtc2 %0, $3" \ + : \ + : "r"( r0 ) ) + +#define gte_ldVXY2( r0 ) __asm__ volatile ( \ + "mtc2 %0, $4" \ + : \ + : "r"( r0 ) ) + +#define gte_ldVZ2( r0 ) __asm__ volatile ( \ + "mtc2 %0, $5" \ + : \ + : "r"( r0 ) ) + +#define gte_ldVXYZ0( r0, r1 ) __asm__ volatile ( \ + "mtc2 %0, $0;" \ + "mtc2 %1, $1;" \ + : \ + : "r"( r0 ), "r"( r1 ) ) + +#define gte_ldVXYZ1( r0, r1 ) __asm__ volatile ( \ + "mtc2 %0, $2;" \ + "mtc2 %1, $3;" \ + : \ + : "r"( r0 ), "r"( r1 ) ) + +#define gte_ldVXYZ2( r0, r1 ) __asm__ volatile ( \ + "mtc2 %0, $4;" \ + "mtc2 %1, $5;" \ + : \ + : "r"( r0 ), "r"( r1 ) ) + +/* clang-format on */ + +#endif /* __psyz */ diff --git a/components/mgs/mgs/source/include/linkvar.h b/components/mgs/mgs/source/include/linkvar.h new file mode 100644 index 00000000..7937985a --- /dev/null +++ b/components/mgs/mgs/source/include/linkvar.h @@ -0,0 +1,257 @@ +#ifndef __MGS_LINKVAR_H__ +#define __MGS_LINKVAR_H__ + +// This file was probably auto-generated in the original source. +// (The memleak shows exactly two tabs between each identifier and definition, +// and no tabstop setting will align them properly). + +extern short linkvarbuf[0x60]; + +//------------------------------------------------------------------------------ +// 0x00 General +#define GM_UnkFlag00 linkvarbuf[ 0 ] +#define GM_DifficultyFlag linkvarbuf[ 1 ] // -1,0,1,2,3 +#define GM_OptionFlag linkvarbuf[ 2 ] +#define GM_BonusItemsFlag linkvarbuf[ 3 ] // 0 = none, 1 = bandana, 2 = stealth, > 2 = tuxedo +#define GM_CurrentDiskFlag linkvarbuf[ 4 ] // 1 or 2 +#define GM_LastResultFlag linkvarbuf[ 5 ] // Store result of last op, so it can be used in gcl scripting +#define GM_CurrentStageFlag linkvarbuf[ 6 ] // hash +#define GM_CurrentMapFlag linkvarbuf[ 7 ] // hash + +//------------------------------------------------------------------------------ +// 0x10 Snake +#define GM_SnakePosX linkvarbuf[ 8 ] +#define GM_SnakePosY linkvarbuf[ 9 ] +#define GM_SnakePosZ linkvarbuf[ 10 ] +#define GM_SnakeCurrentHealth linkvarbuf[ 11 ] +#define GM_SnakeMaxHealth linkvarbuf[ 12 ] // from 256 to around 1400 near end game +#define GM_StatusEvent linkvarbuf[ 13 ] // bit 1 (0x1) = common cold, bit 3 (0x4) = diazepam active, bit 4 (0x8) = blown up +#define GM_CurrentWeaponId linkvarbuf[ 14 ] // -1 = no equipped weapon +#define GM_CurrentItemId linkvarbuf[ 15 ] // -1 = no equipped item +#define GM_PlayerStance linkvarbuf[ 16 ] // 0 = standing, 1 = crouching, 2 = lying down + +//------------------------------------------------------------------------------ +#define GM_Weapons (&linkvarbuf[ 17 ]) +#define GM_CurrentWeapon (GM_Weapons[ GM_CurrentWeaponId ]) + +// 0x22 Weapons ammo (Use GM_Weapons[] instead) +// value -1 = not in inventory +#define GM_SocomFlag linkvarbuf[ 17 ] +#define GM_FamasFlag linkvarbuf[ 18 ] +#define GM_GrenadeFlag linkvarbuf[ 19 ] +#define GM_RemoteMissileFlag linkvarbuf[ 20 ] +#define GM_StingerFlag linkvarbuf[ 21 ] +#define GM_LandMineFlag linkvarbuf[ 22 ] +#define GM_BombFlag linkvarbuf[ 23 ] +#define GM_StunGrenadeFlag linkvarbuf[ 24 ] +#define GM_ChaffGrenadeFlag linkvarbuf[ 25 ] +#define GM_RifleFlag linkvarbuf[ 26 ] + +//----------------------------------------------- +#define GM_WeaponsMax (&linkvarbuf[ 27 ]) + +// 0x36 Weapons max ammo (Use GM_WeaponsMax[] instead) +#define GM_SocomMax linkvarbuf[ 27 ] +#define GM_FamasMax linkvarbuf[ 28 ] +#define GM_GrenadeMax linkvarbuf[ 29 ] +#define GM_RemoteMissileMax linkvarbuf[ 30 ] +#define GM_StingerMax linkvarbuf[ 31 ] +#define GM_LandMineMax linkvarbuf[ 32 ] +#define GM_BombMax linkvarbuf[ 33 ] +#define GM_StunGrenadeMax linkvarbuf[ 34 ] +#define GM_ChaffGrenadeMax linkvarbuf[ 35 ] // $w:チャフグレネード弾数 +#define GM_RifleMax linkvarbuf[ 36 ] // $w:ライフル弾数 + +//------------------------------------------------------------------------------ +#define GM_Items (&linkvarbuf[ 37 ]) +#define GM_CurrentItem (GM_Items[ GM_CurrentItemId ]) + +// 0x4a Items (Use GM_Items[] instead) +// value -1 = not in inventory +#define GM_TabakoFlag linkvarbuf[ 37 ] // $w:たばこ +#define GM_ScopeFlag linkvarbuf[ 38 ] // $w:双眼鏡 +#define GM_CardboardBoxAFlag linkvarbuf[ 39 ] // $w:段ボールヘリポート +#define GM_CardboardBoxBFlag linkvarbuf[ 40 ] // $w:段ボール核保存庫 +#define GM_CardboardBoxCFlag linkvarbuf[ 41 ] // $w:段ボール大雪原 +#define GM_NightVisionFlag linkvarbuf[ 42 ] // $w:暗視ゴーグル +#define GM_ThermalVisionFlag linkvarbuf[ 43 ] // $w:赤外線ゴーグル +#define GM_GasMaskFlag linkvarbuf[ 44 ] // $w:ガスマスク +#define GM_BodyArmorFlag linkvarbuf[ 45 ] // $w:ボディアーマー +#define GM_KetchupFlag linkvarbuf[ 46 ] // $w:ケチャップ +#define GM_StealthFlag linkvarbuf[ 47 ] // $w:光学迷彩 +#define GM_BandanaFlag linkvarbuf[ 48 ] +#define GM_CameraFlag linkvarbuf[ 49 ] +#define GM_RationFlag linkvarbuf[ 50 ] // > 0 = ration ammount +#define GM_MedicineFlag linkvarbuf[ 51 ] // > 0 = medicine ammount +#define GM_TranquilizerFlag linkvarbuf[ 52 ] // > 0 = tranquilizer (diazepam) ammount +#define GM_ShapeKeyFlag linkvarbuf[ 53 ] +#define GM_CardFlag linkvarbuf[ 54 ] // > 0 = card level +#define GM_TimerBombFlag linkvarbuf[ 55 ] // > 0 = timer +#define GM_MineDetectorFlag linkvarbuf[ 56 ] +#define GM_MODiskFlag linkvarbuf[ 57 ] +#define GM_RopeFlag linkvarbuf[ 58 ] +#define GM_HandkerchiefFlag linkvarbuf[ 59 ] +#define GM_SilencerFlag linkvarbuf[ 60 ] + +//----------------------------------------------- +#define GM_ItemsMax (GM_Items + 11) + +// 0x7a Items max capacity (use GM_ItemsMax[] instead) +#define GM_RationMax linkvarbuf[ 61 ] +#define GM_ColdMedicineMax linkvarbuf[ 62 ] +#define GM_TranquilizerMax linkvarbuf[ 63 ] + +//------------------------------------------------------------------------------ +// 0x80 PAL Key +#define GM_EnvironTemp linkvarbuf[ 64 ] // default to 10 or -10 (-30 in warehouse, 30 in blast furnace) +#define GM_ShapeKeyState linkvarbuf[ 65 ] // 0-2-1 icon index +#define GM_ShapeKeyTemp linkvarbuf[ 66 ] // -7200 in warehouse, 7200 in blast furnace +#define GM_ShapeKeyTempMax linkvarbuf[ 67 ] // 9000 +#define GM_ShapeKeyTempMin linkvarbuf[ 68 ] // -9000 + +//------------------------------------------------------------------------------ +// 0x8a Frozen items +#define GM_FrozenItemsState linkvarbuf[ 69 ] +#define GM_BodyTemp linkvarbuf[ 70 ] // 360 (36 degrees?) +#define GM_FrozenItemsTemp linkvarbuf[ 71 ] +#define GM_FrozenItemsTempMin linkvarbuf[ 72 ] // -7200 + +//------------------------------------------------------------------------------ +#define GM_TranquilizerTimer linkvarbuf[ 73 ] + +#define GM_PreviousStageFlag linkvarbuf[ 74 ] // hash + +//------------------------------------------------------------------------------ +// 0x96 Snake common cold +#define GM_SnakeColdTimer linkvarbuf[ 75 ] +#define GM_SnakeColdUnk98 linkvarbuf[ 76 ] +#define GM_SnakeColdUnk9A linkvarbuf[ 77 ] + +//------------------------------------------------------------------------------ +// 0x9c Stats +#define GM_EnemyWatchCount linkvarbuf[ 78 ] // score screen +#define GM_TotalEnemiesKilled linkvarbuf[ 79 ] // score screen +#define GM_UnkFlagA0 linkvarbuf[ 80 ] +#define GM_UnkFlagA2 linkvarbuf[ 81 ] +#define GM_UnkFlagA4 linkvarbuf[ 82 ] +#define GM_UnkFlagA6 linkvarbuf[ 83 ] +#define GM_UnkFlagA8 linkvarbuf[ 84 ] +#define GM_UnkFlagAA linkvarbuf[ 85 ] +#define GM_TotalRationsUsed linkvarbuf[ 86 ] // score screen +#define GM_TotalContinues linkvarbuf[ 87 ] // score screen +#define GM_TotalSaves linkvarbuf[ 88 ] // score screen +#define GM_GameTimeHours linkvarbuf[ 89 ] +#define GM_GameTimeSeconds linkvarbuf[ 90 ] +#define GM_TotalHours linkvarbuf[ 91 ] // score screen +#define GM_TotalSeconds linkvarbuf[ 92 ] // score screen +#define GM_LastSaveHours linkvarbuf[ 93 ] +#define GM_LastSaveSeconds linkvarbuf[ 94 ] +#define GM_UnkFlagBE linkvarbuf[ 95 ] + +//------------------------------------------------------------------------------ +enum // GM_DifficultyFlag +{ + DIFFICULTY_VERY_EASY = -1, // added for MGS:INTEGRAL + DIFFICULTY_EASY = 0, // the Japanese version's original (only) difficulty + DIFFICULTY_NORMAL = 1, + DIFFICULTY_HARD = 2, + DIFFICULTY_EXTREME = 3, +}; + +enum // GM_OptionFlag +{ + OPTION_BUTTON_TYPE_A = 0x0000, + OPTION_BUTTON_TYPE_B = 0x0001, + OPTION_BUTTON_TYPE_C = 0x0002, + OPTION_BUTTON_MASK = 0x0007, + OPTION_UNKNOWN_0008 = 0x0008, + OPTION_UNKNOWN_0010 = 0x0010, + OPTION_TUXEDO = 0x0020, // + Red Ninja, Sneaking Suit Meryl + OPTION_ENGLISH = 0x0100, // 0: Japanese, 1: English + OPTION_VIBRATION_OFF = 0x0400, + OPTION_RADAR_OFF = 0x0800, + OPTION_SHUKAN_REVERSE = 0x1000, + OPTION_UNKNOWN_2000 = 0x2000, + OPTION_CAPTION_OFF = 0x4000, + OPTION_SOUND_MONO = 0x8000, +}; + +enum // GM_StatusEvent +{ + EV_CommonCold = 0x1, + EV_Tranquilizer = 0x4, + EV_BlownUp = 0x8, +}; + +enum // GM_PlayerStance +{ + // ... negative ones too? + SNA_STANCE_STANDING = 0, + SNA_STANCE_SQUAT = 1, + SNA_STANCE_CROUCH = 2, // prone + knocked down + SNA_STANCE_UNK = 3, // first person prone? + // ... more? +}; + +enum // GM_Weapons[] +{ + WP_None = -1, + WP_Socom = 0, + WP_Famas = 1, + WP_Grenade = 2, + WP_Nikita = 3, + WP_Stinger = 4, + WP_Claymore = 5, + WP_C4 = 6, + WP_StunGrenade = 7, + WP_ChaffGrenade = 8, + WP_Rifle = 9, + WP_Max = 10, +}; + +enum // GM_Items[] +{ + IT_None = -1, // types: + IT_Cigs = 0, // 0x8000 + IT_Scope = 1, // 0x8003 + IT_Box1 = 2, // 0x8001 + IT_Box2 = 3, // 0x8001 + IT_Box3 = 4, // 0x8001 + IT_NVG = 5, // 0x8000 + IT_ThermG = 6, // 0x8000 + IT_GasMask = 7, // 0x8000 + IT_BodyArmor = 8, // 0x8000 + IT_Ketchup = 9, // 0x8000 + IT_Stealth = 10, // 0x8000 + IT_Bandana = 11, // 0x8000 + IT_Camera = 12, // 0x8003 + IT_Ration = 13, // 0x2000 + IT_ColdMedicine = 14, // 0x2000 + IT_Diazepam = 15, // 0x2000 + IT_PalKey = 16, // 0 + IT_Card = 17, // 0 + IT_TimerBomb = 18, // 0 + IT_MineDetector = 19, // 0 + IT_Disk = 20, // 0 + IT_Rope = 21, // 0 + IT_Handkerchief = 22, // 0 + IT_Suppressor = 23, // 0 + IT_Max = 24, +}; + +enum // GM_ItemTypes[] +{ + // ... + ITEMTYPE_CONSUMABLE = 0x2000, + ITEMTYPE_BITMASK = 0x4000, // unused + ITEMTYPE_DISABLED = 0x8000, + // ... +}; + +/* TODO: move */ +static inline int GM_InBox(void) +{ + return (GM_CurrentItemId == IT_Box1) || (GM_CurrentItemId == IT_Box2) || (GM_CurrentItemId == IT_Box3); +} + +#endif // __MGS_LINKVAR_H__ diff --git a/components/mgs/mgs/source/include/psxdefs.h b/components/mgs/mgs/source/include/psxdefs.h new file mode 100644 index 00000000..1a0b9dc0 --- /dev/null +++ b/components/mgs/mgs/source/include/psxdefs.h @@ -0,0 +1,99 @@ +/** + * Supplemental definitions for PSX SDK headers + */ +#ifndef __MGS_PSXDEFS_H__ +#define __MGS_PSXDEFS_H__ + +// Due to none of the OpenEvent callbacks using the correct prototype, +// we now have to cast them to avoid warnings. +typedef long (*openevent_cb_t)(); + +// getScratchAddr() but cast as a pointer to the specified type. +// Unlike the original, offset is NOT multiplied by the sizeof(type). +#define getScratchAddr2(type, offset) ((type *)((SCRPAD_ADDR + 0x000)+(offset))) + +/*---------------------------------------------------------------------------*/ +// clang-format off + +#ifdef __psyz +/* The PSX has 1 KiB of fast scratchpad at physical 0x1f800000. On any other + * target it is a plain array; every literal scratchpad address in the engine + * was rewritten to SCRPAD_ADDR + offset, so this one definition relocates all + * of them at once. */ +extern unsigned long psyz_scratchpad[256]; +#define SCRPAD_ADDR ((unsigned long)psyz_scratchpad) +#else +#define SCRPAD_ADDR 0x1F800000u +#endif +#define SCRPAD_SIZE 0x400 + +// These macros were taken from "GTE Advanced Topics" (slide 18), +// originally presented at the March 1996 PlayStation Developer's Conference. +// +// According to stack.txt (in Japanese) from the seminar sample code, +// compiler optimizations should be set to -O1 or higher, otherwise +// the $fp register may be used, potentially resulting in a crash. +// +// In addition, function calls with 5 or more arguments will use the stack +// for passing the 5th argument onwards. The callee will receive garbage data +// if these are set before the stack switch. + +/* scratch pad address (SCRPAD_ADDR + 0x000) - (SCRPAD_ADDR + 0x400) */ +#define SPAD_STACK_ADDR (SCRPAD_ADDR + 0x3fc) + +#ifdef __psyz +/* Parking the stack in the PSX scratchpad is a cache trick with no equivalent + * anywhere else; the code below only needs it to be a no-op. */ +#define SetSpadStack(addr) ((void)(addr)) +#define ResetSpadStack() ((void)0) +#else +#define SetSpadStack(addr) { \ + __asm__ volatile ("move $8,%0" ::"r"(addr):"$8","memory"); \ + __asm__ volatile ("sw $29,0($8)" :: :"$8","memory"); \ + __asm__ volatile ("addiu $8,$8,-4" :: :"$8","memory"); \ + __asm__ volatile ("move $29,$8" :: :"$8","memory"); \ +} + +#define ResetSpadStack() { \ + __asm__ volatile ("addiu $29,$29,4":::"$29","memory"); \ + __asm__ volatile ("lw $29,0($29)" :::"$29","memory"); \ +} +#endif /* __psyz */ + +#define GetStackAddr(addr) { \ + __asm__ volatile ("move $8,%0" ::"r"(addr):"$8","memory"); \ + __asm__ volatile ("sw $29,0($8)" :: :"$8","memory"); \ +} + +// clang-format on +/*---------------------------------------------------------------------------*/ +// libgpu.h's setPrimXX macros set these codes, but sometimes we need +// to specify them ourselves. libgs.h (which MGS does not use) provides +// named constants, but they only account for the POLYGON types. + +#define GPU_CODE_SHADETEX 0x01 /* disables shading */ +#define GPU_CODE_SEMITRANS 0x02 /* enables semitransparency */ + +#define GPU_CODE_POLY_F3 0x20 /* GPU_COM_F3 */ +#define GPU_CODE_POLY_FT3 0x24 /* GPU_COM_TF3 */ +#define GPU_CODE_POLY_F4 0x28 /* GPU_COM_F4 */ +#define GPU_CODE_POLY_FT4 0x2C /* GPU_COM_TF4 */ +#define GPU_CODE_POLY_G3 0x30 /* GPU_COM_G3 */ +#define GPU_CODE_POLY_GT3 0x34 /* GPU_COM_TG3 */ +#define GPU_CODE_POLY_G4 0x38 /* GPU_COM_G4 */ +#define GPU_CODE_POLY_GT4 0x3C /* GPU_COM_TG4 */ +#define GPU_CODE_LINE_F2 0x40 +#define GPU_CODE_LINE_F3 0x48 +#define GPU_CODE_LINE_F4 0x4C +#define GPU_CODE_LINE_G2 0x50 +#define GPU_CODE_LINE_G3 0x58 +#define GPU_CODE_LINE_G4 0x5C +#define GPU_CODE_TILE 0x60 +#define GPU_CODE_SPRT 0x64 +#define GPU_CODE_TILE_1 0x68 +#define GPU_CODE_TILE_8 0x70 +#define GPU_CODE_SPRT_8 0x74 +#define GPU_CODE_TILE_16 0x78 +#define GPU_CODE_SPRT_16 0x7C + +#endif // __MGS_PSXDEFS_H__ diff --git a/components/mgs/mgs/source/include/strcode.h b/components/mgs/mgs/source/include/strcode.h new file mode 100644 index 00000000..980099f5 --- /dev/null +++ b/components/mgs/mgs/source/include/strcode.h @@ -0,0 +1,139 @@ +#ifndef __MGS_STRCODE_H__ +#define __MGS_STRCODE_H__ + +//----------------------------------------------------------------------------- + +#define HASH_PAN2 0x922b // GV_StrCode( "pan2" ) +#define HASH_ITEM 0x5d43 // GV_StrCode( "item" ) + +//----------------------------------------------------------------------------- +// Traps +//----------------------------------------------------------------------------- + +#define HASH_TRAP_ALL 0x14c9 // GV_StrCode( "?" ) +#define HASH_TRAP_KETCHUP 0xc09e // GV_StrCode( "ketchup" ) + +// Events +#define HASH_ENTER 0x0dd2 // GV_StrCode( "入る" ) +#define HASH_LEAVE 0xd5cc // GV_StrCode( "出る" ) +#define HASH_LEAVE2 0x1a19 // GV_StrCode( "leave" ) +#define HASH_KILL 0x3223 // GV_StrCode( "kill" ) +#define HASH_OFF 0xc927 // GV_StrCode( "off" ) +#define HASH_OFF2 0x006b // GV_StrCode( "OFF" ) +#define HASH_ON 0x0e4e // GV_StrCode( "on" ) +#define HASH_ON2 0xd182 // GV_StrCode( "ON" ) +#define HASH_PADON 0x2580 // GV_StrCode( "padon" ) +#define HASH_PADOFF 0xaf6a // GV_StrCode( "padoff" ) +#define HASH_SLOW 0x3e92 // GV_StrCode( "slow" ) +#define HASH_SOUND_ON 0x2761 // GV_StrCode( "音入れる" ) +#define HASH_SOUND_OFF 0xed7f // GV_StrCode( "音切る" ) +#define HASH_TABAKO 0x8012 // GV_StrCode( "tabako" ) +#define HASH_POSITION 0x62b6 // GV_StrCode( "position" ) +#define HASH_STOP 0x5e8b // GV_StrCode( "stop" ) +#define HASH_START 0x9a1f // GV_StrCode( "start" ) +#define HASH_STANCE 0x3238 // GV_StrCode( "stance" ) +#define HASH_RUN_MOVE 0x70fb // GV_StrCode( "run_move" ) +#define HASH_MOTION 0x937a // GV_StrCode( "motion" ) +#define HASH_GO_MOTION 0xbe0a // GV_StrCode( "go_motion" ) +#define HASH_MOVE 0x4b5d // GV_StrCode( "move" ) +#define HASH_MOVE2 0x89cb // GV_StrCode( "移動" ) +#define HASH_VOICE 0x385e // GV_StrCode( "voice" ) +#define HASH_TURN 0xe2e9 // GV_StrCode( "turn" ) +#define HASH_MODE 0x491d // GV_StrCode( "mode" ) +#define HASH_OPERATION 0x4f34 // GV_StrCode( "operation" ) (might be wrong) +#define HASH_MAP 0xf9ad // GV_StrCode( "マップ" ) +#define HASH_LOOP 0xca87 // GV_StrCode( "loop" ) + +//----------------------------------------------------------------------------- +// HZD +//----------------------------------------------------------------------------- + +#define HASH_MAIN 0x7df9 // GV_StrCode( "メイン" ) - Refers to the main area of the map to load +#define HASH_ASIATO 0xdc55 // GV_StrCode( "asiato" ) +#define HASH_POOL 0xca85 // GV_StrCode( "pool" ) +#define HASH_POOLATO 0x7833 // GV_StrCode( "poolato" ) + +//----------------------------------------------------------------------------- +// Textures files (.pcx) +//----------------------------------------------------------------------------- +#define PCX_EMPTY2 0xb05c // GV_StrCode( "empty2" ) +#define PCX_LSIGHT 0x08db // GV_StrCode( "lsight" ) +#define PCX_SOCOM_F 0xe4cc // GV_StrCode( "socom_f" ) +#define PCX_SMOKE 0x512d // GV_StrCode( "smoke" ) +#define PCX_WT_SUD11 0xdf92 // GV_StrCode( "wt_sud11" ) +#define PCX_MAGAZIN 0x7e4c // GV_StrCode( "magazin" ) +#define PCX_FA_FL10 0xfe8e // GV_StrCode( "fa_fl10" ) +#define PCX_BOMB1_FL 0xac92 // GV_StrCode( "bomb1_fl" ) +#define PCX_CANON_SEQ 0x2447 // GV_StrCode( "canon_seq" ) +#define PCX_BLOOD_2 0x7b54 // GV_StrCode( "blood_2" ) +#define PCX_SONIC 0x4cec // GV_StrCode( "sonic" ) +#define PCX_LENSE_FLARE1 0x55a6 // GV_StrCode( "lense_flare1" ) +#define PCX_B_MARK 0xdcd3 // GV_StrCode( "b_mark" ) +#define PCX_Q_MARK 0xfad3 // GV_StrCode( "q_mark" ) +#define PCX_HEART 0x1968 // GV_StrCode( "heart" ) +#define PCX_PCH_FOG 0xf314 // GV_StrCode( "pch_fog" ) +#define PCX_HOSI 0xcafe // GV_StrCode( "hosi" ) +#define PCX_ZZZ 0xf7bb // GV_StrCode( "zzz" ) +#define PCX_ASIATO 0xdc55 // GV_StrCode( "asiato" ) +#define PCX_RCM_L 0x479f // GV_StrCode( "rcm_l" ) +#define PCX_W_BONBORI 0xa9cd // GV_StrCode( "w_bonbori" ) +#define PCX_DOOR2 0x50eb // GV_StrCode( "door2" ) +#define PCX_CD_WARN 0xa0f4 // GV_StrCode( "cd_warn" ) +#define PCX_CD_KEIKOKU 0xa1bc // GV_StrCode( "cd_keikoku" ) +#define PCX_HEAD_LIGHT 0x5699 // GV_StrCode( "head_light" ) +#define PCX_M19_C2_GLASS1HLF 0x3cb9 // GV_StrCode( "m19_c2_glass1hlf" ) +#define PCX_TAMA_01 0xcb50 // GV_StrCode( "tama_01" ) +#define PCX_TAMA_02 0xcb51 // GV_StrCode( "tama_02" ) +#define PCX_TAMA_03 0xcb52 // GV_StrCode( "tama_03" ) +#define PCX_M60_FLASH 0xc61c // GV_StrCode( "m60_flash" ) +#define PCX_JEEP_BONBORI_ADD 0xa7fc // GV_StrCode( "jeep_bonbori_add" ) +#define PCX_FIRE2 0x5b68 // GV_StrCode( "fire2" ) +#define PCX_GOURAUD 0x38a9 // GV_StrCode( "gouraud" ) + +//----------------------------------------------------------------------------- +// Model files (.kmd) +//----------------------------------------------------------------------------- + +// Init "stage" (always loaded before each stage) +#define KMD_NULL 0xe224 // GV_StrCode( "null" ) +#define KMD_SNAKE 0x992d // GV_StrCode( "snake" ) +#define KMD_GOGGLES 0xe1d2 // GV_StrCode( "goggles" ) +#define KMD_GAS_MASK 0x4244 // GV_StrCode( "gas_mask" ) +#define KMD_RIFLE 0x2d3b // GV_StrCode( "rifle" ) +#define KMD_STINGER 0x57f8 // GV_StrCode( "stinger" ) +#define KMD_NIKITA 0x15a9 // GV_StrCode( "nikita" ) +#define KMD_STN_FR 0x76ab // GV_StrCode( "stn_fr" ) +#define KMD_NIK_MIS 0x9a90 // GV_StrCode( "nik_mis" ) +#define KMD_FAMAS 0x4725 // GV_StrCode( "famas" ) +#define KMD_SOCOM2 0x36f6 // GV_StrCode( "socom2" ) +#define KMD_STN_BA 0x761a // GV_StrCode( "stn_ba" ) +#define KMD_CAN_GREN 0x7a64 // GV_StrCode( "can_gren" ) +#define KMD_GRENADE 0x3b88 // GV_StrCode( "grenade" ) +#define KMD_SOCOM 0x21b6 // GV_StrCode( "socom" ) +#define KMD_CLAYMORE 0x17a1 // GV_StrCode( "claymore" ) +#define KMD_C4_BOMB 0xf83d // GV_StrCode( "c4_bomb" ) +#define KMD_CB_BOX 0xe2a9 // GV_StrCode( "cb_box" ) +#define KMD_CIGAR 0x2ef8 // GV_StrCode( "cigar" ) +#define KMD_BANDANA 0xdb4d // GV_StrCode( "bandana" ) +#define KMD_KAGE 0x117c // GV_StrCode( "kage" ) +#define KMD_IPPANHEI 0x96b6 // GV_StrCode( "ippanhei" ) +#define KMD_BOX_01 0x4d5f // GV_StrCode( "box_01" ) +#define KMD_LOW_109P 0xff15 // GV_StrCode( "low_109p" ) + +// Stage s00a/d00a (docks) +#define KMD_SNE_WET2 0x7693 // GV_StrCode( "sne_wet2" ) + +//----------------------------------------------------------------------------- +// Animation files (.oar) +//----------------------------------------------------------------------------- +#define OAR_SNAKE 0x992d // GV_StrCode( "snake" ) +#define OAR_ENEMY 0xa8a1 // GV_StrCode( "enemy" ) + +//----------------------------------------------------------------------------- +// Chara Names +//----------------------------------------------------------------------------- + +#define SNAKE_NAME 0x21ca // GV_StrCode("スネーク") +#define RCM_NAME 0x50ae // GV_StrCode("RCM") + +#endif // __MGS_STRCODE_H__ diff --git a/components/mgs/mgs/source/koba/vr/vrwindow.c b/components/mgs/mgs/source/koba/vr/vrwindow.c new file mode 100644 index 00000000..a56ebefb --- /dev/null +++ b/components/mgs/mgs/source/koba/vr/vrwindow.c @@ -0,0 +1,555 @@ +#include "vrwindow.h" + +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "sound/g_sound.h" + +/*---------------------------------------------------------------------------*/ + +#define EXEC_LEVEL GV_ACTOR_ASSIST + +#define VRWIN_STATE_0 0 +#define VRWIN_STATE_1 1 +#define VRWIN_STATE_2 2 +#define VRWIN_STATE_3 3 +#define VRWIN_STATE_4 4 +#define VRWIN_STATE_5 5 +#define VRWIN_STATE_CLOSED 6 + +/*---------------------------------------------------------------------------*/ + +typedef struct _Work +{ + GV_ACT actor; + RECT m_rect; // margin? + RECT w_rect; // window? + short step; + int f34; + char state; + int proc; + char i_num; // number of lines + char *b_text[16]; + u_short name; + u_short clut; + KCB kcb; + SPRT sprts[2]; + TILE tiles[2]; + LINE_F3 lines1[2]; + LINE_F3 lines2[2]; + DR_TPAGE tpages[2]; + int f16C; + char pad[0x4]; + RECT f174; + int flag; + int f180; + void *font_buffer; + short f188; + short f18A; +} Work; + +/*---------------------------------------------------------------------------*/ + +int vrwindow_X1 = 896; +int vrwindow_Y1 = 256; +int vrwindow_X2 = 896; +int vrwindow_Y2 = 510; + +char SECTION(".bss") vrwindow_strbuf[100]; + +/*---------------------------------------------------------------------------*/ + +static void Vrwindow_800D73A4(Work *work, RECT *rect) +{ + setXY0(&work->tiles[GV_Clock], rect->x, rect->y); + setWH(&work->tiles[GV_Clock], rect->w, rect->h); + + setXY3(&work->lines1[GV_Clock], + rect->x, rect->y, + rect->x + rect->w, rect->y, + rect->x + rect->w, rect->y + rect->h); + + setXY3(&work->lines2[GV_Clock], + rect->x, rect->y, + rect->x, rect->y + rect->h, + rect->x + rect->w, rect->y + rect->h); +} + +static int CheckMessage(Work *work) +{ + GV_MSG *msg; + int n_msgs; + + n_msgs = GV_ReceiveMessage(work->name, &msg); + for (; n_msgs > 0; n_msgs--, msg++) + { + if (msg->message[0] == 0x3213) + { + return 1; + } + } + + return 0; +} + +static void CloseWindow(Work *work) +{ + if (!(GV_PadData[2].status & (PAD_UDLR | PAD_ABXY))) + { + return; + } + + if (work->proc != 0) + { + GCL_ExecProc(work->proc, NULL); + } + + GM_SeSet2(0, 63, SE_MENU_SELECT); + + switch (work->state) + { + case VRWIN_STATE_0: + work->state = VRWIN_STATE_4; + work->step = 0; + break; + + case VRWIN_STATE_1: + work->state = VRWIN_STATE_5; + work->step = 4 - work->step; + break; + + case VRWIN_STATE_2: + work->state = VRWIN_STATE_4; + work->step = 4 - work->step; + break; + + case VRWIN_STATE_3: + work->state = VRWIN_STATE_4; + work->step = 0; + break; + } +} + +static int PrintMessage(Work *work, int size) +{ + int i; + int len; + int idx; + + font_clear(&work->kcb); + + for (i = 0; i < work->f16C; i++) + { + if (i >= work->i_num) + { + break; + } + + len = strlen(work->b_text[work->f34 + i]); + + if (size < len) + { + strncpy(vrwindow_strbuf, work->b_text[work->f34 + i], size); + + idx = work->f180; + work->f180 = 0; + + while (idx < size) + { + if ((work->f180 != 0) || (vrwindow_strbuf[idx] > 0x1F && vrwindow_strbuf[idx] < 0x7F)) + { + work->f180 = 0; + } + else + { + work->f180 = size; + } + + idx++; + } + + if (size != 0 && work->f180 != 0) + { + vrwindow_strbuf[size - 1] = '\0'; + work->f180 = size - 1; + } + else + { + vrwindow_strbuf[size] = '\0'; + work->f180 = size; + } + + if (work->f180 != 0 && vrwindow_strbuf[work->f180 - 1] != ' ') + { + GM_SeSet2(0, 63, 179); + } + + font_draw_string(&work->kcb, 0, i * 18, vrwindow_strbuf, work->kcb.color); + font_update(&work->kcb); + break; + } + + font_draw_string(&work->kcb, 0, i * 18, work->b_text[work->f34 + i], work->kcb.color); + font_update(&work->kcb); + + size -= len; + work->f180 = 0; + } + + if (i == work->f16C || i == work->i_num) + { + return 0; + } + + return 1; +} + +static void Act(Work *work) +{ + RECT rect; + + if (work->f18A != 0) + { + if (work->f188 >= 0 && work->f188 <= 9) + { + work->f188++; + } + else if (work->f188 == 10) + { + GV_PauseLevel |= GV_PAUSE_MENU; + work->f188++; + } + } + + switch (work->state) + { + case VRWIN_STATE_0: + if (CheckMessage(work)) + { + work->state = VRWIN_STATE_4; + } + + addPrim(DG_ChanlOTag(1), &work->sprts[GV_Clock]); + CloseWindow(work); + break; + + case VRWIN_STATE_1: + rect.w = (work->w_rect.w * work->step) / 5; + rect.h = 1; + rect.x = work->w_rect.x + (work->w_rect.w - rect.w) / 2; + rect.y = work->w_rect.y + work->w_rect.h / 2; + Vrwindow_800D73A4(work, &rect); + + if (++work->step == 5) + { + work->state = VRWIN_STATE_2; + work->step = 0; + } + + CloseWindow(work); + break; + + case VRWIN_STATE_2: + rect.w = work->w_rect.w; + rect.h = work->w_rect.h * work->step / 4; + rect.x = work->w_rect.x; + rect.y = work->w_rect.y + (work->w_rect.h - rect.h) / 2; + Vrwindow_800D73A4(work, &rect); + + if (++work->step == 5) + { + work->state = VRWIN_STATE_3; + work->step = 0; + } + + CloseWindow(work); + break; + + case VRWIN_STATE_3: + work->step += 2; + if (!PrintMessage(work, work->step)) + { + work->step -= strlen(work->b_text[work->f34]) * 2; + work->f180 = 0; + + if (work->i_num <= (work->f34++ + work->f16C)) + { + work->state = VRWIN_STATE_0; + work->step = 0; + } + } + + Vrwindow_800D73A4(work, &work->w_rect); + + addPrim(DG_ChanlOTag(1), &work->sprts[GV_Clock]); + CloseWindow(work); + break; + + case VRWIN_STATE_4: + rect.w = work->w_rect.w; + rect.h = work->w_rect.h * (4 - work->step) / 4; + rect.x = work->w_rect.x; + rect.y = work->w_rect.y + (work->w_rect.h - rect.h) / 2; + Vrwindow_800D73A4(work, &rect); + + if (++work->step == 5) + { + work->state = VRWIN_STATE_5; + work->step = 0; + } + break; + + case VRWIN_STATE_5: + rect.w = work->w_rect.w * (4 - work->step) / 4; + rect.h = 1; + rect.x = work->w_rect.x + (work->w_rect.w - rect.w) / 2; + rect.y = work->w_rect.y + work->w_rect.h / 2; + Vrwindow_800D73A4(work, &rect); + + if (++work->step == 5) + { + work->state = VRWIN_STATE_CLOSED; + work->step = 0; + } + break; + + case VRWIN_STATE_CLOSED: + if (++work->step == 3) + { + GV_DestroyActor(&work->actor); + GV_PauseLevel &= ~GV_PAUSE_MENU; + } + return; + } + + addPrim(DG_ChanlOTag(1), &work->lines1[GV_Clock]); + addPrim(DG_ChanlOTag(1), &work->lines2[GV_Clock]); + addPrim(DG_ChanlOTag(1), &work->tiles[GV_Clock]); + addPrim(DG_ChanlOTag(1), &work->tpages[GV_Clock]); +} + +static void Vrwindow_800D7ED8(RECT *rect); + +static void Die(Work *work) +{ + if (work->flag) + { + Vrwindow_800D7ED8(&work->f174); + } + + GV_Free(work->font_buffer); +} + +static void Vrwindow_800D7E54(Work *work, RECT *rect) +{ + rect->x = vrwindow_X1; + rect->y = vrwindow_Y1; + rect->w = work->m_rect.w / 4; + rect->h = work->m_rect.h; + + vrwindow_Y1 += work->w_rect.h; + if (vrwindow_Y1 > 512) + { + printf("vrwindow: Can\'t alloc !! \n"); + } +} + +static void Vrwindow_800D7ED8(RECT *rect) +{ + vrwindow_Y1 = 256; + vrwindow_X2 = 960; +} + +static u_short Vrwindow_800D7EF4(RECT *rect) +{ + rect->x = vrwindow_X2; + rect->y = vrwindow_Y2; + rect->w = 16; + rect->h = 1; + + vrwindow_X2 += 16; + + return getClut(rect->x, rect->y); +} + +static void InitWindowText(KCB *kcb, int x, int y, Work *work) +{ + RECT rect; + + Vrwindow_800D7E54(work, &work->f174); + ClearImage(&work->f174, 0, 0, 0); + + work->clut = Vrwindow_800D7EF4(&rect); + font_init_kcb(kcb, &work->f174, rect.x, rect.y); + font_set_kcb(kcb, -1, -1, 0, 6, 2, 0); + + work->font_buffer = GV_Malloc(font_get_buffer_size(kcb)); + font_set_buffer(kcb, work->font_buffer); + + font_set_color(kcb, 0, 0x6739, 0); + font_clut_update(kcb); +} + +static int GetResources(Work *work, int map) +{ + int i; + + GM_CurrentMap = map; + + work->f188 = 0; + InitWindowText(&work->kcb, work->m_rect.w, work->m_rect.h + 18, work); + + for (i = 0; i < 2; i++) + { + setDrawTPage(&work->tpages[i], 1, 0, getTPage(0, 0, 896, 256)); + + LSTORE(COLOR_GRAY, &work->sprts[i].r0); + LCOPY(&work->m_rect.x, &work->sprts[i].x0); + LCOPY(&work->m_rect.w, &work->sprts[i].w); + setSprt(&work->sprts[i]); + setUV0(&work->sprts[i], (work->f174.x - 896) * 4, work->f174.y); + work->sprts[i].clut = work->clut; + + setTile(&work->tiles[i]); + setRGB0(&work->tiles[i], 0, 0, 0); + setXY0(&work->tiles[i], work->w_rect.x, work->w_rect.y); + setWH(&work->tiles[i], work->w_rect.w, work->w_rect.h); + setSemiTrans(&work->tiles[i], 1); + + setLineF3(&work->lines1[i]); + setLineF3(&work->lines2[i]); + setRGB0(&work->lines1[i], 64, 64, 64); + setRGB0(&work->lines2[i], 64, 64, 64); + + work->f180 = 0; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +void *NewVrWindow(int name, int where) +{ + Work *work; + int i; + int width; + + work = GV_NewActor(EXEC_LEVEL, sizeof(Work)); + if (work != NULL) + { + GV_SetNamedActor(&work->actor, Act, Die, "vrwindow.c"); + + work->state = VRWIN_STATE_1; + work->step = 0; + work->f34 = 0; + + GM_SeSet2(0, 63, SE_MENU_SELECT); + + if (GCL_GetOption('s')) + { + work->f18A = 0; + } + else + { + work->f18A = 1; + } + + work->i_num = GCL_StrToInt(GCL_GetOption('i')); + + if (GCL_GetOption('b')) + { + for (i = 0; i < work->i_num; i++) + { + work->b_text[i] = GCL_GetString((char *)GCL_NextStr()); + if (work->b_text[i] == 0) + { + break; + } + } + } + + if (GCL_GetOption('w')) + { + work->w_rect.x = GCL_StrToInt((char *)GCL_NextStr()); + work->w_rect.y = GCL_StrToInt((char *)GCL_NextStr()); + work->w_rect.w = GCL_StrToInt((char *)GCL_NextStr()); + work->w_rect.h = GCL_StrToInt((char *)GCL_NextStr()); + + width = work->w_rect.w; + if (width & 3) + { + work->w_rect.w += 4 - (width % 4); + } + + work->w_rect.h = (work->i_num * 18) + 18; + work->w_rect.x = (FRAME_WIDTH - work->w_rect.w) >> 1; + work->w_rect.y = (FRAME_HEIGHT - work->w_rect.h) >> 1; + } + else + { + work->w_rect.x = 0; + work->w_rect.y = 0; + work->w_rect.w = 256; + work->w_rect.h = 13; + } + + if (GCL_GetOption('m')) + { + work->m_rect.x = work->w_rect.x + GCL_StrToInt((char *)GCL_NextStr()); + work->m_rect.y = work->w_rect.y + GCL_StrToInt((char *)GCL_NextStr()); + work->m_rect.w = GCL_StrToInt((char *)GCL_NextStr()); + work->m_rect.h = GCL_StrToInt((char *)GCL_NextStr()); + } + else + { + work->m_rect.x = work->w_rect.x; + work->m_rect.y = work->w_rect.y; + work->m_rect.w = work->w_rect.w; + work->m_rect.h = work->w_rect.h; + } + + work->m_rect.x = work->w_rect.x + 16; + work->m_rect.y = work->w_rect.y + 12; + work->m_rect.w = work->w_rect.w - 16; + work->m_rect.h = work->w_rect.h - 12; + + if (GCL_GetOption('p')) + { + work->proc = GCL_StrToInt((char *)GCL_NextStr()); + } + else + { + work->proc = 0; + } + + if (GCL_GetOption('f')) + { + work->flag = TRUE; + } + else + { + work->flag = FALSE; + } + + work->name = name; + work->f16C = work->m_rect.h / 18; + + if (GetResources(work, where) < 0) + { + GV_DestroyActor(&work->actor); + return NULL; + } + } + + return (void *)work; +} diff --git a/components/mgs/mgs/source/koba/vr/vrwindow.h b/components/mgs/mgs/source/koba/vr/vrwindow.h new file mode 100644 index 00000000..26b6a559 --- /dev/null +++ b/components/mgs/mgs/source/koba/vr/vrwindow.h @@ -0,0 +1,6 @@ +#ifndef __MGS_KOBA_VRWINDOW_H__ +#define __MGS_KOBA_VRWINDOW_H__ + +void *NewVrWindow(int name, int where); + +#endif // __MGS_KOBA_VRWINDOW_H__ diff --git a/components/mgs/mgs/source/kojo/celofan2.c b/components/mgs/mgs/source/kojo/celofan2.c new file mode 100644 index 00000000..8ae9943a --- /dev/null +++ b/components/mgs/mgs/source/kojo/celofan2.c @@ -0,0 +1,36 @@ +#if 0 +/****************************************************************************** + * System : METALGEAR^3 for PlayStation + * Computer : PlayStation + * OS : PlayStation + * Compiler : psyq + * Module : セロファン2 + */ + +/****************************************************************************** + * included + */ + +#include +#include +#include +#include +#include +#include +#include "game.h" + +#include "common.h" + +/****************************************************************************** + * functions + */ + +/****************************************************************************** + * definitions and typedefs and structures + */ + +#define CELOFAN_FULL50ADD GV_StrCode("full50_add") +#define CELOFAN_HALF50ADD GV_StrCode("half50_add") +#define CELOFAN_QUAR50ADD GV_StrCode("quar50_add") +#define CELOFAN_FULL50SUB GV_StrCode("full50_sub") +#endif diff --git a/components/mgs/mgs/source/kojo/demo.h b/components/mgs/mgs/source/kojo/demo.h new file mode 100644 index 00000000..2b88c3f9 --- /dev/null +++ b/components/mgs/mgs/source/kojo/demo.h @@ -0,0 +1,671 @@ +#ifndef __MGS_KOJO_DEMO_H__ +#define __MGS_KOJO_DEMO_H__ + +#include +#include +#include +#include "libgv/libgv.h" +#include "game/game.h" + +#include "fmt_dmo.h" + +/****************************************************************************** + * definitions and typedefs and structures + */ + +#define EFFECT_EXPLOSION 1 +#define EFFECT_SMOKE 2 +#define EFFECT_FADEIN 3 +#define EFFECT_FADEOUT 4 +#define EFFECT_TEXT 5 +#define EFFECT_SURPRISEDMARK 6 +#define EFFECT_QUESTIONMARK 7 +#define EFFECT_SLEEPMARK 8 +#define EFFECT_BLOOD 9 +#define EFFECT_BULLET 10 +#define EFFECT_MISSILESMOKE 11 +#define EFFECT_BLOODCIRCLE 12 +#define EFFECT_BREATH 13 +#define EFFECT_SHADOW 14 +#define EFFECT_FOOTPRINTS 15 +#define EFFECT_MESSAGE 16 +#define EFFECT_NINJAEYE 17 +#define EFFECT_BUBBLE 18 +#define EFFECT_SCOPE 19 +#define EFFECT_DARKVISIBLEGOGGLE 20 +#define EFFECT_IRRAYSGOGGLE 21 +#define EFFECT_GUNSMOKE 22 +#define EFFECT_OPTICSCAMOUFLAGE 23 +#define EFFECT_ENVIRONMENTMAPPING 24 +#define EFFECT_PLASMA 25 +#define EFFECT_WINDCIRCLE 26 +#define EFFECT_SEPIA 27 +#define EFFECT_METALGEARLASER 28 +#define EFFECT_UNSHAPEVIEW 29 +#define EFFECT_GUSMASK 30 +#define EFFECT_STOP 31 +#define EFFECT_URINATIONCIRCLE 32 +#define EFFECT_PLASMA2 33 +#define EFFECT_BLUR 34 +#define EFFECT_STEAMSMOKE 35 +#define EFFECT_MONOTONE 36 +#define EFFECT_RIFLESIGHT 37 +#define EFFECT_MI24HIND 38 +#define EFFECT_BLACKSMOKE 39 +#define EFFECT_URINATIONCIRCLE2 40 +#define EFFECT_SIGHT 41 +#define EFFECT_NINJASWORD 42 +#define EFFECT_SUBMARINEROOM 43 +#define EFFECT_BLACKSMOKE2 44 +#define EFFECT_BLASTLINE 45 +#define EFFECT_SMOKELINE 46 +#define EFFECT_SHELLSMOKE 47 +#define EFFECT_CATERPILLERSMOKE 48 +#define EFFECT_CROWEYE 49 +#define EFFECT_OCEROTTEBULLETFIRE 50 +#define EFFECT_OCEROTTEBULLETSMOKE 51 +#define EFFECT_ENVIRONMENTLIGHT 52 +#define EFFECT_CRASHSMOKE 53 +#define EFFECT_PADVIBRATION 54 +#define EFFECT_CELOFAN 55 +#define EFFECT_INVERSLIGHT 56 +#define EFFECT_SPACK 57 +#define EFFECT_STEAMEXPLOSION 58 +#define EFFECT_INVERSLIGHT2 59 +#define EFFECT_CELOFAN2 60 +#define EFFECT_NINJALASER 61 +#define EFFECT_GUSEFFECT 62 +#define EFFECT_SUBMARINEWATER 63 +#define EFFECT_PADVIBRATION2 64 +#define EFFECT_GHOST 65 +#define EFFECT_LIGHT 66 +#define EFFECT_FAMASLIGHT 67 +#define EFFECT_HUMANSHADOW 68 +#define EFFECT_BLOODHAZARD 69 +#define EFFECT_BLOODDRIP 70 +#define EFFECT_NINJAGROUND 71 +#define EFFECT_BOMBLIGHT 72 +#define EFFECT_MGCROOMDISPLAY 73 +#define EFFECT_TEXT2 74 + +typedef struct DEMO_M1E1 +{ + OBJECT object[2][3]; + int field_558_idx[2]; + int field_560; + SVECTOR field_564[2]; +} DEMO_M1E1; + +typedef struct DEMO_HIND +{ + int field_0; + int field_4; + int field_8; + int field_C; +} DEMO_HIND; + +typedef struct DEMO_MODEL +{ + CONTROL control; + OBJECT object; + MATRIX light[2]; + DEMO_M1E1 *extra; // union, can be DEMO_HIND also, depending on the current stage +} DEMO_MODEL; + +typedef struct ACTNODE +{ + struct ACTNODE *prev; + struct ACTNODE *next; + int used; + GV_ACT *actor1; + GV_ACT *actor2; + DMO_CHA chara; + int field_48; + int field_4C; + short field_50; + short field_52; + short field_54; + short field_56; + MATRIX field_58; +} ACTNODE; + +typedef struct tagMGSDEMOACT +{ + GV_ACT actor; + int flag; + int start_time; + int map; + int frame; + DMO_DEF *header; + DEMO_MODEL *models; + ACTNODE chain; + int field_B0; /* unused */ + int field_B4; /* unused */ + int field_B8; /* unused */ + int field_BC; /* unused */ + DMO_DEF *stream; + CONTROL control; + OBJECT object; + MATRIX light[2]; + int field_264; /* unused */ + int field_268; /* unused */ + int field_26C; /* unused */ + DG_CHANLFUNC old_screen; + int old_game_status; + GM_CameraSystemWork old_camera; + int old_item; + int old_weapon; +} MGSDEMOACT, *PMGSDEMOACT, *LPMGSDEMOACT; + +/* This should be replaced by loads of individual structs */ +typedef struct DMO_DATA_0x36 +{ + int field_0; + int field_4_type; + + DG_SVECTOR field_8_vec1; + DG_SVECTOR field_E_vec2; + + // TODO: Is there a cleaner way to model this structure? + // Some of the variants are very similar. Once all + // ShowEffect functions are decompiled those fields + // should be named properly (and maybe it'll be easier + // to find patterns then). + union { + struct + { + int field_14; + short field_18; + } variant_0x3; + + struct + { + int field_14; + short field_18; + } variant_0x4; + + struct + { + int field_14; + short field_18; + short field_1A; + int field_1C; + } variant_0x5; + + struct + { + short field_14; + } variant_0x6; + + struct + { + short field_14; + } variant_0x7; + + struct + { + short field_14; + } variant_0x9; + + struct + { + int field_14; + short field_18; + short field_1A; + short field_1C; + short field_1E; + short field_20; + short field_22; + short field_24; + } variant_0xA; + + struct + { + short field_14; + short field_16; + int field_18; + int field_1C; + } variant_0xC; + + struct + { + int field_14; + SVECTOR field_18; + short field_20; + short field_22; + short field_24; + short field_26; + } variant_0xE; + + struct + { + short field_14; + } variant_0xF; + + struct + { + int address; + int message[7]; + } variant_0x10; + + struct + { + short field_14; + short field_16; + short field_18; + short field_1A; + int field_1C; + int field_20; + short field_24; + short field_26; + short field_28; + short field_2A; + } variant_0x12; + + struct + { + short field_14; + } variant_0x13; + + struct + { + short field_14; + short field_16; + } variant_0x14; + + struct + { + short field_14; + short field_16; + } variant_0x15; + + struct + { + short field_14; + short field_16; + int field_18; + short field_1C; + } variant_0x17; + + struct + { + short field_14; + short field_16; + int field_18; + int field_1C; + } variant_0x18; + + struct + { + int field_14; + short field_18; + short field_1A; + int field_1C; + short field_20; + } variant_0x19; + + struct + { + int field_14; + } variant_0x1A; + + struct + { + short field_14; + short field_16; + short field_18; + short field_1A; + } variant_0x1B; + + struct + { + short field_14; + short field_16; + short field_18; + short field_1A; + short field_1C; + short field_1E; + } variant_0x1C; + + struct + { + short field_14; + short field_16; + int field_18; + int field_1C; + } variant_0x1D; + + struct + { + short field_14; + } variant_0x1E; + + struct + { + short field_14; + short field_16; + int field_18; + int field_1C; + short field_20; + } variant_0x20; + + struct + { + short field_14; + short field_16; + short field_18; + short field_1A; + int field_1C; + } variant_0x21; + + struct + { + short field_14; + short field_16; + short field_18; + short field_1A; + } variant_0x22; + + struct + { + short field_14; + short field_16; + short field_18; + short field_1A; + short field_1C; + short field_1E; + int field_20; + } variant_0x23; + + struct + { + short field_14; + } variant_0x24; + + struct + { + short field_14; + } variant_0x25; + + struct + { + DG_OBJS *field_14; + int field_18; + } variant_0x26; + + struct + { + short field_14; + short field_16; + int field_18; + int field_1C; + } variant_0x28; + + struct + { + short field_14; + short field_16; + short field_18; + short field_1A; + int field_1C; + } variant_0x29; + + struct + { + int field_14; + short field_18; + } variant_0x2A; + + struct + { + short field_14; + } variant_0x2B; + + struct + { + short field_14; + short field_16; + short field_18; + short field_1A; + short field_1C; + } variant_0x2C; + + struct + { + short field_14; + short field_16; + short field_18; + short field_1A; + int field_1C; + short field_20; + short field_22; + } variant_0x2D; + + struct + { + short field_14; + short field_16; + short field_18; + short field_1A; + short field_1C; + short field_1E; + int field_20; + short field_24; + short field_26; + short field_28; + short field_2A; + short field_2C; + } variant_0x2E; + + struct + { + short field_14; + } variant_0x2F; + + struct + { + int field_14; + int field_18; + short field_1C; + short field_1E; + int field_20; + short field_24; + short field_26; + short field_28; + short field_2A; + short field_2C; + short field_2E; + short field_30; + } variant_0x34; + + struct + { + short field_14; + short field_16; + int field_18; + short field_1C; + short field_1E; + short field_20; + short field_22; + short field_24; + short field_26; + } variant_0x35; + + struct + { + short field_14; + short field_16; + int field_18; + } variant_0x36; + + struct + { + short field_14; + short field_16; + int field_18; + short field_1C; + short field_1E; + short field_20; + short field_22; + short field_24; + short field_26; + short field_28; + short field_2A; + short field_2C; + short field_2E; + short field_30; + short field_32; + } variant_0x37; + + struct + { + short field_14; + } variant_0x39; + + struct + { + short field_14; + short field_16; + short field_18; + } variant_0x3A; + + struct + { + int field_14; + int field_18; + int field_1C; + short field_20; + short field_22; + short field_24; + short field_26; + short field_28; + } variant_0x3B; + + struct + { + short field_14; + short field_16; + int field_18; + short field_1C; + short field_1E; + short field_20; + short field_22; + short field_24; + short field_26; + short field_28; + short field_2A; + short field_2C; + short field_2E; + short field_30; + short field_32; + short field_34; + } variant_0x3C; + + struct + { + short field_14; + short field_16; + int field_18; + } variant_0x3D; + + struct + { + short field_14; + short field_16; + short field_18; + short field_1A; + short field_1C; + short field_1E; + short field_20; + short field_22; + } variant_0x3E; + + struct + { + short field_14; + short field_16; + int field_18; + } variant_0x3F; + + struct + { + short field_14; + } variant_0x41; + + struct + { + short field_14; + short field_16; + } variant_0x42; + + struct + { + int field_14; + short field_18; + } variant_0x43; + + struct + { + short field_14; + short field_16; + int field_18; + short field_1C; + short field_1E; + short field_20; + short field_22; + } variant_0x44; + + struct + { + short field_14; + short field_16; + short field_18; + short field_1A; + } variant_0x45; + + struct + { + short field_14; + short field_16; + } variant_0x46; + + struct + { + short field_14; + } variant_0x49; + + struct + { + int field_14; + short field_18; + short field_1A; + int field_1C; + int field_20; + } variant_0x4A; + } data; +} DMO_DATA_0x36; + +/****************************************************************************** + * prototypes + */ + +/* common.c */ +extern void InitChain(ACTNODE *root); +extern void NextChain(ACTNODE *root, ACTNODE *node); +extern void DeleteChain(ACTNODE *root, ACTNODE *node); + +/* unknown.c */ +void sub_8007F06C(DG_VECTOR *param_1, VECTOR *param_2, VECTOR *param_3); +void sub_8007F0D0(VECTOR *out, VECTOR *a, VECTOR *b, VECTOR *c); +VECTOR *sub_8007F1DC(VECTOR *out, DG_VECTOR *arg1, VECTOR *arg2); + +/* demothrd.c */ +extern int DM_ThreadStream(int flag, int unused); +extern int DM_ThreadFile(int flag, char *filename); + +/* demoexec.c */ +extern BOOL CreateDemo(LPMGSDEMOACT lpAct, DMO_DEF *def); +extern BOOL DestroyDemo(LPMGSDEMOACT lpAct); +extern BOOL FrameRunDemo(LPMGSDEMOACT lpAct, DMO_DAT *data); + +#endif // __MGS_KOJO_DEMO_H__ diff --git a/components/mgs/mgs/source/kojo/demoexec.c b/components/mgs/mgs/source/kojo/demoexec.c new file mode 100644 index 00000000..39a319a5 --- /dev/null +++ b/components/mgs/mgs/source/kojo/demoexec.c @@ -0,0 +1,2336 @@ +/****************************************************************************** + * System : METALGEAR^3 for PlayStation + * Computer : PlayStation + * OS : PlayStation + * Compiler : psyq + * Module : + */ + +/****************************************************************************** + * included + */ + +#include +#include +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libhzd/libhzd.h" +#include "game/game.h" +#include "linkvar.h" +#include "strcode.h" +#include "charaid.h" +#include "anime/animconv/anime.h" +#include "bullet/blast.h" + +#include "demo.h" +#include "kojo/m1e1.h" + +#define BODY_FLAG ( DG_FLAG_SHADE | DG_FLAG_TRANS | DG_FLAG_TEXT ) + +extern UnkCameraStruct2 gUnkCameraStruct2_800B7868; +extern BLAST_DATA blast_data_8009F4B8[8]; +extern GM_CameraSystemWork GM_Camera; + +/****************************************************************************** + * functions + */ + +static BOOL ShowEffect(LPMGSDEMOACT lpAct, DMO_DATA_0x36 *data, ACTNODE *node); +static void KillEffect(LPMGSDEMOACT lpAct, int type); +static BOOL ShowEffectExecute(LPMGSDEMOACT lpAct, DMO_DAT *data, ACTNODE *node); +static BOOL ShowEffectStop(LPMGSDEMOACT lpAct, DMO_DAT *data); +static BOOL ShowScene(LPMGSDEMOACT lpAct, DMO_ADJ *adjust); +static void demothrd_m1e1_8007D404(LPMGSDEMOACT lpAct, DMO_ADJ *adjust, DMO_MDL *model_file, DEMO_MODEL *model); +static void demothrd_hind_8007D9C8(LPMGSDEMOACT lpAct, DMO_ADJ *adjust, DMO_MDL *model_file, DEMO_MODEL *model); + +/* in anime/effect/m1e1.c */ +extern void AN_CaterpillerSmoke(SVECTOR *pos); +extern void demothrd_2_8007DA94(SVECTOR *pPosition, SVECTOR *pRotation); +extern void sub_8007DC24(SVECTOR *pPosition); +extern void sub_8007DD80(short param_1, SVECTOR *pPos); +extern void sub_8007DF10(SVECTOR *pRotation, SVECTOR *pTranslation); +extern void sub_8007E0AC(int y, SVECTOR *pPosition); + +/* in demoscrn.c */ +extern void DemoScreenChanl(DG_CHANL *chanl, int idx); + +/****************************************************************************** + * publics + */ + +BOOL CreateDemo(LPMGSDEMOACT lpAct, DMO_DEF *header) +{ + DMO_MAP *maps; + DMO_MDL *models; + DMO_MAP *map; + int i; + DEMO_MODEL *model; + DMO_MDL *model_file; + MATRIX *light; + + lpAct->old_screen = DG_SetChanlSystemUnits(DG_SCREEN_CHANL, DemoScreenChanl); + lpAct->old_game_status = GM_GameStatus; + lpAct->old_camera = GM_Camera; + lpAct->old_item = GM_CurrentItemId; + lpAct->old_weapon = GM_CurrentWeaponId; + + OFFSET_TO_PTR(header, &header->maps); + OFFSET_TO_PTR(header, &header->models); + + InitChain(&lpAct->chain); + + lpAct->header = GV_Malloc(sizeof(DMO_DEF)); + if (!lpAct->header) + { + return 0; + } + + *lpAct->header = *header; + lpAct->header->maps = NULL; + lpAct->header->models = NULL; + + maps = GV_Malloc((sizeof(DMO_MAP) * header->n_maps) | 1); + lpAct->header->maps = maps; + if (!maps) + { + return 0; + } + + models = GV_Malloc((sizeof(DMO_MDL) * header->n_models) | 1); + lpAct->header->models = models; + if (!models) + { + return 0; + } + + memcpy(lpAct->header->maps, header->maps, sizeof(DMO_MAP) * lpAct->header->n_maps); + memcpy(lpAct->header->models, header->models, sizeof(DMO_MDL) * lpAct->header->n_models); + + map = lpAct->header->maps; + for (i = 0; i < lpAct->header->n_maps; i++) + { + if (!GV_GetCache(map->cache_id)) + { + printf("Noload model ( Stage )\n"); + return 0; + } + + map++; + } + + lpAct->models = GV_Malloc((sizeof(DEMO_MODEL) * lpAct->header->n_models) | 1); + if (!lpAct->models) + { + return 0; + } + + memset(lpAct->models, 0, sizeof(DEMO_MODEL) * lpAct->header->n_models); + + model_file = lpAct->header->models; + model = lpAct->models; + for (i = 0; i < lpAct->header->n_models; i++, model_file++, model++) + { + if (!GV_GetCache(model_file->cache_id)) + { + printf("Noload model ( Scene = No.%d )\n", i + 1); + return 0; + } + + if (GM_InitControl(&model->control, model_file->name, lpAct->map) < 0) + { + printf("Error init control ( Scene = No.%d )\n", i + 1); + return 0; + } + + model->control.r_sphere = 0; + model->control.interp = 0; + + if (model_file->flag & 0x1) + { + GM_InitObject(&model->object, model_file->filename, BODY_FLAG | DG_FLAG_ONEPIECE | DG_FLAG_PAINT, 0); + continue; + } + + if (model_file->filename == GV_StrCode("16d_o4a") + || model_file->filename == GV_StrCode("16d_o5a") + || model_file->filename == GV_StrCode("16d_o6a") + || model_file->filename == GV_StrCode("16d_o7a") + || model_file->filename == GV_StrCode("16d_o8a") + || model_file->filename == GV_StrCode("16d_o9a") + || model_file->filename == GV_StrCode("16d_o10a") + || model_file->filename == GV_StrCode("16d_o4b") + || model_file->filename == GV_StrCode("16d_o5b") + || model_file->filename == GV_StrCode("16d_o6b") + || model_file->filename == GV_StrCode("16d_o7b") + || model_file->filename == GV_StrCode("16d_o8b") + || model_file->filename == GV_StrCode("16d_o9b") + || model_file->filename == GV_StrCode("16d_o10b") + || model_file->filename == GV_StrCode("16d_o4c") + || model_file->filename == GV_StrCode("16d_o5c") + || model_file->filename == GV_StrCode("16d_o6c") + || model_file->filename == GV_StrCode("16d_o7c") + || model_file->filename == GV_StrCode("16d_o8c") + || model_file->filename == GV_StrCode("16d_o9c") + || model_file->filename == GV_StrCode("16d_o10c")) + { + GM_InitObject(&model->object, model_file->filename, BODY_FLAG & ~DG_FLAG_SHADE, 0); + } + else if (model_file->filename == GV_StrCode("02a_r8")) + { + model->light[0].t[0] = 100; + model->light[0].t[1] = 110; + model->light[0].t[2] = 110; + + GM_InitObject(&model->object, model_file->filename, BODY_FLAG | DG_FLAG_AMBIENT, 0); + } + else if (model_file->filename == GV_StrCode("mgrexw") + || model_file->filename == GV_StrCode("mgrexll") + || model_file->filename == GV_StrCode("mgrexrl") + || model_file->filename == GV_StrCode("pit_t") + || model_file->filename == GV_StrCode("pit_u") + || model_file->filename == GV_StrCode("pit_liq") + || model_file->filename == GV_StrCode("radar_f1") + || model_file->filename == GV_StrCode("radar_f2") + || model_file->filename == GV_StrCode("radar_f3") + || model_file->filename == GV_StrCode("l_hatch1") + || model_file->filename == GV_StrCode("l_hatch2") + || model_file->filename == GV_StrCode("l_hatch3") + || model_file->filename == GV_StrCode("l_hatch4") + || model_file->filename == GV_StrCode("r_hatch1") + || model_file->filename == GV_StrCode("r_hatch2") + || model_file->filename == GV_StrCode("r_hatch3") + || model_file->filename == GV_StrCode("r_hatch4")) + { + model->light[0].t[0] = 64; + model->light[0].t[1] = 64; + model->light[0].t[2] = 64; + + GM_InitObject(&model->object, model_file->filename, BODY_FLAG | DG_FLAG_AMBIENT, 0); + } + else + { + GM_InitObject(&model->object, model_file->filename, BODY_FLAG, 0); + } + + light = model->light; + + GM_ConfigObjectJoint(&model->object); + GM_ConfigObjectLight(&model->object, light); + + DG_InvisibleObjs(model->object.objs); + + if (model_file->filename == GV_StrCode("m1e1") || model_file->filename == GV_StrCode("m1e1demo")) + { + model->extra = GV_Malloc(sizeof(DEMO_M1E1)); + if (!model->extra) + { + return 0; + } + + memset(model->extra, 0, sizeof(DEMO_M1E1)); + + GM_InitObject(&model->extra->object[0][0], GV_StrCode("m1e1cl1"), BODY_FLAG | DG_FLAG_AMBIENT | DG_FLAG_GBOUND, 0); + GM_InitObject(&model->extra->object[0][1], GV_StrCode("m1e1cl2"), BODY_FLAG | DG_FLAG_AMBIENT | DG_FLAG_GBOUND, 0); + GM_InitObject(&model->extra->object[0][2], GV_StrCode("m1e1cl3"), BODY_FLAG | DG_FLAG_AMBIENT | DG_FLAG_GBOUND, 0); + GM_InitObject(&model->extra->object[1][0], GV_StrCode("m1e1cr1"), BODY_FLAG | DG_FLAG_AMBIENT | DG_FLAG_GBOUND, 0); + GM_InitObject(&model->extra->object[1][1], GV_StrCode("m1e1cr2"), BODY_FLAG | DG_FLAG_AMBIENT | DG_FLAG_GBOUND, 0); + GM_InitObject(&model->extra->object[1][2], GV_StrCode("m1e1cr3"), BODY_FLAG | DG_FLAG_AMBIENT | DG_FLAG_GBOUND, 0); + + GM_ConfigObjectJoint(&model->extra->object[0][0]); + GM_ConfigObjectJoint(&model->extra->object[0][1]); + GM_ConfigObjectJoint(&model->extra->object[0][2]); + GM_ConfigObjectJoint(&model->extra->object[1][0]); + GM_ConfigObjectJoint(&model->extra->object[1][1]); + GM_ConfigObjectJoint(&model->extra->object[1][2]); + + GM_ConfigObjectLight(&model->extra->object[0][0], light); + GM_ConfigObjectLight(&model->extra->object[0][1], light); + GM_ConfigObjectLight(&model->extra->object[0][2], light); + GM_ConfigObjectLight(&model->extra->object[1][0], light); + GM_ConfigObjectLight(&model->extra->object[1][1], light); + GM_ConfigObjectLight(&model->extra->object[1][2], light); + + model->extra->field_560 = 83; + } + else if (model_file->filename == GV_StrCode("hind") || model_file->filename == GV_StrCode("hinddemo")) + { + model->extra = GV_Malloc(sizeof(DEMO_HIND)); + if (!model->extra) + { + return 0; + } + + memset(model->extra, 0, sizeof(DEMO_HIND)); + } + } + + if (!GV_GetCache(GV_CacheID2("null", 'k'))) + { + printf("Noload model ( null.kmd )\n"); + return 0; + } + + if (GM_InitControl(&lpAct->control, 0, lpAct->map) < 0) + { + printf("Error init control ( null.kmd )\n"); + return 0; + } + + lpAct->control.r_sphere = 0; + lpAct->control.interp = 0; + + GM_InitObject(&lpAct->object, GV_StrCode("null"), BODY_FLAG, 0); + + GM_ConfigObjectJoint(&lpAct->object); + GM_ConfigObjectLight(&lpAct->object, lpAct->light); + + DG_InvisibleObjs(lpAct->object.objs); + + GM_GameStatus |= STATE_DEMO; + return 1; +} + +BOOL DestroyDemo(LPMGSDEMOACT lpAct) +{ + ACTNODE *node; + DMO_MDL *model_file; + DEMO_MODEL *model; + int i; + + if (lpAct->flag & 0x2) + { + GM_GameStatus |= STATE_DEMO_VERBOSE; + } + + for (node = lpAct->chain.next; node != &lpAct->chain; node = lpAct->chain.next) + { + if (node->actor1) + { + GV_DestroyOtherActor(node->actor1); + } + + if (node->actor2) + { + GV_DestroyOtherActor(node->actor2); + } + + DeleteChain(&lpAct->chain, node); + GV_Free(node); + } + + if (lpAct->models) + { + model_file = lpAct->header->models; + model = lpAct->models; + for (i = 0; i < lpAct->header->n_models; i++, model_file++, model++) + { + GM_FreeObject(&model->object); + + if (model->extra) + { + if (model_file->filename == GV_StrCode("m1e1") || model_file->filename == GV_StrCode("m1e1demo")) + { + GM_FreeObject(&model->extra->object[0][0]); + GM_FreeObject(&model->extra->object[0][1]); + GM_FreeObject(&model->extra->object[0][2]); + GM_FreeObject(&model->extra->object[1][0]); + GM_FreeObject(&model->extra->object[1][1]); + GM_FreeObject(&model->extra->object[1][2]); + } + + GV_Free(model->extra); + } + } + + GM_FreeControl(&model->control); + GV_Free(lpAct->models); + } + + GM_FreeObject(&lpAct->object); + GM_FreeControl(&lpAct->control); + + if (lpAct->header) + { + if (lpAct->header->maps) + { + GV_Free(lpAct->header->maps); + } + + if (lpAct->header->models) + { + GV_Free(lpAct->header->models); + } + + GV_Free(lpAct->header); + } + + GM_GameStatus &= ~STATE_DEMO; + DG_SetChanlSystemUnits(DG_SCREEN_CHANL, lpAct->old_screen); + GM_GameStatus = lpAct->old_game_status; + GM_Camera = lpAct->old_camera; + GM_CurrentItemId = lpAct->old_item; + GM_CurrentWeaponId = lpAct->old_weapon; + return 1; +} + +BOOL FrameRunDemo(LPMGSDEMOACT lpAct, DMO_DAT *data) +{ + SVECTOR diff; + SVECTOR rot; + VECTOR trans; + int radius; + ACTNODE *node; + DMO_CHA *chara; + int i; + ACTNODE *iter; + DMO_ADJ *adjust; + + OFFSET_TO_PTR(data, &data->chara); + OFFSET_TO_PTR(data, &data->adjust); + + lpAct->control.mov.vx = data->eye_x; + lpAct->control.mov.vy = data->eye_y; + lpAct->control.mov.vz = data->eye_z; + + diff.vx = (data->center_x - data->eye_x) >> 2; + diff.vx = (data->center_y - data->eye_y) >> 2; + diff.vx = (data->center_z - data->eye_z) >> 2; + + radius = SquareRoot0(diff.vx * diff.vx + diff.vz * diff.vz); + lpAct->control.rot.vx = ratan2(diff.vy, radius); + lpAct->control.rot.vy = ratan2(diff.vx, diff.vz); + lpAct->control.rot.vz = data->roll; + + GM_ActControl(&lpAct->control); + GM_ActObject2(&lpAct->object); + DG_GetLightMatrix(&lpAct->control.mov, lpAct->light); + + lpAct->field_268 = 0; + lpAct->field_26C = 0; + + for (node = lpAct->chain.next; node != &lpAct->chain; node = node->next) + { + node->used = 0; + } + + chara = data->chara; + for ( i = 0; i < data->n_charas; i++, chara++ ) + { + node = lpAct->chain.next; + + if ( node != &lpAct->chain ) + { + do + { + if (node->chara.field_0 == chara->field_0) + { + node->used = 1; + break; + } + + node = node->next; + } + while (node != &lpAct->chain); + + if ( node->chara.field_0 == chara->field_0 ) + { + continue; + } + } + + node = GV_Malloc(sizeof(ACTNODE)); + if ( !node ) + { + return 1; + } + + memset(node, 0, sizeof(ACTNODE)); + NextChain(&lpAct->chain, node); + node->used = 1; + + node->chara = *chara; + + // This function uses offset 0x34 of chara despite it seemingly only being 0x34 bytes in size + if ( !ShowEffect(lpAct, (DMO_DATA_0x36 *)chara, node) ) + { + return 0; + } + } + + for ( iter = lpAct->chain.next; iter != &lpAct->chain; ) + { + node = iter; + iter = node->next; + + if ( node->used != 1 && !node->actor1 ) + { + DeleteChain(&lpAct->chain, node); + GV_Free(node); + } + } + + for ( node = lpAct->chain.next; node != &lpAct->chain; node = node->next ) + { + if ( !ShowEffectExecute(lpAct, data, node) ) + { + return 0; + } + } + + ShowEffectStop(lpAct, data); + + adjust = data->adjust; + for ( i = 0; i < data->n_adjusts; i++, adjust++ ) + { + if ( !ShowScene(lpAct, adjust) ) + { + return 0; + } + } + + gUnkCameraStruct2_800B7868.position.vx = data->eye_x; + gUnkCameraStruct2_800B7868.position.vy = data->eye_y; + gUnkCameraStruct2_800B7868.position.vz = data->eye_z; + + gUnkCameraStruct2_800B7868.target.vx = data->center_x; + gUnkCameraStruct2_800B7868.target.vy = data->center_y; + gUnkCameraStruct2_800B7868.target.vz = data->center_z; + + DG_Chanl(0)->clip_distance = data->clip_dist; + + diff.vx = data->center_x - data->eye_x; + diff.vy = data->center_y - data->eye_y; + diff.vz = data->center_z - data->eye_z; + + radius = SquareRoot0(diff.vx * diff.vx + diff.vz * diff.vz); + rot.vx = -ratan2(diff.vy, radius); + rot.vy = ratan2(diff.vx, diff.vz); + rot.vz = 2048; + + diff.vx = data->eye_x; + diff.vy = data->eye_y; + diff.vz = data->eye_z; + + DG_SetPos2(&diff, &rot); + memset(&rot, 0, 8); + + rot.vz = data->roll; + DG_RotatePos(&rot); + ReadRotMatrix(&DG_Chanl(0)->eye); + + DG_TransposeMatrix(&DG_Chanl(0)->eye, &DG_Chanl(0)->eye_inv); + + trans.vx = -DG_Chanl(0)->eye.t[0]; + trans.vy = -DG_Chanl(0)->eye.t[1]; + trans.vz = -DG_Chanl(0)->eye.t[2]; + + ApplyMatrixLV(&DG_Chanl(0)->eye_inv, &trans, (VECTOR *)DG_Chanl(0)->eye_inv.t); + return 1; +} + +/****************************************************************************** + * statics + */ + +static BOOL ShowEffect(LPMGSDEMOACT lpAct, DMO_DATA_0x36 *data, ACTNODE *node) +{ + // TODO: Some funcptr calls are first cast to VoidMakeChara. This is a hack + // to prevent those cases from being merged (GCC "cross jump" optimization). + typedef void (*VoidMakeChara)(); + + int levels[2]; + GV_MSG msg; + SVECTOR svec1, svec2, svec3, svec4; + MATRIX mat1, mat2; + ACTNODE *pIter; + + void *(*funcptr)(); + + DMO_MDL *pDmoModel; + DEMO_MODEL *pModel; + int hzdret; + int i; + + svec1.vx = data->field_E_vec2.vx; + svec1.vy = data->field_E_vec2.vy; + svec1.vz = data->field_E_vec2.vz; + svec2.vx = data->field_8_vec1.vx; + svec2.vy = data->field_8_vec1.vy; + svec2.vz = data->field_8_vec1.vz; + + DG_SetPos2(&svec1, &DG_ZeroVector); + ReadRotMatrix(&mat1); + DG_SetPos2(&svec1, &svec2); + ReadRotMatrix(&mat2); + + switch (data->field_4_type) + { + case EFFECT_EXPLOSION: + funcptr = GM_GetCharaID(DEMOID_EXPLOSION); + if (funcptr != NULL) + { + funcptr(&mat1, &blast_data_8009F4B8[3]); + } + break; + + case EFFECT_SMOKE: + funcptr = GM_GetCharaID(DEMOID_SMOKE); + if (funcptr != NULL) + { + funcptr(&svec1); + } + break; + + case EFFECT_FADEIN: + case EFFECT_FADEOUT: + KillEffect(lpAct, EFFECT_FADEOUT); + + switch (data->field_4_type) + { + case EFFECT_FADEIN: + funcptr = GM_GetCharaID(DEMOID_FADEIN); + if (funcptr != NULL) + { + if (data->data.variant_0x3.field_18 == 0) + { + funcptr(1, data->data.variant_0x3.field_14); + } + else + { + funcptr(3, data->data.variant_0x3.field_14); + } + } + break; + + case EFFECT_FADEOUT: + funcptr = GM_GetCharaID(DEMOID_FADEOUT); + if (funcptr != NULL) + { + if (data->data.variant_0x4.field_18 == 0) + { + node->actor1 = funcptr(0, data->data.variant_0x4.field_14); + } + else + { + node->actor1 = funcptr(2, data->data.variant_0x4.field_14); + } + } + break; + } + break; + + case EFFECT_TEXT: + funcptr = GM_GetCharaID(DEMOID_TEXT); + if (funcptr != NULL) + { + funcptr(data->data.variant_0x5.field_18, data->data.variant_0x5.field_1A, 0, data->data.variant_0x5.field_14, + data->data.variant_0x5.field_1C); + } + break; + + case EFFECT_SURPRISEDMARK: + funcptr = GM_GetCharaID(DEMOID_SURPRISEDMARK); + if (funcptr != NULL) + { + if (data->data.variant_0x6.field_14 == 0) + { + funcptr(&mat1, 2); + } + else + { + funcptr(&mat1, 0); + } + } + break; + + case EFFECT_QUESTIONMARK: + funcptr = GM_GetCharaID(DEMOID_QUESTIONMARK); + if (funcptr != NULL) + { + if (data->data.variant_0x7.field_14 == 0) + { + funcptr(&mat1, 3); + } + else + { + funcptr(&mat1, 1); + } + } + break; + + case EFFECT_SLEEPMARK: + funcptr = GM_GetCharaID(DEMOID_SLEEPMARK); + if (funcptr != NULL) + { + funcptr(&svec1); + } + break; + + case EFFECT_BLOOD: + if (data->data.variant_0x9.field_14 != 4) + { + funcptr = GM_GetCharaID(DEMOID_BLOOD); + if (funcptr != NULL) + { + funcptr(&mat2, data->data.variant_0x9.field_14); + } + } + else + { + funcptr = GM_GetCharaID(DEMOID_BLOOD2); + if (funcptr != NULL) + { + funcptr(&mat2, 0xFF); + } + } + break; + case EFFECT_BULLET: + funcptr = GM_GetCharaID(DEMOID_BULLET); + if (funcptr != NULL) + { + funcptr(data->data.variant_0xA.field_14, &mat2, data->data.variant_0xA.field_18, + data->data.variant_0xA.field_1A, data->data.variant_0xA.field_1C, data->data.variant_0xA.field_1E, + data->data.variant_0xA.field_20, data->data.variant_0xA.field_22, data->data.variant_0xA.field_24); + } + break; + case EFFECT_MISSILESMOKE: + funcptr = GM_GetCharaID(DEMOID_MISSILESMOKE); + if (funcptr != NULL) + { + funcptr(&mat2); + } + break; + + case EFFECT_BLOODCIRCLE: + funcptr = GM_GetCharaID(DEMOID_BLOODCIRCLE); + if (funcptr != NULL) + { + if (data->data.variant_0xC.field_14 >= 4 && data->data.variant_0xC.field_14 <= 7) + { + funcptr(&svec1, data->data.variant_0xC.field_14, data->data.variant_0xC.field_18, + data->data.variant_0xC.field_1C); + } + else + { + node->actor1 = funcptr(&svec1, data->data.variant_0xC.field_14, data->data.variant_0xC.field_18, + data->data.variant_0xC.field_1C); + } + } + break; + + case EFFECT_BREATH: + funcptr = GM_GetCharaID(DEMOID_BREATH); + if (funcptr != NULL) + { + funcptr(&mat2); + } + break; + + case EFFECT_SHADOW: + pModel = lpAct->models; + pDmoModel = lpAct->header->models; + + for (i = 0; i < lpAct->header->n_models; i++, pDmoModel++, pModel++) + { + if (pDmoModel->type == data->data.variant_0xE.field_14) + { + break; + } + } + + if (i >= lpAct->header->n_models) + { + break; + } + + for (pIter = lpAct->chain.next; pIter != &lpAct->chain; pIter = pIter->next) + { + if (pIter->chara.field_4_type == 0xE && pIter->chara.field_14_type == data->data.variant_0xE.field_14) + { + if (pIter->actor1) + { + GV_DestroyOtherActor(pIter->actor1); + pIter->actor1 = NULL; + } + } + } + + if (data->data.variant_0xE.field_20 != 1) + { + funcptr = GM_GetCharaID(DEMOID_SHADOW); + if (funcptr != NULL) + { + node->actor1 = + funcptr(&pModel->object, data->data.variant_0xE.field_18, &node->field_48, &node->field_4C, + data->data.variant_0xE.field_22 & 0xFF, data->data.variant_0xE.field_24 & 0xFF, + data->data.variant_0xE.field_26 & 0xFF); + } + } + break; + + case EFFECT_FOOTPRINTS: + svec1.vy += 0x64; + hzdret = HZD_LevelHazardCheck(lpAct->control.map->hzd, &svec1, HZD_CHK_F_FLOOR); + + do {} while (0); + HZD_GetLevelHeight(levels); + + if (hzdret & 1) + { + svec1.vy = levels[0]; + } + + else if (hzdret & 2) + { + svec1.vy = levels[1]; + } + else + { + svec1.vy = data->field_E_vec2.vy; + } + + funcptr = GM_GetCharaID(DEMOID_FOOTPRINTS); + if (funcptr != NULL) + { + node->actor1 = funcptr(&mat2, data->data.variant_0xF.field_14, svec1.vy); + } + break; + + case EFFECT_MESSAGE: + memset(&msg, 0, sizeof(msg)); + msg.address = data->data.variant_0x10.address; + msg.message[0] = data->data.variant_0x10.message[0]; + msg.message[1] = data->data.variant_0x10.message[1]; + msg.message[2] = data->data.variant_0x10.message[2]; + msg.message[3] = data->data.variant_0x10.message[3]; + msg.message[4] = data->data.variant_0x10.message[4]; + msg.message[5] = data->data.variant_0x10.message[5]; + msg.message[6] = data->data.variant_0x10.message[6]; + msg.message_len = 7; + GV_SendMessage(&msg); + break; + + case EFFECT_NINJAEYE: + funcptr = GM_GetCharaID(DEMOID_NINJAEYE); + if (funcptr != NULL) + { + ((VoidMakeChara)funcptr)(&mat2); + } + break; + + case EFFECT_BUBBLE: + if (data->data.variant_0x12.field_14 == 0) + { + funcptr = GM_GetCharaID(DEMOID_BUBBLE); + if (funcptr != NULL) + { + funcptr(&svec1, data->data.variant_0x12.field_20, data->data.variant_0x12.field_24, &lpAct->field_26C, + data->data.variant_0x12.field_26, data->data.variant_0x12.field_28, + data->data.variant_0x12.field_2A); + } + } + else if (data->data.variant_0x12.field_14 == 1) + { + svec3.vx = data->data.variant_0x12.field_16; + svec3.vy = data->data.variant_0x12.field_18; + svec3.vz = data->data.variant_0x12.field_1A; + + funcptr = GM_GetCharaID(DEMOID_BUBBLE2); + if (funcptr != NULL) + { + funcptr(&svec1, &svec3, data->data.variant_0x12.field_1C, &lpAct->field_26C, + data->data.variant_0x12.field_26, data->data.variant_0x12.field_28, + data->data.variant_0x12.field_2A); + } + } + else + { + lpAct->field_26C = 1; + } + break; + + case EFFECT_SCOPE: + case EFFECT_DARKVISIBLEGOGGLE: + case EFFECT_IRRAYSGOGGLE: + case EFFECT_GUSMASK: + pIter = lpAct->chain.next; + while (pIter != &lpAct->chain) + { + if (pIter->chara.field_4_type == EFFECT_SCOPE || + pIter->chara.field_4_type == EFFECT_DARKVISIBLEGOGGLE || + pIter->chara.field_4_type == EFFECT_IRRAYSGOGGLE || + pIter->chara.field_4_type == EFFECT_GUSMASK) + { + if (pIter->actor1 != NULL) + { + GV_DestroyOtherActor(pIter->actor1); + if (pIter->actor2 != NULL) + { + GV_DestroyOtherActor(pIter->actor2); + } + pIter->actor1 = NULL; + pIter->actor2 = NULL; + } + } + pIter = pIter->next; + } + + switch (data->field_4_type) + { + case EFFECT_SCOPE: + if (data->data.variant_0x13.field_14 != 1) + { + funcptr = GM_GetCharaID(DEMOID_SCOPE); + if (funcptr != NULL) + { + node->actor1 = funcptr(&lpAct->control, &lpAct->object, 0); + } + } + break; + + case EFFECT_DARKVISIBLEGOGGLE: + if (data->data.variant_0x14.field_14 != 1) + { + funcptr = GM_GetCharaID(DEMOID_DARKVISIBLEGOGGLE); + if (funcptr != NULL) + { + node->actor1 = funcptr(&lpAct->control, &lpAct->object, 0); + if (data->data.variant_0x14.field_16 != 0) + { + funcptr = GM_GetCharaID(DEMOID_DARKVISIBLEGOGGLE2); + if (funcptr != NULL) + { + node->actor2 = funcptr(5); + } + } + } + } + break; + + case EFFECT_IRRAYSGOGGLE: + if (data->data.variant_0x15.field_14 != 1) + { + funcptr = GM_GetCharaID(DEMOID_IRRAYSGOGGLE); + if (funcptr != NULL) + { + node->actor1 = funcptr(&lpAct->control, &lpAct->object, 0); + if (data->data.variant_0x15.field_16 != 0) + { + funcptr = GM_GetCharaID(DEMOID_IRRAYSGOGGLE2); + if (funcptr != NULL) + { + node->actor2 = funcptr(6); + } + } + } + } + break; + + case EFFECT_GUSMASK: + if (data->data.variant_0x1E.field_14 != 1) + { + funcptr = GM_GetCharaID(DEMOID_GUSMASK); + if (funcptr != NULL) + { + node->actor1 = funcptr(); + } + } + break; + } + break; + + case EFFECT_GUNSMOKE: + funcptr = GM_GetCharaID(DEMOID_GUNSMOKE); + if (funcptr != NULL) + { + funcptr(&svec1); + } + break; + + case EFFECT_OPTICSCAMOUFLAGE: + pModel = lpAct->models; + pDmoModel = lpAct->header->models; + + for (i = 0; i < lpAct->header->n_models; i++, pDmoModel++, pModel++) + { + if (pDmoModel->type == data->data.variant_0x17.field_18) + { + break; + } + } + + if (i >= lpAct->header->n_models) + { + break; + } + + pIter = lpAct->chain.next; + while (pIter != &lpAct->chain) + { + if ((pIter->chara.field_4_type == 0x17) && (pIter->chara.field_18_xy == data->data.variant_0x17.field_18)) + { + if (pIter->actor1 != NULL) + { + GV_DestroyOtherActor(pIter->actor1); + pIter->actor1 = NULL; + } + } + pIter = pIter->next; + } + + if (data->data.variant_0x17.field_14 != 1 && data->field_4_type == 0x17) + { + if (data->data.variant_0x17.field_1C == 0) + { + funcptr = GM_GetCharaID(DEMOID_OPTICSCAMOUFLAGE); + if (funcptr != NULL) + { + node->actor1 = funcptr(&lpAct->control, &pModel->object, 0); + } + } + else + { + funcptr = GM_GetCharaID(DEMOID_OPTICSCAMOUFLAGE2); + if (funcptr != NULL) + { + node->actor1 = funcptr(&lpAct->control, &pModel->object, 0); + } + } + } + break; + + case EFFECT_ENVIRONMENTMAPPING: + pModel = lpAct->models; + pDmoModel = lpAct->header->models; + + for (i = 0; i < lpAct->header->n_models; i++, pDmoModel++, pModel++) + { + if (pDmoModel->type == data->data.variant_0x18.field_18) + { + break; + } + } + + if (i >= lpAct->header->n_models) + { + break; + } + + pIter = lpAct->chain.next; + while (pIter != &lpAct->chain) + { + if ((pIter->chara.field_4_type == 0x18) && (pIter->chara.field_18_xy == data->data.variant_0x18.field_18)) + { + if (pIter->actor1 != NULL) + { + GV_DestroyOtherActor(pIter->actor1); + pIter->actor1 = NULL; + } + } + pIter = pIter->next; + } + if (data->data.variant_0x18.field_14 == 0) + { + funcptr = GM_GetCharaID(DEMOID_ENVIRONMENTMAPPING); + if (funcptr != NULL) + { + node->actor1 = funcptr(&pModel->object, data->data.variant_0x18.field_1C); + } + } + break; + + case EFFECT_PLASMA: + pModel = lpAct->models; + pDmoModel = lpAct->header->models; + + for (i = 0; i < lpAct->header->n_models; i++, pDmoModel++, pModel++) + { + if (pDmoModel->type == data->data.variant_0x19.field_14) + { + break; + } + } + + if (i >= lpAct->header->n_models) + { + break; + } + + funcptr = GM_GetCharaID(DEMOID_PLASMA); + if (funcptr != NULL) + { + funcptr(&pModel->object, data->data.variant_0x19.field_18, data->data.variant_0x19.field_1A, + data->data.variant_0x19.field_1C, data->data.variant_0x19.field_20); + } + break; + + case EFFECT_WINDCIRCLE: + funcptr = GM_GetCharaID(DEMOID_WINDCIRCLE); + if (funcptr != NULL) + { + funcptr(&mat2, data->data.variant_0x1A.field_14); + } + break; + + case EFFECT_SEPIA: + KillEffect(lpAct, EFFECT_SEPIA); + if (data->data.variant_0x1B.field_14 == 0) + { + funcptr = GM_GetCharaID(DEMOID_SEPIA); + if (funcptr != NULL) + { + node->actor1 = funcptr(data->data.variant_0x1B.field_16, data->data.variant_0x1B.field_18, + data->data.variant_0x1B.field_1A); + } + } + break; + + case EFFECT_METALGEARLASER: + pIter = lpAct->chain.next; + while (pIter != &lpAct->chain && (pIter->chara.field_4_type != 0x1C || pIter->actor1 == NULL)) + { + pIter = pIter->next; + } + svec1.vx = data->data.variant_0x1C.field_14; + svec1.vy = data->data.variant_0x1C.field_16; + svec1.vz = data->data.variant_0x1C.field_18; + svec2.vx = data->data.variant_0x1C.field_1A; + svec2.vy = data->data.variant_0x1C.field_1C; + svec2.vz = data->data.variant_0x1C.field_1E; + if (pIter == &lpAct->chain) + { + funcptr = GM_GetCharaID(DEMOID_METALGEARLASER); + if (funcptr != NULL) + { + node->actor1 = funcptr(&node->field_58); + pIter = node; + } + else + { + break; + } + } + DG_SetPos2(&svec1, &svec2); + ReadRotMatrix(&pIter->field_58); + break; + + case EFFECT_UNSHAPEVIEW: + KillEffect(lpAct, EFFECT_UNSHAPEVIEW); + if (data->data.variant_0x1D.field_14 == 0) + { + funcptr = GM_GetCharaID(DEMOID_UNSHAPEVIEW); + if (funcptr != NULL) + { + node->actor1 = funcptr(data->data.variant_0x1D.field_18, data->data.variant_0x1D.field_1C); + } + } + break; + + case EFFECT_STOP: + pIter = lpAct->chain.next; + lpAct->field_268 = 1; + lpAct->field_26C = 1; + while (pIter != &lpAct->chain) + { + if (pIter->actor1 != NULL) + { + GV_DestroyOtherActor(pIter->actor1); + if (pIter->actor2 != NULL) + { + GV_DestroyOtherActor(pIter->actor2); + } + pIter->actor1 = NULL; + pIter->actor2 = NULL; + } + pIter = pIter->next; + } + break; + + case EFFECT_URINATIONCIRCLE: + if (data->data.variant_0x20.field_20 == 1) + { + pIter = lpAct->chain.next; + while (pIter != &lpAct->chain) + { + if (pIter->chara.field_4_type == EFFECT_URINATIONCIRCLE) + { + if (pIter->actor1 != NULL) + { + GV_DestroyOtherActor(pIter->actor1); + pIter->actor1 = NULL; + } + } + pIter = pIter->next; + } + } + else + { + funcptr = GM_GetCharaID(DEMOID_URINATIONCIRCLE); + if (funcptr != NULL) + { + if (data->data.variant_0x20.field_14 >= 2 && data->data.variant_0x20.field_14 <= 5) + { + funcptr(&svec1, data->data.variant_0x20.field_14 + 2, data->data.variant_0x20.field_18, + data->data.variant_0x20.field_1C); + } + else + { + node->actor1 = funcptr(&svec1, data->data.variant_0x20.field_14 + 2, + data->data.variant_0x20.field_18, data->data.variant_0x20.field_1C); + } + } + } + break; + + case EFFECT_PLASMA2: + svec3.vx = data->data.variant_0x21.field_14; + svec3.vy = data->data.variant_0x21.field_16; + svec3.vz = data->data.variant_0x21.field_18; + funcptr = GM_GetCharaID(DEMOID_PLASMA2); + if (funcptr != NULL) + { + funcptr(&svec1, &svec3, data->data.variant_0x21.field_1C); + } + break; + + case EFFECT_BLUR: + KillEffect(lpAct, EFFECT_BLUR); + if (data->data.variant_0x22.field_14 == 0) + { + funcptr = GM_GetCharaID(DEMOID_BLUR); + if (funcptr != NULL) + { + node->actor1 = funcptr(data->data.variant_0x22.field_16, data->data.variant_0x22.field_18, + data->data.variant_0x22.field_1A); + } + } + break; + + case EFFECT_STEAMSMOKE: + svec3.vx = data->data.variant_0x23.field_14; + svec3.vy = data->data.variant_0x23.field_16; + svec3.vz = data->data.variant_0x23.field_18; + if (data->data.variant_0x23.field_1A != 4) + { + funcptr = GM_GetCharaID(DEMOID_STEAMSMOKE); + if (funcptr != NULL) + { + ((VoidMakeChara)funcptr)(&svec1, &svec3, data->data.variant_0x23.field_1A); + } + } + else + { + funcptr = GM_GetCharaID(DEMOID_STEAMSMOKE2); + if (funcptr != NULL) + { + ((VoidMakeChara)funcptr)(&svec1, &svec3, data->data.variant_0x23.field_1C & 0xFF, + data->data.variant_0x23.field_1E & 0xFF, + data->data.variant_0x23.field_20 & 0xFF); + } + } + break; + + case EFFECT_MONOTONE: + KillEffect(lpAct, EFFECT_MONOTONE); + + if (data->data.variant_0x24.field_14 == 0) + { + funcptr = GM_GetCharaID(DEMOID_MONOTONE); + if (funcptr != NULL) + { + node->actor1 = funcptr(); + } + } + break; + + case EFFECT_RIFLESIGHT: + KillEffect(lpAct, EFFECT_RIFLESIGHT); + + if (data->data.variant_0x25.field_14 == 0) + { + funcptr = GM_GetCharaID(DEMOID_RIFLESIGHT); + if (funcptr != NULL) + { + node->actor1 = funcptr(); + } + } + break; + + case EFFECT_MI24HIND: + pModel = lpAct->models; + pDmoModel = lpAct->header->models; + + for (i = 0; i < lpAct->header->n_models; i++, pDmoModel++, pModel++) + { + if (pDmoModel->filename == GV_StrCode("hind")) + { + break; + } + if (pDmoModel->filename == GV_StrCode("hinddemo")) + { + break; + } + } + + if (i >= lpAct->header->n_models) + { + break; + } + + pModel->extra->object[0][0].objs = data->data.variant_0x26.field_14; + pModel->extra->object[0][0].flag = data->data.variant_0x26.field_18; + break; + + case EFFECT_BLACKSMOKE: + funcptr = GM_GetCharaID(DEMOID_BLACKSMOKE); + if (funcptr != NULL) + { + funcptr(&svec1); + } + break; + + case EFFECT_URINATIONCIRCLE2: + funcptr = GM_GetCharaID(DEMOID_URINATIONCIRCLE2); + if (funcptr != NULL) + { + if (data->data.variant_0x28.field_14 >= 0 && data->data.variant_0x28.field_14 <= 3) + { + funcptr(&svec1, data->data.variant_0x28.field_14 + 4, data->data.variant_0x28.field_18, + data->data.variant_0x28.field_1C); + } + else + { + node->actor1 = funcptr(&svec1, data->data.variant_0x28.field_14 + 4, + data->data.variant_0x28.field_18, data->data.variant_0x28.field_1C); + } + } + break; + + case EFFECT_SIGHT: + pIter = lpAct->chain.next; + + if (pIter != &lpAct->chain) + { + while ((pIter->chara.field_4_type != EFFECT_SIGHT) || !pIter->actor1) + { + pIter = pIter->next; + + if (pIter == &lpAct->chain) + { + break; + } + } + } + + if (pIter == &lpAct->chain) + { + if (data->data.variant_0x29.field_14 != 1) + { + funcptr = GM_GetCharaID(DEMOID_SIGHT); + if (funcptr != NULL) + { + node->actor1 = funcptr(data->data.variant_0x29.field_1C, &data->data.variant_0x29.field_16); + } + } + } + else if (data->data.variant_0x29.field_14 != 0) + { + if (pIter->chara.field_1C_zpad != data->data.variant_0x29.field_1C) + { + GV_DestroyOtherActor(pIter->actor1); + pIter->actor1 = NULL; + funcptr = GM_GetCharaID(DEMOID_SIGHT); + if (funcptr != NULL) + { + node->actor1 = funcptr(data->data.variant_0x29.field_1C, &data->data.variant_0x29.field_16); + } + } + else + { + // TODO: I can't make sense of it (field_28 is used as an int in other places...) + struct copier + { + short a, b; + }; + *(struct copier *)((char *)&pIter->chara.field_14_type + 2) = *(struct copier *)&data->data.variant_0x29.field_16; + } + } + else + { + GV_DestroyOtherActor(pIter->actor1); + pIter->actor1 = NULL; + } + break; + + case EFFECT_NINJASWORD: + pModel = lpAct->models; + pDmoModel = lpAct->header->models; + + for (i = 0; i < lpAct->header->n_models; i++, pDmoModel++, pModel++) + { + if (pDmoModel->type == data->data.variant_0x2A.field_14) + { + break; + } + } + + if (i >= lpAct->header->n_models) + { + break; + } + + pIter = lpAct->chain.next; + while (pIter != &lpAct->chain) + { + if ((pIter->chara.field_4_type == EFFECT_NINJASWORD) && + (pIter->chara.field_14_type == data->data.variant_0x2A.field_14)) + { + if (pIter->actor1 != NULL) + { + GV_DestroyOtherActor(pIter->actor1); + pIter->actor1 = NULL; + } + } + pIter = pIter->next; + } + if (data->data.variant_0x2A.field_18 != 1) + { + funcptr = GM_GetCharaID(DEMOID_NINJASWORD); + if (funcptr != NULL) + { + node->actor1 = funcptr(&pModel->object.objs->objs[4]); + } + } + break; + + case EFFECT_SUBMARINEROOM: + pIter = lpAct->chain.next; + + while (pIter != &lpAct->chain) + { + if (pIter->chara.field_4_type == EFFECT_SUBMARINEROOM) + { + if (pIter->actor1 != NULL) + { + GV_DestroyOtherActor(pIter->actor1); + pIter->actor1 = NULL; + } + } + pIter = pIter->next; + } + + if (data->data.variant_0x2B.field_14 == 0) + { + funcptr = GM_GetCharaID(DEMOID_SUBMARINEROOM); + if (funcptr != NULL) + { + node->actor1 = funcptr(); + } + } + break; + + case EFFECT_BLACKSMOKE2: + svec3.vx = data->data.variant_0x2C.field_14; + svec3.vy = data->data.variant_0x2C.field_16; + svec3.vz = data->data.variant_0x2C.field_18; + funcptr = GM_GetCharaID(DEMOID_BLACKSMOKE2); + if (funcptr != NULL) + { + funcptr(&svec1, &svec3, data->data.variant_0x2C.field_1A, data->data.variant_0x2C.field_1C); + } + break; + + case EFFECT_BLASTLINE: + svec3.vx = data->data.variant_0x2D.field_14; + svec3.vy = data->data.variant_0x2D.field_16; + svec3.vz = data->data.variant_0x2D.field_18; + funcptr = GM_GetCharaID(DEMOID_BLASTLINE); + if (funcptr != NULL) + { + funcptr(data->data.variant_0x2D.field_22, data->data.variant_0x2D.field_1C, data->data.variant_0x2D.field_20, + &svec1, &svec3); + } + break; + + case EFFECT_SMOKELINE: + svec3.vx = data->data.variant_0x2E.field_14; + svec3.vy = data->data.variant_0x2E.field_16; + svec3.vz = data->data.variant_0x2E.field_18; + svec4.vx = data->data.variant_0x2E.field_1A; + svec4.vy = data->data.variant_0x2E.field_1C; + svec4.vz = data->data.variant_0x2E.field_1E; + funcptr = GM_GetCharaID(DEMOID_SMOKELINE); + if (funcptr != NULL) + { + funcptr(data->data.variant_0x2E.field_26, data->data.variant_0x2E.field_20, data->data.variant_0x2E.field_24, + &svec1, &svec3, &svec4, data->data.variant_0x2E.field_28 & 0xFF, + data->data.variant_0x2E.field_2A & 0xFF, data->data.variant_0x2E.field_2C & 0xFF); + } + break; + + case EFFECT_SHELLSMOKE: + funcptr = GM_GetCharaID(DEMOID_SHELLSMOKE); + if (funcptr != NULL) + { + funcptr(data->data.variant_0x2F.field_14, &svec1, &svec2); + } + break; + + case EFFECT_CATERPILLERSMOKE: + funcptr = GM_GetCharaID(DEMOID_CATERPILLERSMOKE); + if (funcptr != NULL) + { + funcptr(&svec2, &svec1); + } + break; + + case EFFECT_CROWEYE: + funcptr = GM_GetCharaID(DEMOID_CROWEYE); + if (funcptr != NULL) + { + funcptr(&svec1); + } + break; + + case EFFECT_OCEROTTEBULLETFIRE: + funcptr = GM_GetCharaID(DEMOID_OCEROTTEBULLETFIRE); + if (funcptr != NULL) + { + funcptr(&mat2); + } + break; + + case EFFECT_OCEROTTEBULLETSMOKE: + funcptr = GM_GetCharaID(DEMOID_OCEROTTEBULLETSMOKE); + if (funcptr != NULL) + { + funcptr(&svec1); + } + break; + + case EFFECT_ENVIRONMENTLIGHT: + svec3.vx = data->data.variant_0x34.field_24; + svec3.vy = data->data.variant_0x34.field_26; + svec3.vz = data->data.variant_0x34.field_28; + svec4.vx = data->data.variant_0x34.field_2A; + svec4.vy = data->data.variant_0x34.field_2C; + svec4.vz = data->data.variant_0x34.field_2E; + if (data->data.variant_0x34.field_30 == 0) + { + funcptr = GM_GetCharaID(DEMOID_ENVIRONMENTLIGHT); + if (funcptr != NULL) + { + funcptr(data->data.variant_0x34.field_14, data->data.variant_0x34.field_18, &svec3, &svec4, + data->data.variant_0x34.field_1C, data->data.variant_0x34.field_20); + } + } + else + { + memset(&msg, 0, sizeof(msg)); + msg.address = data->data.variant_0x34.field_14; + msg.message[0] = HASH_KILL; + msg.message_len = 1; + + GV_SendMessage(&msg); + } + break; + + case EFFECT_CRASHSMOKE: + funcptr = GM_GetCharaID(DEMOID_CRASHSMOKE); + if (funcptr != NULL) + { + funcptr(&svec1, &svec2, data->data.variant_0x35.field_18, data->data.variant_0x35.field_1C, + data->data.variant_0x35.field_1E, data->data.variant_0x35.field_20, + data->data.variant_0x35.field_22 & 0xFF, data->data.variant_0x35.field_24 & 0xFF, + data->data.variant_0x35.field_26 & 0xFF); + } + break; + + case EFFECT_CELOFAN: + funcptr = GM_GetCharaID(DEMOID_CELOFAN); + if (funcptr != NULL) + { + funcptr(data->data.variant_0x37.field_14, data->data.variant_0x37.field_18, data->data.variant_0x37.field_1C, + data->data.variant_0x37.field_1E, data->data.variant_0x37.field_20, data->data.variant_0x37.field_22, + &data->data.variant_0x37.field_24, &data->data.variant_0x37.field_28, data->data.variant_0x37.field_2C, + data->data.variant_0x37.field_2E, data->data.variant_0x37.field_30, data->data.variant_0x37.field_32); + } + break; + + case EFFECT_INVERSLIGHT: + funcptr = GM_GetCharaID(DEMOID_INVERSLIGHT); + if (funcptr != NULL) + { + ((VoidMakeChara)funcptr)(&svec1); + } + break; + + case EFFECT_SPACK: + switch (data->data.variant_0x39.field_14) + { + case 0: + funcptr = GM_GetCharaID(DEMOID_SPACK); + if (funcptr != NULL) + { + ((VoidMakeChara)funcptr)(&mat2); + } + break; + + case 1: + funcptr = GM_GetCharaID(DEMOID_SPACK2); + if (funcptr != 0) + { + funcptr(&mat2, 0); + } + break; + + case 2: + funcptr = GM_GetCharaID(DEMOID_SPACK2); + if (funcptr != 0) + { + funcptr(&mat2, 1); + } + break; + } + break; + + case EFFECT_STEAMEXPLOSION: + svec3.vx = data->data.variant_0x3A.field_14; + svec3.vy = data->data.variant_0x3A.field_16; + svec3.vz = data->data.variant_0x3A.field_18; + funcptr = GM_GetCharaID(DEMOID_STEAMEXPLOSION); + if (funcptr != NULL) + { + funcptr(&svec1, &svec3); + } + break; + case EFFECT_INVERSLIGHT2: + funcptr = GM_GetCharaID(DEMOID_INVERSLIGHT2); + if (funcptr != NULL) + { + funcptr(&svec1, data->data.variant_0x3B.field_14, data->data.variant_0x3B.field_18, + data->data.variant_0x3B.field_1C, data->data.variant_0x3B.field_20, data->data.variant_0x3B.field_22, + data->data.variant_0x3B.field_24, data->data.variant_0x3B.field_26, data->data.variant_0x3B.field_28); + } + break; + case EFFECT_CELOFAN2: + funcptr = GM_GetCharaID(DEMOID_CELOFAN2); + if (funcptr != NULL) + { + funcptr(data->data.variant_0x3C.field_14, data->data.variant_0x3C.field_18, data->data.variant_0x3C.field_1C, + data->data.variant_0x3C.field_1E, data->data.variant_0x3C.field_20, data->data.variant_0x3C.field_22, + data->data.variant_0x3C.field_24, &data->data.variant_0x3C.field_26, &data->data.variant_0x3C.field_2A, + data->data.variant_0x3C.field_2E, data->data.variant_0x3C.field_30, data->data.variant_0x3C.field_32, + data->data.variant_0x3C.field_34); + } + break; + + case EFFECT_NINJALASER: + pModel = lpAct->models; + pDmoModel = lpAct->header->models; + + for (i = 0; i < lpAct->header->n_models; i++, pDmoModel++, pModel++) + { + if (pDmoModel->type == data->data.variant_0x3D.field_18) + { + break; + } + } + + if (i >= lpAct->header->n_models) + { + break; + } + + funcptr = GM_GetCharaID(DEMOID_NINJALASER); + if (funcptr != NULL) + { + funcptr(&pModel->object.objs[6].world.m[1][1], data->data.variant_0x3D.field_14, + data->data.variant_0x3D.field_16); + } + break; + + case EFFECT_GUSEFFECT: + pIter = lpAct->chain.next; + + if (pIter != &lpAct->chain) + { + while ((pIter->chara.field_4_type != EFFECT_GUSEFFECT) || !pIter->actor1) + { + pIter = pIter->next; + + if (pIter == &lpAct->chain) + { + break; + } + } + } + + if (data->data.variant_0x3E.field_14 == 0) + { + if (pIter != &lpAct->chain) + { + GV_DestroyOtherActor(pIter->actor1); + pIter->actor1 = NULL; + } + + funcptr = GM_GetCharaID(DEMOID_GUSEFFECT); + if (funcptr != NULL) + { + svec3.vx = data->data.variant_0x3E.field_16; + svec3.vy = data->data.variant_0x3E.field_18; + svec3.vz = data->data.variant_0x3E.field_1A; + node->actor1 = + funcptr(&svec3, data->data.variant_0x3E.field_1E, data->data.variant_0x3E.field_1C + 1); + } + } + else if (data->data.variant_0x3E.field_14 == 1) + { + if (pIter != &lpAct->chain) + { + GV_DestroyOtherActor(pIter->actor1); + pIter->actor1 = NULL; + } + } + else if (data->data.variant_0x3E.field_14 == 2) + { + if (pIter != &lpAct->chain) + { + memset(&msg, 0, sizeof(msg)); + msg.address = 0x3B8E; + if (data->data.variant_0x3E.field_20 == 0) + { + msg.message[0] = HASH_ON2; + } + else + { + msg.message[0] = HASH_OFF2; + } + msg.message[1] = data->data.variant_0x3E.field_22; + msg.message_len = 2; + + GV_SendMessage(&msg); + } + } + break; + + case EFFECT_SUBMARINEWATER: + pModel = lpAct->models; + pDmoModel = lpAct->header->models; + + for (i = 0; i < lpAct->header->n_models; i++, pDmoModel++, pModel++) + { + if (pDmoModel->type == data->data.variant_0x3F.field_18) + { + break; + } + } + + if (i >= lpAct->header->n_models) + { + break; + } + + pIter = lpAct->chain.next; + while (pIter != &lpAct->chain) + { + if ((pIter->chara.field_4_type == EFFECT_SUBMARINEWATER) && + (pIter->chara.field_18_xy == data->data.variant_0x3F.field_18)) + { + if (pIter->actor1 != NULL) + { + GV_DestroyOtherActor(pIter->actor1); + pIter->actor1 = NULL; + } + } + pIter = pIter->next; + } + + if (data->data.variant_0x3F.field_14 != 1) + { + funcptr = GM_GetCharaID(DEMOID_SUBMARINEWATER); + if (funcptr != NULL) + { + node->actor1 = funcptr(&pModel->object, &pModel->control.rot); + } + } + break; + + case EFFECT_PADVIBRATION: + case EFFECT_PADVIBRATION2: + if (data->data.variant_0x36.field_14 != 0) + { + GM_PadVibration = 1; + } + if (data->data.variant_0x36.field_16 != 0) + { + GM_PadVibration2 = data->data.variant_0x36.field_18; + } + break; + + case EFFECT_GHOST: + KillEffect(lpAct, EFFECT_GHOST); + if (data->data.variant_0x41.field_14 == 0) + { + funcptr = GM_GetCharaID(DEMOID_GHOST); + if (funcptr != NULL) + { + node->actor1 = funcptr(); + } + } + break; + + case EFFECT_LIGHT: + DG_SetTmpLight(&svec1, data->data.variant_0x42.field_14, data->data.variant_0x42.field_16); + break; + + case EFFECT_FAMASLIGHT: + pModel = lpAct->models; + pDmoModel = lpAct->header->models; + + for (i = 0; i < lpAct->header->n_models; i++, pDmoModel++, pModel++) + { + if (pDmoModel->type == data->data.variant_0x43.field_14) + { + break; + } + } + + if (i >= lpAct->header->n_models) + { + break; + } + + pIter = lpAct->chain.next; + + while (pIter != &lpAct->chain) + { + if ((pIter->chara.field_4_type == EFFECT_FAMASLIGHT) && + (pIter->chara.field_14_type == data->data.variant_0x43.field_14)) + { + if (pIter->actor1 != NULL) + { + GV_DestroyOtherActor(pIter->actor1); + pIter->actor1 = NULL; + } + } + pIter = pIter->next; + } + + if (data->data.variant_0x43.field_18 != 1) + { + funcptr = GM_GetCharaID(DEMOID_FAMASLIGHT); + if (funcptr != NULL) + { + node->actor1 = funcptr(pModel->object.objs + 1); + } + } + break; + + case EFFECT_HUMANSHADOW: + pModel = lpAct->models; + pDmoModel = lpAct->header->models; + + for (i = 0; i < lpAct->header->n_models; i++, pDmoModel++, pModel++) + { + if (pDmoModel->type == data->data.variant_0x44.field_18) + { + break; + } + } + + if (i >= lpAct->header->n_models) + { + break; + } + + pIter = lpAct->chain.next; + + if (pIter != &lpAct->chain) + { + while ((pIter->chara.field_4_type != EFFECT_HUMANSHADOW) || !pIter->actor1) + { + pIter = pIter->next; + + if (pIter == &lpAct->chain) + { + break; + } + } + } + + if (pIter == &lpAct->chain) + { + if (data->data.variant_0x44.field_14 != 1) + { + funcptr = GM_GetCharaID(DEMOID_HUMANSHADOW); + + if (funcptr != NULL) + { + node->field_50 = data->data.variant_0x44.field_1E; + node->field_52 = data->data.variant_0x44.field_20; + node->field_54 = data->data.variant_0x44.field_22; + + if (data->data.variant_0x44.field_1C == 0) + { + node->field_56 = 0; + } + else + { + node->field_56 = -1; + } + + node->actor1 = + funcptr(pModel, &pModel->object, &node->field_50, pDmoModel->cache_id); + } + } + } + else if (data->data.variant_0x44.field_14 == 1) + { + GV_DestroyOtherActor(pIter->actor1); + pIter->actor1 = NULL; + } + else + { + pIter->field_50 = data->data.variant_0x44.field_1E; + pIter->field_52 = data->data.variant_0x44.field_20; + pIter->field_54 = data->data.variant_0x44.field_22; + } + break; + + case EFFECT_BLOODHAZARD: + funcptr = GM_GetCharaID(DEMOID_BLOODHAZARD); + if (funcptr != NULL) + { + node->actor1 = funcptr(&mat2, data->data.variant_0x45.field_14, data->data.variant_0x45.field_16, + data->data.variant_0x45.field_18, data->data.variant_0x45.field_1A); + } + break; + + case EFFECT_BLOODDRIP: + funcptr = GM_GetCharaID(DEMOID_BLOODDRIP); + if (funcptr != NULL) + { + funcptr(&svec1, data->data.variant_0x46.field_14, data->data.variant_0x46.field_16); + } + break; + + case EFFECT_NINJAGROUND: + funcptr = GM_GetCharaID(DEMOID_NINJAGROUND); + if (funcptr != NULL) + { + funcptr(&mat2, 400, 400, 400, 10); + } + break; + + case EFFECT_BOMBLIGHT: + funcptr = GM_GetCharaID(DEMOID_BOMBLIGHT); + if (funcptr != NULL) + { + ((VoidMakeChara)funcptr)(&svec1); + } + break; + + case EFFECT_MGCROOMDISPLAY: + pIter = lpAct->chain.next; + while (pIter != &lpAct->chain) + { + if (pIter->chara.field_4_type == EFFECT_MGCROOMDISPLAY) + { + if (pIter->actor1 != NULL) + { + GV_DestroyOtherActor(pIter->actor1); + pIter->actor1 = NULL; + } + } + pIter = pIter->next; + } + + if (data->data.variant_0x49.field_14 == 0) + { + funcptr = GM_GetCharaID(DEMOID_MGCROOMDISPLAY); + if (funcptr != NULL) + { + node->actor1 = funcptr(); + } + } + break; + + case EFFECT_TEXT2: + funcptr = GM_GetCharaID(DEMOID_TEXT2); + + if (funcptr != NULL) + { + funcptr(data->data.variant_0x4A.field_18, data->data.variant_0x4A.field_1A, 0, + data->data.variant_0x4A.field_14, data->data.variant_0x4A.field_1C, data->data.variant_0x4A.field_20); + } + break; + } + + return 1; +} + +static void KillEffect(LPMGSDEMOACT lpAct, int type) +{ + ACTNODE *pSubIter; // $s0 + ACTNODE *pCur; // $a0 + ACTNODE *pCur_; // $s1 + GV_ACT *pPrevious; // $a0 + GV_ACT *pNext; // $a0 + + pSubIter = lpAct->chain.next; + pCur = &lpAct->chain; + if ( pSubIter != pCur ) + { + pCur_ = pCur; + do + { + if ( pSubIter->chara.field_4_type == type ) + { + pPrevious = pSubIter->actor1; + if ( pPrevious ) + { + GV_DestroyOtherActor(pPrevious); + pNext = pSubIter->actor2; + if ( pNext ) + { + GV_DestroyOtherActor(pNext); + } + pSubIter->actor1 = 0; + pSubIter->actor2 = 0; + } + } + pSubIter = pSubIter->next; + } + while ( pSubIter != pCur_ ); + } +} + +static BOOL ShowEffectExecute(LPMGSDEMOACT lpAct, DMO_DAT *data, ACTNODE *node) +{ + DMO_ADJ *adjust; + int idx; + HZD_VEC vec2; + SVECTOR vecPos; + if (node->chara.field_4_type == EFFECT_SHADOW) + { + adjust = data->adjust; + { + for (idx = 0; idx < data->n_adjusts; idx++) + { + if (adjust->type == node->chara.field_14_type) + { + break; + } + ++adjust; + } + + if (idx < data->n_adjusts) + { + vecPos.vx = adjust->pos_x; + vecPos.vy = adjust->pos_y; + vecPos.vz = adjust->pos_z; + idx = HZD_LevelHazardCheck(lpAct->control.map->hzd, &vecPos, HZD_CHK_F_FLOOR); + HZD_GetLevelHeight((int *)&vec2); + node->field_48 = adjust->rot_y; + if ((idx & 1) != 0) + { + node->field_4C = vec2.x + 50; + } + else if ((idx & 2) != 0) + { + node->field_4C = vec2.y + 50; + } + else + { + node->field_4C = adjust->pos_y + 50; + } + + } + } + } + return 1; +} + +static BOOL ShowEffectStop(LPMGSDEMOACT lpAct, DMO_DAT *data) +{ + DMO_CHA *pIter; + int i; + ACTNODE *pNext; + ACTNODE *new_var; + ACTNODE *new_var2; + ACTNODE *root; + pIter = data->chara; + for (i = 0; i < data->n_charas; ++pIter) + { + ++i; + if (pIter->field_4_type == EFFECT_METALGEARLASER) + { + return 1; + } + } + + pNext = lpAct->chain.next; + new_var = &lpAct->chain; + if (pNext != new_var) + { + if (pNext->chara.field_4_type != EFFECT_METALGEARLASER) + { + new_var2 = new_var; + root = &lpAct->chain; + while (1) + { + pNext = pNext->next; + if (pNext == new_var2) + { + break; + } + if (pNext->chara.field_4_type == EFFECT_METALGEARLASER) + { + root = &lpAct->chain; + break; + } + } + + } + } + if (pNext != (&lpAct->chain)) + { + GV_DestroyOtherActor(pNext->actor1); + root = &lpAct->chain; + pNext->actor1 = 0; + DeleteChain(root, pNext); + GV_Free(pNext); + } + return 1; +} + +static BOOL ShowScene(LPMGSDEMOACT lpAct, DMO_ADJ *adjust) +{ + DMO_MDL *model_file; + DEMO_MODEL *model; + int i; + short *rots; + + OFFSET_TO_PTR(adjust, &adjust->rots); + + model_file = lpAct->header->models; + model = lpAct->models; + for (i = 0; i < lpAct->header->n_models; i++, model_file++, model++) + { + if (model_file->type == adjust->type) + { + break; + } + } + + if (i >= lpAct->header->n_models) + { + return 0; + } + + if (!adjust->visible) + { + DG_InvisibleObjs(model->object.objs); + + if (model_file->filename == GV_StrCode("m1e1") || model_file->filename == GV_StrCode("m1e1demo")) + { + DG_InvisibleObjs(model->extra->object[0][0].objs); + DG_InvisibleObjs(model->extra->object[0][1].objs); + DG_InvisibleObjs(model->extra->object[0][2].objs); + DG_InvisibleObjs(model->extra->object[1][0].objs); + DG_InvisibleObjs(model->extra->object[1][1].objs); + DG_InvisibleObjs(model->extra->object[1][2].objs); + } + } + else + { + DG_VisibleObjs(model->object.objs); + + if (model_file->filename == GV_StrCode("m1e1") || model_file->filename == GV_StrCode("m1e1demo")) + { + DG_VisibleObjs(model->extra->object[0][model->extra->field_558_idx[0]].objs); + DG_VisibleObjs(model->extra->object[1][model->extra->field_558_idx[1]].objs); + } + + model->control.mov.vx = adjust->pos_x; + model->control.mov.vy = adjust->pos_y; + model->control.mov.vz = adjust->pos_z; + model->control.rot.vx = adjust->rot_x; + model->control.rot.vy = adjust->rot_y; + model->control.rot.vz = adjust->rot_z; + + if (model_file->flag & 0x1) + { + GM_ActControl(&model->control); + GM_ActObject2(&model->object); + } + else + { + rots = adjust->rots; + for (i = 0; i < adjust->n_rots; i++, rots += 3) + { + model->object.rots[i].vx = rots[0]; + model->object.rots[i].vy = rots[1]; + model->object.rots[i].vz = rots[2]; + } + + if (model_file->filename == GV_StrCode("m1e1") || model_file->filename == GV_StrCode("m1e1demo")) + { + demothrd_m1e1_8007D404(lpAct, adjust, model_file, model); + } + else if (model_file->filename == GV_StrCode("hind") || model_file->filename == GV_StrCode("hinddemo")) + { + demothrd_hind_8007D9C8(lpAct, adjust, model_file, model); + } + + GM_ActMotion(&model->object); + GM_ActControl(&model->control); + GM_ActObject(&model->object); + DG_GetLightMatrix(&model->control.mov, model->light); + } + } + + return 1; +} + +static inline int magic_calc(SVECTOR* vecTmp, DEMO_MODEL *model) +{ + int distance1 = SquareRoot0(((vecTmp->vx * vecTmp->vx) + (vecTmp->vy * vecTmp->vy)) + (vecTmp->vz * vecTmp->vz)); + + int rTan1 = ratan2(vecTmp->vx, vecTmp->vz); + int tmp4 = rTan1; + tmp4 -= model->control.rot.vy + model->object.rots[0].vy; + tmp4 = abs(tmp4); + return (distance1 * (1024 - tmp4)) / 1024; +} + +static void demothrd_m1e1_8007D404(LPMGSDEMOACT lpAct, DMO_ADJ *adjust, DMO_MDL *model_file, DEMO_MODEL *model) +{ + DEMO_M1E1 *data; + int tmp1; + int i; + SVECTOR vec; + SVECTOR vecTmp; + SVECTOR smokeVecs[10]; + + data = model->extra; + for (i = 0; i < 3; i++) + { + GM_ActMotion(&data->object[0][i]); + GM_ActMotion(&data->object[1][i]); + } + + DG_SetPos2(&model->control.mov, &model->control.rot); + DG_RotatePos(model->object.rots); + + M1E1GetCaterpillerVertex(&model->extra->object[0][0], &model->extra->object[1][0], smokeVecs, 1); + for (i = 0; i < 10; i++) + { + smokeVecs[i].vy = smokeVecs[i].vy + 300; + } + + DG_PutVector(smokeVecs, smokeVecs, 10); + + memset(&vec, 0, sizeof(SVECTOR)); + vec.vx = data->object[1][0].objs->objs[0].model->min.vx + ((data->object[1][0].objs->objs[0].model->max.vx - data->object[1][0].objs->objs[0].model->min.vx) / 2); + DG_PutVector(&vec, &vec, 1); + + vecTmp.vx = vec.vx - data->field_564[0].vx; + vecTmp.vy = vec.vy - data->field_564[0].vy; + vecTmp.vz = vec.vz - data->field_564[0].vz; + + tmp1 = magic_calc(&vecTmp, model); + + if (abs(tmp1) >= data->field_560) + { + AN_CaterpillerSmoke(&smokeVecs[rand() % 5]); + DG_InvisibleObjs(data->object[0][data->field_558_idx[0]].objs); + if (tmp1 > 0) + { + data->field_558_idx[0]++; + } + else + { + data->field_558_idx[0]--; + } + if (data->field_558_idx[0] < 0) + { + data->field_558_idx[0] = 2; + } + if (data->field_558_idx[0] >= 3) + { + data->field_558_idx[0] = 0; + } + DG_VisibleObjs(data->object[0][data->field_558_idx[0]].objs); + data->field_564[0] = vec; + } + + + memset(&vec, 0, sizeof(SVECTOR)); + vec.vx = data->object[1][0].objs->objs[0].model->min.vx + ((data->object[1][0].objs->objs[0].model->max.vx - data->object[1][0].objs->objs[0].model->min.vx) / 2); + DG_SetPos2(&model->control.mov, &model->control.rot); + + DG_PutVector(&vec, &vec, 1); + vecTmp.vx = vec.vx - data->field_564[1].vx; + vecTmp.vy = vec.vy - data->field_564[1].vy; + vecTmp.vz = vec.vz - data->field_564[1].vz; + + tmp1 = magic_calc(&vecTmp, model); + + if (abs(tmp1) >= data->field_560) + { + AN_CaterpillerSmoke(&smokeVecs[(rand() % 5) + 5]); + DG_InvisibleObjs(data->object[1][data->field_558_idx[1]].objs); + if (tmp1 > 0) + { + data->field_558_idx[1]++; + } + else + { + data->field_558_idx[1]--; + } + if (data->field_558_idx[1] < 0) + { + data->field_558_idx[1] = 2; + } + if (data->field_558_idx[1] >= 3) + { + data->field_558_idx[1] = 0; + } + DG_VisibleObjs(data->object[1][data->field_558_idx[1]].objs); + data->field_564[1] = vec; + } + DG_SetPos2(&model->control.mov, &model->control.rot); + DG_RotatePos(model->object.rots); + for (i = 0; i < 3; i++) + { + GM_ActObject(&data->object[0][i]); + GM_ActObject(&data->object[1][i]); + } +} + +static void demothrd_hind_8007D9C8(LPMGSDEMOACT lpAct, DMO_ADJ *adjust, DMO_MDL *model_file, DEMO_MODEL *model) +{ + DEMO_HIND *pTmp = (DEMO_HIND *)model->extra; // TODO: Would be cleaner as a union + + pTmp->field_8 = pTmp->field_8 - pTmp->field_0; + if ( pTmp->field_8 < 0 ) + { + pTmp->field_8 = pTmp->field_8 + 4096; + } + + pTmp->field_C = pTmp->field_C - pTmp->field_4; + if ( pTmp->field_C < 0 ) + { + pTmp->field_C = pTmp->field_C + 4096; + } + + model->object.rots[1].vy = pTmp->field_8; + model->object.rots[2].vx = pTmp->field_C; +} diff --git a/components/mgs/mgs/source/kojo/demoscrn.c b/components/mgs/mgs/source/kojo/demoscrn.c new file mode 100644 index 00000000..c9217405 --- /dev/null +++ b/components/mgs/mgs/source/kojo/demoscrn.c @@ -0,0 +1,320 @@ +/****************************************************************************** + * System : METALGEAR^3 for PlayStation + * Computer : PlayStation + * OS : PlayStation + * Compiler : psyq + * Module : + */ + +/****************************************************************************** + * included + */ + +#include +#include +#include +#include "libdg/libdg.h" + +/****************************************************************************** + * functions + */ + +void DemoScreenModelsSingle(DG_OBJS *pObjs, int n_models) +{ + MATRIX *pMatrix; + VECTOR *pVector; + DG_OBJ *pObj; + int count; + + pMatrix = (MATRIX *)(SCRPAD_ADDR + 0x020); + pVector = (VECTOR *)(SCRPAD_ADDR + 0x000); + + pMatrix->t[0] -= pVector[56].vx; + pMatrix->t[1] -= pVector[56].vy; + pMatrix->t[2] -= pVector[56].vz; + + gte_SetRotMatrix((MATRIX *)(SCRPAD_ADDR + 0x000)); + gte_SetTransMatrix((MATRIX *)(SCRPAD_ADDR + 0x000)); + + ApplyRotMatrixLV((VECTOR *)(SCRPAD_ADDR + 0x034), (VECTOR *)(SCRPAD_ADDR + 0x054)); + + gte_ldclmv((SCRPAD_ADDR + 0x020)); + gte_rtir(); + gte_stclmv((SCRPAD_ADDR + 0x040)); + + gte_ldclmv((SCRPAD_ADDR + 0x022)); + gte_rtir(); + gte_stclmv((SCRPAD_ADDR + 0x042)); + + gte_ldclmv((SCRPAD_ADDR + 0x024)); + gte_rtir(); + gte_stclmv((SCRPAD_ADDR + 0x044)); + + pObj = pObjs->objs; + for (count = n_models; count > 0; count--) + { + pObj->world = *(MATRIX *)(SCRPAD_ADDR + 0x020); + pObj->screen = *(MATRIX *)(SCRPAD_ADDR + 0x040); + pObj++; + } +} + +void DemoScreenModels(DG_OBJS* pObjs, int n_models) +{ + MATRIX *pMatrix; + VECTOR *pVector; + int x, y, z; + DG_OBJ *pObj; + int count; + + gte_SetRotMatrix((MATRIX *)(SCRPAD_ADDR + 0x000)); + gte_SetTransMatrix((MATRIX *)(SCRPAD_ADDR + 0x000)); + + pMatrix = (MATRIX *)(SCRPAD_ADDR + 0x040); + pVector = (VECTOR *)(SCRPAD_ADDR + 0x000); + + x = pVector[56].vx; + y = pVector[56].vy; + z = pVector[56].vz; + + pObj = pObjs->objs; + for (count = n_models; count > 0; count--) + { + pMatrix->t[0] -= x; + pMatrix->t[1] -= y; + pMatrix->t[2] -= z; + + ApplyRotMatrixLV((VECTOR *)pMatrix->t, (VECTOR *)pObj->screen.t); + + gte_ldclmv(&pMatrix->m[0][0]); + gte_rtir(); + gte_stclmv(&pObj->screen.m[0][0]); + + gte_ldclmv(&pMatrix->m[0][1]); + gte_rtir(); + gte_stclmv(&pObj->screen.m[0][1]); + + gte_ldclmv(&pMatrix->m[0][2]); + gte_rtir(); + gte_stclmv(&pObj->screen.m[0][2]); + + pObj++; + pMatrix++; + } +} + +void DemoApplyMovs(DG_OBJS* pObjs, int n_models) +{ + SVECTOR *pMovs; + MATRIX *pMatrix; + DG_OBJ *pObj; + int count; + + pMovs = pObjs->movs; + gte_SetRotMatrix((SCRPAD_ADDR + 0x020)); + + pMatrix = (MATRIX *)(SCRPAD_ADDR + 0x040); + + pObj = pObjs->objs; + for (count = n_models; count > 0; count--) + { + gte_SetTransMatrix((MATRIX *)(SCRPAD_ADDR + 0x040) + pObj->model->parent); + gte_ldv0(pMovs); + gte_rt(); + gte_ReadRotMatrix(pMatrix); + gte_stlvnl((VECTOR *)pMatrix->t); + + pObj->world = *pMatrix; + + pMovs++; + pObj++; + pMatrix++; + } +} + +void DemoApplyRots(DG_OBJS *pObjs, int n_models) +{ + MATRIX *pWorld; + DG_OBJ *pObj; + MATRIX *pSavedTransform; + SVECTOR *pRots; + SVECTOR *pWaistRot; + SVECTOR *pAdjust; + DG_MDL *pMdl; + MATRIX *pWorkMatrix; + int count; + MATRIX *pParent; + + pWorld = (MATRIX *)(SCRPAD_ADDR + 0x040); + pObj = pObjs->objs; + pSavedTransform = (MATRIX *)(SCRPAD_ADDR + 0x360); + pRots = pObjs->rots; + pWaistRot = pObjs->waist_rot; + pAdjust = pObjs->adjust; + pMdl = pObj->model; + pWorkMatrix = (MATRIX *)(SCRPAD_ADDR + 0x340); + + if (pWaistRot) + { + RotMatrixZYX_gte(pWaistRot, pWorkMatrix); + } + else + { + RotMatrixZYX_gte(pObjs->rots, pWorkMatrix); + } + + pWorkMatrix->t[0] = pMdl->pos.vx; + pWorkMatrix->t[1] = pMdl->pos.vy; + pWorkMatrix->t[2] = pMdl->pos.vz; + + if (pAdjust == NULL) + { + gte_CompMatrix((SCRPAD_ADDR + 0x020), pWorkMatrix, pWorkMatrix); + + for (count = n_models; count > 0; count--) + { + pMdl = pObj->model; + pParent = (MATRIX *)(SCRPAD_ADDR + 0x040) + pMdl->parent; + + RotMatrixZYX_gte(pRots, pWorld); + + pWorld->t[0] = pMdl->pos.vx; + pWorld->t[1] = pMdl->pos.vy; + pWorld->t[2] = pMdl->pos.vz; + + if (count == (n_models - 1)) + { + gte_CompMatrix(pWorkMatrix, pWorld, pWorld); + } + else + { + gte_CompMatrix(pParent, pWorld, pWorld); + } + + pObj->world = *pWorld; + pObj++; + pWorld++; + pRots++; + } + } + else + { + *pSavedTransform = *(MATRIX *)(SCRPAD_ADDR + 0x020); + *(MATRIX *)(SCRPAD_ADDR + 0x020) = DG_ZeroMatrix; + + for (count = n_models; count > 0; count--) + { + pMdl = pObj->model; + pParent = (MATRIX *)(SCRPAD_ADDR + 0x040) + pMdl->parent; + + RotMatrixZYX_gte(pRots, pWorld); + + pWorld->t[0] = pMdl->pos.vx; + pWorld->t[1] = pMdl->pos.vy; + pWorld->t[2] = pMdl->pos.vz; + + if (count == (n_models - 1)) + { + gte_CompMatrix(pWorkMatrix, pWorld, pWorld); + } + else + { + gte_CompMatrix(pParent, pWorld, pWorld); + } + + if (pAdjust->vz != 0) + { + RotMatrixZ(pAdjust->vz, pWorld); + } + + if (pAdjust->vx != 0) + { + RotMatrixX(pAdjust->vx, pWorld); + } + + if (pAdjust->vy != 0) + { + RotMatrixY(pAdjust->vy, pWorld); + } + + pObj++; + pWorld++; + pRots++; + pAdjust++; + } + + *(MATRIX *)(SCRPAD_ADDR + 0x020) = *pSavedTransform; + pWorld = (MATRIX *)(SCRPAD_ADDR + 0x040); + + pObj = pObjs->objs; + for (count = n_models; count > 0; count--) + { + gte_CompMatrix((SCRPAD_ADDR + 0x020), pWorld, pWorld); + pObj->world = *pWorld; + pObj++; + pWorld++; + } + } +} + +void DemoScreenObjs(DG_OBJS *pObjs) +{ + int n_models; + + n_models = pObjs->n_models; + + if (pObjs->root) + { + pObjs->world = *pObjs->root; + } + + *(MATRIX *)getScratchAddr(8) = pObjs->world; + + if (pObjs->flag & DG_FLAG_ONEPIECE) + { + DemoScreenModelsSingle(pObjs, n_models); + return; + } + + if (pObjs->rots) + { + DemoApplyRots(pObjs, n_models); + } + else if (pObjs->movs) + { + DemoApplyMovs(pObjs, n_models); + } + + DemoScreenModels(pObjs, n_models); +} + +void DemoScreenChanl(DG_CHANL *chanl, int idx) +{ + typedef struct + { + MATRIX matrix; + char pad[0x360]; + int translation[3]; + } SCREEN_SPAD; + + DG_OBJS **ppObjs; + SCREEN_SPAD *scrpad; + int count; + + ppObjs = chanl->queue; + + scrpad = (SCREEN_SPAD *)getScratchAddr(0); + scrpad->matrix = chanl->eye_inv; + scrpad->matrix.t[0] = scrpad->matrix.t[1] = scrpad->matrix.t[2] = 0; + + scrpad->translation[0] = chanl->eye.t[0]; + scrpad->translation[1] = chanl->eye.t[1]; + scrpad->translation[2] = chanl->eye.t[2]; + + DG_AdjustOverscan(&scrpad->matrix); + + for (count = chanl->objs_index; count > 0; count--) + { + DemoScreenObjs(*ppObjs++); + } +} diff --git a/components/mgs/mgs/source/kojo/demothrd.c b/components/mgs/mgs/source/kojo/demothrd.c new file mode 100644 index 00000000..a77e133d --- /dev/null +++ b/components/mgs/mgs/source/kojo/demothrd.c @@ -0,0 +1,285 @@ +/****************************************************************************** + * System : METALGEAR^3 for PlayStation + * Computer : PlayStation + * OS : PlayStation + * Compiler : psyq + * Module : + */ + +/****************************************************************************** + * included + */ + +#include "demo.h" + +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libfs/libfs.h" +#include "libgcl/libgcl.h" + +extern int demodebug_finish_proc; + +/****************************************************************************** + * functions + */ + +static void ActStream(LPMGSDEMOACT lpAct); +static void DieStream(LPMGSDEMOACT lpAct); +static void ActFile(LPMGSDEMOACT lpAct); +static void DieFile(LPMGSDEMOACT lpAct); + +/****************************************************************************** + * publics + */ + +int DM_ThreadStream(int flag, int unused) +{ + LPMGSDEMOACT lpAct; + + lpAct = GV_NewActor(GV_ACTOR_MANAGER, sizeof(MGSDEMOACT)); + if (!lpAct) + { + return 0; + } + + lpAct->flag = flag; + lpAct->frame = -1; + + GV_SetNamedActor(&lpAct->actor, &ActStream, &DieStream, "demothrd.c"); + + lpAct->map = GM_CurrentMap; + FS_StreamOpen(); + return 1; +} + +int DM_ThreadFile(int flag, char *filename) +{ + LPMGSDEMOACT lpAct; + int fd; + char *buffer; + int fsize, length; + + lpAct = GV_NewActor(GV_ACTOR_MANAGER, sizeof(MGSDEMOACT)); + + if ( !lpAct ) + { + return 0; + } + + lpAct->flag = flag; + lpAct->frame = -1; + + GV_SetNamedActor(&lpAct->actor, &ActFile, &DieFile, "demothrd.c"); + + lpAct->map = GM_CurrentMap; + FS_EnableMemfile(0, 0); + lpAct->stream = (void *)0x80200000; + + MakeFullPath(filename, (char *)&lpAct->chain.used); + printf("Demo file = \"%s\"\n", (char *)&lpAct->chain.used); + + fd = PCopen((char *)&lpAct->chain.used, 0, 0); + if ( fd < 0 ) + { + printf("\"%s\" not found\n", (char *)&lpAct->chain.used); + GV_DestroyActor(&lpAct->actor); + return 0; + } + + fsize = PClseek(fd, 0, 2); + PClseek(fd, 0, 0); + + buffer = (char *)lpAct->stream; + + while ( fsize > 0 ) + { + length = (fsize <= 0x8000) ? fsize : 0x8000; + length = PCread(fd, buffer, length); + + fsize -= length; + + if ( length < 0 ) + { + PCclose(fd); + GV_DestroyActor(&lpAct->actor); + return 0; + } + + buffer += length; + } + + PCclose(fd); + return 1; +} + +/****************************************************************************** + * statics + */ + +static void ActStream(LPMGSDEMOACT lpAct) +{ + int ticks; + char *data; + int status; + int temp; + DMO_DEF *def; + + ticks = FS_StreamGetTick(); + + if (lpAct->frame == -1) + { + data = FS_StreamGetData(5); + + if (data) + { + def = (DMO_DEF *)(data - 4); + status = CreateDemo(lpAct, def); + + FS_StreamClear(data); + + if (status == 0) + { + GV_DestroyActor(&lpAct->actor); + } + + lpAct->frame = 0; + } + + return; + } + + if (lpAct->start_time == 0) + { + lpAct->start_time = ticks - 2; + } + + lpAct->frame = (ticks - lpAct->start_time) / 2; + status = 0; + temp = 0; + + if (lpAct->frame <= lpAct->header->n_frames) + { + while (1) + { + data = FS_StreamGetData(5); + + if (!data) + { + if (FS_StreamGetEndFlag() == 1) + { + GV_DestroyActor(&lpAct->actor); + } + + return; + } + + def = (DMO_DEF *)(data - 4); + if (def->frame >= lpAct->frame) + { + break; + } + + FS_StreamClear(data); + } + + status = FrameRunDemo(lpAct, (DMO_DAT *)def); + + if (status == 0) + { + FS_StreamStop(); + } + else + { + FS_StreamClear(data); + } + } + + if (status == temp) + { + GV_DestroyActor(&lpAct->actor); + } +} + +static void DieStream(LPMGSDEMOACT lpAct) +{ + DestroyDemo(lpAct); + FS_StreamClose(); + DG_UnDrawFrameCount = 0x7fff0000; +} + +static void ActFile(LPMGSDEMOACT lpAct) +{ + int time; + int new_time; + int success; + + time = VSync(-1); + + if (lpAct->frame == -1) + { + if (!CreateDemo(lpAct, (DMO_DEF *)lpAct->stream)) + { + printf("Error:Initialize demo\n"); + GV_DestroyActor(&lpAct->actor); + } + + lpAct->frame = 0; + return; + } + + if (lpAct->start_time == 0) + { + lpAct->start_time = time - 2; + printf("PlayDemoSound\n"); + } + + if (lpAct->flag & 4) + { + new_time = lpAct->frame + 1; + } + else + { + new_time = (time - lpAct->start_time) / 2; + } + + lpAct->frame = new_time; + + if (lpAct->header->n_frames < lpAct->frame) + { + success = 0; + } + else + { + while (lpAct->frame != lpAct->stream->frame) + { + lpAct->stream = (DMO_DEF *)((char *)lpAct->stream + lpAct->stream->tag); + } + + success = FrameRunDemo(lpAct, (DMO_DAT *)lpAct->stream); + } + + if (GV_PadData[1].status & PAD_CROSS) + { + success = 0; + } + + if (success == 0) + { + GV_DestroyActor(&lpAct->actor); + } +} + +static void DieFile(LPMGSDEMOACT lpAct) +{ + DestroyDemo(lpAct); + FS_EnableMemfile(1, 1); + + if (demodebug_finish_proc != -1) + { + GCL_ExecProc(demodebug_finish_proc, NULL); + } +} diff --git a/components/mgs/mgs/source/kojo/famaslit.c b/components/mgs/mgs/source/kojo/famaslit.c new file mode 100644 index 00000000..af839717 --- /dev/null +++ b/components/mgs/mgs/source/kojo/famaslit.c @@ -0,0 +1,139 @@ +/****************************************************************************** + * System : METALGEAR^3 for PlayStation + * Computer : PlayStation + * OS : PlayStation + * Compiler : psyq + * Module : + */ + +/****************************************************************************** + * included + */ + +#include "famaslit.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" + +/****************************************************************************** + * definitions and typedefs and structures + */ + +typedef struct tagFAMASLIGHT +{ + GV_ACT actor; + int nMap; + DG_PRIM *prim; + MATRIX *world; +} FAMASLIGHT, *LPFAMASLIGHT; + +/****************************************************************************** + * locals + */ + +static RECT famaslit_rect = {40, 40, 80, 80}; +static SVECTOR famaslit_svec = {0, -400, 60, 0}; + +/****************************************************************************** + * functions + */ + +static void Act(LPFAMASLIGHT lpAct); +static void Die(LPFAMASLIGHT lpAct); + +/****************************************************************************** + * publics + */ + +void *NewFamasLight(MATRIX *world) +{ + DG_TEX *tex; + LPFAMASLIGHT lpAct; + DG_PRIM *prim; + int x, y, w, h; + int x2, y2, w2, h2; + + tex = DG_GetTexture(GV_StrCode("famas_l")); + if (tex == NULL) + { + return NULL; + } + + lpAct = GV_NewActor(GV_ACTOR_USER, sizeof(FAMASLIGHT)); + if (lpAct == NULL) + { + return NULL; + } + + GV_SetNamedActor(&lpAct->actor, Act, Die, "famaslit.c"); + + lpAct->world = world; + lpAct->nMap = GM_CurrentMap; + + prim = GM_MakePrim(DG_PRIM_RECTANGLE | DG_PRIM_POLY_FT4, 1, &famaslit_svec, &famaslit_rect); + lpAct->prim = prim; + + DG_GroupPrim(prim, 0); + + lpAct->prim->raise = 400; + +#define POLY ((POLY_FT4 *)lpAct->prim->packs[0]) + + x = tex->off_x; + w = tex->w; + y = tex->off_y; + h = tex->h; + setUVWH(&POLY[0], x, y, w, h); + POLY[0].tpage = tex->tpage; + POLY[0].clut = tex->clut; + //DG_SetPacketTexture4(&POLY[0], tex); + + x2 = tex->off_x; + w2 = tex->w; + y2 = tex->off_y; + h2 = tex->h; + setUVWH(&POLY[1], x2, y2, w2, h2); + POLY[1].tpage = tex->tpage; + POLY[1].clut = tex->clut; + //DG_SetPacketTexture4(&POLY[1], tex); + + POLY[0].tpage &= ~0x60; + POLY[1].tpage &= ~0x60; + + POLY[0].tpage |= 0x20; + POLY[1].tpage |= 0x20; + + setPolyFT4(&POLY[0]); + setPolyFT4(&POLY[1]); + + setRGB0(&POLY[0], 250, 250, 250); + setRGB0(&POLY[1], 200, 200, 200); + + setSemiTrans(&POLY[0], 1); + setSemiTrans(&POLY[1], 1); + +#undef POLY + + return (void *)lpAct; +} + +/****************************************************************************** + * statics + */ + +static void Act(LPFAMASLIGHT lpAct) +{ + lpAct->prim->world = *lpAct->world; + DG_VisiblePrim(lpAct->prim); +} + +static void Die(LPFAMASLIGHT lpAct) +{ + GM_FreePrim(lpAct->prim); +} diff --git a/components/mgs/mgs/source/kojo/famaslit.h b/components/mgs/mgs/source/kojo/famaslit.h new file mode 100644 index 00000000..050449f1 --- /dev/null +++ b/components/mgs/mgs/source/kojo/famaslit.h @@ -0,0 +1,10 @@ +#ifndef __MGS_KOJO_FAMASLIT_H__ +#define __MGS_KOJO_FAMASLIT_H__ + +#include +#include +#include + +void *NewFamasLight(MATRIX *world); + +#endif // __MGS_KOJO_FAMASLIT_H__ diff --git a/components/mgs/mgs/source/kojo/inverlt2.c b/components/mgs/mgs/source/kojo/inverlt2.c new file mode 100644 index 00000000..d75161ad --- /dev/null +++ b/components/mgs/mgs/source/kojo/inverlt2.c @@ -0,0 +1,350 @@ +/****************************************************************************** + * System : METALGEAR^3 for PlayStation + * Computer : PlayStation + * OS : PlayStation + * Compiler : psyq + * Module : + */ + +/****************************************************************************** + * included + */ + +#include "inverlt2.h" + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" + +#include "okajima/stngrnd.h" // for AN_Stn_G_* + +/****************************************************************************** + * definitions and typedefs and structures + */ + +typedef struct tagINVERSLIGHT2 +{ + GV_ACT actor; + int nMap; + DG_PRIM *prim[8]; + RECT rect[8]; + SVECTOR vec[8]; + int nR; + int nG; + int nB; + int fD0; + int fD4; + int fD8; + int fDC; + SVECTOR fE0; + int fE8; +} INVERSLIGHT2, *LPINVERSLIGHT2; + +/****************************************************************************** + * functions + */ + +static void Act(LPINVERSLIGHT2 lpAct); +static void Die(LPINVERSLIGHT2 lpAct); +static void InitRects(LPINVERSLIGHT2 lpAct, int scale); + +/****************************************************************************** + * publics + */ + +void *NewInverseLight2(SVECTOR *arg0, int arg1, int arg2, int arg3, int nR, int nG, int nB, int arg7, int arg8) +{ + LPINVERSLIGHT2 lpAct; + DG_TEX *tex; + int i; + int x, y, w, h; + int x2, y2, w2, h2; + + lpAct = GV_NewActor(GV_ACTOR_USER, sizeof(INVERSLIGHT2)); + if (lpAct == NULL) + { + return NULL; + } + + GV_SetNamedActor(&lpAct->actor, Act, Die, "inverlt2.c"); + + lpAct->nR = nR; + lpAct->nG = nG; + lpAct->nB = nB; + + lpAct->fD0 = arg1; + lpAct->fD4 = arg1; + lpAct->fD8 = arg2; + lpAct->fDC = arg3; + + lpAct->nMap = GM_CurrentMap; + + lpAct->fE0 = *arg0; + + if (lpAct->nR < 5) + { + lpAct->nR = 5; + } + + if (lpAct->nG < 5) + { + lpAct->nG = 5; + } + + if (lpAct->nB < 5) + { + lpAct->nB = 5; + } + + if (lpAct->fD8 <= 0) + { + lpAct->fD8 = 1; + } + + lpAct->rect[0].w = arg7 / 6; + lpAct->rect[0].h = arg7 / 6; + lpAct->rect[1].w = arg7 / 3; + lpAct->rect[1].h = arg7 / 3; + lpAct->rect[2].w = arg7 / 2; + lpAct->rect[2].h = arg7 / 2; + lpAct->rect[3].w = (arg7 * 4) / 6; + lpAct->rect[3].h = (arg7 * 4) / 6; + lpAct->rect[4].w = (arg7 * 5) / 6; + lpAct->rect[4].h = (arg7 * 5) / 6; + lpAct->rect[5].w = arg7; + lpAct->rect[5].h = arg7; + lpAct->rect[6].w = arg8 / 2; + lpAct->rect[6].h = arg8 / 2; + lpAct->rect[7].w = arg8; + lpAct->rect[7].h = arg8; + + lpAct->vec[0].vz = 320; + lpAct->vec[1].vz = 320; + lpAct->vec[2].vz = 320; + lpAct->vec[3].vz = 320; + lpAct->vec[4].vz = 320; + lpAct->vec[5].vz = 320; + lpAct->vec[6].vz = 320; + lpAct->vec[7].vz = 320; + + InitRects(lpAct, 100); + + tex = DG_GetTexture(GV_StrCode("refrection6")); + + for (i = 0; i < 8; i++) + { + lpAct->prim[i] = GM_MakePrim(DG_PRIM_RECTANGLE | DG_PRIM_ON_CAMERA | DG_PRIM_POLY_FT4, 1, &lpAct->vec[i], &lpAct->rect[i]); + + lpAct->prim[i]->raise = 320; + +#define POLY ((POLY_FT4 *)lpAct->prim[i]->packs[0]) + setPolyFT4(&POLY[0]); + setPolyFT4(&POLY[1]); + setRGB0(&POLY[0], lpAct->nR, lpAct->nG, lpAct->nB); + setRGB0(&POLY[1], lpAct->nR - 5, lpAct->nG - 5, lpAct->nB - 5); + + x = tex->off_x; + w = tex->w; + y = tex->off_y; + h = tex->h; + setUVWH(&POLY[0], x, y, w, h); + POLY[0].tpage = tex->tpage; + POLY[0].clut = tex->clut; + //DG_SetPacketTexture4(&POLY[0], tex); + + x2 = tex->off_x; + w2 = tex->w; + y2 = tex->off_y; + h2 = tex->h; + setUVWH(&POLY[1], x2, y2, w2, h2); + POLY[1].tpage = tex->tpage; + POLY[1].clut = tex->clut; + //DG_SetPacketTexture4(&POLY[1], tex); + + setSemiTrans(&POLY[0], 1); + setSemiTrans(&POLY[1], 1); +#undef POLY + } + + return (void *)lpAct; +} + +/****************************************************************************** + * statics + */ + +// Can't match below function without this macro +#define ACTASSERT(cond) \ +do { \ + if (cond) { \ + GV_DestroyActor(&lpAct->actor); \ + return; \ + } \ +} while (0) + +static void Act(LPINVERSLIGHT2 lpAct) +{ + int temp_s3; + int temp_lo; + int temp_a1; + int nR, nG, nB; + int i; + + ACTASSERT(lpAct->fD0 <= 0); + + temp_s3 = lpAct->fD4 - lpAct->fD0; + temp_lo = ((lpAct->fD8 - temp_s3) * 100) / lpAct->fD8; + + GM_CurrentMap = lpAct->nMap; + + if ((temp_lo == 0) && (lpAct->fE8 == 0)) + { + lpAct->fE8 = 1; + AN_Stn_G_Sonic(&lpAct->fE0); + AN_Stn_G_Center(&lpAct->fE0); + sub_800790E8(); + } + else if (lpAct->fE8 == 1) + { + lpAct->fE8 = 2; + sub_8007913C(); + } + + InitRects(lpAct, temp_lo); + + if (lpAct->fDC < temp_s3) + { + temp_a1 = lpAct->fD4 - lpAct->fDC; + if (temp_a1 <= 0) + { + temp_lo = 0; + } + else + { + temp_lo = ((lpAct->fD4 - temp_s3) * 100) / temp_a1; + } + + nR = (lpAct->nR * temp_lo) / 100; + nG = (lpAct->nG * temp_lo) / 100; + nB = (lpAct->nB * temp_lo) / 100; + + if (nR < 5) + { + nR = 5; + } + + if (nG < 5) + { + nG = 5; + } + + if (nB < 5) + { + nB = 5; + } + + for (i = 0; i < 8; i++) + { + ((POLY_FT4 *)lpAct->prim[i]->packs[0])[0].r0 = nR; + ((POLY_FT4 *)lpAct->prim[i]->packs[0])[0].g0 = nG; + ((POLY_FT4 *)lpAct->prim[i]->packs[0])[0].b0 = nB; + + ((POLY_FT4 *)lpAct->prim[i]->packs[0])[1].r0 = nR - 5; + ((POLY_FT4 *)lpAct->prim[i]->packs[0])[1].g0 = nG - 5; + ((POLY_FT4 *)lpAct->prim[i]->packs[0])[1].b0 = nB - 5; + } + } + + lpAct->fD0--; +} + +static void Die(LPINVERSLIGHT2 lpAct) +{ + int i; + + if (lpAct->fE8 == 1) + { + sub_8007913C(); + } + + for (i = 0; i < 8; i++) + { + GM_FreePrim(lpAct->prim[i]); + } +} + +static void InitRects(LPINVERSLIGHT2 lpAct, int scale) +{ + SVECTOR sxy; + SVECTOR sp18; + long p; + long flag; + short screen; + + + SetRotMatrix(&DG_Chanl(0)->eye_inv); + SetTransMatrix(&DG_Chanl(0)->eye_inv); + RotTransPers(&lpAct->fE0, (long *)&sxy, &p, &flag); + + if (sxy.vy == 0) + { + sp18.vx = 0; + } + else + { + screen = abs((sxy.vx * 112) / sxy.vy); + sp18.vx = (screen * scale) / 100; + } + + if (sxy.vx == 0) + { + sp18.vy = 0; + } + else + { + screen = abs((sxy.vy * 160) / sxy.vx); + sp18.vy = (screen * scale) / 100; + } + + if (sxy.vx <= 0) + { + sp18.vx *= -1; + } + + if (sxy.vy <= 0) + { + sp18.vy *= -1; + } + + lpAct->rect[0].x = (lpAct->rect[0].w / 2) - sxy.vx + (sp18.vx / 6); + lpAct->rect[0].y = (lpAct->rect[0].h / 2) - sxy.vy + (sp18.vy / 6); + + lpAct->rect[1].x = (lpAct->rect[1].w / 2) - sxy.vx + (sp18.vx / 3); + lpAct->rect[1].y = (lpAct->rect[1].h / 2) - sxy.vy + (sp18.vy / 3); + + lpAct->rect[2].x = (lpAct->rect[2].w / 2) - sxy.vx + (sp18.vx / 2); + lpAct->rect[2].y = (lpAct->rect[2].h / 2) - sxy.vy + (sp18.vy / 2); + + lpAct->rect[3].x = (lpAct->rect[3].w / 2) - sxy.vx + ((sp18.vx * 4) / 6); + lpAct->rect[3].y = (lpAct->rect[3].h / 2) - sxy.vy + ((sp18.vy * 4) / 6); + + lpAct->rect[4].x = (lpAct->rect[4].w / 2) - sxy.vx + ((sp18.vx * 5) / 6); + lpAct->rect[4].y = (lpAct->rect[4].h / 2) - sxy.vy + ((sp18.vy * 5) / 6); + + lpAct->rect[5].x = (lpAct->rect[5].w / 2) - sxy.vx + sp18.vx; + lpAct->rect[5].y = (lpAct->rect[5].h / 2) - sxy.vy + sp18.vy; + + sp18.vx >>= 1; + sp18.vy >>= 1; + + lpAct->rect[6].x = (lpAct->rect[6].w / 2) - sxy.vx + (-sp18.vx / 2); + lpAct->rect[6].y = (lpAct->rect[6].h / 2) - sxy.vy + (-sp18.vy / 2); + + lpAct->rect[7].x = (lpAct->rect[7].w / 2) - sxy.vx - sp18.vx; + lpAct->rect[7].y = (lpAct->rect[7].h / 2) - sxy.vy - sp18.vy; +} diff --git a/components/mgs/mgs/source/kojo/inverlt2.h b/components/mgs/mgs/source/kojo/inverlt2.h new file mode 100644 index 00000000..9605221e --- /dev/null +++ b/components/mgs/mgs/source/kojo/inverlt2.h @@ -0,0 +1,10 @@ +#ifndef __MGS_KOJO_INVERLT2_H__ +#define __MGS_KOJO_INVERLT2_H__ + +#include +#include +#include + +void *NewInverseLight2(SVECTOR *arg0, int arg1, int arg2, int arg3, int nR, int nG, int nB, int arg7, int arg8); + +#endif // __MGS_KOJO_INVERLT2_H__ diff --git a/components/mgs/mgs/source/kojo/m1e1.h b/components/mgs/mgs/source/kojo/m1e1.h new file mode 100644 index 00000000..7688d75f --- /dev/null +++ b/components/mgs/mgs/source/kojo/m1e1.h @@ -0,0 +1,64 @@ +#ifndef __MGS_KOJO_M1E1_H__ +#define __MGS_KOJO_M1E1_H__ + +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "game/game.h" + +/****************************************************************************** + * definitions and typedefs and structures + */ + +typedef struct tagM1E1ACTOR +{ + GV_ACT actor; + CONTROL control; + MATRIX field_9C_light_matrices[2]; + OBJECT field_DC; + OBJECT field_1C0[3]; + OBJECT field_46C[3]; + TARGET *field_718_targets[10]; + BOOL bInitialize; + char field_744[0x61C]; + int field_D60; + int field_D64; + int field_D68; + int field_D6C; + char field_D70[0x1C]; + int field_D8C; + char field_D90[0xD0]; + int nSoundVolume; + int field_E64; + int field_E68; + int field_E6C; + int field_E70; + int field_E74; + int field_E78; + int nLeftCaterDirection; + int nRightCaterDirection; + int nCaterLength; + int field_E88; + SVECTOR svectLeftCater; + SVECTOR svectRightCater; + char field_E9C[0xC4]; + int nLeftCaterSmokeTCount; + int nRightCaterSmokeTCount; + int field_F68; + int field_F6C; + int nCaterSoundTCount; + int nCaterSoundStartTCount; +} M1E1ACTOR, *LPM1E1ACTOR; + +/****************************************************************************** + * prototypes + */ + +/* m1e1catr.c */ +void M1E1Caterpiller(LPM1E1ACTOR lpAct); +void M1E1GetCaterpillerVertex(OBJECT *obj1, OBJECT *obj2, SVECTOR *pos, int a4); + +#endif // __MGS_KOJO_M1E1_H__ diff --git a/components/mgs/mgs/source/kojo/m1e1catr.c b/components/mgs/mgs/source/kojo/m1e1catr.c new file mode 100644 index 00000000..60165523 --- /dev/null +++ b/components/mgs/mgs/source/kojo/m1e1catr.c @@ -0,0 +1,302 @@ +/****************************************************************************** + * System : METALGEAR^3 for PlayStation + * Computer : PlayStation + * OS : PlayStation + * Compiler : psyq + * Module : M1E1 カタピラ + */ + +/****************************************************************************** + * included + */ + +#include +#include +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" +#include "m1e1.h" + +// in anime/effect/m1e1.c +extern void AN_CaterpillerSmoke(SVECTOR *pos); +extern void demothrd_2_8007DA94(SVECTOR *pPosition, SVECTOR *pRotation); + +/****************************************************************************** + * definitions and typedefs and structures + */ + +#define SE_M1E1_CATERPILLERDEAD1 (182) +#define SE_M1E1_CATERPILLERDEAD2 (190) +#define M1E1_SOUNDCATERPILLERDEAD1 (30) + +/****************************************************************************** + * publics + */ + +void M1E1Caterpiller(LPM1E1ACTOR lpAct) +{ + SVECTOR svect1; + SVECTOR svect2; + SVECTOR svect3; + int diff; + int nSound; + int nLength; + DG_MDL *model; + DG_MDL *model2; + + diff = lpAct->field_D60 - lpAct->field_D8C; + nSound = 2; + + if (diff > 0) + { + nSound += ((diff - (lpAct->field_D68 - lpAct->field_D8C)) * 5) / diff; + } + + if ((GV_Time & 1) == 0) + { + DG_SetPos2(&lpAct->control.mov, &lpAct->control.turn); + svect3 = lpAct->control.turn; + + while (svect3.vy < -2048) + { + svect3.vy += 4096; + } + + while (svect3.vy > 2048) + { + svect3.vy -= 4096; + } + + memset(&svect1, 0, 8); + + model = lpAct->field_1C0[0].objs->objs[0].model; + svect1.vx = model->min.vx + (model->max.vx - model->min.vx) / 2; + + DG_PutVector(&svect1, &svect1, 1); + + if (lpAct->bInitialize == TRUE) + { + lpAct->svectLeftCater = svect1; + } + + svect2.vx = svect1.vx - lpAct->svectLeftCater.vx; + svect2.vy = svect1.vy - lpAct->svectLeftCater.vy; + svect2.vz = svect1.vz - lpAct->svectLeftCater.vz; + + nLength = SquareRoot0(svect2.vx * svect2.vx + + svect2.vy * svect2.vy + svect2.vz * svect2.vz); + nLength = (nLength * (1024 - abs(ratan2(svect2.vx, svect2.vz) - svect3.vy))) >> 10; + + if (abs(nLength) < lpAct->nCaterLength) + { + return; + } + + svect3 = lpAct->control.rot; + svect3.vy += 2048; + + if (nLength > 0) + { + if (lpAct->nLeftCaterDirection != 1 && lpAct->nLeftCaterSmokeTCount == 0) + { + lpAct->nLeftCaterSmokeTCount = 6; + demothrd_2_8007DA94(&lpAct->field_718_targets[4]->center, &svect3); + } + + lpAct->nLeftCaterDirection = 1; + } + else + { + if (lpAct->nLeftCaterDirection != -1 && lpAct->nLeftCaterSmokeTCount == 0) + { + lpAct->nLeftCaterSmokeTCount = 6; + demothrd_2_8007DA94(&lpAct->field_718_targets[0]->center, &lpAct->control.rot); + } + + lpAct->nLeftCaterDirection = -1; + } + + AN_CaterpillerSmoke(&lpAct->field_718_targets[rand() % 5]->center); + DG_InvisibleObjs(lpAct->field_1C0[lpAct->field_E74].objs); + + lpAct->field_E74 += lpAct->nLeftCaterDirection; + + if (lpAct->field_E74 < 0) + { + lpAct->field_E74 = 2; + } + + if (lpAct->field_E74 > 2) + { + lpAct->field_E74 = 0; + } + + DG_VisibleObjs(lpAct->field_1C0[lpAct->field_E74].objs); + + lpAct->svectLeftCater = svect1; + + if ((nSound >= 3) && (lpAct->nCaterSoundTCount <= 0)) + { + if (lpAct->nCaterSoundStartTCount == 0) + GM_SeSetPan(&lpAct->control.mov, + SE_M1E1_CATERPILLERDEAD1, lpAct->nSoundVolume); + else + GM_SeSetPan(&lpAct->control.mov, + SE_M1E1_CATERPILLERDEAD2, lpAct->nSoundVolume); + + lpAct->nCaterSoundTCount = nSound; + lpAct->nCaterSoundStartTCount = M1E1_SOUNDCATERPILLERDEAD1; + } + } + else + { + memset(&svect1, 0, 8); + + model2 = lpAct->field_46C[0].objs->objs[0].model; + svect1.vx = model2->min.vx + (model2->max.vx - model2->min.vx) / 2; + + DG_SetPos2(&lpAct->control.mov, &lpAct->control.turn); + DG_PutVector(&svect1, &svect1, 1); + + if (lpAct->bInitialize == TRUE) + { + lpAct->svectRightCater = svect1; + } + + svect2.vx = svect1.vx - lpAct->svectRightCater.vx; + svect2.vy = svect1.vy - lpAct->svectRightCater.vy; + svect2.vz = svect1.vz - lpAct->svectRightCater.vz; + + nLength = SquareRoot0(svect2.vx * svect2.vx + + svect2.vy * svect2.vy + svect2.vz * svect2.vz); + nLength = (nLength * (1024 - abs(ratan2(svect2.vx, svect2.vz) - svect3.vy))) >> 10; + + if (abs(nLength) < lpAct->nCaterLength) + { + return; + } + + svect3 = lpAct->control.rot; + svect3.vy += 2048; + + if (nLength > 0) + { + if (lpAct->nRightCaterDirection != 1 && lpAct->nRightCaterSmokeTCount == 0) + { + lpAct->nRightCaterSmokeTCount = 6; + demothrd_2_8007DA94(&lpAct->field_718_targets[9]->center, &svect3); + } + + lpAct->nRightCaterDirection = 1; + } + else + { + if (lpAct->nRightCaterDirection != -1 && lpAct->nRightCaterSmokeTCount == 0) + { + lpAct->nRightCaterSmokeTCount = 6; + demothrd_2_8007DA94(&lpAct->field_718_targets[5]->center, &lpAct->control.rot); + } + + lpAct->nRightCaterDirection = -1; + } + + AN_CaterpillerSmoke(&lpAct->field_718_targets[(rand() % 5) + 5]->center); + DG_InvisibleObjs(lpAct->field_46C[lpAct->field_E78].objs); + + lpAct->field_E78 += lpAct->nLeftCaterDirection; + + if (lpAct->field_E78 < 0) + { + lpAct->field_E78 = 2; + } + + if (lpAct->field_E78 > 2) + { + lpAct->field_E78 = 0; + } + + DG_VisibleObjs(lpAct->field_46C[lpAct->field_E78].objs); + + lpAct->svectRightCater = svect1; + + if ((nSound >= 3) && (lpAct->nCaterSoundTCount <= 0)) + { + if (lpAct->nCaterSoundStartTCount == 0) + GM_SeSetPan(&lpAct->control.mov, + SE_M1E1_CATERPILLERDEAD1, lpAct->nSoundVolume); + else + GM_SeSetPan(&lpAct->control.mov, + SE_M1E1_CATERPILLERDEAD2, lpAct->nSoundVolume); + + lpAct->nCaterSoundTCount = nSound; + lpAct->nCaterSoundStartTCount = M1E1_SOUNDCATERPILLERDEAD1; + } + } +} + +void M1E1GetCaterpillerVertex(OBJECT *obj1, OBJECT *obj2, SVECTOR *pos, int a4) +{ + DG_MDL *model; + int vx; + + /* left caterpillar */ + model = obj1->objs->objs[0].model; + vx = model->min.vx; + if ( a4 == 1 ) + { + vx += (model->max.vx - vx) >> 1; + } + + pos[0].vx = vx; + pos[0].vy = obj1->objs->objs[0].model->min.vy; + pos[0].vz = 2 * obj1->objs->objs[0].model->max.vz / 3; + + pos[1].vx = vx; + pos[1].vy = obj1->objs->objs[0].model->min.vy; + pos[1].vz = obj1->objs->objs[0].model->max.vz / 3; + + pos[2].vx = vx; + pos[2].vy = obj1->objs->objs[0].model->min.vy; + pos[2].vz = 0; + + pos[3].vx = vx; + pos[3].vy = obj1->objs->objs[0].model->min.vy; + pos[3].vz = obj1->objs->objs[0].model->min.vz / 3; + + pos[4].vx = vx; + pos[4].vy = obj1->objs->objs[0].model->min.vy; + pos[4].vz = (2 * obj1->objs->objs[0].model->min.vz) / 3; + + /* right caterpillar */ + model = obj2->objs->objs[0].model; + vx = model->max.vx; + if ( a4 == 1 ) + { + vx += (model->min.vx - vx) >> 1; + } + + pos[5].vx = vx; + pos[5].vy = obj2->objs->objs[0].model->min.vy; + pos[5].vz = 2 * obj2->objs->objs[0].model->max.vz / 3; + + pos[6].vx = vx; + pos[6].vy = obj2->objs->objs[0].model->min.vy; + pos[6].vz = obj2->objs->objs[0].model->max.vz / 3; + + pos[7].vx = vx; + pos[7].vy = obj2->objs->objs[0].model->min.vy; + pos[7].vz = 0; + + pos[8].vx = vx; + pos[8].vy = obj2->objs->objs[0].model->min.vy; + pos[8].vz = obj2->objs->objs[0].model->min.vz / 3; + + pos[9].vx = vx; + pos[9].vy = obj2->objs->objs[0].model->min.vy; + pos[9].vz = 2 * obj2->objs->objs[0].model->min.vz / 3; +} diff --git a/components/mgs/mgs/source/kojo/mgrexhzd.c b/components/mgs/mgs/source/kojo/mgrexhzd.c new file mode 100644 index 00000000..eed11bba --- /dev/null +++ b/components/mgs/mgs/source/kojo/mgrexhzd.c @@ -0,0 +1,37 @@ +#if 0 +/****************************************************************************** + * System : METALGEAR^3 for PlayStation + * Computer : PlayStation + * OS : PlayStation + * Compiler : psyq + * Module : メタルギアREX 地形 + */ + +/****************************************************************************** + * included + */ + +#include +#include +#include +#include +#include +#include +#include "game.h" + +#include "common.h" +#include "mgrex.h" + +/****************************************************************************** + * publics + */ + +/****************************************************************************** + * 壁当たり検査 + * caution : この関数は処理能力を要求します。 + */ +int MGRexOnlineHazardCheck( /* HZD_OnlineHazardCheck 関数戻り値 */ + HZD_HDL* lpHazard, /* 当たり???????? */ + SVECTOR* lpSVect1, /* 直線開始座 +*/ +#endif diff --git a/components/mgs/mgs/source/kojo/sstorm.c b/components/mgs/mgs/source/kojo/sstorm.c new file mode 100644 index 00000000..278a0f93 --- /dev/null +++ b/components/mgs/mgs/source/kojo/sstorm.c @@ -0,0 +1,401 @@ +/****************************************************************************** + * System : METALGEAR^3 for PlayStation + * Computer : PlayStation + * OS : PlayStation + * Compiler : psyq + * Module : 雪嵐 + */ + +/****************************************************************************** + * included + */ + +#include "sstorm.h" + +#include +#include +#include +#include +#include + +#include "common.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "anime/animconv/anime.h" +#include "game/game.h" + +extern UnkCameraStruct2 gUnkCameraStruct2_800B7868; + +/****************************************************************************** + * definitions and typedefs and structures + */ + +typedef struct tagSNOWSTORM +{ + GV_ACT actor; + int field_20; + int field_24; + short field_28; + short field_2A; + short field_2C; + short field_2E; + short field_30; + short field_32; + short field_34; + short field_36; + int field_38; + int field_3C; + BOOL bActive; + int field_44; + SVECTOR field_48; + ANIMATION anime; + char field_6C[21]; + int field_84; + int field_88; + int field_8C; + int field_90; + int field_94; + int field_98; + int field_9C; + int field_A0; + int field_A4; + int field_A8; + int field_AC; + int field_B0; + int field_B4; + int field_B8; + PRESCRIPT pre_script; +} SNOWSTORM, *LPSNOWSTORM; + +/****************************************************************************** + * locals + */ + +static char s11i_dword_800C36B4[] = { + 0x00,0x10,0x01,0x00, 0x05,0x01,0xFE,0x0C, + 0x00,0x00,0x01,0xFF, 0x08,0x00,0x00,0x00, + 0x02,0x00,0x01,0x0D, 0x0F +}; + +/****************************************************************************** + * functions + */ + +static void Act(LPSNOWSTORM lpAct); +static void Die(LPSNOWSTORM lpAct); +static int CheckMessage(unsigned short name, int nhashes, unsigned short *hashes); + +/****************************************************************************** + * publics + */ + +void *NewSnowStorm(int arg0, int arg1) +{ + LPSNOWSTORM lpAct; + SVECTOR svec1, svec2; + short field_30; + int xw, xw2; + char *param; + + lpAct = GV_NewActor(GV_ACTOR_USER, sizeof(SNOWSTORM)); + if (lpAct == NULL) + { + return NULL; + } + + GV_SetNamedActor(&lpAct->actor, Act, Die, "sstorm.c"); + + lpAct->bActive = TRUE; + lpAct->field_38 = 4; + lpAct->field_20 = arg0; + lpAct->field_24 = arg1; + lpAct->field_3C = 2000; + + if (GCL_GetOption('a')) + { + param = GCL_NextStr(); + if (param) + { + lpAct->field_28 = GCL_StrToInt(param); + } + param = GCL_NextStr(); + if (param) + { + lpAct->field_2A = GCL_StrToInt(param); + } + param = GCL_NextStr(); + if (param) + { + lpAct->field_2C = GCL_StrToInt(param); + } + param = GCL_NextStr(); + if (param) + { + lpAct->field_30 = GCL_StrToInt(param); + } + param = GCL_NextStr(); + if (param) + { + lpAct->field_32 = GCL_StrToInt(param); + } + param = GCL_NextStr(); + if (param) + { + lpAct->field_34 = GCL_StrToInt(param); + } + } + + if (GCL_GetOption('e')) + { + param = GCL_NextStr(); + if (param) + { + lpAct->field_38 = GCL_StrToInt(param); + } + param = GCL_NextStr(); + if (param) + { + lpAct->field_3C = GCL_StrToInt(param); + } + } + + if (GCL_GetOption('p')) + { + param = GCL_NextStr(); + if (param) + { + svec2.vx = (GCL_StrToInt(param) << 12) / 360; + } + param = GCL_NextStr(); + if (param) + { + svec2.vy = (GCL_StrToInt(param) << 12) / 360; + } + param = GCL_NextStr(); + if (param) + { + svec2.vz = (GCL_StrToInt(param) << 12) / 360; + } + param = GCL_NextStr(); + if (param) + { + svec1.vx = 0; + svec1.vy = 0; + svec1.vz = 0; + DG_SetPos2(&svec1, &svec2); + svec1.vz = GCL_StrToInt(param) / 3; + DG_PutVector(&svec1, &svec1, 1); + lpAct->field_48.vx = svec1.vx; + lpAct->field_48.vy = svec1.vy; + lpAct->field_48.vz = svec1.vz; + } + } + + field_30 = lpAct->field_30; + xw = lpAct->field_32 - lpAct->field_2A; + + lpAct->anime.size_w = field_30 - lpAct->field_28; + if (xw < lpAct->anime.size_w) + { + xw = lpAct->anime.size_w; + } + lpAct->anime.size_w = xw; + + xw2 = lpAct->field_34 - lpAct->field_2C; + if (xw2 < lpAct->anime.size_w) + { + xw2 = lpAct->anime.size_w; + } + lpAct->anime.size_w = xw2; + + lpAct->anime.texdev_x = 1; + lpAct->anime.texdev_y = 1; + lpAct->anime.n_anime = 1; + lpAct->anime.n_verts = 1; + lpAct->anime.amb = 3; + lpAct->anime.v = 0xFF; + lpAct->anime.pre_script = &lpAct->pre_script; + lpAct->anime.script = lpAct->field_6C; + lpAct->anime.raise = lpAct->field_3C; + lpAct->anime.size_h = lpAct->anime.size_w; + + return (void *)lpAct; +} + +/****************************************************************************** + * statics + */ + +static void Act(LPSNOWSTORM lpAct) +{ + VECTOR vect; + SVECTOR svect1; + SVECTOR svect2; + unsigned short mesg_list[2]; + int div; + unsigned int xw; + int lhs, rhs; + + GM_CurrentMap = lpAct->field_24; + lpAct->field_44++; + + mesg_list[0] = GV_StrCode("run"); + mesg_list[1] = GV_StrCode("stop"); + + switch (CheckMessage(lpAct->field_20, COUNTOF(mesg_list), mesg_list)) + { + case 0: + lpAct->bActive = TRUE; + printf("s-end\n"); + break; + case 1: + lpAct->bActive = FALSE; + printf("s-start\n"); + break; + } + + if (lpAct->bActive == FALSE || lpAct->field_44 % lpAct->field_38 != 0) + { + return; + } + + if (GM_lpsvectWind == NULL) + { + lpAct->pre_script.speed = lpAct->field_48; + } + else + { + lpAct->pre_script.speed = *GM_lpsvectWind; + } + vect.vx = lpAct->field_28 + (rand() & 0xFF) * (lpAct->field_30 - lpAct->field_28) / 256; + vect.vy = lpAct->field_2A + (rand() & 0xFF) * (lpAct->field_32 - lpAct->field_2A) / 256; + vect.vz = lpAct->field_2C + (rand() & 0xFF) * (lpAct->field_34 - lpAct->field_2C) / 256; + + svect1 = lpAct->pre_script.speed; + svect1.vx = svect1.vx < 0 ? -svect1.vx : svect1.vx; + svect1.vy = svect1.vy < 0 ? -svect1.vy : svect1.vy; + svect1.vz = svect1.vz < 0 ? -svect1.vz : svect1.vz; + + if (svect1.vx > svect1.vy && svect1.vz < svect1.vx) + { + if (lpAct->pre_script.speed.vx < 0) + { + vect.vx = lpAct->field_30 + lpAct->anime.size_w / 4; + } + else + { + vect.vx = lpAct->field_28 - lpAct->anime.size_w / 4; + } + } + else if (svect1.vy > svect1.vx && svect1.vz < svect1.vy) + { + if (lpAct->pre_script.speed.vy < 0) + { + vect.vy = lpAct->field_32 + lpAct->anime.size_w / 4; + } + else + { + vect.vy = lpAct->field_2A - lpAct->anime.size_w / 4; + } + } + else + { + if (lpAct->pre_script.speed.vz < 0) + { + vect.vz = lpAct->field_34 + lpAct->anime.size_w / 4; + } + else + { + vect.vz = lpAct->field_2C - lpAct->anime.size_w / 4; + } + } + + vect.vx += GM_PlayerPosition.vx; + vect.vy += GM_PlayerPosition.vy; + vect.vz += GM_PlayerPosition.vz; + + if (GM_PlayerStatus & PLAYER_WATCH) + { + svect1.vx = gUnkCameraStruct2_800B7868.target.vx - gUnkCameraStruct2_800B7868.position.vx; + svect1.vy = gUnkCameraStruct2_800B7868.target.vy - gUnkCameraStruct2_800B7868.position.vy; + svect1.vz = gUnkCameraStruct2_800B7868.target.vz - gUnkCameraStruct2_800B7868.position.vz; + + svect2.vx = -ratan2(svect1.vy, SquareRoot0(svect1.vx * svect1.vx + svect1.vz * svect1.vz)); + svect2.vy = ratan2(svect1.vx, svect1.vz); + svect2.vz = 0; + + memset(&svect1, 0, sizeof(SVECTOR)); + DG_SetPos2(&svect1, &svect2); + + svect1.vz = (lpAct->anime.size_w * 2) / 3; + DG_PutVector(&svect1, &svect1, 1); + + vect.vx += svect1.vx; + vect.vy += svect1.vy; + vect.vz += svect1.vz; + } + vect.vx = MIN(vect.vx, 32000); + vect.vx = MAX(vect.vx, -32000); + vect.vy = MIN(vect.vy, 32000); + vect.vy = MAX(vect.vy, -32000); + vect.vz = MIN(vect.vz, 32000); + vect.vz = MAX(vect.vz, -32000); + + lpAct->pre_script.pos.vx = vect.vx; + lpAct->pre_script.pos.vy = vect.vy; + lpAct->pre_script.pos.vz = vect.vz; + + div = SquareRoot0(lpAct->pre_script.speed.vx * lpAct->pre_script.speed.vx + + lpAct->pre_script.speed.vy * lpAct->pre_script.speed.vy + + lpAct->pre_script.speed.vz * lpAct->pre_script.speed.vz); + memcpy(lpAct->field_6C, s11i_dword_800C36B4, 21); + xw = lpAct->anime.size_w / div + 1; + + lhs = xw >> 8; + rhs = (xw & 0xFF) << 8; + *(short *)&lpAct->field_6C[8] = (lhs & 0xFF) | rhs; + + switch (rand() % 3) + { + case 0: + lpAct->anime.tex = GV_StrCode("snow_ex1"); + break; + case 1: + lpAct->anime.tex = GV_StrCode("snow_ex2"); + break; + case 2: + lpAct->anime.tex = GV_StrCode("snow_ex3"); + break; + } + NewAnime(NULL, NULL, &lpAct->anime); +} + +static void Die(LPSNOWSTORM lpAct) +{ + /* do nothing */ +} + +// Less optimized version of d03a_red_alrt_800C437C (lol) +static int CheckMessage(unsigned short name, int nhashes, unsigned short *hashes) +{ + GV_MSG *msg; + int nmsgs; + int found; + int i; + + found = -1; + nmsgs = GV_ReceiveMessage(name, &msg); + + for (; nmsgs > 0; nmsgs--, msg++) + { + for (i = 0; i < nhashes; i++) + { + if (msg->message[0] == hashes[i]) + { + found = i; + } + } + } + + return found; +} diff --git a/components/mgs/mgs/source/kojo/sstorm.h b/components/mgs/mgs/source/kojo/sstorm.h new file mode 100644 index 00000000..8e0bcd70 --- /dev/null +++ b/components/mgs/mgs/source/kojo/sstorm.h @@ -0,0 +1,6 @@ +#ifndef __MGS_KOJO_SSTORM_H__ +#define __MGS_KOJO_SSTORM_H__ + +void *NewSnowStorm(int arg0, int arg1); + +#endif // __MGS_KOJO_SSTORM_H__ diff --git a/components/mgs/mgs/source/kojo/unknown.c b/components/mgs/mgs/source/kojo/unknown.c new file mode 100644 index 00000000..7d764492 --- /dev/null +++ b/components/mgs/mgs/source/kojo/unknown.c @@ -0,0 +1,763 @@ +#include "kojo/demo.h" + +/****************************************************************************** + * definitions and typedefs and structures + */ + +#define __detx(a, b, c) (((b).y - (a).y) * ((c).z - (b).z) - ((b).z - (a).z) * ((c).y - (b).y)) +#define __dety(a, b, c) (((b).z - (a).z) * ((c).x - (b).x) - ((b).x - (a).x) * ((c).z - (b).z)) +#define __detz(a, b, c) (((b).x - (a).x) * ((c).y - (b).y) - ((b).y - (a).y) * ((c).x - (b).x)) + +/****************************************************************************** + * publics + */ + +void sub_8007E1C0(HZD_VEC *pOut, HZD_VEC *pOut2, MATRIX *pTransform, SVECTOR *pMin, SVECTOR *pMax) +{ + VECTOR cross; + SVECTOR *va; + SVECTOR *vb; + int idx; + int i; + int len; + int min, max; + + va = (SVECTOR *)getScratchAddr(0); + vb = (SVECTOR *)getScratchAddr(16); + + va[0].vx = pMin->vx; + va[0].vy = pMin->vy; + va[0].vz = pMin->vz; + + va[1].vx = pMin->vx; + va[1].vy = pMin->vy; + va[1].vz = pMax->vz; + + va[2].vx = pMax->vx; + va[2].vy = pMin->vy; + va[2].vz = pMax->vz; + + va[3].vx = pMax->vx; + va[3].vy = pMin->vy; + va[3].vz = pMin->vz; + + va[4].vx = pMin->vx; + va[4].vy = pMax->vy; + va[4].vz = pMin->vz; + + va[5].vx = pMin->vx; + va[5].vy = pMax->vy; + va[5].vz = pMax->vz; + + va[6].vx = pMax->vx; + va[6].vy = pMax->vy; + va[6].vz = pMax->vz; + + va[7].vx = pMax->vx; + va[7].vy = pMax->vy; + va[7].vz = pMin->vz; + + DG_SetPos(pTransform); + DG_PutVector(va, va, 8); + + vb[0] = va[0]; + + idx = 0; + for (i = 0; i < 8; i++) + { + if ((va[i].vx < vb[0].vx) || ((va[i].vx == vb[0].vx) && (va[i].vz < vb[0].vz))) + { + idx = i; + vb[0] = va[i]; + } + } + + if (idx >= 4) + { + idx -= 4; + } + + vb[4] = vb[0]; + + vb[0].vy = MIN(va[idx].vy, va[idx + 4].vy); + vb[4].vy = MAX(va[idx].vy, va[idx + 4].vy); + + if (++idx >= 4) + { + idx = 0; + } + + vb[1] = (va[idx].vz >= va[idx + 4].vz) ? va[idx] : va[idx + 4]; + vb[5] = vb[1]; + vb[1].vy = MIN(va[idx].vy, va[idx + 4].vy); + vb[5].vy = MAX(va[idx].vy, va[idx + 4].vy); + + if (++idx >= 4) + { + idx = 0; + } + + vb[2] = (va[idx].vx >= va[idx + 4].vx) ? va[idx] : va[idx + 4]; + vb[6] = vb[2]; + vb[2].vy = MIN(va[idx].vy, va[idx + 4].vy); + vb[6].vy = MAX(va[idx].vy, va[idx + 4].vy); + + if (++idx >= 4) + { + idx = 0; + } + + vb[3] = (va[idx].vz <= va[idx + 4].vz) ? va[idx] : va[idx + 4]; + vb[7] = vb[3]; + vb[3].vy = MIN(va[idx].vy, va[idx + 4].vy); + vb[7].vy = MAX(va[idx].vy, va[idx + 4].vy); + + pOut[0].x = vb[0].vx; + pOut[0].z = vb[0].vz; + pOut[0].y = vb[0].vy; + pOut[0].h = vb[4].vy - vb[0].vy; + + pOut[1].x = vb[1].vx; + pOut[1].z = vb[1].vz; + pOut[1].y = vb[1].vy; + pOut[1].h = vb[5].vy - vb[1].vy; + + pOut[2].x = vb[1].vx; + pOut[2].z = vb[1].vz; + pOut[2].y = vb[1].vy; + pOut[2].h = vb[5].vy - vb[1].vy; + + pOut[3].x = vb[2].vx; + pOut[3].z = vb[2].vz; + pOut[3].y = vb[2].vy; + pOut[3].h = vb[6].vy - vb[2].vy; + + pOut[4].x = vb[3].vx; + pOut[4].z = vb[3].vz; + pOut[4].y = vb[3].vy; + pOut[4].h = vb[7].vy - vb[3].vy; + + pOut[5].x = vb[2].vx; + pOut[5].z = vb[2].vz; + pOut[5].y = vb[2].vy; + pOut[5].h = vb[6].vy - vb[2].vy; + + pOut[6].x = vb[0].vx; + pOut[6].z = vb[0].vz; + pOut[6].y = vb[0].vy; + pOut[6].h = vb[4].vy - vb[0].vy; + + pOut[7].x = vb[3].vx; + pOut[7].z = vb[3].vz; + pOut[7].y = vb[3].vy; + pOut[7].h = vb[7].vy - vb[3].vy; + + pOut2[2].x = vb[0].vx; + pOut2[2].z = vb[0].vz; + pOut2[2].y = vb[0].vy; + + pOut2[3].x = vb[3].vx; + pOut2[3].z = vb[3].vz; + pOut2[3].y = vb[3].vy; + + pOut2[4].x = vb[2].vx; + pOut2[4].z = vb[2].vz; + pOut2[4].y = vb[2].vy; + + pOut2[5].x = vb[1].vx; + pOut2[5].z = vb[1].vz; + pOut2[5].y = vb[1].vy; + pOut2[5].h = 0; + + pOut2[0] = pOut2[2]; + pOut2[0].h = 0; + + pOut2[0].x = MIN(pOut2[0].x, pOut2[3].x); + pOut2[0].x = MIN(pOut2[0].x, pOut2[4].x); + pOut2[0].x = MIN(pOut2[0].x, pOut2[5].x); + + pOut2[0].z = MIN(pOut2[0].z, pOut2[3].z); + pOut2[0].z = MIN(pOut2[0].z, pOut2[4].z); + pOut2[0].z = MIN(pOut2[0].z, pOut2[5].z); + + pOut2[0].y = MIN(pOut2[0].y, pOut2[3].y); + pOut2[0].y = MIN(pOut2[0].y, pOut2[4].y); + pOut2[0].y = MIN(pOut2[0].y, pOut2[5].y); + + pOut2[1] = pOut2[2]; + pOut2[1].h = 0; + + pOut2[1].x = MAX(pOut2[1].x, pOut2[3].x); + pOut2[1].x = MAX(pOut2[1].x, pOut2[4].x); + pOut2[1].x = MAX(pOut2[1].x, pOut2[5].x); + + pOut2[1].z = MAX(pOut2[1].z, pOut2[3].z); + pOut2[1].z = MAX(pOut2[1].z, pOut2[4].z); + pOut2[1].z = MAX(pOut2[1].z, pOut2[5].z); + + pOut2[1].y = MAX(pOut2[1].y, pOut2[3].y); + pOut2[1].y = MAX(pOut2[1].y, pOut2[4].y); + pOut2[1].y = MAX(pOut2[1].y, pOut2[5].y); + + cross.vx = __detx(pOut2[2], pOut2[3], pOut2[4]) >> 16; + cross.vy = __dety(pOut2[2], pOut2[3], pOut2[4]) >> 16; + cross.vz = __detz(pOut2[2], pOut2[3], pOut2[4]) >> 16; + + len = cross.vx * cross.vx + cross.vy * cross.vy + cross.vz * cross.vz; + len = SquareRoot0(len); + if (len == 0) + { + len = 1; + } + + pOut2[2].h = (cross.vx * 255) / len; + pOut2[3].h = (cross.vz * 255) / len; + pOut2[4].h = (cross.vy * 255) / len; + if (pOut2[4].h == 0) + { + pOut2[4].h = 1; + } + + pOut2[8].x = vb[4].vx; + pOut2[8].z = vb[4].vz; + pOut2[8].y = vb[4].vy; + + pOut2[9].x = vb[7].vx; + pOut2[9].z = vb[7].vz; + pOut2[9].y = vb[7].vy; + + pOut2[10].x = vb[6].vx; + pOut2[10].z = vb[6].vz; + pOut2[10].y = vb[6].vy; + + pOut2[11].x = vb[5].vx; + pOut2[11].z = vb[5].vz; + pOut2[11].y = vb[5].vy; + pOut2[11].h = 0; + + pOut2[6].x = pOut2[0].x; + pOut2[6].z = pOut2[0].z; + pOut2[6].h = 0; + + min = MIN(pOut2[8].y, pOut2[9].y); + min = MIN(min, pOut2[10].y); + min = MIN(min, pOut2[11].y); + pOut2[6].y = min; + + pOut2[7].x = pOut2[1].x; + pOut2[7].z = pOut2[1].z; + pOut2[7].h = 0; + + max = MAX(pOut2[8].y, pOut2[9].y); + max = MAX(max, pOut2[10].y); + max = MAX(max, pOut2[11].y); + pOut2[7].y = max; + + pOut2[8].h = pOut2[2].h; + pOut2[9].h = pOut2[3].h; + pOut2[10].h = pOut2[4].h; +} + +void sub_8007EF34(SVECTOR *pOutVec1, SVECTOR *pOutVec2, MATRIX *pInMtx) +{ + int tmp; + int distance; + MATRIX rotMtx; + SVECTOR vec1; + SVECTOR vec2; + ReadRotMatrix(&rotMtx); + pOutVec1->vx = pInMtx->t[0]; + pOutVec1->vy = pInMtx->t[1]; + pOutVec1->vz = pInMtx->t[2]; + DG_SetPos(pInMtx); + vec1.vx = 0; + vec1.vy = 0; + vec1.vz = 0; + vec2.vx = 0; + vec2.vy = 0; + vec2.vz = 100; + DG_PutVector(&vec1, &vec1, 1); + DG_PutVector(&vec2, &vec2, 1); + tmp = (short) (vec2.vx - vec1.vx) * (short) (vec2.vx - vec1.vx); + vec2.vx -= vec1.vx; + vec2.vy -= vec1.vy; + vec2.vz -= vec1.vz; + distance = SquareRoot0(tmp + (vec2.vz * vec2.vz)); + pOutVec2->vx = -((short) ratan2(vec2.vy, distance)); + pOutVec2->vy = ratan2(vec2.vx, vec2.vz); + pOutVec2->vz = 0; + DG_SetPos(&rotMtx); +} + +void sub_8007F06C(DG_VECTOR *param_1, VECTOR *param_2, VECTOR *param_3) +{ + param_1[0].vx = param_2->vx; + param_1[0].vy = param_2->vy; + param_1[0].vz = param_2->vz; + param_1[1].vx = param_3->vx - param_2->vx; + param_1[1].vy = param_3->vy - param_2->vy; + param_1[1].vz = param_3->vz - param_2->vz; +} + +void sub_8007F0D0(VECTOR *out, VECTOR *a, VECTOR *b, VECTOR *c) +{ + out->vx = (b->vy - a->vy) * (c->vz - b->vz) - (b->vz - a->vz) * (c->vy - b->vy); + out->vy = (b->vz - a->vz) * (c->vx - b->vx) - (b->vx - a->vx) * (c->vz - b->vz); + out->vz = (b->vx - a->vx) * (c->vy - b->vy) - (b->vy - a->vy) * (c->vx - b->vx); + out->pad = out->vx * -a->vx + out->vy * -a->vy + out->vz * -a->vz; +} + +VECTOR * sub_8007F1DC(VECTOR *out, DG_VECTOR *arg1, VECTOR *arg2) +{ + VECTOR vec; + int area; + int weight; + + area = arg2->vx * arg1[1].vx + arg2->vy * arg1[1].vy + arg2->vz * arg1[1].vz; + if (area == 0) + { + vec.vx = 0x7FFFFFFF; + vec.vy = 0x7FFFFFFF; + vec.vz = 0x7FFFFFFF; + } + else + { + weight = ((-arg2->vx * arg1[0].vx - arg2->vy * arg1[0].vy - arg2->vz * arg1[0].vz - arg2->pad) << 12) / area; + vec.vx = arg1->vx + ((arg1[1].vx * weight) >> 12); + vec.vy = arg1->vy + ((arg1[1].vy * weight) >> 12); + vec.vz = arg1->vz + ((arg1[1].vz * weight) >> 12); + } + + *out = vec; + return out; +} + +/****************************************************************************** + * publics + */ + +void InitChain(ACTNODE *root) +{ + if (root) + { + root->prev = root; + root->next = root; + } +} + +void NextChain(ACTNODE *root, ACTNODE *node) +{ + if (root && node) + { + node->prev = root->prev; + node->next = root; + + if (root->prev == root) + { + root->next = node; + } + else + { + root->prev->next = node; + } + + root->prev = node; + } +} + +void DeleteChain(ACTNODE *root, ACTNODE *node) +{ + int equal; + + if (root && node) + { + /* compiler removes the check if inlined */ + equal = node->prev == root; + + if (equal) + { + root->next = node->next; + } + else + { + node->prev->next = node->next; + } + + node->next->prev = node->prev; + InitChain(node); + } +} + +/****************************************************************************** + * definitions and typedefs and structures + */ + +typedef struct +{ + char pad[16]; + SVECTOR pre[8]; + SVECTOR post[8]; + HZD_SEG segment; + SVECTOR unknown; +} SCRPAD_DATA_8007F3F8; + +SVECTOR svector_8009F7AC = {0, 255, 0, 0}; + +/****************************************************************************** + * publics + */ + +void sub_8007F3F8(HZD_SEG *pIn, HZD_FLR *pOut, MATRIX *pTransform, SVECTOR *pMin, SVECTOR *pMax) +{ + SCRPAD_DATA_8007F3F8 *scrpad; + SVECTOR *pIter; + int count; + int a, b, c, d; + int hzd_h; + int min, v1, v2; + + scrpad = (SCRPAD_DATA_8007F3F8 *)getScratchAddr(0); + + DG_SetPos(pTransform); + DG_RotVector(&svector_8009F7AC, &scrpad->unknown, 1); + + scrpad->pre[0].vx = pMin->vx; + scrpad->pre[0].vy = pMin->vy; + scrpad->pre[0].vz = pMin->vz; + + scrpad->pre[1].vx = pMin->vx; + scrpad->pre[1].vy = pMin->vy; + scrpad->pre[1].vz = pMax->vz; + + scrpad->pre[2].vx = pMax->vx; + scrpad->pre[2].vy = pMin->vy; + scrpad->pre[2].vz = pMax->vz; + + scrpad->pre[3].vx = pMax->vx; + scrpad->pre[3].vy = pMin->vy; + scrpad->pre[3].vz = pMin->vz; + + scrpad->pre[4].vx = pMin->vx; + scrpad->pre[4].vy = pMax->vy; + scrpad->pre[4].vz = pMin->vz; + + scrpad->pre[5].vx = pMin->vx; + scrpad->pre[5].vy = pMax->vy; + scrpad->pre[5].vz = pMax->vz; + + scrpad->pre[6].vx = pMax->vx; + scrpad->pre[6].vy = pMax->vy; + scrpad->pre[6].vz = pMax->vz; + + scrpad->pre[7].vx = pMax->vx; + scrpad->pre[7].vy = pMax->vy; + scrpad->pre[7].vz = pMin->vz; + + DG_PutVector(scrpad->pre, scrpad->post, 8); + + pIter = scrpad->post; + for (count = 4; count > 0; count--, pIter++) + { + a = pIter[0].vx; + b = pIter[4].vx; + c = pIter[0].vz; + d = pIter[4].vz; + + pIter[0].vx = pIter[4].vx = (a + b) / 2; + pIter[0].vz = pIter[4].vz = (c + d) / 2; + } + + hzd_h = scrpad->post[4].vy - scrpad->post[0].vy; + + scrpad->segment.p1.x = scrpad->post[0].vx; + scrpad->segment.p1.z = scrpad->post[0].vz; + scrpad->segment.p1.y = scrpad->post[0].vy; + scrpad->segment.p1.h = hzd_h; + + scrpad->segment.p2.x = scrpad->post[1].vx; + scrpad->segment.p2.z = scrpad->post[1].vz; + scrpad->segment.p2.y = scrpad->post[1].vy; + scrpad->segment.p2.h = hzd_h; + + HZD_SetDynamicSegment(&scrpad->segment, pIn); + + scrpad->segment.p1.x = scrpad->post[1].vx; + scrpad->segment.p1.z = scrpad->post[1].vz; + scrpad->segment.p1.y = scrpad->post[1].vy; + scrpad->segment.p1.h = hzd_h; + + scrpad->segment.p2.x = scrpad->post[2].vx; + scrpad->segment.p2.z = scrpad->post[2].vz; + scrpad->segment.p2.y = scrpad->post[2].vy; + scrpad->segment.p2.h = hzd_h; + + HZD_SetDynamicSegment(&scrpad->segment, pIn + 1); + + scrpad->segment.p1.x = scrpad->post[3].vx; + scrpad->segment.p1.z = scrpad->post[3].vz; + scrpad->segment.p1.y = scrpad->post[3].vy; + scrpad->segment.p1.h = hzd_h; + + scrpad->segment.p2.x = scrpad->post[2].vx; + scrpad->segment.p2.z = scrpad->post[2].vz; + scrpad->segment.p2.y = scrpad->post[2].vy; + scrpad->segment.p2.h = hzd_h; + + HZD_SetDynamicSegment(&scrpad->segment, pIn + 2); + + scrpad->segment.p1.x = scrpad->post[0].vx; + scrpad->segment.p1.z = scrpad->post[0].vz; + scrpad->segment.p1.y = scrpad->post[0].vy; + scrpad->segment.p1.h = hzd_h; + + scrpad->segment.p2.x = scrpad->post[3].vx; + scrpad->segment.p2.z = scrpad->post[3].vz; + scrpad->segment.p2.y = scrpad->post[3].vy; + scrpad->segment.p2.h = hzd_h; + + HZD_SetDynamicSegment(&scrpad->segment, pIn + 3); + + pOut[0].p1.x = scrpad->post[0].vx; + pOut[0].p1.z = scrpad->post[0].vz; + pOut[0].p1.y = scrpad->post[0].vy; + + pOut[0].p2.x = scrpad->post[3].vx; + pOut[0].p2.z = scrpad->post[3].vz; + pOut[0].p2.y = scrpad->post[3].vy; + + pOut[0].p3.x = scrpad->post[2].vx; + pOut[0].p3.z = scrpad->post[2].vz; + pOut[0].p3.y = scrpad->post[2].vy; + + pOut[0].p4.x = scrpad->post[1].vx; + pOut[0].p4.z = scrpad->post[1].vz; + pOut[0].p4.y = scrpad->post[1].vy; + pOut[0].p4.h = 0; + + pOut[1].p1.x = scrpad->post[4].vx; + pOut[1].p1.z = scrpad->post[4].vz; + pOut[1].p1.y = scrpad->post[4].vy; + + pOut[1].p2.x = scrpad->post[7].vx; + pOut[1].p2.z = scrpad->post[7].vz; + pOut[1].p2.y = scrpad->post[7].vy; + + pOut[1].p3.x = scrpad->post[6].vx; + pOut[1].p3.z = scrpad->post[6].vz; + pOut[1].p3.y = scrpad->post[6].vy; + + pOut[1].p4.x = scrpad->post[5].vx; + pOut[1].p4.z = scrpad->post[5].vz; + pOut[1].p4.y = scrpad->post[5].vy; + pOut[1].p4.h = 0; + + pOut[0].b1.h = 0; + pOut[0].b2.h = 0; + pOut[1].b1.h = 0; + pOut[1].b2.h = 0; + + //start + v1 = scrpad->post[0].vx; + + min = scrpad->post[1].vx; + if (min > v1) + { + min = v1; + } + + v2 = scrpad->post[1].vx; + if (v2 < v1) + { + v2 = v1; + } + v1 = v2; + + v2 = scrpad->post[2].vx; + if (v2 > min) + { + v2 = min; + } + min = v2; + + v2 = scrpad->post[2].vx; + if (v1 > v2) + { + v2 = v1; + } + v1 = v2; + + v2 = scrpad->post[3].vx; + if (v2 > min) + { + v2 = min; + } + min = v2; + + v2 = scrpad->post[3].vx; + if (v1 > v2) + { + v2 = v1; + } + v1 = v2; + + pOut[0].b1.x = min; + pOut[1].b1.x = min; + pOut[0].b2.x = v1; + pOut[1].b2.x = v1; + + + //next + v1 = scrpad->post[0].vy; + + min = scrpad->post[1].vy; + if (min > v1) + { + min = v1; + } + + v2 = scrpad->post[1].vy; + if (v2 < v1) + { + v2 = v1; + } + v1 = v2; + + v2 = scrpad->post[2].vy; + if (v2 > min) + { + v2 = min; + } + min = v2; + + v2 = scrpad->post[2].vy; + if (v1 > v2) + { + v2 = v1; + } + v1 = v2; + + v2 = scrpad->post[3].vy; + if (v2 > min) + { + v2 = min; + } + min = v2; + + v2 = scrpad->post[3].vy; + if (v1 > v2) + { + v2 = v1; + } + v1 = v2; + + pOut[0].b1.y = min; + pOut[0].b2.y = v1; + + //next + v1 = scrpad->post[0].vz; + + min = scrpad->post[1].vz; + if (min > v1) + { + min = v1; + } + + v2 = scrpad->post[1].vz; + if (v2 < v1) + { + v2 = v1; + } + v1 = v2; + + v2 = scrpad->post[2].vz; + if (v2 > min) + { + v2 = min; + } + min = v2; + + v2 = scrpad->post[2].vz; + if (v1 > v2) + { + v2 = v1; + } + v1 = v2; + + v2 = scrpad->post[3].vz; + if (v2 > min) + { + v2 = min; + } + min = v2; + + v2 = scrpad->post[3].vz; + if (v1 > v2) + { + v2 = v1; + } + v1 = v2; + + pOut[0].b1.z = min; + pOut[1].b1.z = min; + pOut[0].b2.z = v1; + pOut[1].b2.z = v1; + + //next + v1 = scrpad->post[4].vy; + min = scrpad->post[5].vy; + if (min > v1) + { + min = v1; + } + + v2 = scrpad->post[5].vy; + if (v2 < v1) + { + v2 = v1; + } + v1 = v2; + + v2 = scrpad->post[6].vy; + if (v2 > min) + { + v2 = min; + } + min = v2; + + v2 = scrpad->post[6].vy; + if (v1 > v2) + { + v2 = v1; + } + v1 = v2; + + v2 = scrpad->post[7].vy; + if (v2 > min) + { + v2 = min; + } + min = v2; + + v2 = scrpad->post[7].vy; + if (v1 > v2) + { + v2 = v1; + } + v1 = v2; + + pOut[1].b1.y = min; + pOut[1].b2.y = v1; + + if (scrpad->unknown.vy == 0) + { + scrpad->unknown.vy = 1; + } + + pOut[0].p1.h = scrpad->unknown.vx; + pOut[0].p2.h = scrpad->unknown.vz; + pOut[0].p3.h = scrpad->unknown.vy; + + pOut[1].p1.h = scrpad->unknown.vx; + pOut[1].p2.h = scrpad->unknown.vz; + pOut[1].p3.h = scrpad->unknown.vy; +} diff --git a/components/mgs/mgs/source/libdg/bound.c b/components/mgs/mgs/source/libdg/bound.c new file mode 100644 index 00000000..5225d354 --- /dev/null +++ b/components/mgs/mgs/source/libdg/bound.c @@ -0,0 +1,572 @@ +#include "libdg.h" +#include "common.h" +#include "game/game.h" + +#ifdef __psyz +/* A bounding box that failed the on-screen test is not always really off + * screen. + * + * The eight corners are projected through the GTE, and the GTE pins a screen + * coordinate at -1024 / +1023 whenever the perspective divide overflows -- + * which it does for any vertex nearer the eye than H/2, H being the projection + * distance DG_Clip installs (320 here, so anything within 160 units). Worse, a + * corner BEHIND the eye comes back with its sign flipped, so an object the + * player is standing inside can land with all eight corners on one side of the + * screen and read as "entirely off to the right". + * + * The console has the same GTE and the same blind spot; it just rarely matters + * there, because on a 30 fps machine the camera is where the designers put it. + * Here the frame rate wanders, so an object sitting on the boundary saturates + * on one frame and not the next: it winks in and out. That is the "algunas + * partes tiemblan" -- geometry flickering at close range, not a texture or + * timing problem. + * + * So when the box is saturated, do not trust it. Answer 1 (visible, needs + * clipping) instead of 0 (cull) and let the rasterizer decide per pixel, which + * it has to do for these primitives anyway. It costs the objects nearest the + * camera -- the large ones -- but drawing something too often is a frame-rate + * bill, while culling it wrongly is a hole in the wall. + * + * mgs_dbg_cull_sat boxes kept because the projection overflowed + * mgs_dbg_cull_off boxes genuinely off screen, culled + */ +unsigned mgs_dbg_cull_sat, mgs_dbg_cull_off; +unsigned mgs_dbg_clip_dist; + +/* Does this box actually straddle the near plane? + * + * The first version of this test asked whether a projected X or Y had come + * back pinned at the GTE's -1024/+1023 limit. That is the symptom, not the + * cause: a big object far away -- a corridor wall running past both screen + * edges -- clamps exactly the same way while its projection is perfectly + * trustworthy. It kept 97% of everything the screen test rejected and cost 40% + * more rasterizing for geometry that really was out of view. + * + * The cause is available directly. gte_stsz3c has already left the eight + * corners' view-space Z at SCRPAD+0x6C, and the divide overflows exactly when + * SZ <= H/2. So ask that: only a box with a corner inside the near limit has an + * untrustworthy projection, and only that box gets drawn anyway. */ +static int mgs_box_crosses_near(void) +{ + const long *z = (const long *)(SCRPAD_ADDR + 0x6C); + long limit = (long)(mgs_dbg_clip_dist >> 1); + int i, behind = 0; + + for (i = 0; i < 8; i++) + { + if (z[i] <= limit) + { + behind++; + } + } + + /* Answering "1" here means "I cannot judge this box, draw it and let the + * rasterizer sort it out per pixel". That is the right answer for a box + * that STRADDLES the near plane, because its projection really is + * meaningless. It is the wrong answer for a box entirely BEHIND it, and + * the first version of this test could not tell the two apart -- both have + * a corner with z <= limit, so both were drawn. + * + * That is not a rare case. Walking towards the dock's lift, 16090 of 21306 + * boxes came back "cannot judge", 25093 models reached a primitive queue + * sized for a fraction of that, the queue dropped packets mid-list and the + * GPU reader desynced on the remains -- the white screen. Most of those + * boxes were simply behind the camera: an overhead shot puts the whole + * floor the player just walked across out of the frustum, corner by + * corner. + * + * All eight corners behind the near plane is unambiguous and needs no + * divide: nothing in that box can appear. Cull it. Only a genuine straddle + * keeps its licence to be drawn unjudged. */ + if (behind == 8) + { + return 0; + } + return behind > 0; +} + +/* evaluates to the bound_mode to use for a box that failed the screen test */ +#define MGS_CULL_RESULT(a2, a3, t0, t1) \ + (mgs_box_crosses_near() ? (mgs_dbg_cull_sat++, 1) \ + : (mgs_dbg_cull_off++, 0)) +#else +#define MGS_CULL_RESULT(a2, a3, t0, t1) 0 +#endif + +STATIC void DG_WriteObjClut(DG_OBJ *obj, int idx); +STATIC void DG_WriteObjClutUV(DG_OBJ *obj, int idx); +STATIC void DG_BoundIrTexture(DG_CHANL *chanl, int idx); + +static inline void copy_bounding_box_to_spad(DG_BOUND *bounds) +{ + DG_BOUND *bounding_box = (DG_BOUND *)SCRPAD_ADDR; + bounding_box->min.vx = bounds->min.vx; + bounding_box->min.vy = bounds->min.vy; + bounding_box->min.vz = bounds->min.vz; + + bounding_box->max.vx = bounds->max.vx; + bounding_box->max.vy = bounds->max.vy; + bounding_box->max.vz = bounds->max.vz; +} + +static inline void set_svec_from_bounding_box(int i, SVECTOR *svec) +{ + svec->vx = i & 1 ? ((long *)SCRPAD_ADDR)[3] : ((long *)SCRPAD_ADDR)[0]; + svec->vy = i & 2 ? ((long *)SCRPAD_ADDR)[4] : ((long *)SCRPAD_ADDR)[1]; + svec->vz = i & 4 ? ((long *)SCRPAD_ADDR)[5] : ((long *)SCRPAD_ADDR)[2]; +} + +void DG_BoundStart(void) +{ + /* do nothing */ +} + +STATIC void DG_BoundObjs(DG_OBJS *objs, int idx, unsigned int flag, int in_bound_mode) +{ + int i, i2, i3, a2, t0, a3, t1; + int bound_mode; + int n_models; + int n_bounding_box_vec; + int ret, extra; + long *test; + DG_OBJ *obj; + DVECTOR *dvec; + SVECTOR *svec; + DG_VECTOR *vec3_1; + DG_VECTOR *vec3_2; + DG_BOUND *mdl_bounds; + + n_models = objs->n_models; + obj = (DG_OBJ *)&objs->objs; + + for (; n_models > 0; --n_models) + { + bound_mode = 0; + if (in_bound_mode) + { + bound_mode = 2; + if (flag & DG_FLAG_BOUND) + { + gte_SetRotMatrix(&obj->screen); + gte_SetTransMatrix(&obj->screen); + + svec = (SVECTOR *)(SCRPAD_ADDR + 0x18); + mdl_bounds = (DG_BOUND *)&obj->model->min; + copy_bounding_box_to_spad(mdl_bounds); + vec3_1 = (DG_VECTOR *)(SCRPAD_ADDR + 0x30); + vec3_2 = (DG_VECTOR *)(SCRPAD_ADDR + 0x60); + i = 9; + + while (i > 0) + { + n_bounding_box_vec = 3; + do + { + set_svec_from_bounding_box(i, svec); + ++svec; + --i; + --n_bounding_box_vec; + } while (n_bounding_box_vec > 0); + + svec = (SVECTOR *)(SCRPAD_ADDR + 0x18); + gte_stsxy3c(vec3_1); + gte_stsz3c(vec3_2); + + gte_ldv3c((SVECTOR *)(SCRPAD_ADDR + 0x18)); + vec3_1++; + vec3_2++; + gte_rtpt_b(); + } + + gte_stsxy3c(vec3_1); + gte_stsz3c(vec3_2); + + // probably start of another inline func + a2 = *(short *)(SCRPAD_ADDR + 0x3C); + t0 = *(short *)(SCRPAD_ADDR + 0x3E); + a3 = a2; + t1 = t0; + dvec = (DVECTOR *)(SCRPAD_ADDR + 0x3C); + + for (i2 = 7; i2 > 0; --i2) + { + dvec++; + // loc_800187FC: + if (dvec->vx < a2) + { + a2 = dvec->vx; + } + else + { + if (a3 < dvec->vx) + a3 = dvec->vx; + } + if (dvec->vy < t0) + { + t0 = dvec->vy; + } + else + { + if (t1 < dvec->vy) + t1 = dvec->vy; + } + } + // loc_80018858 + // this seems ridiculous but was the only way it matched + if ((a2 >= 0xA1) || (a3 < -0xA0) || (t0 >= 0x71) || (t1 < -0x70)) + { +#ifdef MGS_NO_BOUND_CULL + /* measurement build: keep everything and let the + * rasterizer clip, so the screen coverage it produces is + * the ceiling this scene can reach. Comparing that against + * the normal build says whether missing picture is a + * culling problem at all. */ + extra = 1; +#else + extra = MGS_CULL_RESULT(a2, a3, t0, t1); +#endif + } + else + { + ret = ((a3 >= 0xA1) || (a2 < -0xA0) || (t1 >= 0x71) || (t0 < -0x70)) ? 1 : 2; + test = (long *)(SCRPAD_ADDR + 0x6C); + i3 = 8; + while (i3 > 0) + { + --i3; + if (*test) + { + extra = ret; + goto END; + } + test++; + } +#ifdef __psyz + { + /* culled although the bounding box is on screen: the + * eight projected Z values were all zero. Show them -- + * if the depth FIFO is not being filled, every model + * disappears and characters lose their parts. */ + static int b = 4; + if (b > 0) + { + long *z = (long *)(SCRPAD_ADDR + 0x6C); + b--; + printf("[zcull] bbox %d..%d,%d..%d z:", a2, a3, + t0, t1); + for (i3 = 0; i3 < 8; i3++) + { + printf(" %ld", z[i3]); + } + printf("\n"); + } + } +#endif + extra = 0; + } + END: + ret = extra; + bound_mode = ret; + } + } + + // loc_800188E4 + obj->bound_mode = bound_mode; + if (bound_mode) + { + obj->free_count = 8; + if (!obj->packs[idx]) + { + int res = DG_MakeObjPacket(obj, idx, flag); + if (res < 0) + { + obj->bound_mode = 0; + if (flag & DG_FLAG_GBOUND) + { + objs->bound_mode = 0; + return; + } + } + } + } + else + { + if (obj->packs[idx]) + { + --obj->free_count; + if (obj->free_count <= 0) + { + DG_FreeObjPacket(obj, idx); + } + } + } + obj++; + } + +#ifdef __psyz + /* NOT a bug, measured: welded children always find their parent already + * transformed. A probe counting the cases reported "culled-parent 0, + * no-packs 0" over thousands of models -- what looked like two thirds + * reading stale data was entirely sub-models whose parent index is -1, + * i.e. standalone scenery objects that weld to nothing. A pass that + * un-culled parents was written, measured at zero effect, and removed. */ +#endif +} + +void DG_BoundChanl(DG_CHANL *chanl, int idx) +{ + int i, i2, i3, a2, t0, a3, t1; + int n_objs; + int bound_mode; + DG_OBJS **objs; + int local_group_id; + DVECTOR *dvec; + SVECTOR *svec; + DG_VECTOR *vec3_1; + DG_VECTOR *vec3_2; + DG_BOUND *mdl_bounds; + int n_bounding_box_vec; + long *test; + unsigned int flag; + + DG_Clip(&chanl->clip_rect, chanl->clip_distance); + + objs = chanl->queue; + n_objs = chanl->objs_index; + local_group_id = DG_CurrentGroupID; + + for (; n_objs > 0; --n_objs) + { + DG_OBJS *current_objs = *objs; + objs++; + flag = current_objs->flag; + + bound_mode = 0; + if (!(flag & DG_FLAG_INVISIBLE)) + { + if (!current_objs->group_id || (current_objs->group_id & local_group_id)) + { + bound_mode = 2; + if (flag & DG_FLAG_GBOUND) + { + gte_SetRotMatrix(¤t_objs->objs->screen); + gte_SetTransMatrix(¤t_objs->objs->screen); + + svec = (SVECTOR *)(SCRPAD_ADDR + 0x18); + mdl_bounds = (DG_BOUND *)¤t_objs->def->min; + copy_bounding_box_to_spad(mdl_bounds); + vec3_1 = (DG_VECTOR *)(SCRPAD_ADDR + 0x30); + vec3_2 = (DG_VECTOR *)(SCRPAD_ADDR + 0x60); + i = 9; + + while (i > 0) + { + n_bounding_box_vec = 3; + do + { + set_svec_from_bounding_box(i, svec); + ++svec; + --i; + --n_bounding_box_vec; + } while (n_bounding_box_vec > 0); + + svec = (SVECTOR *)(SCRPAD_ADDR + 0x18); + gte_stsxy3c(vec3_1); + gte_stsz3c(vec3_2); + + gte_ldv3c((SVECTOR *)(SCRPAD_ADDR + 0x18)); + vec3_1++; + vec3_2++; + gte_rtpt_b(); + } + + gte_stsxy3c(vec3_1); + gte_stsz3c(vec3_2); + + // probably start of another inline func + a2 = *(short *)(SCRPAD_ADDR + 0x3C); + t0 = *(short *)(SCRPAD_ADDR + 0x3E); + a3 = a2; + t1 = t0; + dvec = (DVECTOR *)(SCRPAD_ADDR + 0x3C); + + for (i2 = 7; i2 > 0; --i2) + { + dvec++; + if (dvec->vx < a2) + { + a2 = dvec->vx; + } + else + { + if (a3 < dvec->vx) + a3 = dvec->vx; + } + if (dvec->vy < t0) + { + t0 = dvec->vy; + } + else + { + if (t1 < dvec->vy) + t1 = dvec->vy; + } + } + + if ((a2 >= 0xA1) || (a3 < -0xA0) || (t0 >= 0x71) || (t1 < -0x70)) + { +#ifdef MGS_NO_BOUND_CULL + /* experiment: keep everything, clipped by the GPU */ + bound_mode = 1; +#else + bound_mode = MGS_CULL_RESULT(a2, a3, t0, t1); +#endif + } + else + { + bound_mode = ((a3 >= 0xA1) || (a2 < -0xA0) || (t1 >= 0x71) || (t0 < -0x70)) ? 1 : 2; + test = (long *)(SCRPAD_ADDR + 0x6C); + i3 = 8; + while (i3 > 0) + { + --i3; + if (*test) + { + goto END; + } + test++; + } + bound_mode = 0; + } + END: + } + } + } + // loc_80018CE0: + current_objs->bound_mode = bound_mode; + DG_BoundObjs(current_objs, idx, flag, bound_mode); + } + + DG_BoundIrTexture(chanl, idx); +} + +void DG_BoundEnd(void) +{ + /* do nothing */ +} + +// Possibly a different file. + +STATIC DG_TEX DG_UnknownTexture = {0}; + +/* Replace the CLUT for this model with a plain white one for thermal goggles */ +STATIC void DG_WriteObjClut(DG_OBJ *obj, int idx) +{ + int n_packs; + POLY_GT4 *pPack = obj->packs[idx]; + short val = 0x3FFF; + if (pPack && pPack->clut != val) + { + while (obj) + { + n_packs = obj->n_packs; + while (n_packs > 0) + { + pPack->clut = val; + + ++pPack; + --n_packs; + } + + obj = obj->extend; + } + } +} + +/* Restore the CLUT for this model */ +STATIC void DG_WriteObjClutUV(DG_OBJ *obj, int idx) +{ + unsigned short id; + POLY_GT4 *pack; + int n_packs; + short *tex_ids; + DG_TEX *texture; + unsigned short current_id; + + pack = obj->packs[idx]; + + if (pack && pack->clut == 0x3FFF) + { + texture = &DG_UnknownTexture; + id = 0; + while (obj) + { + tex_ids = obj->model->materials; + for (n_packs = obj->n_packs; n_packs > 0; --n_packs) + { + current_id = *tex_ids; + tex_ids++; + if ((current_id & 0xFFFF) != id) + { + id = current_id; + texture = DG_GetTexture(id); + } + pack->clut = texture->clut; + pack++; + } + obj = obj->extend; + } + } +} + +// there must be a way to match this without the repetition +STATIC void DG_BoundIrTexture(DG_CHANL *chanl, int idx) +{ + DG_OBJS **queue; + int n_objects; + DG_OBJS *objs; + DG_OBJ *obj; + int n_models; + + queue = chanl->queue; + if (GM_GameStatus & STATE_THERMG) + { + for (n_objects = chanl->objs_index; n_objects > 0; n_objects--) + { + objs = *queue++; + + if (objs->flag & DG_FLAG_IRTEXTURE && objs->bound_mode != 0) + { + obj = objs->objs; + + for (n_models = objs->n_models; n_models > 0; n_models--) + { + if (obj->bound_mode != 0) + { + DG_WriteObjClut(obj, idx); + } + + obj++; + } + } + } + } + else + { + for (n_objects = chanl->objs_index; n_objects > 0; n_objects--) + { + objs = *queue++; + + if (objs->flag & DG_FLAG_IRTEXTURE && objs->bound_mode != 0) + { + obj = objs->objs; + + for (n_models = objs->n_models; n_models > 0; n_models--) + { + DG_WriteObjClutUV(obj, idx); + obj++; + } + } + } + } +} diff --git a/components/mgs/mgs/source/libdg/chanl.c b/components/mgs/mgs/source/libdg/chanl.c new file mode 100644 index 00000000..95d7c624 --- /dev/null +++ b/components/mgs/mgs/source/libdg/chanl.c @@ -0,0 +1,461 @@ +#define __LIBDG_CHANL_C__ + +#include "libdg.h" + +#include +#include +#include +#include +#include "common.h" + +/*** data ***/ + +STATIC DG_CHANLFUNC DG_ChanlUnits[] = { + DG_ScreenChanl, // 0: DG_SCREEN_CHANL + DG_BoundChanl, // 1: DG_BOUND_CHANL + DG_TransChanl, // 2: DG_TRANS_CHANL + DG_ShadeChanl, // 3: DG_SHADE_CHANL + DG_PrimChanl, // 4: DG_PRIM_CHANL + DG_DivideChanl, // 5: DG_DIVIDE_CHANEL + DG_SortChanl // 6: DG_SORT_CHANL +}; + +/*** $gp ***/ +STATIC int DG_ObjectQueueVoided = FALSE; + +// standard +STATIC int SECTION(".sbss") dword_800AB974; +STATIC int SECTION(".sbss") dword_800AB978; + +STATIC CVECTOR SECTION(".sbss") DG_BackGroundColor; + +short SECTION(".sbss") N_ChanlPerfMax; +short SECTION(".sbss") word_800AB982; +unsigned short SECTION(".sbss") gCurrentRootCnt_800AB984; + +/*** bss ***/ +extern DG_CHANL DG_Chanls[3]; + +extern OT_TYPE ot_background[68]; +extern OT_TYPE ot_primitive[516]; +extern OT_TYPE ot_overlay[4]; + +extern void *obj_queue_background[8]; +extern void *obj_queue_primitive[256]; + +extern DR_ENV bg_drawenv[2]; + +extern OT_TYPE ptr_800B1400[256]; +extern u_short gOldRootCnt_800B1DC8[32]; + +// gets value of root counter 1 which determines time taken to finished drawing +// sets it to word_800AB982 +STATIC void DG_DrawSyncCallback( void ) +{ + word_800AB982 = GetRCnt(RCntCNT1); +} + +// guessed function name +STATIC void DG_SetChanlOrderingTable( DG_CHANL *chanl, OT_TYPE *ot, int ot_size, void **queue, short queue_size, short link, short dblbuf ) +{ + int size; + + size = (1 << ot_size) + 1; + + chanl->ot[0] = ot; + chanl->ot[1] = &ot[size]; + chanl->objs_index = 0; + chanl->queue = (DG_OBJS **)queue; + chanl->ot_size = ot_size; + chanl->dirty = 0; + chanl->prim_index = queue_size; + chanl->queue_size = queue_size; + chanl->link = link; + chanl->dblbuf = dblbuf; +} + +// guessed function name +STATIC void DG_SetChanlDrawEnv( DG_CHANL *chanl, DRAWENV *env, int backround ) +{ + DRAWENV dr_env; + int width; + + dr_env = *env; + width = chanl->dblbuf ? dword_800AB978 : 0; + + chanl->new_clip_rect.x = dr_env.clip.x - dr_env.ofs[0]; + chanl->new_clip_rect.y = dr_env.clip.y - dr_env.ofs[1]; + chanl->new_clip_rect.w = dr_env.clip.w; + chanl->new_clip_rect.h = dr_env.clip.h; + SetDrawEnv(chanl->new_env, &dr_env); + + dr_env.clip.x += width; + dr_env.ofs[0] += width; + SetDrawEnv(&chanl->new_env[1], &dr_env); + + if (backround) + { + dr_env.isbg = 0; + SetDrawEnv(&bg_drawenv[1], &dr_env); + + dr_env.clip.x -= width; + dr_env.ofs[0] -= width; + SetDrawEnv(&bg_drawenv[0], &dr_env); + } +} + +// guessed function name +STATIC void DG_CopyChanlDrawEnv( DG_CHANL *chanl, int idx ) +{ + chanl->clip_rect = chanl->new_clip_rect; + chanl->env1[idx] = chanl->new_env[idx]; +} + +// guessed function name +void DG_InitChanlSystem( int width ) +{ + DRAWENV drawEnv; + DG_CHANL *chanl; + + DrawSyncCallback(DG_DrawSyncCallback); + dword_800AB978 = width; + + /* channel 0 */ + chanl = DG_Chanls; + DG_SetChanlOrderingTable(chanl, ot_background, 5, obj_queue_background, 8, -1, 1); + DG_InitDrawEnv(&drawEnv, 0, 0, FRAME_WIDTH, FRAME_HEIGHT); + drawEnv.isbg = 1; + DG_SetChanlDrawEnv(chanl, &drawEnv, 1); + DG_CopyChanlDrawEnv(chanl, 0); + DG_CopyChanlDrawEnv(chanl, 1); + + /* channel 1 */ + chanl++; + DG_SetChanlOrderingTable(chanl, ot_primitive, 8, obj_queue_primitive, 256, 16, 1); + DG_InitDrawEnv(&drawEnv, 0, 0, FRAME_WIDTH, FRAME_HEIGHT); + drawEnv.ofs[0] = 160; + drawEnv.ofs[1] = 112; + DG_SetChanlDrawEnv(chanl, &drawEnv, 0); + DG_CopyChanlDrawEnv(chanl, 0); + DG_CopyChanlDrawEnv(chanl, 1); + + chanl->env2[0] = bg_drawenv[0]; + chanl->env2[1] = bg_drawenv[1]; + + /* channel 2 */ + chanl++; + DG_SetChanlOrderingTable(chanl, ot_overlay, 0, NULL, 0, 8, 1); + DG_InitDrawEnv(&drawEnv, 0, 0, FRAME_WIDTH, FRAME_HEIGHT); + DG_SetChanlDrawEnv(chanl, &drawEnv, 0); + DG_CopyChanlDrawEnv(chanl, 0); + DG_CopyChanlDrawEnv(chanl, 1); + + chanl->env2[0] = bg_drawenv[0]; + chanl->env2[1] = bg_drawenv[1]; +} + +void DG_DrawOTag( int which ) +{ + gOldRootCnt_800B1DC8[0] = gCurrentRootCnt_800AB984; + gCurrentRootCnt_800AB984 = GetRCnt(RCntCNT1); + + /* channels 1 and 2 are linked into channel 0 */ + /* DR_ENV begins with O_TAG, so its first two words are exactly an + * OT_TYPE: it doubles as the head of the chain. Safe cast -- unlike + * casting an ARRAY of u_long, which would mis-stride. */ + DrawOTag((OT_TYPE *)&DG_Chanls[0].env1[which]); +} + +void DG_ClearChanlSystem( int which ) +{ + DG_CHANL *chanl; + int i; + int size; + OT_TYPE *ot; + OT_TYPE *ot_end; + DR_ENV *env1; + DR_ENV *env2; + OT_TYPE *chain; + + chanl = DG_Chanls; + for (i = 3 ; i > 0; i--) + { + size = (1 << chanl->ot_size) + 1; + + ot = chanl->ot[which]; + ot_end = &ot[size - 1]; + + /* the ot is in reverse order */ + ClearOTagR(ot, size); + + if (chanl->dirty > 0) + { + DG_CopyChanlDrawEnv(chanl, which); + chanl->dirty--; + } + + env1 = &chanl->env1[which]; + env2 = &chanl->env2[which]; + + /* execute the channel in the order env1 -> ot -> env2 */ + catPrim(env1, ot_end); + catPrim(ot, env2); + + if (chanl->link >= 0) + { + /* link this channel into the background channel */ + chain = DG_Chanls[0].ot[which]; + chain += chanl->link; + addPrims(chain, env1, env2); + } + else + { + termPrim(env2); + } + + chanl++; + } +} + +void DG_RenderPipeline( int idx ) +{ + unsigned short *pPerfArrayIter; + DG_CHANLFUNC *chanlfunc; + int start_idx; + int i; + + pPerfArrayIter = &gOldRootCnt_800B1DC8[1]; + if (DG_ObjectQueueVoided) + { + // Store the counter and set as 2 valid items + *pPerfArrayIter = GetRCnt(0xF2000001); + N_ChanlPerfMax = 2; + } + else + { + GV_ZeroMemory(ptr_800B1400, sizeof(ptr_800B1400)); + chanlfunc = DG_ChanlUnits; + + if (!DG_HikituriFlag) + { + start_idx = 7; + } + else + { + start_idx = 1; + } + + for (i = start_idx; i > 0; --i) + { + *pPerfArrayIter++ = GetRCnt(RCntCNT1); + // Call the render func, saving the time of the previous pass + (*chanlfunc)(&DG_Chanls[1], idx); + chanlfunc++; + } + *pPerfArrayIter++ = GetRCnt(RCntCNT1); + N_ChanlPerfMax = (pPerfArrayIter) - &gOldRootCnt_800B1DC8[0]; + } +} + +void DG_SetRenderChanlDrawEnv( int idx, DRAWENV *pDrawEnv ) +{ + DG_CHANL *chanl = &DG_Chanls[idx + 1]; + DG_SetChanlDrawEnv(chanl, pDrawEnv, 0); + chanl->dirty = 2; +} + +int DG_QueueObjs( DG_OBJS *objs ) +{ + DG_CHANL *chanl; + int n_chanl, n_objs; + + n_chanl = objs->chanl + 1; + chanl = &DG_Chanls[n_chanl]; + + n_objs = chanl->objs_index; + + if (n_objs >= chanl->prim_index) + { + return -1; + } + else + { + chanl->queue[n_objs++] = objs; + chanl->objs_index = n_objs; + return 0; + } +} + +void DG_DequeueObjs( DG_OBJS *objs ) +{ + DG_CHANL *chanl; + int n_chanl, n_objs, i; + DG_OBJS **chanl_objs; + + n_chanl = objs->chanl + 1; + chanl = &DG_Chanls[n_chanl]; + + n_objs = chanl->objs_index; + chanl_objs = chanl->queue; + + for (i = n_objs; i > 0; --i) + { + if (*chanl_objs == objs) + goto END; + chanl_objs++; + } + return; + +END: + --i; + for (; i > 0; --i) + { + chanl_objs[0] = chanl_objs[1]; + chanl_objs++; + } + + n_objs--; + chanl->objs_index = n_objs; +} + +int DG_QueuePrim( DG_PRIM *prim ) +{ + int t = prim->chanl + 1; + DG_CHANL *chanl = &DG_Chanls[t]; + int idx = chanl->prim_index; + + if (idx <= chanl->objs_index) + { + return -1; + } + idx = idx - 1; + chanl->queue[idx] = (DG_OBJS *)prim; // 58 + chanl->prim_index = idx; + return 0; +} + +void DG_DequeuePrim( DG_PRIM *prim ) +{ + int i; + int group; + int queue_size; + int n_free_prims; + + DG_CHANL *chanl; + DG_OBJS **chanl_objs; + + group = prim->chanl + 1; + chanl = &DG_Chanls[group]; + + n_free_prims = chanl->prim_index; + queue_size = chanl->queue_size; + + chanl_objs = &chanl->queue[queue_size]; + i = queue_size - n_free_prims; + + for (; i > 0; --i) + { + --chanl_objs; + if (*chanl_objs == (DG_OBJS *)prim) + goto END; + } + return; + +END: + --i; + for (; i > 0; --i) + { + chanl_objs[0] = chanl_objs[-1]; + chanl_objs--; + } + + n_free_prims++; + chanl->prim_index = n_free_prims; +} + +void DG_InitDrawEnv( DRAWENV *pDrawEnv, int clipX1, int clipY1, int clipX2, int clipY2 ) +{ + setRECT(&pDrawEnv->clip, clipX1, clipY1, clipX2, clipY2); + pDrawEnv->ofs[0] = clipX1; + pDrawEnv->ofs[1] = clipY1; + pDrawEnv->dtd = 1; + pDrawEnv->dfe = 0; + setRECT(&pDrawEnv->tw, 0, 0, 0, 0); + setRGB0(pDrawEnv, 0, 0, 0); + pDrawEnv->tpage = 0; + pDrawEnv->isbg = 0; +} + +void DG_FreeObjectQueue( void ) +{ + DG_CHANL *chanl; + DG_OBJS **queue; + DG_OBJS *objs; + int i; + + chanl = &DG_Chanls[1]; + queue = (DG_OBJS **)chanl->queue; + + DG_ObjectQueueVoided = TRUE; + + for (i = chanl->objs_index; i > 0; i--) + { + objs = *queue++; + DG_FreeObjsPacket(objs, 0); + DG_FreeObjsPacket(objs, 1); + } +} + +void DG_RestartMainChanlSystem( void ) +{ + DG_ObjectQueueVoided = FALSE; + DG_UnDrawFrameCount = 1; +} + +void DG_SetBackgroundRGB( int r, int g, int b ) +{ + DRAWENV drawEnv; + DG_CHANL *chanl = &DG_Chanls[0]; + + DG_InitDrawEnv(&drawEnv, 0, 0, FRAME_WIDTH, FRAME_HEIGHT); + drawEnv.isbg = 1; + setRGB0(&drawEnv, r, g, b); + DG_SetChanlDrawEnv(chanl, &drawEnv, 1); + chanl->dirty = 2; +} + +void DG_SetRGB( int r, int g, int b ) +{ + DG_BackGroundColor.r = r; + DG_BackGroundColor.b = b; + DG_BackGroundColor.g = g; + + DG_SetBackgroundRGB(r, g, b); +} + +void DG_BackGroundBlack( void ) +{ + DG_SetBackgroundRGB(0, 0, 0); +} + +void DG_BackGroundNormal( void ) +{ + DG_SetBackgroundRGB( + DG_BackGroundColor.r, + DG_BackGroundColor.g, + DG_BackGroundColor.b); +} + +void DG_SetBackGroundTile( TILE *tile ) +{ + tile->r0 = DG_BackGroundColor.r; + tile->g0 = DG_BackGroundColor.g; + tile->b0 = DG_BackGroundColor.b; +} + +DG_CHANLFUNC DG_SetChanlSystemUnits( int idx, DG_CHANLFUNC newfunc ) +{ + DG_CHANLFUNC oldfunc = DG_ChanlUnits[idx]; + DG_ChanlUnits[idx] = newfunc; + return oldfunc; +} diff --git a/components/mgs/mgs/source/libdg/dgd.c b/components/mgs/mgs/source/libdg/dgd.c new file mode 100644 index 00000000..24bd3dc2 --- /dev/null +++ b/components/mgs/mgs/source/libdg/dgd.c @@ -0,0 +1,199 @@ +#include +#include +#include +#include + +#include "common.h" +#include "libdg/libdg.h" +#include "libgv/libgv.h" +#include "mts/mts.h" +#include "game/game.h" + +extern GV_PAD *GM_CurrentPadData; + +/*---------------------------------------------------------------------------*/ + +typedef struct { + GV_ACT actor; +} Work; + +extern Work DG_WorkFirst; +extern Work DG_WorkLast; +extern int dword_800B3790; + +int DG_FrameRate = 2; +int DG_HikituriFlag = 0; // 引きつり = twitching +int DG_HikituriFlagOld = 0; // 〃 〃 + +STATIC int DG_TickCount = -1; + +/*---------------------------------------------------------------------------*/ + +#ifdef __psyz +unsigned mgs_dbg_vsynccb, mgs_dbg_drawotag; +#endif + +int DG_VSyncCallbackFunc(void) +{ +#ifdef __psyz + mgs_dbg_vsynccb++; +#endif + if (DrawSync(1) > 0) + { + dword_800B3790++; + if (dword_800B3790 < 30) + { + return 0; + } + printf("@"); + ResetGraph(1); + dword_800B3790 = 0; + } + return 1; +} + +void DG_ActFirst(Work *work) +{ + int ticks; + + dword_800B3790 = 0; + + DG_HikituriFlagOld = DG_HikituriFlag; + +#ifdef __psyz + /* Konami's frame-drop-to-catch-up ("hikituri" = stutter) mechanism, off. + * + * When a frame overruns its budget the flag is raised, and then TWO + * different actors consult it: DG_SwapFrame skips DG_DrawOTag, and + * libgv/gvd.c's Act() skips the GV_Clock buffer flip. On the console + * those two decisions are always taken from the same value, so a dropped + * frame just repeats the previous picture. Here the actors run at + * different points of the list and can read the flag from different + * frames -- flip without draw, and the panel shows a buffer nobody drew: + * a black field between good ones, which is the flicker on the LCD. + * + * The mechanism exists to hold a 33 ms real-time budget this board + * cannot promise anyway. Pinning both flags low keeps every frame drawn + * and every flip paired with a draw: a heavy scene now runs slower + * instead of strobing. */ + DG_HikituriFlagOld = 0; + DG_HikituriFlag = 0; +#endif + + if (GM_GameStatus & STATE_NOSLOW) + { + if (DG_TickCount == -1) + { + DG_TickCount = mts_get_tick_count(); + DG_HikituriFlag = 0; + } + + if (!DG_HikituriFlag) + { + mts_wait_vbl(DG_FrameRate); + } + + ticks = mts_get_tick_count(); +#ifdef __psyz + /* see the note above: never raise it */ + (void)ticks; + DG_HikituriFlag = 0; +#else + if (DG_TickCount + 2 < ticks) + { + DG_HikituriFlag = 1; + } + else + { + DG_HikituriFlag = 0; + } +#endif + + DG_TickCount += 2; + } + else + { + mts_wait_vbl(DG_FrameRate); + DG_TickCount = -1; + DG_HikituriFlag = 0; + } + + DG_SwapFrame(); + + GV_UpdatePadSystem(); +#ifdef __psyz + /* The keyboard-over-serial pad ages its held buttons here, not on the + * vblank tick: this is the one point that means "a frame has read the + * pad", and the frame rate is not tied to the vblank rate. */ + { + void Mgs_PadConsumed(void); + Mgs_PadConsumed(); + } +#endif + GM_CurrentPadData = GV_PadData; + + if ((GM_PlayerStatus & PLAYER_SECOND_AVAILABLE) != 0) + { + if (GV_PadData[1].status | GV_PadData[1].release) + { + GM_CurrentPadData = &GV_PadData[1]; + } + } +} + +void DG_ActLast(Work *work) +{ + DG_RenderFrame(); +} + +void DG_ResetPipeline(void) +{ + DG_InitLightSystem(); + DG_RenderPipeline_Init(); + + DG_SetCurrentGroup(0); + + DG_ReloadPalette(); + DG_ResetPaletteEffect(); + DG_SetRGB(0, 0, 0); + + printf("Object Queue %d\n", DG_Chanl(0)->objs_index); + printf("Primitive Queue %d\n", DG_Chanl(0)->queue_size - DG_Chanl(0)->prim_index); + + DG_Chanl(0)->objs_index = 0; + DG_Chanl(0)->prim_index = DG_Chanl(0)->queue_size; +} + +void DG_ResetTextureCache(void) +{ + DG_InitTextureSystem(); + DG_LoadResidentTextureCache(); +} + +void DG_StartDaemon(void) +{ + mts_set_vsync_task(); + mts_set_vsync_callback_func(DG_VSyncCallbackFunc); + + DG_InitDispEnv(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT, 320); + DG_InitChanlSystem(SCREEN_WIDTH); + DG_ClearResidentTexture(); + DG_ResetPipeline(); + + GV_SetLoader('p', DG_LoadInitPcx); // *.pcx format + GV_SetLoader('k', DG_LoadInitKmd); // *.kmd format + GV_SetLoader('l', DG_LoadInitLit); // *.lit format + GV_SetLoader('n', DG_LoadInitNar); // *.nar format + GV_SetLoader('o', DG_LoadInitOar); // *.oar format + GV_SetLoader('z', DG_LoadInitKmdar); // *.zmd format + GV_SetLoader('i', DG_LoadInitImg); // *.img format + GV_SetLoader('s', DG_LoadInitSgt); // *.sgt format + + // Wait for vsync, swap frame, fetch input + GV_InitActor(GV_ACTOR_DAEMON, &DG_WorkFirst, NULL); + GV_SetNamedActor(&DG_WorkFirst, DG_ActFirst, NULL, "dgd.c"); + + // Render new frame + GV_InitActor(GV_ACTOR_DAEMON2, &DG_WorkLast, NULL); + GV_SetNamedActor(&DG_WorkLast, DG_ActLast, NULL, "dgd.c"); +} diff --git a/components/mgs/mgs/source/libdg/display.c b/components/mgs/mgs/source/libdg/display.c new file mode 100644 index 00000000..1494320f --- /dev/null +++ b/components/mgs/mgs/source/libdg/display.c @@ -0,0 +1,476 @@ +#define __LIBDG_DISPLAY_C__ + +#include +#include +#include + +#include "libdg.h" +#include "common.h" +#include "libgv/libgv.h" +#include "menu/menuman.h" +#ifdef __psyz +#include "game/camera.h" /* GM_Camera, for the runaway-view probe */ +#endif + +/*** data ***/ +STATIC VECTOR SECTION(".data") DG_UpVector = {0, -4096, 0, 0}; + +/*** $gp ***/ +int DG_UnDrawFrameCount = 0; +STATIC int DG_CurrentBuffer = -1; + +STATIC int SECTION(".sbss") gClipHeights_800AB960[2]; +int SECTION(".sbss") DG_CurrentGroupID; +short SECTION(".sbss") DG_ClipMin[2]; +short SECTION(".sbss") DG_ClipMax[2]; + +/*** bss ***/ +extern DISPENV g_dispenv; +extern VECTOR DG_RightVector; + +void DG_InitDispEnv(int x, short y, short w, short h, int clipH) +{ + DISPENV *dispenv = &g_dispenv; + RECT *disp; + RECT *screen; + + disp = &dispenv->disp; + screen = &dispenv->screen; + + setRECT(disp, x, y, w, h); + setRECT(screen, 0, 0, 256, SCREEN_HEIGHT); + + dispenv->isinter = 0; + dispenv->isrgb24 = 0; + + // For some reason lets overwrite what we already setup + dispenv->screen.y = 8; + dispenv->screen.h = FRAME_HEIGHT; + + gClipHeights_800AB960[0] = x; + gClipHeights_800AB960[1] = x + clipH; +} + +void DG_ChangeReso(int flag) +{ +#if 0 + DRAWENV drawenv; + + if ((flag & 1) == 0) { + g_dispenv.disp.w = 320; + g_dispenv.screen.x = 0; + g_dispenv.screen.w = 255; + } else { + g_dispenv.disp.w = 384; + g_dispenv.screen.x = 26; + g_dispenv.screen.w = 212; + } + + if ((flag & 2) == 0) { + g_dispenv.screen.h = 256; + } else { + g_dispenv.screen.h = 224; + } + + g_dispenv.screen.y = 16; + + if ((flag & 4) == 0) { + g_dispenv.disp.y = 0; + g_dispenv.disp.h = 256; + } else { + g_dispenv.disp.h = 224; + g_dispenv.disp.y = (g_dispenv.screen.h - 224) / 2; + } + + if ((flag & 8) != 0) { + PutDispEnv(&g_dispenv); + } + + SetDefDrawEnv(&drawenv, 0, 0, 320, g_dispenv.disp.h); + drawenv.isbg = 1; + DG_SetRenderChanlDrawEnv(-1, &drawenv); +#endif +} + +void DG_RenderPipeline_Init(void) +{ + DG_ClearChanlSystem(0); + DG_ClearChanlSystem(1); + DG_RenderPipeline(0); + DG_RenderPipeline(1); +} + +void DG_SwapFrame(void) +{ + int activeBuffer = GV_Clock; + +#ifdef MGS_TURNTABLE + /* ~11 s per revolution at this stage's frame rate: slow enough to study a + * joint as it comes round, fast enough not to have to wait for it. */ + { extern int mgs_dbg_spin; mgs_dbg_spin = (mgs_dbg_spin + 32) & 4095; } +#endif + +#ifdef __psyz + { + /* the frame loop keeps ticking but nothing reaches the GPU: print the + * three values this function branches on */ + static int beat; + beat++; + if ((beat % 120) == 0) + { + extern unsigned mgs_dbg_drawotag; + extern unsigned sotn_exeque_count; + extern unsigned mgs_vblank_count; + extern DG_CHANL DG_Chanls[]; + extern unsigned psyz_prof_exeq_us, psyz_prof_load_us, + psyz_prof_load_n; + extern unsigned psyz_prim_by_code[8]; + extern unsigned psyz_texel_zero, psyz_texel_ok, psyz_flat_px; + extern unsigned mgs_dbg_objs_seen, mgs_dbg_objs_culled, + mgs_dbg_models_culled, mgs_dbg_models_drawn; + extern unsigned mgs_dbg_cull_sat, mgs_dbg_cull_off, + mgs_dbg_clip_dist; + printf("[cull] objs %u culled %u | models drawn %u culled %u\n", + mgs_dbg_objs_seen, mgs_dbg_objs_culled, + mgs_dbg_models_drawn, mgs_dbg_models_culled); + /* saturated = the perspective divide overflowed, so the box says + * nothing about where the object is and it is drawn anyway; + * offscreen = a well-formed box that really is out of view */ + printf("[bbox] kept-saturated %u culled %u | H %u clip %d,%d..%d,%d\n", + mgs_dbg_cull_sat, mgs_dbg_cull_off, mgs_dbg_clip_dist, + DG_ClipMin[0], DG_ClipMin[1], DG_ClipMax[0], DG_ClipMax[1]); + { + /* When "kept-saturated" runs away, every object's projection is + * overflowing and the whole scene gets drawn at whatever size + * the broken divide produced -- a bright, flickering wall of + * geometry rather than a light. The question is whether the + * CAMERA caused it, and the eye matrix answers that directly + * without needing a header out of game/: its translation is + * where the view sits, its diagonal says whether the rotation + * is still sane. camera.c can leave GM_CameraList indexed at -1 + * when no trigger matches ("change camera -1" in the log), so + * this is the number to line that message up against. If the + * eye holds still while saturation explodes, the camera is + * innocent and the fault is in the projection itself. */ + /* Print the camera the GAME holds, not the view matrix. + * eye_inv.t is NOT a position: this file builds it by applying + * eye_inv's rotation to a forward vector, so it is a rotated + * direction. Reading it as "where the camera is" produced a + * confident story about the view flying upward that the data + * never supported. GM_Camera.position and .target are the real + * thing, and printing both separates three different faults: + * the camera ran away, its target ran away, or neither did and + * the saturation comes from the geometry instead. */ + extern GM_CameraSystemWork GM_Camera; + printf("[cam] pos %d,%d,%d target %d,%d,%d\n", + GM_Camera.position.vx, GM_Camera.position.vy, + GM_Camera.position.vz, GM_Camera.target.vx, + GM_Camera.target.vy, GM_Camera.target.vz); + } + mgs_dbg_cull_sat = 0; mgs_dbg_cull_off = 0; + mgs_dbg_objs_seen = 0; mgs_dbg_objs_culled = 0; + mgs_dbg_models_drawn = 0; mgs_dbg_models_culled = 0; + extern unsigned psyz_tri_off, psyz_tri_tiny, psyz_tri_ok, psyz_tri_huge; + /* oversize = wider than 1023 or taller than 511, which the PSX GPU + * refuses outright. These are polygons whose projection saturated + * because they cross behind the camera; drawing them is what put a + * moving white sheet over the dock. */ + printf("[tri] offscreen %u subpixel %u oversize %u drawable %u\n", + psyz_tri_off, psyz_tri_tiny, psyz_tri_huge, psyz_tri_ok); + psyz_tri_off = 0; + psyz_tri_tiny = 0; + psyz_tri_huge = 0; + psyz_tri_ok = 0; + printf("[texel] transparent %u drawn %u flat %u\n", + psyz_texel_zero, psyz_texel_ok, psyz_flat_px); + psyz_texel_zero = 0; + psyz_texel_ok = 0; + psyz_flat_px = 0; + printf("[prims] 2x %u 3x %u 4x %u 5x %u 6x %u 7x %u\n", + psyz_prim_by_code[2], psyz_prim_by_code[3], + psyz_prim_by_code[4], psyz_prim_by_code[5], + psyz_prim_by_code[6], psyz_prim_by_code[7]); + printf("[swap] f%d vbl %u q %d/%d %d/%d drawot %u exeq %u " + "raster_us %u load_us %u loads %u\n", + beat, mgs_vblank_count, + DG_Chanls[0].objs_index, DG_Chanls[0].prim_index, + DG_Chanls[1].objs_index, DG_Chanls[1].prim_index, + mgs_dbg_drawotag, sotn_exeque_count, + psyz_prof_exeq_us, psyz_prof_load_us, psyz_prof_load_n); + psyz_prof_exeq_us = 0; + psyz_prof_load_us = 0; + psyz_prof_load_n = 0; + } + } +#endif + + if ((GV_PauseLevel & GV_PAUSE_READERROR) != 0 || DG_UnDrawFrameCount > 0) + { +#ifdef __psyz + { + /* Which of the two conditions is holding the screen blank? They + * mean very different things: READERROR is "the disc is failing" + * (set from a streamed-audio underrun or a CD task error), while + * UnDrawFrameCount is "something else owns the screen" -- the + * movie player parks it at 0x7fff0000 while an FMV runs. */ + static unsigned n; + if ((n++ % 240u) == 0u) + { + extern const char *mgs_dbg_undraw_src; + printf("[skip] pause %02x readerr %d undraw %d set-by %s\n", + GV_PauseLevel, + (GV_PauseLevel & GV_PAUSE_READERROR) ? 1 : 0, + DG_UnDrawFrameCount, mgs_dbg_undraw_src); + } + } +#endif + if (DG_CurrentBuffer < 0) + { + DG_CurrentBuffer = activeBuffer; + } + if ((GV_PauseLevel & GV_PAUSE_READERROR) == 0) + { + --DG_UnDrawFrameCount; + } + } + else if (DG_CurrentBuffer < 0 || activeBuffer != DG_CurrentBuffer) + { + DISPENV *p = &g_dispenv; +#ifdef __psyz + { static int b = 3; if (b > 0) { b--; printf("[swap] branch DRAW\n"); } } +#endif + p->disp.x = gClipHeights_800AB960[activeBuffer]; + + PutDispEnv(&g_dispenv); +#ifdef __psyz + /* The panel is not a CRT: pushing a buffer costs a real 120 KB read + * out of PSRAM, strided across rows, competing with the rasterizer on + * the other core. Tell the scanout task that the picture changed so it + * sends each frame once instead of on a fixed timer -- a scene that + * takes four vblanks to draw was being sent twice. */ + { extern unsigned mgs_frame_seq; mgs_frame_seq++; } +#endif + if (!DG_HikituriFlagOld) + { +#ifdef __psyz + { extern unsigned mgs_dbg_drawotag; mgs_dbg_drawotag++; } +#endif + DG_DrawOTag(1 - activeBuffer); +#ifdef __psyz + { + /* Flush the queue NOW and count what actually landed in the + * buffer we just drew. If this is high but the panel is + * black, something wipes it afterwards; if it is zero, the + * drawing never reaches VRAM at all. */ + static int b = 6; + if (b > 0) + { + extern unsigned short* g_RawVram; + unsigned nz = 0, yy, xx, base = (1 - activeBuffer) * 320u; + b--; + DrawSync(0); + for (yy = 0; yy < 224; yy += 4) + { + for (xx = 0; xx < 320; xx += 4) + { + if (g_RawVram[yy * 1024u + base + xx]) { nz++; } + } + } + printf("[drawn] buffer %d (x %u) nonzero %u of 4480\n", + 1 - activeBuffer, base, nz); + } + } +#endif + } + DG_CurrentBuffer = -1; + } + GV_ClearMemorySystem(activeBuffer); + if (!DG_HikituriFlagOld) + { + GV_ClearMemorySystem(GV_NORMAL_MEMORY); + } + MENU_ResetSystem(); + DG_ClearChanlSystem(activeBuffer); + DG_ClearTmpLight(); +} + +void DG_RenderFrame(void) +{ + DG_RenderPipeline(GV_Clock); +} + +void DG_LookAt(DG_CHANL *chanl, SVECTOR *eye, SVECTOR *center, int clip_distance) +{ + VECTOR forward; + VECTOR up; + VECTOR right; + MATRIX *view; + + chanl->clip_distance = clip_distance; + + view = &chanl->eye; + view->t[0] = eye->vx; + view->t[1] = eye->vy; + view->t[2] = eye->vz; + + forward.vx = (signed short)(((u_short)center->vx - (u_short)eye->vx)); + forward.vy = (signed short)(((u_short)center->vy - (u_short)eye->vy)); + forward.vz = (signed short)(((u_short)center->vz - (u_short)eye->vz)); + + OuterProduct12(&DG_UpVector, &forward, &right); + + if (!right.vx && !right.vy && !right.vz) + { + right = DG_RightVector; + } + else + { + DG_RightVector = right; + } + + VectorNormal(&right, &right); + VectorNormal(&forward, &forward); + + OuterProduct12(&forward, &right, &up); + + view->m[0][0] = right.vx; + view->m[0][1] = up.vx; + view->m[0][2] = forward.vx; + + view->m[1][0] = right.vy; + view->m[1][1] = up.vy; + view->m[1][2] = forward.vy; + + view->m[2][0] = right.vz; + view->m[2][1] = up.vz; + view->m[2][2] = forward.vz; + + DG_TransposeMatrix(view, &chanl->eye_inv); + + forward.vx = -view->t[0]; + forward.vy = -view->t[1]; + forward.vz = -view->t[2]; + + ApplyMatrixLV(&chanl->eye_inv, &forward, (VECTOR *)chanl->eye_inv.t); +} + +void DG_AdjustOverscan(MATRIX *matrix) +{ + matrix->m[1][0] = (matrix->m[1][0] * 58) / 64; + matrix->m[1][1] = (matrix->m[1][1] * 58) / 64; + matrix->m[1][2] = (matrix->m[1][2] * 58) / 64; + matrix->t[1] = (matrix->t[1] * 58) / 64; +} + +void DG_Clip(RECT *clip_rect, int dist) +{ + int x_tmp; + int y_tmp; + + gte_SetGeomScreen(dist); + +#ifdef __psyz + { + /* H decides where the perspective divide starts to overflow (anything + * nearer than H/2), which is what the [bbox] counters are testing */ + extern unsigned mgs_dbg_clip_dist; + mgs_dbg_clip_dist = (unsigned)dist; + } +#endif + + x_tmp = clip_rect->x; + DG_ClipMin[0] = x_tmp; + DG_ClipMax[0] = clip_rect->w + x_tmp - 1; + + y_tmp = clip_rect->y; + DG_ClipMin[1] = y_tmp; + DG_ClipMax[1] = clip_rect->h + y_tmp - 1; +} + +void DG_ApplyMatrix(MATRIX *world, MATRIX *in) +{ + MATRIX out; + + gte_SetRotMatrix(world); + + gte_ldclmv(in); + gte_rtir(); + gte_stclmv(&out); + + gte_ldclmv(&in->m[0][1]); + gte_rtir(); + gte_stclmv(&out.m[0][1]); + + gte_ldclmv(&in->m[0][2]); + gte_rtir(); + gte_stclmv(&out.m[0][2]); + + gte_SetTransMatrix(world); + + gte_ldlv0(in->t); + gte_rt(); + gte_stlvnl(out.t); + + DG_AdjustOverscan(&out); + + gte_SetRotMatrix(&out); + gte_SetTransMatrix(&out); +} + +void DG_OffsetDispEnv(int offset) +{ + g_dispenv.screen.y += offset; + g_dispenv.screen.h -= offset; + PutDispEnv(&g_dispenv); + g_dispenv.screen.y -= offset; + g_dispenv.screen.h += offset; +} + +void DG_ClipDispEnv(int x, int y) +{ + RECT screen; + + screen = g_dispenv.screen; + g_dispenv.screen.x = 128 - x / 2; + g_dispenv.screen.w = x; + g_dispenv.screen.y = 120 - y / 2; + g_dispenv.screen.h = y; + PutDispEnv(&g_dispenv); + g_dispenv.screen = screen; +} + +void DG_DisableClipping(void) +{ + DRAWENV drawenv; + + DG_InitDrawEnv(&drawenv, + g_dispenv.disp.x, g_dispenv.disp.y, + g_dispenv.disp.w, g_dispenv.disp.h); + PutDrawEnv(&drawenv); +} + +void DG_FadeScreen(int amount) +{ + DR_TPAGE tpage; + TILE tile; + + DG_DisableClipping(); + + setDrawTPage(&tpage, 1, 1, GetTPage(0, 2, 0, 0)); + DrawPrim(&tpage); + + tile.x0 = 0; + tile.y0 = 0; + tile.w = FRAME_WIDTH; + tile.h = FRAME_HEIGHT; + LSTORE(amount << 16 | amount << 8 | amount, &tile.r0); + setTile(&tile); + setSemiTrans(&tile, 1); + DrawPrim(&tile); +} + +// guessed function name +DISPENV *DG_GetDisplayEnv(void) +{ + return &g_dispenv; +} diff --git a/components/mgs/mgs/source/libdg/divide.c b/components/mgs/mgs/source/libdg/divide.c new file mode 100644 index 00000000..50dbc2f3 --- /dev/null +++ b/components/mgs/mgs/source/libdg/divide.c @@ -0,0 +1,661 @@ +#include "libdg.h" +#include "common.h" + +extern GV_HEAP MemorySystems_800AD2F0[MAX_MEMSYS]; +extern OT_TYPE ptr_800B1400[256]; + +typedef struct DG_DivideMem +{ + OT_TYPE *ot; // 0x00 + short field_04; // 0x04 + u_short raise; // 0x06 + long opz; // 0x08 outer product + int field_0C; // 0x0C some sort of delta + long field_10; // 0x10 + long field_14; // 0x14 + long field_18; // 0x18 + POLY_GT4 *pack; // 0x1C + GV_HEAP *pHeap; // 0x20 + GV_ALLOC *pAlloc; // 0x24 + int n_packs; // 0x28 + void *pDataStart; // 0x2C + int size; // 0x30 + DG_RVECTOR *rvec; // 0x34 +} DG_DivideMem; + +static inline DG_DivideMem *GetDivideMem() +{ + return (DG_DivideMem *)(SCRPAD_ADDR); +} + +STATIC void *DG_SplitMemory( int memIdx, int* n_split, int size ); +STATIC POLY_GT4 *DG_InitDividePacks( int memIdx ); +STATIC void *DG_AllocDividePackMem( GV_HEAP *heap, GV_ALLOC **alloc_list, int *size ); +STATIC POLY_GT4 *DG_GetDividePacks( void ); +STATIC int DG_GetRVectorCode( DG_RVECTOR *rvec ); +STATIC void DG_SetRVectorCode( DG_RVECTOR *rvec ); +STATIC void DG_SubdivideRVectorPoints( DG_RVECTOR *rvec1, DG_RVECTOR *rvec2, DG_RVECTOR *rvec3 ); +STATIC void DG_SetRVectorDelta( DG_RVECTOR *rvec1, DG_RVECTOR *rvec2, DG_RVECTOR *rvec3 ); +STATIC int DG_CopyPackToRVector( DG_RVECTOR *rvec ); +STATIC void DG_SubDivideRVectors( void ); +STATIC void DG_InitRVector( DG_OBJ *obj, int idx ); +STATIC void DG_AddSubdividedPrim( DG_OBJ *obj, int idx ); + +STATIC void *DG_SplitMemory( int memIdx, int *n_split, int size ) +{ + int i, split_count; + GV_HEAP *heap; + GV_ALLOC *alloc; + + split_count = 0; + heap = &MemorySystems_800AD2F0[ memIdx ]; + + alloc = heap->units; + i = heap->used; + while ( i > 0 ) + { + if (alloc->state == GV_ALLOC_STATE_FREE) + { + split_count += (alloc[1].start - alloc[0].start ) / size; + } + --i; + alloc++; + } + + *n_split = split_count; + return heap; +} + +STATIC POLY_GT4 *DG_InitDividePacks( int memIdx ) +{ + POLY_GT4 *pack; + GV_HEAP *heap; + + DG_DivideMem *divide_mem = GetDivideMem(); + +#ifdef __psyz + /* 0x34 = 52 = the PACKED POLY_GT4. PSY-Z's is 56 (8-byte header): slots + * carved at the console size make consecutive packs overlap by 4 bytes, + * each one's last vertex overwriting the next one's tag. */ + heap = DG_SplitMemory( memIdx, ÷_mem->n_packs, sizeof(POLY_GT4) ); +#else + heap = DG_SplitMemory( memIdx, ÷_mem->n_packs, 0x34 ); +#endif + + divide_mem->pHeap = heap; + divide_mem->pAlloc = 0; + pack = DG_AllocDividePackMem( heap, ÷_mem->pAlloc, ÷_mem->size ); + + divide_mem->pDataStart = pack; + return pack; +} + +void DG_DivideStart( void ) +{ + /* do nothing */ +} + +STATIC void *DG_AllocDividePackMem( GV_HEAP *heap, GV_ALLOC **alloc_list, int *size ) +{ + int i; + int alloc_idx; + GV_ALLOC *allocs; + + allocs = *alloc_list; + + if (!allocs) + { + allocs = heap->units; + } + else + { + allocs++; + } + + //gets the number of allocs between the current one and the total + alloc_idx = (int)(allocs - 2); + alloc_idx -= (int)heap; + alloc_idx >>= 3; + + i = heap->used - alloc_idx; + + for ( ; i > 0 ; --i ) + { + if ( allocs->state == GV_ALLOC_STATE_FREE ) + { + alloc_list[0] = allocs; + *size = allocs[1].start - allocs[0].start; + return allocs->start; + } + allocs++; + } + + *size = 0; + return 0; +} + +STATIC POLY_GT4 *DG_GetDividePacks( void ) +{ + POLY_GT4 *pack_addr; + DG_DivideMem *divide_mem; + + divide_mem = GetDivideMem(); + +#ifdef __psyz + divide_mem->size = divide_mem->size - (int)sizeof(POLY_GT4); +#else + divide_mem->size = divide_mem->size - 0x34; +#endif + + if (divide_mem->size < 0) + { + divide_mem->pDataStart = DG_AllocDividePackMem( divide_mem->pHeap, ÷_mem->pAlloc, ÷_mem->size ); + } + else + { + divide_mem->n_packs -= 1; + pack_addr = divide_mem->pDataStart; +#ifdef __psyz + divide_mem->pDataStart = (char *)divide_mem->pDataStart + sizeof(POLY_GT4); +#else + divide_mem->pDataStart += 0x34; +#endif + return pack_addr; + } + + if ( divide_mem->pDataStart ) + { + return DG_GetDividePacks(); + } + else + { + divide_mem->n_packs = 0; + return 0; + } +} + +STATIC int DG_GetRVectorCode( DG_RVECTOR *rvec ) +{ + int code; + + if ( !rvec->c.cd ) return 0; + + code = rvec[3].c.cd; + + return rvec->c.cd & rvec[1].c.cd & code & rvec[4].c.cd; +} + +STATIC void DG_SetRVectorCode( DG_RVECTOR *rvec ) +{ + char code = 0; + + if ( rvec->sxy.vx < -160 ) + { + code = 1; + } + else if ( 160 < rvec->sxy.vx ) + { + code = 2; + } + + if ( rvec->sxy.vy < -112 ) + { + code |= 4; + } + else if ( 112 < rvec->sxy.vy ) + { + code |= 8; + } + + if ( !rvec->sz ) + { + code |= 16; + } + + rvec->c.cd = code; +} + +STATIC void DG_SubdivideRVectorPoints( DG_RVECTOR *rvec1, DG_RVECTOR *rvec2, DG_RVECTOR *rvec3 ) +{ + rvec3->v.vx = (rvec1->v.vx + rvec2->v.vx) / 2; + rvec3->v.vy = (rvec1->v.vy + rvec2->v.vy) / 2; + rvec3->v.vz = (rvec1->v.vz + rvec2->v.vz) / 2; + + rvec3->uv[0] = (rvec1->uv[0] + rvec2->uv[0]) / 2; + rvec3->uv[1] = (rvec1->uv[1] + rvec2->uv[1]) / 2; + + rvec3->c.r = (rvec1->c.r + rvec2->c.r) / 2; + rvec3->c.g = (rvec1->c.g + rvec2->c.g) / 2; + rvec3->c.b = (rvec1->c.b + rvec2->c.b) / 2; +} + +STATIC void DG_SetRVectorDelta( DG_RVECTOR *rvec1, DG_RVECTOR *rvec2, DG_RVECTOR *rvec3 ) +{ + int vy_diff, vx_diff, delta; + + vy_diff = rvec2->sxy.vy - rvec1->sxy.vy; + vx_diff = rvec1->sxy.vx - rvec2->sxy.vx; + delta = GetDivideMem()->field_0C; + + if ( vy_diff >= 0 ) + { + if (vx_diff >= 0) + { + if ( vx_diff / 2 < vy_diff ) + { + rvec3->sxy.vx += delta; + } + //loc_80019370: + if (vy_diff / 2 < vx_diff) + { + rvec3->sxy.vy += delta; + } + } + else + { + //loc_80019398 + vx_diff = -vx_diff; + if ( vx_diff / 2 < vy_diff ) + { + rvec3->sxy.vx += delta; + } + //loc_8001941C: + if (vy_diff / 2 < vx_diff) + { + rvec3->sxy.vy -= delta; + } + } + } + else + { + vy_diff = -vy_diff; + if (vx_diff >= 0) + { + if ( vx_diff / 2 < vy_diff ) + { + rvec3->sxy.vx -= delta; + } + //loc_80019370: + if (vy_diff / 2 < vx_diff) + { + rvec3->sxy.vy += delta; + } + } + else + { + //loc_800193EC + vx_diff = -vx_diff; + if ( vx_diff / 2 < vy_diff ) + { + rvec3->sxy.vx -= delta; + } + //loc_8001941C: + if (vy_diff / 2 < vx_diff) + { + rvec3->sxy.vy -= delta; + } + } + } +} + +STATIC int DG_CopyPackToRVector( DG_RVECTOR *rvec ) +{ + int v1; + DG_DivideMem *divide_mem; + DG_DivideMem *divide_mem2; + POLY_GT4 *pack; + POLY_GT4 *pack2; + OT_TYPE *ot; + int z_idx; + + if ( DG_GetRVectorCode( rvec ) ) return 0; + + divide_mem = GetDivideMem(); + + if ( ( (unsigned int)divide_mem->rvec < (SCRPAD_ADDR + 0x254) ) && ( divide_mem->n_packs >= 4 ) ) + { + gte_NormalClip( *(int*)&rvec->sxy, *(int*)&rvec[1].sxy, *(int*)&rvec[3].sxy, ÷_mem->opz ); + v1 = divide_mem->opz; + + if ( v1 < 0 ) v1 = -v1; + + if ( divide_mem->field_14 < v1 ) + { + DG_RVECTOR *rvec_temp = divide_mem->rvec; + rvec_temp[0] = rvec[0]; + rvec_temp[2] = rvec[1]; + rvec_temp[6] = rvec[3]; + rvec_temp[8] = rvec[4]; + return 1; + } + } + + pack = DG_GetDividePacks(); + + if ( !pack ) return 0; + + divide_mem2 = GetDivideMem(); + + z_idx = (unsigned short)( ( ( rvec->sz + rvec[4].sz ) / 2 ) - divide_mem2->raise ); + + if ( z_idx < 0 ) z_idx = 0; + + //loc_800195F0 + LCOPY2( &rvec[0].sxy, &pack->x0, &rvec[1].sxy, &pack->x1 ); + LCOPY2( &rvec[3].sxy, &pack->x2, &rvec[4].sxy, &pack->x3 ); + + SCOPYL2( &rvec[0].uv, &pack->u0, &rvec[1].uv, &pack->u1 ); + SCOPYL2( &rvec[3].uv, &pack->u2, &rvec[4].uv, &pack->u3 ); + + LCOPY2( &rvec[0].c, &pack->r0, &rvec[1].c, &pack->r1 ); + LCOPY2( &rvec[3].c, &pack->r2, &rvec[4].c, &pack->r3 ); + + pack2 = divide_mem2->pack; + pack->code = pack2->code; + + SCOPYL( &pack2->clut, &pack->clut ); + SCOPYL( &pack2->tpage, &pack->tpage ); + + ot = divide_mem2->ot; + ot = &ot[ ( unsigned char ) z_idx ]; + + /* first radix pass, same as add_prim_mid: low byte picks the bucket, high + * byte rides in the length field until DG_SortChanl re-buckets by it */ + addPrim( ot, pack ); + setlen( pack, z_idx >> 8 ); + return 0; +} + +static inline void divide_setup( void ) +{ + DG_RVECTOR *rvec; + rvec = GetDivideMem()->rvec; + + DG_SubdivideRVectorPoints( &rvec[0], &rvec[2], &rvec[1] ); + DG_SubdivideRVectorPoints( &rvec[6], &rvec[8], &rvec[7] ); + + gte_ldv3( &rvec[1], &rvec[1], &rvec[7] ); + gte_rtpt(); + + DG_SubdivideRVectorPoints( &rvec[0], &rvec[6], &rvec[3] ); + DG_SubdivideRVectorPoints( &rvec[1], &rvec[7], &rvec[4] ); + DG_SubdivideRVectorPoints( &rvec[2], &rvec[8], &rvec[5] ); + + gte_stsxy3( &rvec[1].sxy, &rvec[1].sxy, &rvec[7].sxy ); + gte_stsz3( &rvec[1].sz, &rvec[1].sz, &rvec[7].sz ); + gte_ldv3( &rvec[3], &rvec[4] , &rvec[5] ); + gte_rtpt(); + + DG_SetRVectorCode( &rvec[1] ); + DG_SetRVectorCode( &rvec[7] ); + DG_SetRVectorDelta( &rvec[0], &rvec[2], &rvec[1] ); + DG_SetRVectorDelta( &rvec[8], &rvec[6], &rvec[7] ); + + gte_stsxy3( &rvec[3].sxy, &rvec[4].sxy, &rvec[5].sxy ); + gte_stsz3( &rvec[3].sz, &rvec[4].sz, &rvec[5].sz ); + + DG_SetRVectorCode( &rvec[3] ); + DG_SetRVectorCode( &rvec[4] ); + DG_SetRVectorCode( &rvec[5] ); + DG_SetRVectorDelta( &rvec[2], &rvec[8], &rvec[5] ); + DG_SetRVectorDelta( &rvec[6], &rvec[0], &rvec[3] ); +} + +//todo: odd function requires do while hack, need to revisit +STATIC void DG_SubDivideRVectors( void ) +{ + int mask = 1; + DG_DivideMem *divide_mem; + + divide_setup(); + do {} while (0); //hack thats needed to make it use the right registers + + divide_mem = GetDivideMem(); + + divide_mem->rvec += 9; + + if ( DG_CopyPackToRVector( ÷_mem->rvec[-9] ) ) DG_SubDivideRVectors(); + if ( DG_CopyPackToRVector( ÷_mem->rvec[-8] ) ) DG_SubDivideRVectors(); + if ( DG_CopyPackToRVector( ÷_mem->rvec[-6] ) && mask ) DG_SubDivideRVectors(); + if ( DG_CopyPackToRVector( ÷_mem->rvec[-5] ) ) DG_SubDivideRVectors(); + + divide_mem->rvec -= 9; +} + +typedef struct cpystrct { + unsigned long cpy[2]; +} cpystrct; + +static inline void copy_verts(unsigned char *faceIndexOffset, SVECTOR *vertexIndexOffset) +{ + *(cpystrct*)(SCRPAD_ADDR + 0x038) = *(cpystrct*)&vertexIndexOffset[faceIndexOffset[0]]; + *(cpystrct*)(SCRPAD_ADDR + 0x060) = *(cpystrct*)&vertexIndexOffset[faceIndexOffset[1]]; + *(cpystrct*)(SCRPAD_ADDR + 0x0B0) = *(cpystrct*)&vertexIndexOffset[faceIndexOffset[3]]; + *(cpystrct*)(SCRPAD_ADDR + 0x0D8) = *(cpystrct*)&vertexIndexOffset[faceIndexOffset[2]]; +} + +//function seems to call scratchpad addresses directly rather than through a struct +STATIC void DG_InitRVector( DG_OBJ *obj, int idx ) +{ + POLY_GT4 *pack; + POLY_GT4 *org_pack; + int n_packs; + + org_pack = obj->packs[ idx ]; + *(short*)(SCRPAD_ADDR + 0x006) = obj->raise; + + while ( obj ) + { + unsigned char *faceIndexOffset = obj->model->vindices; + SVECTOR *vertexIndexOffset = obj->model->vertices; + n_packs = obj->n_packs; + pack = org_pack; + + for ( --n_packs ; n_packs >= 0 ; --n_packs ) + { + int pack_raise = pack->tag & 0xFFFF; + int pack_addr = pack->tag >> 8; + + if ( ( *(unsigned int*)(SCRPAD_ADDR + 0x014) < pack_addr ) && + ( pack_raise < *(int*)(SCRPAD_ADDR + 0x018) ) && + ( *(int*)(SCRPAD_ADDR + 0x028) >= 4 ) ) + { + *(short*)pack = 0; + *(int*)(SCRPAD_ADDR + 0x01C) = (int)pack; + + if ( pack_addr & 0x100 ) + { + *(int*)(SCRPAD_ADDR + 0x00C) = -*(int*)(SCRPAD_ADDR + 0x010); + } + else + { + *(int*)(SCRPAD_ADDR + 0x00C) = *(int*)(SCRPAD_ADDR + 0x010); + } + + //loc_80019A04: + *(int*)(SCRPAD_ADDR + 0x034) = (int)(SCRPAD_ADDR + 0x038); + + copy_verts( faceIndexOffset, vertexIndexOffset ); + + LCOPY2( &pack->x0 , (void*)(SCRPAD_ADDR + 0x044) , &pack->x1 , (void*)(SCRPAD_ADDR + 0x06C) ); + LCOPY2( &pack->x2 , (void*)(SCRPAD_ADDR + 0x0BC) , &pack->x3 , (void*)(SCRPAD_ADDR + 0x0E4) ); + SCOPYL2( &pack->u0 , (void*)(SCRPAD_ADDR + 0x03E) , &pack->u1 , (void*)(SCRPAD_ADDR + 0x066) ); + SCOPYL2( &pack->u2 , (void*)(SCRPAD_ADDR + 0x0B6) , &pack->u3 , (void*)(SCRPAD_ADDR + 0x0DE) ); + LCOPY2( &pack->r0 , (void*)(SCRPAD_ADDR + 0x040) , &pack->r1 , (void*)(SCRPAD_ADDR + 0x068) ); + LCOPY2( &pack->r2 , (void*)(SCRPAD_ADDR + 0x0B8) , &pack->r3 , (void*)(SCRPAD_ADDR + 0x0E0) ); + + gte_ldv3( (SCRPAD_ADDR + 0x060) , (SCRPAD_ADDR + 0x0B0) , (SCRPAD_ADDR + 0x0D8) ); + gte_rtpt(); + gte_stsz3( (SCRPAD_ADDR + 0x070) , (SCRPAD_ADDR + 0x0C0) , (SCRPAD_ADDR + 0x0E8) ); + + gte_ldv0( (SCRPAD_ADDR + 0x038) ); + gte_rtps(); + gte_stsz( (SCRPAD_ADDR + 0x048) ); + + DG_SetRVectorCode( (DG_RVECTOR*)(SCRPAD_ADDR + 0x038) ); + DG_SetRVectorCode( (DG_RVECTOR*)(SCRPAD_ADDR + 0x060) ); + DG_SetRVectorCode( (DG_RVECTOR*)(SCRPAD_ADDR + 0x0B0) ); + DG_SetRVectorCode( (DG_RVECTOR*)(SCRPAD_ADDR + 0x0D8) ); + DG_SubDivideRVectors(); + + } + else + { + if ( pack_raise ) + { + OT_TYPE *ot; + u_short raise; + + ot = GetDivideMem()->ot; + raise = GetDivideMem()->raise; + + raise = pack_raise - raise; + pack_raise = raise; + ot = &ot[ ( u_char ) pack_raise ]; + + /* first radix pass: bucket by the low byte of the depth and + * park the high byte in the length field, which the second + * pass (DG_SortChanl) re-buckets by. PSY-Q packed both into + * one 32-bit tag; PSY-Z keeps addr and len apart. */ + addPrim( ot, pack ); + setlen( pack, pack_raise >> 8 ); + } + } + pack++; + faceIndexOffset += 4; + } + + obj = obj->extend; + org_pack = pack; + } +} + +static inline void add_prim_mid( OT_TYPE *ot, POLY_GT4 *pack, int z_idx, int raise ) +{ + OT_TYPE *temp; + z_idx = (z_idx - raise); + z_idx &= 0xFFFF; + + temp = &ot[ ( unsigned char ) z_idx ]; + + /* see the note in DG_DividePrim: low byte selects the bucket, high byte is + * stashed in the length field for the second radix pass */ + addPrim( temp, pack ); + setlen( pack, z_idx >> 8 ); +} + +STATIC void DG_AddSubdividedPrim( DG_OBJ *obj, int idx ) +{ + POLY_GT4 *org_pack; + POLY_GT4 *pack; + int raise; + int n_packs; + OT_TYPE *ot_temp; + u_short pack_raise; + + org_pack = obj->packs[ idx ]; + raise = obj->raise; //t1 + + while ( obj ) + { + n_packs = obj->n_packs; + pack = (POLY_GT4*)getaddr( &org_pack ); + + /* was three lines reading the pointer parked at scratchpad offset 0; + * that is exactly DG_DivideMem::ot, which lives there */ + ot_temp = GetDivideMem()->ot; + + for ( --n_packs ; n_packs >= 0 ; --n_packs ) + { + pack_raise = pack->tag; + if ( pack_raise ) + { + add_prim_mid( ot_temp, pack, pack_raise, raise ); + } + pack++; + } + obj = obj->extend; + org_pack = pack; + } +} + +void DG_DivideChanl( DG_CHANL *chanl, int idx ) +{ + int i, j, x; + DG_OBJS **objs_queue; + DG_OBJS *objs; + DG_OBJ *obj; + DG_DivideMem *divide_mem; + + if ( !DG_InitDividePacks( idx ) ) return; + + DG_Clip( &chanl->clip_rect, chanl->clip_distance ); + + divide_mem = GetDivideMem(); + divide_mem->ot = ptr_800B1400; + divide_mem->field_14 = 0x800; + + if ( chanl->clip_distance > 1000) + { + divide_mem->field_18 = 60000; + } + else + { + divide_mem->field_18 = 8192; + } + + objs_queue = chanl->queue; + //s6 = 1 + x = 1; + for ( i = chanl->objs_index ; i > 0 ; --i ) + { + objs = *objs_queue; + objs_queue++; + + if ( !objs->bound_mode ) continue; + + obj = objs->objs; + if ( !( objs->flag & DG_FLAG_GBOUND ) ) + { + for ( j = objs->n_models ; j > 0 ; --j ) + { + if (obj->bound_mode) + { + gte_SetRotMatrix( &obj->screen ); + gte_SetTransMatrix( &obj->screen ); + + if ( obj->model->flags & DG_MODEL_TRANS ) + { + GetDivideMem()->field_10 = 0; + } + else + { + GetDivideMem()->field_10 = x; + } + + SetSpadStack( SPAD_STACK_ADDR ); + DG_InitRVector( obj, idx ); + ResetSpadStack(); + + } + obj++; + } + } + else + { + for ( j = objs->n_models ; j > 0 ; --j ) + { + if (obj->bound_mode) + { + DG_AddSubdividedPrim( obj, idx ); + } + obj++; + } + } + } +} + +void DG_DivideEnd( void ) +{ + /* do nothing */ +} diff --git a/components/mgs/mgs/source/libdg/libdg.h b/components/mgs/mgs/source/libdg/libdg.h new file mode 100644 index 00000000..15726eab --- /dev/null +++ b/components/mgs/mgs/source/libdg/libdg.h @@ -0,0 +1,676 @@ +#ifndef __MGS_LIBDG_H__ +#define __MGS_LIBDG_H__ + +#include +#include +#include +#include "inline_n.h" +#include + +#include "libgv/libgv.h" +#include "fmt_tex.h" +#include "fmt_mot.h" + +/*---------------------------------------------------------------------------*/ + +typedef struct DG_VECTOR +{ + int vx; + int vy; + int vz; +} DG_VECTOR; /* long word type 3D vector (without padding) */ + +typedef struct DG_SVECTOR +{ + short vx; + short vy; + short vz; +} DG_SVECTOR; /* short word type 3D vector (without padding) */ + +typedef struct DG_RVECTOR +{ + DG_SVECTOR v; + u_char uv[2]; + CVECTOR c; + DVECTOR sxy; + u_long sz; +} DG_RVECTOR; /* division vertex data (without padding) */ + +typedef struct DG_PVECTOR +{ + long vxy; + long vz; +} DG_PVECTOR; + +/*---------------------------------------------------------------------------*/ + +#define DG_MAX_TEXTURES 512 + +enum DG_MODEL_FLAGS { + DG_MODEL_TRANS = 0x00002, + DG_MODEL_UNLIT = 0x00004, + DG_MODEL_BOTHFACE = 0x00400, + DG_MODEL_INDIRECT = 0x10000, +}; + +typedef struct _DG_MDL +{ + int flags; + int n_faces; + DG_VECTOR min; + DG_VECTOR max; + DG_VECTOR pos; + int parent; + int extend; + int n_verts; + SVECTOR *vertices; + unsigned char *vindices; + int n_normals; + SVECTOR *normals; + unsigned char *nindices; + unsigned char *texcoords; + unsigned short *materials; // hashed texture names + int padding; +} DG_MDL; + +typedef struct _DG_DEF +{ + int n_visible; // ??? + int n_models; + DG_VECTOR min; + DG_VECTOR max; + DG_MDL model[ 0 ]; +} DG_DEF; + +// clang-format off +typedef struct _DG_TEX { + u_short id; // + u_char used; // + u_char col; // + u_short tpage; // + u_short clut; // + u_char off_x; // + u_char off_y; // + u_char w; // + u_char h; // +} DG_TEX; + +typedef struct _DG_OBJ { + MATRIX world; // 0x00 + MATRIX screen; // 0x20 + DG_MDL *model; // 0x40 + CVECTOR *rgbs; // 0x44 + struct _DG_OBJ *extend; // 0x48 + short bound_mode; // 0x4C + short free_count; // 0x4E + short raise; // 0x50 + short n_packs; // 0x52 + POLY_GT4 *packs[ 2 ]; // 0x54 +} DG_OBJ; + +typedef struct { + MATRIX world; // 0x00 + MATRIX *root; // 0x20 + DG_DEF *def; // 0x24 + u_long flag; // 0x28 + u_short group_id; // 0x2C + short n_models; // 0x2E + short chanl; // 0x30 + short bound_mode; // 0x32 + MATRIX *light; // 0x34 + SVECTOR *rots; // 0x38 + SVECTOR *adjust; // 0x3C + SVECTOR *waist_rot; // 0x40 + SVECTOR *movs; // 0x44 + DG_OBJ objs[ 0 ]; // 0x48 +} DG_OBJS; + +enum { + DG_FLAG_TEXT = 0x0001, // + DG_FLAG_PAINT = 0x0002, // + DG_FLAG_TRANS = 0x0004, // + DG_FLAG_SHADE = 0x0008, // + DG_FLAG_BOUND = 0x0010, // + DG_FLAG_GBOUND = 0x0020, // + DG_FLAG_ONEPIECE = 0x0040, // + DG_FLAG_INVISIBLE = 0x0080, // + DG_FLAG_AMBIENT = 0x0100, // + DG_FLAG_IRTEXTURE = 0x0200, // + DG_FLAG_ENVMAP = 0x0400, // +}; +// clang-format on + +typedef struct { + u_int id; + DG_DEF def; +} DG_KMDPACK; + +typedef struct { + u_int ident; + u_int n_kmd; + u_int vert_offset; + u_int body_len; + DG_KMDPACK kmd[ 0 ]; +} DG_ZMD_DEF; + +typedef struct { // libdg internal + DG_VECTOR min; + DG_VECTOR max; +} DG_BOUND; + +struct _DG_PRIM; +typedef POLY_FT4 * ( *TPRIM_FN )( struct _DG_PRIM *prim, POLY_FT4 *packs, int n_packs ); + +typedef struct _DG_PRIM +{ + MATRIX world; + MATRIX *root; + int type; + u_short group_id; + short prim_count; + short chanl; + short raise; + short psize; + short verts; + short voffset; + short vstep; + SVECTOR *pos; + RECT *rect; + void *packs[ 2 ]; + short n_prims; + void *userdata; + TPRIM_FN handler; +} DG_PRIM; + +typedef struct DG_LIT +{ + SVECTOR pos; + unsigned short field_8_brightness; + unsigned short field_A_radius; + CVECTOR field_C_color; +} DG_LIT; + +typedef struct DG_FixedLight +{ + int field_0_lightCount; + DG_LIT *field_4_pLights; +} DG_FixedLight; + +typedef struct DG_TmpLightList +{ + int n_lights; + DG_LIT lights[ 8 ]; +} DG_TmpLightList; + +typedef struct DG_LitVertex +{ + SVECTOR intensity[2]; + CVECTOR color[2]; +} DG_LitVertex; + +typedef struct DG_IMG_ATTRIB +{ + unsigned char texid; + unsigned char xoff; + unsigned char yoff; + unsigned char unused; +} DG_IMG_ATTRIB; + +typedef struct DG_IMG +{ + unsigned short image_width; + unsigned short image_height; + unsigned short tile_width; + unsigned short tile_height; + unsigned short *textures; // textures[0] = count + DG_IMG_ATTRIB *attribs; + unsigned char *tilemap; +} DG_IMG; + +/*---------------------------------------------------------------------------*/ + +typedef struct DG_NARS +{ + unsigned int unknown0; + unsigned char *unknown1; +} DG_NARS; + +typedef struct DG_OAR +{ + MOTION_ARCHIVE *archive; + unsigned int n_joint; + unsigned int n_motion; + MOTION_TABLE *table; + char oarData[ 0 ]; +} DG_OAR; + +/*---------------------------------------------------------------------------*/ + +typedef struct DG_Image +{ + RECT dim; + unsigned char data[ 512 ]; +} DG_Image; + +typedef struct DG_CHANL +{ + OT_TYPE *ot[ 2 ]; // 257 pointers? // One for each active buffer + short ot_size; + short link; + short dblbuf; /* double buffer */ + short dirty; + MATRIX eye_inv; + MATRIX eye; + short clip_distance; + short queue_size; + short prim_index; + short objs_index; + + /* TODO: retype as (void **) */ + DG_OBJS **queue; // queue of DG_OBJS followed by DG_PRIM + + RECT clip_rect; + RECT new_clip_rect; + DR_ENV env1[ 2 ]; // used when entering the channel + DR_ENV env2[ 2 ]; // used when returning to the background channel + DR_ENV new_env[ 2 ]; +} DG_CHANL; + +enum DG_PRIM_TYPE { + DG_PRIM_LINE_F2, // 0 + DG_PRIM_LINE_F3, // 1 + DG_PRIM_LINE_F4, // 2 + DG_PRIM_LINE_G2, // 3 + DG_PRIM_LINE_G3, // 4 + DG_PRIM_LINE_G4, // 5 + DG_PRIM_SPRT, // 6 + DG_PRIM_SPRT_8, // 7 + DG_PRIM_SPRT_16, // 8 + DG_PRIM_TILE, // 9 + DG_PRIM_TILE_1, // 10 + DG_PRIM_TILE_8, // 11 + DG_PRIM_TILE_16, // 12 + DG_PRIM_POLY_F3, // 13 + DG_PRIM_POLY_F4, // 14 + DG_PRIM_POLY_G3, // 15 + DG_PRIM_POLY_G4, // 16 + DG_PRIM_POLY_FT3, // 17 + DG_PRIM_POLY_FT4, // 18 + DG_PRIM_POLY_GT3, // 19 + DG_PRIM_POLY_GT4, // 20 + DG_PRIM_LINE_FT2, // 21 + DG_PRIM_LINE_GT2, // 22 + DG_PRIM_FREE, // 23 + DG_PRIM_MAX // 24 +}; + +// clang-format off +enum { + DG_PRIM_VISIBLE = 0x0000, + DG_PRIM_INVISIBLE = 0x0100, + + DG_PRIM_ON_WORLD = 0x0000, + DG_PRIM_ON_CAMERA = 0x0200, + + DG_PRIM_VERTICES = 0x0000, + DG_PRIM_RECTANGLE = 0x0400, + + DG_PRIM_SORTONLY = 0x0800, + DG_PRIM_ONESIDE = 0x1000, + DG_PRIM_FREEPACKS = 0x2000, +}; +// clang-format on + +enum DG_CHANL_UNIT +{ + DG_SCREEN_CHANL, + DG_BOUND_CHANL, + DG_TRANS_CHANL, + DG_SHADE_CHANL, + DG_PRIM_CHANL, + DG_DIVIDE_CHANEL, + DG_SORT_CHANL, + DG_CHANL_UNIT_MAX +}; + +/*---------------------------------------------------------------------------*/ + +static inline void DG_GroupObjs( DG_OBJS *objs, int group_id ) +{ + objs->group_id = group_id; +} + +#ifndef __LIBDG_DISPLAY_C__ +static inline void DG_GroupObjsEx( DG_OBJS *objs ) +{ + extern int DG_CurrentGroupID; + + DG_GroupObjs( objs, DG_CurrentGroupID ); +} +#endif + +static inline void DG_VisibleObjs( DG_OBJS *objs ) +{ + objs->flag &= ~DG_FLAG_INVISIBLE; +} + +static inline void DG_InvisibleObjs( DG_OBJS *objs ) +{ + objs->flag |= DG_FLAG_INVISIBLE; +} + +#ifndef __LIBDG_DISPLAY_C__ +static inline void DG_SetCurrentGroup( int group_id ) +{ + extern int DG_CurrentGroupID; + + DG_CurrentGroupID = group_id; +} +#endif + +static inline void DG_UnAmbientObjs( DG_OBJS *objs ) +{ + objs->flag &= ~DG_FLAG_AMBIENT; +} + +static inline void DG_AmbientObjs( DG_OBJS *objs ) +{ + objs->flag |= DG_FLAG_AMBIENT; +} + +static inline void DG_GroupPrim( DG_PRIM *prim, int group_id ) +{ + prim->group_id = group_id; +} + +static inline void DG_VisiblePrim( DG_PRIM *prim ) +{ + prim->type &= ~DG_PRIM_INVISIBLE; +} + +static inline void DG_InvisiblePrim( DG_PRIM *prim ) +{ + prim->type |= DG_PRIM_INVISIBLE; +} + +static inline void DG_RaisePrim( DG_PRIM *prim, int raise ) +{ + prim->raise = raise; +} + +static inline void DG_UnShadeObjs( DG_OBJS *objs ) +{ + objs->flag &= ~DG_FLAG_SHADE; +} + +static inline void DG_UnBoundObjs( DG_OBJS *objs ) +{ + objs->flag &= ~DG_FLAG_BOUND; +} + +static inline void DG_UnGBoundObjs( DG_OBJS *objs ) +{ + objs->flag &= ~DG_FLAG_GBOUND; +} + +static inline void DG_GBoundObjs( DG_OBJS *objs ) +{ + objs->flag |= DG_FLAG_GBOUND; +} + +/*---------------------------------------------------------------------------*/ + +typedef void (*DG_CHANLFUNC)(DG_CHANL *chanl, int idx); + +/* dgd.c */ +extern int DG_FrameRate; +extern int DG_HikituriFlag; +extern int DG_HikituriFlagOld; + +void DG_ResetPipeline( void ); +void DG_ResetTextureCache( void ); +void DG_StartDaemon(void); + +/* bound.c */ +void DG_BoundStart( void ); +void DG_BoundChanl( DG_CHANL *chanl, int idx ); +void DG_BoundEnd( void ); + +/* chanl.c */ +#ifndef __LIBDG_CHANL_C__ +extern short N_ChanlPerfMax; +extern short word_800AB982; +#endif + +void DG_InitChanlSystem( int width ); +void DG_DrawOTag( int which ); +void DG_ClearChanlSystem( int which ); +void DG_RenderPipeline( int idx ); +void DG_SetRenderChanlDrawEnv( int idx, DRAWENV *pDrawEnv ); +int DG_QueueObjs( DG_OBJS *prim ); +void DG_DequeueObjs( DG_OBJS *objs ); +int DG_QueuePrim( DG_PRIM *prim ); +void DG_DequeuePrim( DG_PRIM *prim ); +void DG_InitDrawEnv( DRAWENV *pDrawEnv, int clipX1, int clipY1, int clipX2, int clipY2 ); +void DG_FreeObjectQueue( void ); +void DG_RestartMainChanlSystem( void ); +void DG_SetBackgroundRGB( int r, int g, int b ); +void DG_SetRGB( int r, int b, int g ); +void DG_BackGroundBlack( void ); +void DG_BackGroundNormal( void ); +void DG_SetBackGroundTile( TILE *tile ); +DG_CHANLFUNC DG_SetChanlSystemUnits( int idx, DG_CHANLFUNC newfunc ); + +/* display.c */ +extern int DG_UnDrawFrameCount; +#ifndef __LIBDG_DISPLAY_C__ +extern int DG_CurrentGroupID; +extern short DG_ClipMin[2]; +extern short DG_ClipMax[2]; +#endif + +void DG_InitDispEnv( int x, short y, short w, short h, int clipH ); +void DG_ChangeReso( int ); +void DG_RenderPipeline_Init( void ); +void DG_SwapFrame( void ); +void DG_RenderFrame( void ); +void DG_LookAt( DG_CHANL *chanl, SVECTOR *eye, SVECTOR *center, int clip_distance ); +void DG_AdjustOverscan( MATRIX *matrix ); +void DG_Clip( RECT *clip_rect, int dist ); +void DG_OffsetDispEnv( int offset ); +void DG_ClipDispEnv( int x, int y ); +void DG_DisableClipping( void ); +void DG_FadeScreen( int amount ); +DISPENV *DG_GetDisplayEnv( void ); + +/* divide.c */ +void DG_DivideStart( void ); +void DG_DivideChanl( DG_CHANL *chanl, int idx ); +void DG_DivideEnd( void ); + +/* light.c */ +extern MATRIX DG_LightMatrix; +extern MATRIX DG_ColorMatrix; + +extern SVECTOR DG_Ambient; +extern u_long DG_PacketCode[2]; + +void DG_InitLightSystem( void ); +void DG_SetAmbient( int vx, int vy, int vz ); +void DG_SetMainLightDir( int x, int y, int z ); +void DG_SetMainLightCol( int r, int g, int b ); +void DG_ResetFixedLight( void ); +void DG_SetFixedLight( DG_LIT *light, int light_count ); +void DG_ClearTmpLight( void ); +int DG_SetTmpLight( SVECTOR *svec, int brightness, int radius ); +void DG_GetLightVector( VECTOR *in_vec, int divisor, SVECTOR *out_vec ); +int DG_GetLightMatrix( SVECTOR *vec, MATRIX *mtx ); +void DG_SetLightMatrix( MATRIX* mtx, int trans_x ); +int DG_GetLightMatrix2( SVECTOR *vec, MATRIX *mtx ); + +/* loader.c */ +int DG_LoadInitKmd( void *buf, int id ); +int DG_LoadInitNar( void *buf, int id ); +int DG_LoadInitOar( void *buf, int id ); +int DG_LoadInitImg( void *buf, int id ); +int DG_LoadInitSgt( void *buf, int id ); +int DG_LoadInitLit( void *buf, int id ); +int DG_LoadInitPcx( void *buf, int id ); +int DG_LoadInitKmdar(void *buf, int id ); + +/* matrix.c */ +void DG_MatrixRot( MATRIX *mat, SVECTOR *svec ); +void DG_MatrixRotYXZ( MATRIX *mat, SVECTOR *svec ); +void DG_MatrixRotZYX( MATRIX *mat, SVECTOR *vec ); +void DG_TransposeMatrix( MATRIX *in, MATRIX *out ); +void DG_ShadowMatrix( MATRIX *out, MATRIX *in, int param_3 ); +void DG_ReflectVector( SVECTOR *in, SVECTOR *translation, SVECTOR *out ); +void DG_ReflectMatrix( SVECTOR *svec, MATRIX *in, MATRIX *out ); + +/* o.c */ +DG_OBJS *DG_MakeObjs( DG_DEF *def, int flag, int chanl ); +void DG_FreeObjs( DG_OBJS *objs ); +void DG_SetObjsRots( DG_OBJS *objs, SVECTOR *rot ); +void DG_SetObjsMovs( DG_OBJS *objs, SVECTOR *mov ); + +/* opack.c */ +void DG_WriteObjPacketUV( DG_OBJ* obj, int idx ); +void DG_WriteObjPacketRGB( DG_OBJ *obj, int idx ); +int DG_MakeObjPacket( DG_OBJ *obj, int idx, int flags ); +void DG_FreeObjPacket( DG_OBJ *obj, int idx ); +int DG_MakeObjsPacket( DG_OBJS *objs, int idx ); +void DG_FreeObjsPacket( DG_OBJS *objs, int idx ); + +/* palette.c */ +void DG_StorePalette( void ); +void DG_ReloadPalette( void ); + +/* prim.c */ +extern MATRIX DG_ZeroMatrix; +extern SVECTOR DG_ZeroVector; + +void DG_PrimStart( void ); +void DG_PrimChanl( DG_CHANL *chanl, int idx ); +void DG_PrimEnd( void ); +DG_PRIM *DG_MakePrim(int type, int prim_count, int chanl, SVECTOR *pos, RECT *rect); +void DG_FreePrim( DG_PRIM *prim ); +void DG_SetFreePrimParam( int psize, int verts, int voffset, int vstep ); + +/* pshade.c */ +int DG_MakePreshade( DG_OBJS *prim, DG_LIT *light, int numLights ); +void DG_FreePreshade( DG_OBJS *prim ); + +/* pos.c */ +void DG_SetPos( MATRIX *world ); +void DG_SetPos2( SVECTOR *mov, SVECTOR *rot ); +void DG_MovePos( SVECTOR *mov ); +void DG_RotatePos( SVECTOR *rot ); +void DG_PutObjs( DG_OBJS *objs ); +void DG_PutPrim( DG_PRIM *prim ); +void DG_PutVector( SVECTOR *from, SVECTOR *to, int n ); +void DG_RotVector( SVECTOR *from, SVECTOR *to, int n ); +void DG_PersVector( SVECTOR *from, DVECTOR *to, int n ); +void DG_PointCheck( SVECTOR *svector, int n_points ); +int DG_PointCheckOne( DVECTOR *line ); + +/* screen.c */ +// void DG_ScreenModelsSingle( DG_OBJS *objs, int n_obj ); +// void DG_ScreenModels( DG_OBJS *objs, int n_obj ); +// void DG_ApplyMovs( DG_OBJS *objs, int n_obj ); +// void DG_ApplyRots( DG_OBJS *objs, int n_obj ); +void DG_ScreenChanl( DG_CHANL *chanl, int idx ); + +/* shade.c */ +void DG_ShadeStart( void ); +void DG_ShadeChanl(DG_CHANL *chanl, int idx); +void DG_ShadeEnd( void ); + +/* sort.c */ +void DG_SortChanl( DG_CHANL *chanl, int idx ); + +/* text.c */ +void DG_InitTextureSystem( void ); +DG_TEX *DG_GetTexture( int id ); +void DG_SetTexture( int id, int tp, int abr, RECT *img, RECT *pal, int col ); +void DG_GetTextureRect( DG_TEX *tex, RECT *rect ); +void DG_GetClutRect( DG_TEX *tex, RECT *rect ); +void DG_ClearResidentTexture( void ); +void DG_SaveResidentTextureCache( void ); +void DG_LoadResidentTextureCache( void ); + +/* trans.c */ +void DG_TransStart( void ); +void DG_TransChanl( DG_CHANL *chanl, int idx ); +void DG_TransEnd( void ); + +/* palette.c */ +void DG_StorePalette2( void ); +void DG_StorePaletteEffect( void ); +void DG_ResetPaletteEffect( void ); +void sub_80079004( unsigned short param_1 ); +void sub_800790E8( void ); +void sub_8007913C( void ); +void DG_SetExtPaletteMakeFunc( void (*param_1)(void), u_short (*param_2)(u_short) ); +void DG_ResetExtPaletteMakeFunc(void); +void DG_MakeEffectPalette( unsigned short *param_1, int param_2 ); + +/*---------------------------------------------------------------------------*/ + +static inline DG_CHANL *DG_Chanl( int idx ) +{ + extern DG_CHANL DG_Chanls[ 3 ]; + return &DG_Chanls[ idx + 1 ]; +} + +static inline OT_TYPE *DG_ChanlOTag(int index) +{ + extern int GV_Clock; + return DG_Chanl(index)->ot[GV_Clock]; +} + +static inline void DG_SetPacketTexture( POLY_FT4 *packs, DG_TEX *tex ) +{ + int x, y, w, h; + x = tex->off_x ; + w = tex->w ; + y = tex->off_y ; + h = tex->h ; + + setUVWH( packs, x, y, w, h ) ; +} + +static inline void DG_SetPacketTexture4( POLY_FT4 *packs, DG_TEX *tex ) +{ + DG_SetPacketTexture( packs, tex ) ; + packs->tpage = tex->tpage ; + packs->clut = tex->clut ; +} + +/*---------------------------------------------------------------------------*/ + +// clang-format off +// gte_MulMatrix0 but without updating the current rotation matrix +#define DG_MulRotMatrix0(r1, r2) \ +{ \ + gte_ldclmv(r1); \ + gte_rtir(); \ + gte_stclmv(r2); \ + gte_ldclmv((char *)r1 + 2); \ + gte_rtir(); \ + gte_stclmv((char *)r2 + 2); \ + gte_ldclmv((char *)r1 + 4); \ + gte_rtir(); \ + gte_stclmv((char *)r2 + 4); \ +} + +// gte_CompMatrix but without updating the current matrices +#define DG_CompMatrix(r1, r2) \ +{ \ + gte_ldlv0((char *)r1 + 20); \ + gte_rt(); \ + gte_stlvnl((char *)r2 + 20); \ + DG_MulRotMatrix0(r1, r2); \ +} +// clang-format on + +#endif // __MGS_LIBDG_H__ diff --git a/components/mgs/mgs/source/libdg/light.c b/components/mgs/mgs/source/libdg/light.c new file mode 100644 index 00000000..ea65c813 --- /dev/null +++ b/components/mgs/mgs/source/libdg/light.c @@ -0,0 +1,291 @@ +#include +#include +#include + +#include "libdg.h" +#include "common.h" +#include "libgv/libgv.h" +#include "game/game.h" + +/*---------------------------------------------------------------------------*/ + +/*** data ***/ +MATRIX DG_LightMatrix = { + {{ 0x0000, 0x0000, 0x1000 }, + { 0x0000, 0x0000, 0x0000 }, + { 0x0000, 0x0000, 0x0000 }}, + { 0, 0, 0 } +}; + +MATRIX DG_ColorMatrix = { + {{ 0x1000, 0x1000, 0x1000 }, + { 0x1000, 0x1000, 0x1000 }, + { 0x1000, 0x1000, 0x1000 }}, + { 0, 0, 0 } +}; + +/*** $gp ***/ +SVECTOR DG_Ambient = { 0, 0, 0, 0 }; + +u_long DG_PacketCode[2] = { 0x3C808080, 0x3E808080 }; + +/*** bss ***/ +extern DG_FixedLight gFixedLights_800B1E08[8]; +extern DG_TmpLightList LightSystems_800B1E48[2]; + +/*---------------------------------------------------------------------------*/ + +static inline DG_TmpLightList *getTempLight( int idx ) +{ + return &LightSystems_800B1E48[ idx ]; +} + +void DG_InitLightSystem( void ) +{ + DG_ResetFixedLight(); + LightSystems_800B1E48[0].n_lights = 0; + LightSystems_800B1E48[1].n_lights = 0; + DG_SetAmbient( 0 , 0 , 0 ); +} + +void DG_SetAmbient( int vx, int vy, int vz ) +{ + DG_Ambient.vx = (short)vx; + DG_Ambient.vy = (short)vy; + DG_Ambient.vz = (short)vz; + gte_SetBackColor( vx, vy, vz ); + + vx = ( ( 0x100 - vx ) >> 1 ) & 0xFF; + vy = ( ( 0x100 - vy ) << 7 ) & 0xFF00; + vz = ( ( 0x100 - vz ) << 15 ) & 0xFF0000; + + DG_PacketCode[0] = 0x3C000000 | vx | vy | vz; + DG_PacketCode[1] = DG_PacketCode[0] | 0x2000000; +} + +void DG_SetMainLightDir( int x, int y, int z ) +{ + VECTOR in; + VECTOR out; + + in.vx = x; + in.vy = y; + in.vz = z; + + VectorNormal(&in, &out); + DG_LightMatrix.m[0][0] = out.vx; + DG_LightMatrix.m[0][1] = out.vy; + DG_LightMatrix.m[0][2] = out.vz; +} + +void DG_SetMainLightCol( int r, int g, int b ) +{ + DG_ColorMatrix.m[0][0] = r * 16; + DG_ColorMatrix.m[1][0] = g * 16; + DG_ColorMatrix.m[2][0] = b * 16; +} + +void DG_ResetFixedLight( void ) +{ + DG_FixedLight *pLightIter = &gFixedLights_800B1E08[0]; + int i = COUNTOF(gFixedLights_800B1E08); + while (i > 0) + { + i--; + pLightIter->field_0_lightCount = 0; + pLightIter->field_4_pLights = 0; + pLightIter++; + } +} + +void DG_SetFixedLight( DG_LIT *light, int light_count ) +{ + DG_FixedLight *pLightIter; + DG_LIT **current_light; + int i; + + pLightIter = &gFixedLights_800B1E08[0]; + i = 7; + current_light = &gFixedLights_800B1E08[0].field_4_pLights; + + for (; i >= 1; i--, current_light += 2, pLightIter++) + { + if (current_light[0] == light) + { + return; + } + + if (pLightIter->field_0_lightCount == 0) + { + pLightIter->field_0_lightCount = light_count; + current_light[0] = light; + return; + } + } +} + +void DG_ClearTmpLight( void ) +{ + DG_TmpLightList* tmp = getTempLight( GV_Time & 1 ); + tmp->n_lights = 0; +} + +int DG_SetTmpLight( SVECTOR *svec, int brightness, int radius ) +{ + int n_lights; + DG_TmpLightList *tmp; + DG_LIT *light; + + tmp = getTempLight( GV_Time & 1 ); + n_lights = tmp->n_lights; + + if (n_lights >= 8) return -1; + + tmp->n_lights++; + light = &tmp->lights[n_lights]; + light->pos = *svec; + light->field_8_brightness = brightness; + light->field_A_radius = radius; + *(int*)&light->field_C_color = 0xFFFFFF; + + return 0; +} + +void DG_GetLightVector( VECTOR *in_vec, int divisor, SVECTOR *out_vec ) +{ + int vec_length; + int val; + int multiplier; + VECTOR vec_squared; + + Square0(in_vec, &vec_squared); + vec_length = SquareRoot0(vec_squared.vx + vec_squared.vy + vec_squared.vz); + if (vec_length == 0) + { + vec_length = 1; + } + multiplier = divisor * 2 - vec_length; + if (multiplier < 0) + { + multiplier = 0; + } + + val = in_vec->vx * 0xc00; + val = (val / vec_length) * multiplier; + out_vec->vx = val / divisor; + + val = in_vec->vy * 0xc00; + val = (val / vec_length) * multiplier; + out_vec->vy = val / divisor; + + val = in_vec->vz * 0xc00; + val = (val / vec_length) * multiplier; + out_vec->vz = val / divisor; +} + +int DG_GetLightMatrix( SVECTOR *vec, MATRIX *mtx ) +{ + int lightCount, lightCount2; + int lightRadius; + DG_LIT *pLightIter; + short *pColorOut; + int lightsAvailable; + DG_SVECTOR *lightOut; + DG_FixedLight *pFixedLightsIter; + VECTOR lightDistance; + DG_TmpLightList *pTmpLightList; + + lightOut = (DG_SVECTOR *)mtx->m[1]; + pColorOut = &mtx[1].m[0][1]; + pFixedLightsIter = gFixedLights_800B1E08; + lightsAvailable = 2; + + memcpy(&mtx[0].m, DG_LightMatrix.m, 20); + memcpy(&mtx[1].m, DG_ColorMatrix.m, 20); + + pTmpLightList = &LightSystems_800B1E48[~GV_Time & 1]; + lightCount2 = pTmpLightList->n_lights; + pLightIter = pTmpLightList->lights; + + while (1) + { + for (lightCount = lightCount2; lightCount > 0; lightCount--, pLightIter++) + { + lightRadius = pLightIter->field_A_radius; + + lightDistance.vx = vec->vx - pLightIter->pos.vx; + + if (lightDistance.vx >= -lightRadius && lightDistance.vx <= lightRadius) + { + lightDistance.vy = vec->vy - pLightIter->pos.vy; + + if (lightDistance.vy >= -lightRadius && lightDistance.vy <= lightRadius) + { + lightDistance.vz = vec->vz - pLightIter->pos.vz; + + if (lightDistance.vz >= -lightRadius && lightDistance.vz <= lightRadius) + { + DG_GetLightVector(&lightDistance, pLightIter->field_8_brightness, (SVECTOR *)lightOut); + lightOut++; + + pColorOut[0] = pLightIter->field_C_color.r << 4; + pColorOut[3] = pLightIter->field_C_color.g << 4; + pColorOut[6] = pLightIter->field_C_color.b << 4; + pColorOut++; + + if (--lightsAvailable == 0) + { + goto exit; + } + } + } + } + } + + if (pFixedLightsIter->field_0_lightCount == 0) + { + break; + } + + lightCount2 = pFixedLightsIter->field_0_lightCount; + pLightIter = pFixedLightsIter->field_4_pLights; + + pFixedLightsIter++; + } + +exit: + return 2 - lightsAvailable; +} + +void DG_SetLightMatrix( MATRIX* mtx, int trans_x ) +{ + mtx[1].t[0] = trans_x; +} + +int DG_GetLightMatrix2( SVECTOR* vec, MATRIX* mtx ) +{ + int ret = 2; + + if ( GM_GameStatus & STATE_THERMG ) + { + unsigned long* mtx_lng = (unsigned long*)&mtx[1]; + mtx_lng[0] = 0; + mtx_lng[1] = 0; + mtx_lng[2] = 0; + mtx_lng[3] = 0; + mtx_lng[4] = 0; + + mtx->t[0] = 130; + mtx->t[1] = 104; + mtx->t[2] = 80; + } + else + { + ret = DG_GetLightMatrix(vec, mtx); + mtx->t[0] = DG_Ambient.vx; + mtx->t[1] = DG_Ambient.vy; + mtx->t[2] = DG_Ambient.vz; + } + + return ret; +} diff --git a/components/mgs/mgs/source/libdg/loader.c b/components/mgs/mgs/source/libdg/loader.c new file mode 100644 index 00000000..c552e9ca --- /dev/null +++ b/components/mgs/mgs/source/libdg/loader.c @@ -0,0 +1,463 @@ +#include +#include +#include + +#include "libdg.h" +#ifdef __psyz +#include "esp_attr.h" +#endif +#include "common.h" +#include "libgv/libgv.h" +#include "fmt_sgt.h" + +/*---------------------------------------------------------------------------*/ + +static void LinkModelToParent(DG_MDL *mdl, DG_MDL *parent) +{ + unsigned int uVar2; + int faces; + unsigned char *fio; + unsigned char *fio2; + short flag; + SVECTOR *vio; + SVECTOR *vio2; + int index; + unsigned short pad; + int iter; + int offset; + + // provides the indexing order for referencing the transformed vertex sections + static unsigned char kVertexIndexingOrder[] = {0, 1, 3, 2}; + + vio = mdl->vertices; + flag = 0; + fio = mdl->vindices; + + for (iter = mdl->n_faces * 4; iter > 0; iter--) + { + index = *fio; + vio2 = &vio[index]; + + if (vio2->pad != -1) + { + flag |= index; + *fio |= 0x80; + } + + fio++; + } + + if (flag & 0x80) + { + return; + } + + vio2 = mdl->vertices; + + for (iter = mdl->n_verts; iter > 0; iter--) + { + pad = vio2->pad; + + if (pad != 0xffff) + { + fio2 = parent->vindices; + + for (faces = parent->n_faces * 4; faces > 0; faces--) + { + if ((*fio2 & 0x7f) == pad) + { + break; + } + + fio2++; + } + + offset = fio2 - parent->vindices; +#ifdef __psyz + /* byte offset of the parent's transformed vertex inside its pack + * array: 52/8 are the PACKED POLY_GT4's size and x0 position; the + * 8-byte-header layout is 56/12 (the 12-byte xy stride matches). + * Left packed, every skeleton-linked vertex read the wrong word + * of the parent's pack. */ + uVar2 = (offset / 4) * 56; + + vio2->pad = kVertexIndexingOrder[offset & 3] * 12 + uVar2 + 12; +#else + uVar2 = (offset / 4) * 52; + + vio2->pad = kVertexIndexingOrder[offset & 3] * 12 + uVar2 + 8; +#endif + } + + vio2++; + } +} + +int DG_LoadInitKmd(void *buf, int id) +{ + DG_DEF *def = (DG_DEF *)buf; + DG_MDL *mdl = def->model; + int remaining = def->n_models; + + while (--remaining >= 0) + { + if (mdl->vertices) + { + mdl->vertices = (__typeof__(mdl->vertices))((char *)mdl->vertices + ((unsigned int)def)); + } + if (mdl->vindices) + { + mdl->vindices = (__typeof__(mdl->vindices))((char *)mdl->vindices + ((unsigned int)def)); + } + if (mdl->normals) + { + mdl->normals = (__typeof__(mdl->normals))((char *)mdl->normals + ((unsigned int)def)); + } + if (mdl->nindices) + { + mdl->nindices = (__typeof__(mdl->nindices))((char *)mdl->nindices + ((unsigned int)def)); + } + if (mdl->texcoords) + { + mdl->texcoords = (__typeof__(mdl->texcoords))((char *)mdl->texcoords + ((unsigned int)def)); + } + if (mdl->materials) + { + mdl->materials = (__typeof__(mdl->materials))((char *)mdl->materials + ((unsigned int)def)); + } + if (mdl->parent >= 0) + { + LinkModelToParent(mdl, &def->model[mdl->parent]); + } + ++mdl; + } + return 1; +} + +/*---------------------------------------------------------------------------*/ + +int DG_LoadInitNar(void *buf, int id) +{ + DG_NARS *nar = (DG_NARS *)buf; + nar->unknown1 = (u_char *)nar + (u_int)nar->unknown1; + return 1; +} + +int DG_LoadInitOar(void *buf, int id) +{ + DG_OAR *oar = (DG_OAR *)buf; + oar->archive = (MOTION_ARCHIVE*)&oar->oarData[ ( ( (oar->n_joint + 2) ) * oar->n_motion) * 2 ]; + oar->table = (MOTION_TABLE*)oar->oarData; + return 1; +} + +int DG_LoadInitImg(void *buf, int id) +{ + DG_IMG *img = (DG_IMG *)buf; + img->textures = (unsigned short *)((char *)img + (unsigned int)img->textures); + img->attribs = (DG_IMG_ATTRIB *)((char *)img + (unsigned int)img->attribs); + img->tilemap = (char *)img + (unsigned int)img->tilemap; + return 1; +} + +int DG_LoadInitSgt(void *buf, int id) +{ + SgtFile *sgt = (SgtFile *)buf; + sgt->unknown1 = (u_char *)sgt + (u_int)sgt->unknown1; + sgt->unknown2 = (u_char *)sgt + (u_int)sgt->unknown2; + sgt->unknown3 = (u_char *)sgt + (u_int)sgt->unknown3; + sgt->unknown4 = (u_char *)sgt + (u_int)sgt->unknown4; + sgt->unknown5 = (u_char *)sgt + (u_int)sgt->unknown5; + return 1; +} + +int DG_LoadInitLit(void *buf, int id) +{ + return 1; +} + +/*---------------------------------------------------------------------------*/ + +// The size of this buffer is just a guess based on the next address of a +// variable that IDA knows about. +extern unsigned char pcxBuffer_800B3798[128]; + +#define PCX_RLE_THRESHOLD 0xC0 + +static unsigned char *DG_PcxRead8Bpp(unsigned char *pcxData, unsigned char *imageData, int imageSize) +{ + unsigned char *palette; + do + { + char maybeRunLength = *pcxData++; + if (maybeRunLength <= PCX_RLE_THRESHOLD) + { + --imageSize; + *imageData++ = maybeRunLength; + } + else + { + int runLength = maybeRunLength - PCX_RLE_THRESHOLD; + char color = *pcxData++; + imageSize -= runLength; + while (--runLength >= 0) + { + *imageData++ = color; + } + } + palette = pcxData; + } while (imageSize > 0); + return palette; +} + +static unsigned char *DG_PcxRead4Bpp(unsigned char *pcxData, unsigned char *imageData, + int bytes_per_line, int width, int height) +{ + int i = height; + while (--i >= 0) + { + unsigned char *rp; + unsigned char *gp; + unsigned char *bp; + unsigned char *ap; + int lineRemaining; + + unsigned char *pos = pcxBuffer_800B3798; + lineRemaining = 4 * bytes_per_line; + do + { + char maybeRunLength = *pcxData++; + if (maybeRunLength <= PCX_RLE_THRESHOLD) + { + --lineRemaining; + *pos++ = maybeRunLength; + } + else + { + int runLength = maybeRunLength - PCX_RLE_THRESHOLD; + unsigned char color = *pcxData++; + lineRemaining -= runLength; + while (--runLength >= 0) + { + *pos++ = color; + } + } + } while (lineRemaining > 0); + + rp = pcxBuffer_800B3798; + gp = rp + bytes_per_line; + bp = gp + bytes_per_line; + ap = bp + bytes_per_line; + for (lineRemaining = width; lineRemaining > 0; lineRemaining -= 4) + { + int r = *rp++; + int g = *gp++; + int b = *bp++; + int a = *ap++; + int shift = 128; + int shiftEnd = 8 * (lineRemaining < 4); + do + { + unsigned char color = 0; + + if (shift & r) + color |= 1; + if (shift & g) + color |= 2; + if (shift & b) + color |= 4; + if (shift & a) + color |= 8; + shift >>= 1; + + if (shift & r) + color |= 0x10; + if (shift & g) + color |= 0x20; + if (shift & b) + color |= 0x40; + if (shift & a) + color |= 0x80; + + *imageData++ = color; + shift >>= 1; + } while (shift != shiftEnd); + } + } + return pcxData; +} + +static void DG_PcxReadPalette(unsigned char *pcxPalette, unsigned char *imageData, int width) +{ + unsigned short *imagePalette; + int remaining; + unsigned char r, g, b; + unsigned short color; + + imagePalette = (unsigned short *)imageData; + remaining = width; + while (--remaining >= 0) + { + r = *pcxPalette; + g = *(pcxPalette + 1); + b = *(pcxPalette + 2); + color = !!((r | g | b) & 7) << 5; + if (r || g || b) + { + color |= b >> 3; + color <<= 5; + color |= g >> 3; + color <<= 5; + color |= r >> 3; + } + *imagePalette++ = color; + pcxPalette += 3; + } +} + +int DG_LoadInitPcx(void *buf, int id) +{ + PCXDATA *pcx; + unsigned short flags; + int min_x, min_y; + int width, height; + DG_Image *images; + + pcx = (PCXDATA *)buf; + flags = pcx->info.flags; + + min_x = pcx->min_x - 1; + min_y = pcx->min_y - 1; + width = pcx->max_x - min_x; + height = pcx->max_y - min_y; + + if (!(flags & 1)) + { + width /= 2; + } + +#ifdef __psyz + /* The decode scratch must NOT come from the GV packet arena: this loader + * runs again every frame for animated textures, and the arena's allocator + * handed back a region overlapping the LIVE model packs -- the decoded + * pixels sprayed over the characters' polygons (the corrupted-color + * triangles). A dedicated static buffer cannot collide with anything. + * 160 KB covers 512x256 plus headers; anything bigger falls back. */ + { + static unsigned char EXT_RAM_BSS_ATTR pcx_scratch[0x28000]; + if ((unsigned)(width * height + 528) <= sizeof(pcx_scratch)) + { + images = (DG_Image *)pcx_scratch; + } + else + { + images = NULL; + printf("[pcx] %dx%d too big for the static scratch\n", + width, height); + } + } + if (images) + { +#else + if (GV_AllocMemory2(GV_Clock, width * height + 528, (void **)&images)) + { +#endif + DG_Image *imageA; + DG_Image *imageB; + unsigned char *palette; + + imageB = images; + imageB->dim.x = pcx->info.cx; + imageB->dim.y = pcx->info.cy; + imageB->dim.w = pcx->info.n_colors; + imageB->dim.h = 1; + + imageA = images + 1; + imageA->dim.x = pcx->info.px; + imageA->dim.y = pcx->info.py; + imageA->dim.w = width / 2; + imageA->dim.h = height; + + if (flags & 1) + { + palette = DG_PcxRead8Bpp(pcx->data, imageA->data, width * height) + 1; + } + else + { + DG_PcxRead4Bpp(pcx->data, imageA->data, pcx->bytes_per_line, width, height); + palette = &pcx->header_palette[0]; + } + + DG_PcxReadPalette(palette, imageB->data, imageB->dim.w); + LoadImage(&imageB->dim, (u_long *)imageB->data); + LoadImage(&imageA->dim, (u_long *)imageA->data); +#ifdef __psyz + /* the scratch is static: handing it to the GV allocator to free + * would corrupt the arena's bookkeeping */ + images = NULL; +#else + GV_FreeMemory2(GV_Clock, (void **)&images); +#endif + + if (id) + { + DG_SetTexture((unsigned short)id, flags & 1, (flags & 0x30) >> 4, + &imageA->dim, &imageB->dim, imageB->dim.w); + } + return 1; + } + + return 0; +} + +/*---------------------------------------------------------------------------*/ + +int DG_LoadInitKmdar(void *buf, int id) +{ + DG_ZMD_DEF *zmd = (DG_ZMD_DEF *)buf; + DG_KMDPACK *kmd = &zmd->kmd[0]; + unsigned int offset = (unsigned int)kmd + zmd->vert_offset; + int remaining = zmd->n_kmd + 1; + + while (--remaining > 0) + { + DG_DEF *def = &kmd->def; + int cache_id; + int n_models = def->n_models; + DG_MDL *mdl = &def->model[0]; + + while (--n_models >= 0) + { + mdl->vertices = (__typeof__(mdl->vertices))((char *)mdl->vertices + (offset)); + if (mdl->vindices) + { + mdl->vindices = (__typeof__(mdl->vindices))((char *)mdl->vindices + (offset)); + } + if (mdl->normals) + { + mdl->normals = (__typeof__(mdl->normals))((char *)mdl->normals + (offset)); + } + if (mdl->nindices) + { + mdl->nindices = (__typeof__(mdl->nindices))((char *)mdl->nindices + (offset)); + } + if (mdl->texcoords) + { + mdl->texcoords = (__typeof__(mdl->texcoords))((char *)mdl->texcoords + (offset)); + } + if (mdl->materials) + { + mdl->materials = (__typeof__(mdl->materials))((char *)mdl->materials + (offset)); + } + if (mdl->parent >= 0) + { + LinkModelToParent(mdl, &def->model[mdl->parent]); + } + ++mdl; + } + cache_id = GV_CacheID(kmd->id, 'k'); + kmd = (DG_KMDPACK *)mdl; + GV_SetCache(cache_id, def); + } + return 1; +} diff --git a/components/mgs/mgs/source/libdg/matrix.c b/components/mgs/mgs/source/libdg/matrix.c new file mode 100644 index 00000000..99d2e12a --- /dev/null +++ b/components/mgs/mgs/source/libdg/matrix.c @@ -0,0 +1,245 @@ +#include "libdg.h" +#include +#include +#include + +void DG_MatrixRot( MATRIX *mat, SVECTOR *svec ) +{ + int iVar1; + int iVar2; + int iVar3; + + iVar1 = mat->m[0][2]; + iVar2 = SquareRoot0(0x1000000 - iVar1 * iVar1); + + if (iVar2 < 0x80) + { + iVar3 = 0x400; + + if (iVar1 <= 0) + { + iVar3 = 3072; + } + + svec->vy = iVar3; + svec->vx = ratan2(mat->m[1][2], mat->m[1][1]); + svec->vz = 0; + } + else + { + svec->vy = ratan2(iVar1, iVar2) & 0xfff; + svec->vx = ratan2((-mat->m[1][2] << 12) / iVar2, (mat->m[2][2] << 12) / iVar2) & 0xfff; + svec->vz = ratan2((-mat->m[0][1] << 12) / iVar2, (mat->m[0][0] << 12) / iVar2) & 0xfff; + } +} + +void DG_MatrixRotYXZ( MATRIX *mat, SVECTOR *svec ) +{ + int iVar1; + int iVar2; + int iVar3; + + iVar1 = -mat->m[1][2]; + iVar2 = SquareRoot0(0x1000000 - iVar1 * iVar1); + + if (iVar2 < 0x80) + { + iVar3 = 0x400; + + if (iVar1 <= 0) + { + iVar3 = 3072; + } + + svec->vx = iVar3; + svec->vy = ratan2(-mat->m[2][0], mat->m[0][0]); + svec->vz = 0; + } + else + { + svec->vx = ratan2(iVar1, iVar2) & 0xfff; + svec->vy = ratan2((mat->m[0][2] << 12) / iVar2, (mat->m[2][2] << 12) / iVar2) & 0xfff; + svec->vz = ratan2((mat->m[1][0] << 12) / iVar2, (mat->m[1][1] << 12) / iVar2) & 0xfff; + } + + if (abs(svec->vz) > 0x400) + { + svec->vz = (svec->vz + 0x800) & 0xfff; + svec->vy = (svec->vy + 0x800) & 0xfff; + svec->vx = (0x800 - svec->vx) & 0xfff; + } +} + +void DG_MatrixRotZYX( MATRIX *mat, SVECTOR *svec ) +{ + int iVar1; + int iVar2; + int iVar3; + + iVar1 = -mat->m[2][0]; + iVar2 = SquareRoot0(0x1000000 - iVar1 * iVar1); + + if (iVar2 < 0x80) + { + iVar3 = 0x400; + + if (iVar1 <= 0) + { + iVar3 = 3072; + } + + svec->vy = iVar3; + svec->vx = ratan2(-mat->m[1][2], mat->m[1][1]); + svec->vz = 0; + } + else + { + svec->vy = ratan2(iVar1, iVar2) & 0xfff; + svec->vx = ratan2((mat->m[2][1] << 12) / iVar2, (mat->m[2][2] << 12) / iVar2) & 0xfff; + svec->vz = ratan2((mat->m[1][0] << 12) / iVar2, (mat->m[0][0] << 12) / iVar2) & 0xfff; + } +} + +void DG_TransposeMatrix( MATRIX *in, MATRIX *out ) +{ + short temp; + + temp = in->m[1][0]; + out->m[1][0] = in->m[0][1]; + out->m[0][1] = temp; + + temp = in->m[2][0]; + out->m[2][0] = in->m[0][2]; + out->m[0][2] = temp; + + temp = in->m[2][1]; + out->m[2][1] = in->m[1][2]; + out->m[1][2] = temp; + + if (in != out) + { + out->m[0][0] = in->m[0][0]; + out->m[1][1] = in->m[1][1]; + out->m[2][2] = in->m[2][2]; + } +} + +void DG_ShadowMatrix( MATRIX *out, MATRIX *in, int param_3 ) +{ + int x, y, z; + short *pElement; + int count; + int iVar1; + int iVar2; + int iVar3; + + x = in->m[0][0] + in->m[1][0] + in->m[2][0]; + y = in->m[0][1] + in->m[1][1] + in->m[2][1]; + z = in->m[0][2] + in->m[1][2] + in->m[2][2]; + + if (y < 0) + { + pElement = &out->m[0][0]; + + for (count = 3; count > 0; count--) + { + iVar1 = (pElement[3] << 12) / y; + iVar2 = x * iVar1; + + if (iVar2 < 0) + { + iVar2 += 0xfff; + } + + iVar3 = z * iVar1; + pElement[0] -= iVar2 >> 12; + + if (iVar3 < 0) + { + iVar3 += 0xfff; + } + + pElement[6] -= iVar3 >> 12; + pElement++; + } + + iVar1 = (param_3 << 12) / y; + iVar2 = x * iVar1; + + if (iVar2 < 0) + { + iVar2 += 0xfff; + } + + iVar3 = z * iVar1; + out->t[0] -= iVar2 >> 12; + + if (iVar3 < 0) + { + iVar3 += 0xfff; + } + + out->t[2] -= iVar3 >> 12; + } + + out->t[1] -= param_3; + + out->m[1][0] = 0; + out->m[1][1] = 0; + out->m[1][2] = 0; +} + +void DG_ReflectVector( SVECTOR *in, SVECTOR *translation, SVECTOR *out ) +{ + MATRIX mat; + + mat.m[0][0] = in->vx; + mat.m[0][1] = mat.m[1][0] = in->vy; + mat.m[0][2] = mat.m[2][0] = in->vz; + + mat.t[0] = translation->vx; + mat.t[1] = translation->vy; + mat.t[2] = translation->vz; + + gte_SetRotMatrix(&mat); + gte_SetTransMatrix(&mat); + gte_ldsv(translation); + gte_rtir(); + gte_stsv(out); + + out->vz = 0; + out->vy = 0; + out->vx *= -2; + + gte_ldv0(out); + gte_rt(); + gte_stsv(out); +} + +void DG_ReflectMatrix( SVECTOR *svec, MATRIX *in, MATRIX *out ) +{ + MATRIX transpose; + SVECTOR reflected; + int element; + + DG_TransposeMatrix(in, &transpose); + gte_SetRotMatrix(&transpose); + gte_ldsv(svec); + gte_rtir(); + gte_stsv(&reflected); + + element = reflected.vx; + out->m[0][0] = in->m[0][0] - svec->vx * element / 2048; + out->m[1][0] = in->m[1][0] - svec->vy * element / 2048; + out->m[2][0] = in->m[2][0] - svec->vz * element / 2048; + + element = reflected.vy; + out->m[0][1] = in->m[0][1] - svec->vx * element / 2048; + out->m[1][1] = in->m[1][1] - svec->vy * element / 2048; + out->m[2][1] = in->m[2][1] - svec->vz * element / 2048; + + element = reflected.vz; + out->m[0][2] = in->m[0][2] - svec->vx * element / 2048; + out->m[1][2] = in->m[1][2] - svec->vy * element / 2048; + out->m[2][2] = in->m[2][2] - svec->vz * element / 2048; +} diff --git a/components/mgs/mgs/source/libdg/obj.c b/components/mgs/mgs/source/libdg/obj.c new file mode 100644 index 00000000..d89093e5 --- /dev/null +++ b/components/mgs/mgs/source/libdg/obj.c @@ -0,0 +1,106 @@ +#include "libdg.h" + +#include +#include +#include +#include "common.h" + +STATIC int DG_MakeObjs_helper( DG_MDL *mdl ) +{ + int val; + int mask; + unsigned int flags; + + flags = mdl->flags; + val = 0; + + if ((flags & 0x300) != 0) + { + mask = 4 - (flags >> 12 & 3); + val = mask * 0xfa; + if ((flags & 0x100) == 0) + { + val = mask * -0xfa; + } + } + + return val; +} + +DG_OBJS *DG_MakeObjs( DG_DEF *def, int flag, int chanl ) +{ + DG_MDL *model = (DG_MDL *)&def[1]; + + const int objs_size = sizeof(DG_OBJS) + (sizeof(DG_OBJ) * def->n_models); + DG_OBJS *objs_buf = (DG_OBJS *)GV_Malloc(objs_size); + + if (!objs_buf) + { + return 0; + } + else + { + int numMesh; + DG_OBJ *obj; + + GV_ZeroMemory(objs_buf, objs_size); + objs_buf->world = DG_ZeroMatrix; + + objs_buf->def = def; + + objs_buf->n_models = def->n_visible; + + objs_buf->flag = flag; + objs_buf->chanl = chanl; + objs_buf->light = &DG_LightMatrix; + + obj = &objs_buf->objs[0]; + for (numMesh = def->n_models; numMesh > 0; numMesh--) + { + obj->model = model; + if (model->extend < 0) + { + obj->extend = 0; + } + else + { + obj->extend = &objs_buf->objs[model->extend]; + } + + obj->raise = DG_MakeObjs_helper(model); + obj->n_packs = model->n_faces; + obj++; + model++; + } + return objs_buf; + } +} + +void DG_FreeObjs( DG_OBJS *objs ) +{ + int n_models; + DG_OBJ *obj; + + n_models = objs->n_models; + obj = objs->objs; + while (n_models > 0) + { + DG_FreeObjPacket(obj, 0); + DG_FreeObjPacket(obj, 1); + --n_models; + ++obj; + } + DG_FreePreshade(objs); + GV_Free(objs); +} + +void DG_SetObjsRots( DG_OBJS *objs, SVECTOR *rot ) +{ + objs->rots = rot; +} + +void DG_SetObjsMovs( DG_OBJS *objs, SVECTOR *mov ) +{ + objs->movs = mov; +} + diff --git a/components/mgs/mgs/source/libdg/opack.c b/components/mgs/mgs/source/libdg/opack.c new file mode 100644 index 00000000..77d1d867 --- /dev/null +++ b/components/mgs/mgs/source/libdg/opack.c @@ -0,0 +1,255 @@ +#include "libdg.h" +#include "common.h" +#ifdef __psyz +#include +#endif + +STATIC DG_TEX dword_8009D3C4 = {0}; + +STATIC int DG_AllocPacks( DG_OBJ *obj, int idx ) +{ + int total_packs = 0; + DG_OBJ *object = obj; + + while (object) + { +#ifdef __psyz + /* Walk defensively. A DG_OBJ's extend chain is short -- a handful of + * links -- and terminates in NULL. When a stage tears down while the + * render pipeline is mid-frame, this can be reached with an obj whose + * extend still points at freed memory, and the walk then follows + * whatever is there: LoadProhibited on a wild address, board reboots. + * The console never sees it because there the teardown and the render + * are locked in step. Nothing legitimate has hundreds of links or a + * pointer outside PSRAM, so stop rather than fault -- an object drawn + * with too few packs for one frame is invisible; a reset is not. */ + static int reported = 8; + if (total_packs > 4096 || + ((unsigned)(uintptr_t)object >> 24) != 0x3Cu) + { + if (reported > 0) + { + reported--; + printf("[opack] bad extend chain at %p (packs so far %d)\n", + (void *)object, total_packs); + } + break; + } +#endif + total_packs += object->n_packs; + object = object->extend; + } + +#ifdef __psyz + /* The walk above stops at a bad link, but by then it may already have + * added a garbage n_packs -- the first crash after adding that guard came + * back with total_packs = -32640, which sails into GV_AllocMemory2 as a + * huge unsigned size. Refuse instead: DG_MakeObjPacket answers -1 and + * DG_BoundObjs simply marks the object invisible for the frame. */ + if (total_packs <= 0 || total_packs > 4096) + { + static int reported = 8; + if (reported > 0) + { + reported--; + printf("[opack] refusing %d packs for obj %p\n", total_packs, + (void *)obj); + } + return -1; + } +#endif + + if (!GV_AllocMemory2(idx, total_packs * sizeof(POLY_GT4), (void **)&obj->packs[idx])) + { + return -1; + } +#ifdef __psyz + if (0) printf("[packs] obj %p model %p buf[%d] %p..%p n %d\n", (void *)obj, + (void *)obj->model, idx, (void *)obj->packs[idx], + (void *)((char *)obj->packs[idx] + total_packs * sizeof(POLY_GT4)), + total_packs); +#endif + return 0; +} + +STATIC void DG_InitPolyGT4Pack( DG_OBJ *obj, int idx ) +{ + POLY_GT4 *pack; + + int rgbCode = 0x3E808080; + + if ( !(obj->model->flags & DG_MODEL_TRANS) ) + { + rgbCode = 0x3C808080; + } + + pack = obj->packs[idx]; + while (obj) + { + int n_packs; + for (n_packs = obj->n_packs; n_packs > 0; n_packs--) + { + setPolyGT4(pack); + + *(int *)&pack->r0 = rgbCode; + *(int *)&pack->r1 = rgbCode; + *(int *)&pack->r2 = rgbCode; + *(int *)&pack->r3 = rgbCode; + pack++; + } + obj = obj->extend; + } +} + +static inline void Apply( DG_TEX *tex, unsigned char *texcoords, POLY_GT4 *pack ) +{ + unsigned int u0 = tex->off_x; + unsigned int v0 = tex->off_y; + int u11 = tex->w + 1; + int v11 = tex->h + 1; + + pack->u0 = ((texcoords[0] * u11) / 256) + u0; + pack->v0 = ((texcoords[1] * v11) / 256) + v0; + pack->u1 = ((texcoords[2] * u11) / 256) + u0; + pack->v1 = ((texcoords[3] * v11) / 256) + v0; + pack->u2 = ((texcoords[6] * u11) / 256) + u0; + pack->v2 = ((texcoords[7] * v11) / 256) + v0; + pack->u3 = ((texcoords[4] * u11) / 256) + u0; + pack->v3 = ((texcoords[5] * v11) / 256) + v0; + pack->tpage = tex->tpage; + pack->clut = tex->clut; +} + +void DG_WriteObjPacketUV( DG_OBJ* obj, int idx ) +{ + unsigned short id; + POLY_GT4 *pack; + int n_packs; + short *tex_ids; + DG_TEX *texture; + unsigned short current_id; + unsigned char *texcoords; + + pack = obj->packs[ idx ]; + + if ( pack ) + { + texture = &dword_8009D3C4; + id = 0; + + while ( obj ) + { + tex_ids = obj->model->materials; + texcoords = obj->model->texcoords; + for (n_packs = obj->n_packs; n_packs > 0 ; --n_packs ) + { + current_id = *tex_ids; + tex_ids++; + + if ( current_id != id ) + { + id = current_id; + texture = DG_GetTexture( current_id ); + } + + Apply(texture, texcoords, pack); + pack++; + texcoords += 8; + } + obj = obj->extend; + } + } +} + +void DG_WriteObjPacketRGB( DG_OBJ *obj, int idx ) +{ + POLY_GT4 *pack = obj->packs[idx]; + if (pack && obj) + { + do { + CVECTOR *pack_rgbs = obj->rgbs; + if (pack_rgbs) + { + int n_packs; + for (n_packs = obj->n_packs; n_packs > 0; --n_packs) + { + LCOPY2(&pack_rgbs[0], &pack->r0, &pack_rgbs[1], &pack->r1); + LCOPY2(&pack_rgbs[3], &pack->r2, &pack_rgbs[2], &pack->r3); + + ++pack; + pack_rgbs += 4; // to next set of rgb + } + } + obj = obj->extend; + } while (obj); + } +} + +int DG_MakeObjPacket( DG_OBJ *obj, int idx, int flags ) +{ + if (DG_AllocPacks(obj, idx) < 0) + { + return -1; + } + + DG_InitPolyGT4Pack(obj, idx); + + if ((flags & DG_FLAG_TEXT) != 0) + { + DG_WriteObjPacketUV(obj, idx); + } + + if ((flags & DG_FLAG_PAINT) != 0) + { + DG_WriteObjPacketRGB(obj, idx); + } + + return 0; +} + +void DG_FreeObjPacket( DG_OBJ *obj, int idx ) +{ + POLY_GT4 **ppPack; + + ppPack = &obj->packs[idx]; + if (*ppPack) + { + GV_FreeMemory2(idx, (void **)ppPack); + *ppPack = 0; + } +} + +int DG_MakeObjsPacket( DG_OBJS *objs, int idx ) +{ + + int flag = objs->flag; + int n_models = objs->n_models; + + DG_OBJ *obj = objs->objs; + while (n_models > 0) + { + if (!obj->packs[idx]) + { + if (DG_MakeObjPacket(obj, idx, flag) < 0) + { + return -1; + } + } + obj++; + n_models--; + } + return 0; +} + +void DG_FreeObjsPacket( DG_OBJS *objs, int idx ) +{ + int n_models; + DG_OBJ *obj; + + n_models = objs->n_models; + for (obj = objs->objs; n_models > 0; ++obj) + { + DG_FreeObjPacket(obj, idx); + --n_models; + } +} diff --git a/components/mgs/mgs/source/libdg/palette.c b/components/mgs/mgs/source/libdg/palette.c new file mode 100644 index 00000000..998c9bcc --- /dev/null +++ b/components/mgs/mgs/source/libdg/palette.c @@ -0,0 +1,19 @@ +#include "libdg.h" +#include +#include +#include +#include "common.h" + +STATIC RECT palette1 = { 768, 226, 256, 30 }; +STATIC RECT palette2 = { 768, 196, 256, 30 }; + +void DG_StorePalette(void) +{ + MoveImage(&palette1, palette2.x, palette2.y); +} + +void DG_ReloadPalette(void) +{ + MoveImage(&palette2, palette1.x, palette1.y); +} + diff --git a/components/mgs/mgs/source/libdg/pos.c b/components/mgs/mgs/source/libdg/pos.c new file mode 100644 index 00000000..cc6dc8cb --- /dev/null +++ b/components/mgs/mgs/source/libdg/pos.c @@ -0,0 +1,195 @@ +#include "libdg.h" +#include +#include +#include +#include "common.h" + +extern DG_CHANL DG_Chanls[3]; + +/*---------------------------------------------------------------------------*/ + +void DG_SetPos( MATRIX *world ) +{ + gte_SetRotMatrix( world ); + gte_SetTransMatrix( world ); +} + +void DG_SetPos2( SVECTOR *mov, SVECTOR *rot ) +{ + MATRIX tmp_mat; + + RotMatrixYXZ_gte( rot, &tmp_mat ); + + tmp_mat.t[0] = mov->vx; + tmp_mat.t[1] = mov->vy; + tmp_mat.t[2] = mov->vz; + + gte_SetRotMatrix( &tmp_mat ); + gte_SetTransMatrix( &tmp_mat ); +} + +void DG_MovePos( SVECTOR *mov ) +{ + VECTOR tmp_vec; + + gte_ldv0( mov ); + gte_rt(); + gte_stlvnl( &tmp_vec ); + gte_SetTransVector( &tmp_vec ); +} + +void DG_RotatePos( SVECTOR *rot ) +{ + MATRIX tmp_mat; + + RotMatrixYXZ_gte( rot, &tmp_mat ); + MulRotMatrix( &tmp_mat ); + gte_SetRotMatrix( &tmp_mat ); +} + +/*---------------------------------------------------------------------------*/ + +void DG_PutObjs( DG_OBJS *objs ) +{ + gte_ReadRotMatrix( &objs->world ); +} + +void DG_PutPrim( DG_PRIM *prim ) +{ + gte_ReadRotMatrix( &prim->world ); +} + +void DG_PutVector( SVECTOR *from, SVECTOR *to, int n ) +{ + while (--n >= 0) { + gte_ldv0( from ); + gte_rt(); + gte_stsv( to ); + from++; + to++; + } +} + +void DG_RotVector( SVECTOR *from, SVECTOR *to, int n ) +{ + while (--n >= 0) { + gte_ldsv( from ); + gte_rtir(); + gte_stsv( to ); + from++; + to++; + } +} + +void DG_PersVector( SVECTOR *from, DVECTOR *to, int n ) +{ + while (--n >= 0) { + gte_ldv0( from ); + gte_rtps(); + gte_stsxy( to ); + from++; + to++; + } +} + +/*---------------------------------------------------------------------------*/ + +#define MAX_X (unsigned int)385 +#define MAX_Y (unsigned int)305 +#define DOES_TOUCH 1 +#define DOESNT_TOUCH 0 + +// TODO: find a way to incorporate both this and pointcheckone's touch check in one function that matches +static inline void check_touches(SVECTOR *svector, DVECTOR *first_points, DVECTOR *second_points, int n_points) +{ + while (n_points > 0) + { + svector->pad = DOESNT_TOUCH; + + if (*(long *)second_points) + { + if (((first_points->vx + 0xC0) < MAX_X) && ((first_points->vy + 0x98) < MAX_Y)) + { + svector->pad = DOES_TOUCH; + } + } + + n_points--; + second_points++; + first_points++; + svector++; + } +} + +void DG_PointCheck( SVECTOR *svector, int n_points ) +{ + MATRIX *matrix; + DVECTOR *first_points; + DVECTOR *second_points; + + int n_initial_points; + MATRIX *matrix2 = (MATRIX *)SCRPAD_ADDR; + + gte_ReadRotMatrix(matrix2); + + matrix = &DG_Chanls[1].eye_inv; + gte_SetRotMatrix(matrix); + gte_SetTransMatrix(matrix); + + gte_ldv3c(svector); + + first_points = (DVECTOR *)(SCRPAD_ADDR + 0x20); // 3 sets of dvector + second_points = (DVECTOR *)(SCRPAD_ADDR + 0x2C); // 3 sets of dvector + + svector += 3; + gte_rtpt_b(); + + for (n_initial_points = n_points - 3; n_initial_points > 0; n_initial_points -= 3) + { + gte_stsxy3c(first_points); + gte_stsz3c(second_points); + gte_ldv3c(svector); + gte_rtpt(); + + check_touches(svector - 3, first_points, second_points, 3); + + svector += 3; + n_points -= 3; + } + + gte_stsxy3c(first_points); + gte_stsz3c(second_points); + + check_touches(svector - 3, first_points, second_points, n_points); + + gte_SetRotMatrix((MATRIX *)SCRPAD_ADDR); + gte_SetTransMatrix((MATRIX *)SCRPAD_ADDR); +} + +int DG_PointCheckOne( DVECTOR *line ) +{ + DVECTOR first_points; + DVECTOR second_points; + + MATRIX *matrix = &DG_Chanls[1].eye_inv; + gte_SetRotMatrix(matrix); + gte_SetTransMatrix(matrix); + + gte_ldv0(line); + gte_rtps(); + + gte_stsxy(&first_points); + gte_stsz(&second_points); + + if (!*(long *)&second_points) + { + return 0; + } + + if (!(first_points.vx + 0xC0 < MAX_X)) + { + return 0; + } + + return first_points.vy + 0x98 < MAX_Y; +} diff --git a/components/mgs/mgs/source/libdg/prim.c b/components/mgs/mgs/source/libdg/prim.c new file mode 100644 index 00000000..5fcf11ec --- /dev/null +++ b/components/mgs/mgs/source/libdg/prim.c @@ -0,0 +1,746 @@ +#include "libdg.h" + +#include +#include +#include +#include "common.h" +#include "libgv/libgv.h" + +/*** data *******************************************************/ + +typedef struct _DG_PRIM_INFO { + unsigned char psize; + unsigned char verts; + unsigned char voffset; + unsigned char vstep; +} DG_PRIM_INFO; + +// psize, verts, voffset, vstep +#ifdef __psyz +/* These byte layouts describe the PACKED prims of the console: 4-byte tag, + * first vertex at +8. PSY-Z's prims carry an 8-byte tag+len header, so every + * prim is 4 bytes bigger and every vertex sits 4 bytes later. Left as the + * console values, each transformed vertex landed on the prim's COMMAND word + * and the whole 3D world reached the GPU as code-0 garbage. The inter-vertex + * stride does not change. */ +STATIC DG_PRIM_INFO DG_PrimInfos[DG_PRIM_MAX] = { + { 20, 2, 12, 4 }, // DG_PRIM_LINE_F2 + { 28, 3, 12, 4 }, // DG_PRIM_LINE_F3 + { 32, 4, 12, 4 }, // DG_PRIM_LINE_F4 + { 24, 2, 12, 8 }, // DG_PRIM_LINE_G2 + { 36, 3, 12, 8 }, // DG_PRIM_LINE_G3 + { 44, 4, 12, 8 }, // DG_PRIM_LINE_G4 + { 24, 1, 12, 0 }, // DG_PRIM_SPRT + { 20, 1, 12, 0 }, // DG_PRIM_SPRT_8 + { 20, 1, 12, 0 }, // DG_PRIM_SPRT_16 + { 20, 1, 12, 0 }, // DG_PRIM_TILE + { 16, 1, 12, 0 }, // DG_PRIM_TILE_1 + { 16, 1, 12, 0 }, // DG_PRIM_TILE_8 + { 16, 1, 12, 0 }, // DG_PRIM_TILE_16 + { 24, 3, 12, 4 }, // DG_PRIM_POLY_F3 + { 28, 4, 12, 4 }, // DG_PRIM_POLY_F4 + { 32, 3, 12, 8 }, // DG_PRIM_POLY_G3 + { 40, 4, 12, 8 }, // DG_PRIM_POLY_G4 + { 36, 3, 12, 8 }, // DG_PRIM_POLY_FT3 + { 44, 4, 12, 8 }, // DG_PRIM_POLY_FT4 + { 44, 3, 12, 12 }, // DG_PRIM_POLY_GT3 + { 56, 4, 12, 12 }, // DG_PRIM_POLY_GT4 + { 44, 2, 12, 8 }, // DG_PRIM_LINE_FT2 + { 56, 2, 12, 12 }, // DG_PRIM_LINE_GT2 + { 16, 1, 12, 0 } // DG_PRIM_FREE +}; +#else +STATIC DG_PRIM_INFO DG_PrimInfos[DG_PRIM_MAX] = { + { 16, 2, 8, 4 }, // DG_PRIM_LINE_F2 + { 24, 3, 8, 4 }, // DG_PRIM_LINE_F3 + { 28, 4, 8, 4 }, // DG_PRIM_LINE_F4 + { 20, 2, 8, 8 }, // DG_PRIM_LINE_G2 + { 32, 3, 8, 8 }, // DG_PRIM_LINE_G3 + { 40, 4, 8, 8 }, // DG_PRIM_LINE_G4 + { 20, 1, 8, 0 }, // DG_PRIM_SPRT + { 16, 1, 8, 0 }, // DG_PRIM_SPRT_8 + { 16, 1, 8, 0 }, // DG_PRIM_SPRT_16 + { 16, 1, 8, 0 }, // DG_PRIM_TILE + { 12, 1, 8, 0 }, // DG_PRIM_TILE_1 + { 12, 1, 8, 0 }, // DG_PRIM_TILE_8 + { 12, 1, 8, 0 }, // DG_PRIM_TILE_16 + { 20, 3, 8, 4 }, // DG_PRIM_POLY_F3 + { 24, 4, 8, 4 }, // DG_PRIM_POLY_F4 + { 28, 3, 8, 8 }, // DG_PRIM_POLY_G3 + { 36, 4, 8, 8 }, // DG_PRIM_POLY_G4 + { 32, 3, 8, 8 }, // DG_PRIM_POLY_FT3 + { 40, 4, 8, 8 }, // DG_PRIM_POLY_FT4 + { 40, 3, 8, 12 }, // DG_PRIM_POLY_GT3 + { 52, 4, 8, 12 }, // DG_PRIM_POLY_GT4 + { 40, 2, 8, 8 }, // DG_PRIM_LINE_FT2 + { 52, 2, 8, 12 }, // DG_PRIM_LINE_GT2 + { 12, 1, 8, 0 } // DG_PRIM_FREE +}; +#endif + +MATRIX DG_ZeroMatrix = { + {{0x1000, 0x0000, 0x0000}, + {0x0000, 0x1000, 0x0000}, + {0x0000, 0x0000, 0x1000}}, + {0, 0, 0}}; + +SVECTOR DG_ZeroVector = { 0, 0, 0, 0 }; + +/****************************************************************/ + +// Number of vertices to process at once +// ALIGN_DOWN( SCRATCHPAD_SIZE / sizeof(SVECTOR), 3 ) +#define BATCH_SIZE (126) + +void DG_PrimStart( void ) +{ + /* do nothing */ +} + +STATIC void DG_AdjustLaserPrim( DVECTOR *xy0, DVECTOR *xy1, DVECTOR *xy2, DVECTOR *xy3 ) +{ + int x0 = xy0->vx; + int x1 = xy1->vx; + int y0 = xy0->vy; + int y1 = xy1->vy; + + int dx = x1 - x0; + int dy = y1 - y0; + + if ( dx < 0 ) + { + dx = -dx; + } + + if ( dy < 0 ) + { + dy = -dy; + } + + if ( dy >= dx ) + { + xy2->vx = x0 + 2; + xy2->vy = y0; + xy3->vx = x1 + 2; + xy3->vy = y1; + } + else + { + xy2->vy = y0 + 1; + xy2->vx = x0; + xy3->vx = x1; + xy3->vy = y1 + 1; + } +} + +STATIC void DG_AdjustLaserPrims( DG_PRIM *prim, int type ) +{ + POLY_FT4 *ft2; + POLY_GT4 *gt2; + int i; + + if ( type == DG_PRIM_LINE_FT2 ) + { + ft2 = (POLY_FT4 *)prim->packs[GV_Clock]; + + for (i = prim->prim_count ; i > 0; --i ) + { + DG_AdjustLaserPrim((DVECTOR *)&ft2->x0, (DVECTOR *)&ft2->x1, (DVECTOR *)&ft2->x2, (DVECTOR *)&ft2->x3); + ft2++; + } + } + else // type == DG_PRIM_LINE_GT2 + { + gt2 = (POLY_GT4 *)prim->packs[GV_Clock]; + + for (i = prim->prim_count ; i > 0; --i ) + { + DG_AdjustLaserPrim((DVECTOR *)>2->x0, (DVECTOR *)>2->x1, (DVECTOR *)>2->x2, (DVECTOR *)>2->x3); + gt2++; + } + } +} + +// process vecs in spad +STATIC SVECTOR *_RotTransPers( SVECTOR *in, int n_verts ) +{ + SVECTOR *out; + + out = (SVECTOR *)getScratchAddr(0); + for ( --n_verts ; n_verts >= 0 ; n_verts-- ) + { + gte_ldv3c( in ); + gte_rtpt(); + gte_stsxy3( &out[0].vx, &out[1].vx, &out[2].vx ); + gte_stsz3( &out[0].vz, &out[1].vz, &out[2].vz ); + in += 3; + out += 3; + } + + return in; +} + +STATIC char *_MakeXYZ( DG_PRIM *prim, char *out, int n_prims ) +{ + int psize; + int verts; + int voffset; + int vstep; + SVECTOR *in; + char *vert; + + psize = prim->psize; + verts = prim->verts; + voffset = prim->voffset; + vstep = prim->vstep; + + in = (SVECTOR *)getScratchAddr(0); + + for ( n_prims--; n_prims >= 0; n_prims-- ) + { + vert = out + voffset; + + SCOPYL(&in->vz, out); + + switch( verts ) + { + case 4: + LCOPY(in, vert); + vert += vstep; + in++; + + case 3: + LCOPY(in, vert); + vert += vstep; + in++; + + case 2: + LCOPY(in, vert); + vert += vstep; + in++; + + case 1: + LCOPY(in, vert); + in++; + } + + out += psize; + } + + return out; +} + +STATIC void MakePrims( DG_PRIM *prim ) +{ + int n_verts; + int vert_batch; + int prim_batch; + SVECTOR *verts; + char *packs; + int n_prims; + + n_verts = prim->verts; + + if ( n_verts == 4 ) + { + vert_batch = 40; + prim_batch = 30; + } + else + { + vert_batch = 42; + prim_batch = BATCH_SIZE / n_verts; + } + + verts = prim->pos; + packs = (char *)prim->packs[ GV_Clock ]; + + for ( n_prims = prim->prim_count; n_prims > prim_batch; n_prims -= prim_batch ) + { + verts = _RotTransPers( verts, vert_batch ); + packs = _MakeXYZ( prim, packs, prim_batch ); + } + + _RotTransPers( verts, ( n_prims * n_verts + 2 ) / 3 ); + _MakeXYZ( prim, packs, n_prims ); +} + +STATIC char *_MakeXYZOneface( DG_PRIM *prim, char *out, int n_prims ) +{ + int psize; + int verts; + int voffset; + int vstep; + SVECTOR *in; + char *vert; + int *area; + + psize = prim->psize; + verts = prim->verts; + voffset = prim->voffset; + vstep = prim->vstep; + + in = (SVECTOR *)getScratchAddr(0); + + for ( n_prims--; n_prims >= 0; n_prims-- ) + { + area = (int *)getScratchAddr(252); + gte_NormalClip( *(int *)&in[0].vx, *(int *)&in[1].vx, *(int *)&in[2].vx, getScratchAddr(252) ); + + vert = out + voffset; + + if ( *area <= 0 ) + { + SSTOREL(0, out); + in += verts; + } + else + { + SCOPYL(&in->vz, out); + + switch( verts ) + { + case 4: + gte_stsxy0( vert ); + vert += vstep; + in++; + + case 3: + gte_stsxy1( vert ); + vert += vstep; + in++; + + case 2: + gte_stsxy2( vert ); + vert += vstep; + in++; + + case 1: + LCOPY(in, vert); + in++; + } + } + + out += psize; + } + + return out; +} + +STATIC void MakePrimsOneface( DG_PRIM *prim ) +{ + int n_verts; + int vert_batch; + int prim_batch; + SVECTOR *verts; + char *packs; + int n_prims; + + n_verts = prim->verts; + + if ( n_verts == 4 ) + { + vert_batch = 40; + prim_batch = 30; + } + else + { + vert_batch = 42; + prim_batch = BATCH_SIZE / n_verts; + } + + verts = prim->pos; + packs = (char *)prim->packs[ GV_Clock ]; + + for ( n_prims = prim->prim_count; n_prims > prim_batch; n_prims -= prim_batch ) + { + verts = _RotTransPers( verts, vert_batch ); + packs = _MakeXYZOneface( prim, packs, prim_batch ); + } + + _RotTransPers( verts, ( n_prims * n_verts + 2 ) / 3 ); + _MakeXYZOneface( prim, packs, n_prims ); +} + +STATIC char *_MakeXYZRectangleSingle( DG_PRIM *prim, char *out, int n_prims ) +{ + RECT *rect; + int psize; + int x, y; + SVECTOR *in; + + rect = prim->rect; + psize = prim->psize; + + x = rect->x; + y = rect->y; + + in = (SVECTOR *)getScratchAddr(0); + + for ( n_prims--; n_prims >= 0; n_prims-- ) + { + SCOPYL(&in->vz, out); +#ifdef __psyz + /* The only hard-coded prim offsets left in this file. +8/+10 are x0/y0 + * on the console; with PSY-Z's 8-byte tag+len header they land on + * r0/g0/b0 AND the command byte, so every TILE/SPRT rectangle reached + * the GPU with its opcode replaced by the low byte of a screen + * coordinate. The table already carries the right value (12 here). */ + SSTOREL(in->vx - x, out + prim->voffset); + SSTOREL(in->vy - y, out + prim->voffset + 2); +#else + SSTOREL(in->vx - x, out + 8); + SSTOREL(in->vy - y, out + 10); +#endif + + in++; + out += psize; + } + + return out; +} + +STATIC void MakePrimsRectangleSingle( DG_PRIM *prim ) +{ + SVECTOR *verts; + char *packs; + int n_prims; + + verts = prim->pos; + packs = (char *)prim->packs[GV_Clock]; + + for ( n_prims = prim->prim_count; n_prims > BATCH_SIZE; n_prims -= BATCH_SIZE ) + { + verts = _RotTransPers(verts, BATCH_SIZE / 3); + packs = _MakeXYZRectangleSingle(prim, packs, BATCH_SIZE); + } + + _RotTransPers(verts, ( n_prims + 2 ) / 3); + _MakeXYZRectangleSingle(prim, packs, n_prims); +} + +#define INIT_GEOM_OFFSET( addr ) \ + ((VECTOR *)(addr))->vy = 0; \ + ((VECTOR *)(addr))->vx = 0; + +#define SET_GEOM_OFFSET( vec, addr ) \ + gte_SetGeomOffset( (vec)->vx , (vec)->vy ); \ + ((VECTOR *)(addr))->vz = *(int *)&(vec)->vz; \ + gte_SetTransVector( addr ); + +STATIC char *_MakeXYZRectangle( DG_PRIM *prim, char *out, int n_prims ) +{ + SVECTOR bound[2]; + RECT *rect; + int x, y; + int psize; + int voffset; + int vstep; + SVECTOR *in; + DVECTOR *processed; + char *vert; + + rect = prim->rect; + + x = -rect->x; + bound[0].vx = x; + + y = -rect->y; + bound[0].vy = y; + bound[0].vz = 0; + + bound[1].vx = rect->w + x; + bound[1].vy = rect->h + y; + bound[1].vz = 0; + + gte_ldv01c ( bound ); + gte_SetRotMatrix( &DG_ZeroMatrix ); + + psize = prim->psize; + voffset = prim->voffset; + vstep = prim->vstep; + + INIT_GEOM_OFFSET( getScratchAddr(252) ); + + in = (SVECTOR *)getScratchAddr(0); + processed = (DVECTOR *)getScratchAddr(246); + + SET_GEOM_OFFSET( in, (VECTOR *)getScratchAddr(252) ); + gte_rtpt(); + + in++; + + gte_stsxy01( &processed[0] , &processed[1] ); + + for ( n_prims--; n_prims >= 0; n_prims-- ) + { + SET_GEOM_OFFSET( in, (VECTOR *)getScratchAddr(252) ); + gte_rtpt(); + + vert = out + voffset; + + SSTOREL( in[-1].vz, out ); + + LCOPY( &processed[0], vert ); + vert += vstep; + + SSTOREL( processed[1].vx, vert + 0 ); + SSTOREL( processed[0].vy, vert + 2 ); + vert += vstep; + + SSTOREL( processed[0].vx, vert + 0 ); + SSTOREL( processed[1].vy, vert + 2 ); + vert += vstep; + + LCOPY( &processed[1], vert ); + + in++; + out += psize; + + gte_stsxy01( &processed[0] , &processed[1] ); + } + + gte_SetGeomOffset( 0, 0 ); + return out; +} + +STATIC void MakePrimsRectangle( DG_PRIM *prim ) +{ + SVECTOR *verts; + char *packs; + int n_prims; + + verts = prim->pos; + packs = (char *)prim->packs[GV_Clock]; + + for ( n_prims = prim->prim_count; n_prims > (BATCH_SIZE - 3); n_prims -= (BATCH_SIZE - 3) ) + { + verts = _RotTransPers(verts, (BATCH_SIZE - 3) / 3); + packs = _MakeXYZRectangle(prim, packs, (BATCH_SIZE - 3)); + } + + _RotTransPers(verts, ( n_prims + 2 ) / 3); + _MakeXYZRectangle(prim, packs, n_prims); +} + +STATIC void MakePrimsFreePacks( DG_PRIM *prim ) +{ + int prim_count; + POLY_FT4 *packs; + + prim_count = prim->prim_count; + packs = (POLY_FT4 *)prim->packs[GV_Clock]; + + _RotTransPers(prim->pos, prim->n_prims); + prim->handler(prim, packs, prim_count); +} + +//todo: this is dumb, must be something else +static inline void AdjustOverscan( MATRIX *matrix, int val ) +{ + matrix->m[1][0] = val; + //matrix->m[1][0] = (matrix->m[1][0] * 58) / 64; + matrix->m[1][1] = (matrix->m[1][1] * 58) / 64; + matrix->m[1][2] = (matrix->m[1][2] * 58) / 64; + + matrix->t[1] = (matrix->t[1] * 58) / 64; +} + +void DG_PrimChanl( DG_CHANL *chanl, int idx ) +{ + MATRIX modelview; + int n_prims; + RECT *clip_rect; + int group_id; + MATRIX *eye; + DG_PRIM **queue; + DG_PRIM *prim; + int type; + int x; + + n_prims = chanl->queue_size - chanl->prim_index; + clip_rect = &chanl->clip_rect; + + if ( n_prims == 0 ) + { + return; + } + + DG_Clip( clip_rect, chanl->clip_distance ); + + group_id = DG_CurrentGroupID; + eye = &chanl->eye_inv; + + queue = (DG_PRIM **)&chanl->queue[ chanl->prim_index ]; + for ( ; n_prims > 0 ; n_prims-- ) + { + prim = *queue++; + type = prim->type; + + if ( type & ( DG_PRIM_INVISIBLE | DG_PRIM_SORTONLY ) ) + { + continue; + } + + if ( prim->group_id && !( prim->group_id & group_id ) ) + { + continue; + } + + if ( !( type & DG_PRIM_ON_CAMERA ) ) + { + if ( prim->root ) + { + prim->world = *prim->root; + } + + gte_CompMatrix( eye, &prim->world, &modelview ); + + x = (modelview.m[1][0] * 58) / 64; + AdjustOverscan( &modelview, x ); + + gte_SetRotMatrix( &modelview ); + gte_SetTransMatrix( &modelview ); + } + else + { + gte_SetRotMatrix( &DG_ZeroMatrix ); + gte_SetTransMatrix( &DG_ZeroMatrix ); + } + + if ( !( type & DG_PRIM_FREEPACKS ) ) + { + if ( !( type & DG_PRIM_RECTANGLE ) ) + { + if ( type & DG_PRIM_ONESIDE ) + { + MakePrimsOneface( prim ); + } + else + { + MakePrims( prim ); + } + } + else + { + if ( prim->verts == 1 ) + { + MakePrimsRectangleSingle( prim ); + } + else + { + MakePrimsRectangle( prim ); + } + } + } + else + { + MakePrimsFreePacks( prim ); + } + + type &= 0x1F; + + if ( type == DG_PRIM_LINE_FT2 || type == DG_PRIM_LINE_GT2 ) + { + DG_AdjustLaserPrims( prim, type ); + } + } +} + +void DG_PrimEnd( void ) +{ + /* do nothing */ +} + +DG_PRIM *DG_MakePrim( int type, int prim_count, int chanl, SVECTOR *pos, RECT *rect ) +{ + DG_PRIM_INFO *info; + int pack_size; + DG_PRIM *prim; + + info = &DG_PrimInfos[type & 31]; + pack_size = info->psize * prim_count; + + prim = GV_Malloc(sizeof(DG_PRIM) + pack_size * 2); + if (!prim) + { + return 0; + } + + GV_ZeroMemory(prim, sizeof(DG_PRIM)); + prim->world = DG_ZeroMatrix; + + prim->type = type; + prim->prim_count = prim_count; + prim->chanl = chanl; + prim->pos = pos; + prim->rect = rect; + + // copy prim info + prim->psize = info->psize; + prim->verts = info->verts; + prim->voffset = info->voffset; + prim->vstep = info->vstep; + + // Point to data after the end of the structure + prim->packs[0] = &prim[1]; + prim->packs[1] = (char *)&prim[1] + pack_size; + +#ifdef __psyz + /* Give every pack its length. GV_ZeroMemory above only clears the DG_PRIM + * header, so the pack area arrives holding whatever the heap block held + * before, and each caller is trusted to write the length itself. Most do. + * The one drawing the dock's searchlights does not, and its packs reached + * the queue with a stale word where the length belongs -- the same value in + * every consecutive pack, which is what a leftover colour array looks like. + * The reader then dropped each packet and resumed mid-primitive. + * + * On the console this could not happen: the length shared a word with the + * next-pointer, and addPrim() rewrote that word on every insertion, so a + * forgotten setlen() was invisible. PSY-Z splits them, and addPrim() no + * longer touches the length -- so "forgotten" now means "never written". + * That is why this is a port bug and not a game bug. + * + * len counts the words after the tag: psize covers the 12-byte header plus + * the payload, so psize/4 - 2. Verified against PSY-Z's own macros -- + * FT4 44 -> 9, GT4 56 -> 12, TILE 20 -> 3. Callers that do set their own + * length just write the same value again. */ + { + int len = info->psize / 4 - 2; + int i, n; + + if (len > 0) + { + for (i = 0; i < 2; i++) + { + char *p = prim->packs[i]; + for (n = prim_count; n > 0; n--, p += info->psize) + { + setlen(p, len); + } + } + } + } +#endif + + return prim; +} + +void DG_FreePrim( DG_PRIM *prim ) +{ + if (prim) + { + GV_DelayedFree(prim); + } +} + +void DG_SetFreePrimParam( int psize, int verts, int voffset, int vstep ) +{ + DG_PRIM_INFO *info; + + info = &DG_PrimInfos[DG_PRIM_FREE]; + info->psize = psize; + info->verts = verts; + info->voffset = voffset; + info->vstep = vstep; +} diff --git a/components/mgs/mgs/source/libdg/pshade.c b/components/mgs/mgs/source/libdg/pshade.c new file mode 100644 index 00000000..51cecfca --- /dev/null +++ b/components/mgs/mgs/source/libdg/pshade.c @@ -0,0 +1,289 @@ +#include "libdg.h" +#include +#include +#include +#include "common.h" + +extern DG_LitVertex DG_LitVertices_800B7A50[84]; + +STATIC void prim_lighting( SVECTOR *pVerts, int numVerts, DG_LitVertex *pOut, DG_LIT *light, int n_lights ) +{ + VECTOR distance; + VECTOR position; + int remaining; + int lights; + DG_LIT *plit; + int radius; + int brightness; + + for (--numVerts; numVerts >= 0; pVerts++, pOut++, numVerts--) + { + gte_ldv0(pVerts); + gte_rt(); + gte_stlvnl0(&position.vx); + gte_stlvnl1(&position.vy); + gte_stlvnl2(&position.vz); + + remaining = 2; + + plit = light; + for (lights = n_lights; lights > 0; lights--, plit++) + { + radius = plit->field_A_radius; + + distance.vx = position.vx - plit->pos.vx; + if ((distance.vx < -radius) || (distance.vx > radius)) + { + continue; + } + + distance.vy = position.vy - plit->pos.vy; + if ((distance.vy < -radius) || (distance.vy > radius)) + { + continue; + } + + distance.vz = position.vz - plit->pos.vz; + if ((distance.vz < -radius) || (distance.vz > radius)) + { + continue; + } + + brightness = plit->field_8_brightness; + + if (--remaining == 0) + { + DG_GetLightVector(&distance, plit->field_8_brightness, &pOut->intensity[1]); + LCOPY(&plit->field_C_color, &pOut->color[1]); + break; + } + + DG_GetLightVector(&distance, brightness, &pOut->intensity[0]); + LCOPY(&plit->field_C_color, &pOut->color[0]); + } + + switch (remaining) + { + case 2: + LSTORE(0, &pOut->color[0]); + + case 1: + LSTORE(0, &pOut->color[1]); + break; + } + } +} + +STATIC void prim_80031B00( DG_MDL *mdl, DG_LIT *light, int n_lights ) +{ + unsigned int numVerts; + SVECTOR *pVerts; + DG_LitVertex *pLitVertices; + + numVerts = mdl->n_verts; + pVerts = mdl->vertices; + pLitVertices = (DG_LitVertex *)getScratchAddr(0); + + // If there are many verts do the first patch in the SPAD + if (numVerts > 42) + { + prim_lighting(pVerts, 42, pLitVertices, light, n_lights); + pVerts += 42; + numVerts -= 42; + pLitVertices = DG_LitVertices_800B7A50; + } + + prim_lighting(pVerts, numVerts, pLitVertices, light, n_lights); +} + +STATIC CVECTOR *SetUnlitCVector( DG_MDL *mdl, CVECTOR *cvec ) +{ + int color; + int i; + + color = 0x3C808080; + if (mdl->flags & DG_MODEL_TRANS) + { + color = 0x3E808080; + } + + for (i = 4 * mdl->n_faces; i > 0; ++cvec, --i) + { + LSTORE(color, cvec); + } + + return cvec; +} + +static inline void *GetLightMatrix(void) +{ + return &DG_LightMatrix; +} + +STATIC CVECTOR *DG_MakePreshade_helper( DG_MDL *mdl, CVECTOR *cvec, DG_OBJS *objs ) +{ + MATRIX light; + MATRIX color; + MATRIX world_light; + CVECTOR *pPacketCode; + int faces; + unsigned char *pFio; + unsigned char *pNfo; + SVECTOR *pNio; + unsigned int index; + DG_LitVertex *pLitVertex; + + memcpy(&light, GetLightMatrix(), 8); + + color.m[0][0] = DG_ColorMatrix.m[0][0]; + color.m[1][0] = DG_ColorMatrix.m[1][0]; + color.m[2][0] = DG_ColorMatrix.m[2][0]; + + pPacketCode = (CVECTOR *)&DG_PacketCode[0]; + + if (mdl->flags & DG_MODEL_TRANS) + { + pPacketCode = (CVECTOR *)&DG_PacketCode[1]; + } + + gte_ldrgb(pPacketCode); + + pFio = mdl->vindices; + pNfo = mdl->nindices; + pNio = mdl->normals; + + for (faces = mdl->n_faces * 4; faces > 0; faces--) + { + index = *pFio; + + if (index < 42) + { + pLitVertex = (DG_LitVertex *)getScratchAddr(0) + index; + } + else + { + pLitVertex = (DG_LitVertex *)&DG_LitVertices_800B7A50[index - 42]; + } + + *(SVECTOR *)&light.m[1] = pLitVertex->intensity[0]; + *(SVECTOR *)&light.m[2] = pLitVertex->intensity[1]; + + gte_SetRotMatrix(&light); + DG_MulRotMatrix0(&objs->world, &world_light); + gte_SetLightMatrix(&world_light); + + color.m[0][1] = pLitVertex->color[0].r << 4; + color.m[1][1] = pLitVertex->color[0].g << 4; + color.m[2][1] = pLitVertex->color[0].b << 4; + color.m[0][2] = pLitVertex->color[1].r << 4; + color.m[1][2] = pLitVertex->color[1].g << 4; + color.m[2][2] = pLitVertex->color[1].b << 4; + + gte_SetColorMatrix(&color); + gte_ldv0(pNio + *pNfo++); + gte_ncs(); + gte_strgb(cvec++); + + pFio++; + } + + return cvec; +} + +STATIC int CalcCVecLen( DG_DEF *def ) +{ + int total_faces; + int i; + DG_MDL *model; + + total_faces = 0; + model = (DG_MDL *)&def[1]; + for (i = def->n_models; i > 0; i--) + { + total_faces += model->n_faces; + ++model; + } + return 0x10 * total_faces; // TODO: sizeof(CVECTOR) ?? +} + +int DG_MakePreshade( DG_OBJS *objs, DG_LIT *light, int n_lights ) +{ + CVECTOR *cvec; + DG_DEF *def; + int size; + MATRIX rotation; + DG_OBJ *obj; + int i; + DG_MDL *model; + + cvec = objs->objs[0].rgbs; + def = objs->def; + + if (!cvec) + { + size = CalcCVecLen(def); + cvec = GV_Malloc(size); + + if (!cvec) + { + return -1; + } + } + + gte_ReadRotMatrix(&rotation); + + obj = objs->objs; + + for (i = def->n_models; i > 0; i--) + { + obj->rgbs = cvec; + model = obj->model; + + gte_SetRotMatrix(&objs->world); + gte_SetTransMatrix(&objs->world); + + prim_80031B00(model, light, n_lights); + + if (model->flags & DG_MODEL_UNLIT) + { + cvec = SetUnlitCVector(model, cvec); + } + else + { + cvec = DG_MakePreshade_helper(model, cvec, objs); + } + + obj++; + } + + obj = objs->objs; + + for (i = objs->n_models; i > 0; i--) + { + if (obj->packs[0]) + { + DG_WriteObjPacketRGB(obj, 0); + } + + if (obj->packs[1]) + { + DG_WriteObjPacketRGB(obj, 1); + } + + obj++; + } + + gte_SetRotMatrix(&rotation); + gte_SetTransMatrix(&rotation); + return 0; +} + +void DG_FreePreshade( DG_OBJS *objs ) +{ + CVECTOR *pBuffer = objs->objs[0].rgbs; + if (pBuffer) + { + GV_Free(pBuffer); + objs->objs[0].rgbs = 0; + } +} diff --git a/components/mgs/mgs/source/libdg/screen.c b/components/mgs/mgs/source/libdg/screen.c new file mode 100644 index 00000000..2064aa67 --- /dev/null +++ b/components/mgs/mgs/source/libdg/screen.c @@ -0,0 +1,529 @@ +#include "libdg.h" +#include +#include +#include +#include "common.h" + +typedef struct +{ + MATRIX eye; + MATRIX world; + MATRIX temp[ 24 ]; + MATRIX root; + MATRIX world2; +} ScrPad; + +#define SCRPAD ((ScrPad *)SCRPAD_ADDR) + +#define EYE (&(SCRPAD->eye)) +#define WORLD (&(SCRPAD->world)) +#define TEMP (SCRPAD->temp) +#define ROOT (&(SCRPAD->root)) +#define WORLD2 (&(SCRPAD->world2)) + +STATIC void DG_ScreenModelsSingle( DG_OBJS *objs, int n_models ) +{ + DG_OBJ *obj; + + // view x world -> screen + gte_CompMatrix(EYE, WORLD, TEMP); + + obj = objs->objs; + for (; n_models > 0; n_models--) + { + obj->world = *WORLD; + obj->screen = *TEMP; + obj++; + } +} + +void DG_ScreenModels( DG_OBJS *objs, int n_models ) +{ + MATRIX *screen; + DG_OBJ *obj; + + gte_SetRotMatrix(EYE); + gte_SetTransMatrix(EYE); + + screen = TEMP; + + obj = objs->objs; + for (; n_models > 0; n_models--) + { + DG_CompMatrix(screen, &obj->screen); + obj++; + screen++; + } +} + +#ifdef VR_EXE +STATIC void DG_ScreenModelsEnvmap( DG_OBJS *objs, int n_models ) +{ + MATRIX *screen; + DG_OBJ *obj; + + gte_SetRotMatrix(EYE); + gte_SetTransMatrix(EYE); + + screen = TEMP; + + obj = objs->objs; + for (; n_models > 0; n_models--) + { + *screen = obj->world; + + DG_CompMatrix(screen, &obj->screen); + obj++; + screen++; + } +} +#endif + +STATIC void DG_ApplyMovs( DG_OBJS *objs, int n_models ) +{ + SVECTOR *movs; + MATRIX *world; + MATRIX *out; + DG_OBJ *obj; + MATRIX *parent; + + movs = objs->movs; + + world = WORLD; + gte_SetRotMatrix(world); + + out = TEMP; + + obj = objs->objs; + for (; n_models > 0; n_models--) + { + parent = TEMP + obj->model->parent; + + gte_SetTransMatrix(parent); + gte_ldv0(movs); + gte_rt(); + gte_ReadRotMatrix(out); + gte_stlvnl(out->t); + + obj->world = *out; + + movs++; + obj++; + out++; + } +} + +STATIC void DG_ApplyRots( DG_OBJS *objs, int n_models ) +{ + MATRIX *out; + DG_OBJ *obj; + MATRIX *world; + SVECTOR *rots; + SVECTOR *adjust; + SVECTOR *waist_rot; + DG_MDL *model; + MATRIX *root; + int i; + MATRIX *parent; + MATRIX *modelm; + + out = TEMP; + obj = objs->objs; + world = WORLD2; + rots = objs->rots; + adjust = objs->adjust; + waist_rot = objs->waist_rot; + model = obj->model; + root = ROOT; + +#ifdef MGS_TURNTABLE + /* Turntable: present every rig upright and face-on to whatever camera the + * stage happens to have picked, spinning about the screen's vertical. + * + * A fixed rotation is not enough. s11e watches its room from overhead, so a + * character turning about its own Y axis only ever shows the top of its + * head -- which is exactly the view that hides the joints worth judging. + * Deriving the pose from the view matrix makes the shot independent of the + * camera: EYE is orthonormal, so its transpose is its inverse, and + * + * WORLD.R = EYE^T . Ry(angle) + * + * cancels the camera out and leaves precisely the requested spin in view + * space. WORLD is what the pelvis hangs off and what root is composed + * against, so the whole hierarchy follows; MulMatrix0 touches only the 3x3, + * leaving the translation, so the character stays where the game put it and + * the scenery around it is untouched. + * + * Only rigs: n_models is 16 for a person and small for scenery. */ + if ( n_models >= 12 ) + { + extern int mgs_dbg_spin; + int s = rsin( mgs_dbg_spin ), c = rcos( mgs_dbg_spin ); + MATRIX inv, ry; + int a, b; + + for ( a = 0; a < 3; a++ ) + { + for ( b = 0; b < 3; b++ ) + { + inv.m[a][b] = EYE->m[b][a]; + } + } + + ry.m[0][0] = c; ry.m[0][1] = 0; ry.m[0][2] = s; + ry.m[1][0] = 0; ry.m[1][1] = 4096; ry.m[1][2] = 0; + ry.m[2][0] = -s; ry.m[2][1] = 0; ry.m[2][2] = c; + + MulMatrix0( &inv, &ry, WORLD ); + } +#endif + +#ifdef __psyz + { + /* MEASUREMENT, not a fix. + * + * Every number we have about this skeleton -- the bind pose, the joint + * map, the hinge signs -- was computed on a PC from the retail disc. + * None of it has ever been compared against what this board actually + * computes, and three visual guesses have now failed. So: freeze the + * pose (all angles zero, no adjust, no waist twist, world = identity) + * and print the sixteen resulting joint positions. In that state the + * answer is arithmetic -- each joint sits at the sum of the pos + * offsets down its chain -- so the board's output can be checked + * against exact integers with no interpretation and no human looking + * at the panel. */ + extern int mgs_dbg_bindpose; + static SVECTOR mgs_zero_rots[32]; + if ( mgs_dbg_bindpose ) + { + rots = mgs_zero_rots; + adjust = 0; + waist_rot = 0; + *WORLD = DG_ZeroMatrix; + } + } +#endif + + if ( waist_rot ) + { + RotMatrixZYX_gte( waist_rot, root ); + } + else + { + RotMatrixZYX_gte( rots, root ); + } + + root->t[0] = model->pos.vx; + root->t[1] = model->pos.vy; + root->t[2] = model->pos.vz; + + if ( !adjust ) + { + gte_CompMatrix( WORLD, root, root ); + } + else + { + *world = *WORLD; + *WORLD = DG_ZeroMatrix; + } + + for ( i = n_models; i > 0; i-- ) + { + model = obj->model; + parent = TEMP + model->parent; + + RotMatrixZYX_gte( rots, out ); + + out->t[0] = model->pos.vx; + out->t[1] = model->pos.vy; + out->t[2] = model->pos.vz; + + if ( i == ( n_models - 1 ) ) + { + modelm = root; + } + else + { + modelm = parent; + } + + gte_CompMatrix( modelm, out, out ); + + if ( !adjust ) + { + obj->world = *out; + } + else + { + if ( adjust->vz ) RotMatrixZ( adjust->vz, out ); + if ( adjust->vx ) RotMatrixX( adjust->vx, out ); + if ( adjust->vy ) RotMatrixY( adjust->vy, out ); + adjust++; + } + + obj++; + out++; + rots++; + } + + if ( adjust ) + { + out = TEMP; + obj = objs->objs; + + gte_SetRotMatrix( world ); + gte_SetTransMatrix( world ); + + for ( i = n_models; i > 0; i-- ) + { + DG_CompMatrix( out, out ); + obj->world = *out; + obj++; + out++; + } + } + +#ifdef __psyz + { + /* Dump the assembled skeleton. Three checks per joint, each of which + * fails loudly and names the culprit: + * det must be 4096 -- a value of -4096 is a REFLECTION, which by + * itself explains a knee bending backwards and feet pointing + * opposite ways + * d2 the squared distance from this joint to its parent, which in + * the bind pose must equal the squared length of its own pos + * offset; anything else means composition is not rigid + * t the absolute position, to compare against the disc */ + extern int mgs_dbg_skel; + + /* Every rig the engine assembles gets audited, not just the one that + * happened to be on screen when a burst fired. Two properties have to + * hold for ANY character in ANY pose, so they can be checked without + * knowing which character this is or what it is doing: + * + * det == 4096 the matrix is a rotation. A negative determinant is a + * reflection, which alone explains a knee that bends + * backwards and feet pointing opposite ways. + * d2 == want a joint sits exactly its own bone length from its + * parent. Rigid composition cannot stretch a limb. + * + * Both drift a little from the truncating fixed-point multiplies, hence + * the tolerances; anything outside them is a real defect. This is what + * answers "are the soldiers assembled correctly too" -- they use the + * same 16-bone rig and the same code, so the moment one walks on screen + * it is audited and any complaint names it. Silence means clean. */ + { + static unsigned joints, bad, rigs, scaled, beat; + int ref_det = 0, ref_ratio = -1, rig_scaled = 0; + int k, longest = -1, longest_want = 0; + + rigs++; + + /* Pick the reference bone before comparing anything: the LONGEST + * one. The ratio is d2/want, and both are squares of quantities the + * fixed-point pipeline rounds by about a unit, so a short bone + * carries enormous relative noise -- the chest offset is 5 units + * and its ratio swings by a third from rounding alone. Referencing + * the thigh instead puts the noise floor near 0.5%, which is what + * makes a 12% tolerance meaningful rather than decorative. */ + for ( k = 0; k < n_models; k++ ) + { + SVECTOR *q = &objs->objs[ k ].model->pos; + int w = q->vx * q->vx + q->vy * q->vy + q->vz * q->vz; + if ( w > longest_want ) + { + longest_want = w; + longest = k; + } + } + if ( longest >= 0 ) + { + DG_OBJ *o = &objs->objs[ longest ]; + int p = o->model->parent; + MATRIX *m = &o->world; + MATRIX *pm = ( p >= 0 ) ? &objs->objs[ p ].world : m; + int dx = m->t[0] - pm->t[0]; + int dy = m->t[1] - pm->t[1]; + int dz = m->t[2] - pm->t[2]; + ref_ratio = (int)( ( (long long)( dx * dx + dy * dy + dz * dz ) + << 8 ) / longest_want ); + } + for ( k = 0; k < n_models; k++ ) + { + DG_OBJ *o = &objs->objs[ k ]; + MATRIX *m = &o->world; + int p = o->model->parent; + MATRIX *pm = ( p >= 0 ) ? &objs->objs[ p ].world : m; + int dx = m->t[0] - pm->t[0]; + int dy = m->t[1] - pm->t[1]; + int dz = m->t[2] - pm->t[2]; + int d2 = dx * dx + dy * dy + dz * dz; + int want = o->model->pos.vx * o->model->pos.vx + + o->model->pos.vy * o->model->pos.vy + + o->model->pos.vz * o->model->pos.vz; + int c0 = ( m->m[1][1] * m->m[2][2] - m->m[1][2] * m->m[2][1] ) >> 12; + int c1 = ( m->m[1][0] * m->m[2][2] - m->m[1][2] * m->m[2][0] ) >> 12; + int c2 = ( m->m[1][0] * m->m[2][1] - m->m[1][1] * m->m[2][0] ) >> 12; + int det = ( m->m[0][0] * c0 - m->m[0][1] * c1 + m->m[0][2] * c2 ) >> 12; + const char *why = 0; + + joints++; + + if ( k == 0 ) + { + ref_det = det; + rig_scaled = ( det < 3891 || det > 4301 ); + } + + /* A rig may legitimately be scaled -- the game animates that, + * and a uniform scale is not a defect, it is an effect. So the + * test is not "det is 4096" but "every bone agrees with every + * other bone", which holds under any similarity and fails the + * moment composition stops being rigid: + * + * det < 0 a REFLECTION: the classic knee that bends + * backwards and feet pointing opposite ways + * det != ref_det one bone scaled differently from the rest, + * i.e. the matrix is no longer a similarity + * d2/want the same across the whole rig. Comparing the + * ratio rather than the value is what makes + * this scale-invariant, so no cube root and no + * assumption about what the game is doing. + */ + /* Deliberately relative, sign included. An absolute "det must be + * positive" test flags every shadow and every water reflection + * in the dock -- those are drawn by mirroring and flattening + * the character on purpose, so the whole rig comes out negative + * and hugely anisotropic, and it is correct. What can never + * happen is ONE bone disagreeing with the other fifteen. */ + if ( ( det ^ ref_det ) < 0 ) + { + why = "mirrored relative to its own rig"; + } + else if ( det - ref_det > ( ( ref_det < 0 ? -ref_det : ref_det ) >> 4 ) || + ref_det - det > ( ( ref_det < 0 ? -ref_det : ref_det ) >> 4 ) ) + { + why = "scale differs from rig"; + } + else if ( want > 64 && ref_ratio > 0 ) + { + /* 12% on the ratio of squares is 6% on the length -- well + * clear of the rounding floor, and far under the breakage + * that was visible: a limb that had stopped following its + * parent was out by whole multiples, not percent. */ + int ratio = (int)( ( (long long)d2 << 8 ) / want ); + int tol = ( ref_ratio >> 3 ) + 8; + if ( ratio - ref_ratio > tol || ref_ratio - ratio > tol ) + { + why = "bone length inconsistent"; + } + } + + if ( why ) + { + bad++; + if ( bad <= 24 ) + { + printf( "[rig] BAD n=%d joint %d parent %d det=%d " + "(rig %d) d2=%d want=%d model=%p -- %s\n", + n_models, k, p, det, ref_det, d2, want, + (void *)o->model, why ); + } + } + } + + scaled += rig_scaled; + + if ( ( beat++ % 900u ) == 0u ) + { + printf( "[rig] %u rigs (%u scaled by the game), %u joints, " + "%u bad\n", + rigs, scaled, joints, bad ); + } + } + + if ( mgs_dbg_skel > 0 ) + { + int k; + mgs_dbg_skel--; + printf( "SKEL n=%d adjust=%d waist=%d\n", n_models, + objs->adjust ? 1 : 0, objs->waist_rot ? 1 : 0 ); + for ( k = 0; k < n_models; k++ ) + { + DG_OBJ *o = &objs->objs[ k ]; + MATRIX *m = &o->world; + int p = o->model->parent; + MATRIX *pm = ( p >= 0 ) ? &objs->objs[ p ].world : m; + int dx = m->t[0] - pm->t[0]; + int dy = m->t[1] - pm->t[1]; + int dz = m->t[2] - pm->t[2]; + int c0 = ( m->m[1][1] * m->m[2][2] - m->m[1][2] * m->m[2][1] ) >> 12; + int c1 = ( m->m[1][0] * m->m[2][2] - m->m[1][2] * m->m[2][0] ) >> 12; + int c2 = ( m->m[1][0] * m->m[2][1] - m->m[1][1] * m->m[2][0] ) >> 12; + int det = ( m->m[0][0] * c0 - m->m[0][1] * c1 + m->m[0][2] * c2 ) >> 12; + + printf( " %2d p%3d rot(%4d %4d %4d) t(%7d %7d %7d) d2=%d want=%d det=%d\n", + k, p, objs->rots[k].vx, objs->rots[k].vy, objs->rots[k].vz, + m->t[0], m->t[1], m->t[2], dx * dx + dy * dy + dz * dz, + o->model->pos.vx * o->model->pos.vx + + o->model->pos.vy * o->model->pos.vy + + o->model->pos.vz * o->model->pos.vz, det ); + } + } + } +#endif +} + +STATIC void DG_ScreenObjs( DG_OBJS *objs ) +{ + int n_models; + + n_models = objs->n_models; + + if (objs->root) + { + objs->world = *objs->root; + } + + *WORLD = objs->world; + + if (objs->flag & DG_FLAG_ONEPIECE) + { + DG_ScreenModelsSingle(objs, n_models); + } +#ifdef VR_EXE + else if (objs->flag & DG_FLAG_ENVMAP) + { + DG_ScreenModelsEnvmap(objs, n_models); + } +#endif + else + { + if (objs->rots) + { + DG_ApplyRots(objs, n_models); + } + else if (objs->movs) + { + DG_ApplyMovs(objs, n_models); + } + + DG_ScreenModels(objs, n_models); + } +} + +void DG_ScreenChanl( DG_CHANL *chanl, int idx ) +{ + DG_OBJS **queue; + int i; + + queue = chanl->queue; + + *EYE = chanl->eye_inv; + DG_AdjustOverscan(EYE); + + for (i = chanl->objs_index; i > 0; i--) + { + DG_ScreenObjs(*queue++); + } +} diff --git a/components/mgs/mgs/source/libdg/shade.c b/components/mgs/mgs/source/libdg/shade.c new file mode 100644 index 00000000..c77a04b0 --- /dev/null +++ b/components/mgs/mgs/source/libdg/shade.c @@ -0,0 +1,267 @@ +#include "libdg.h" +#include +#include +#include +#include "common.h" + +void DG_ShadeStart( void ) +{ + /* do nothing */ +} + +//just an index using an int shifted to get each byte of the face normal idx, but didnt match that way +static inline void DG_ShadePack( unsigned int *nindices, POLY_GT4 *packs, char *colors ) +{ + unsigned int f0, f1, f2, f3; + + f0 = *nindices; + f1 = *nindices; + f2 = *nindices; + f3 = *nindices; + + f0 <<= 2; + f1 >>= 6; + f2 >>= 22; + f3 >>= 14; + + f0 &= 0x3FC; + f1 &= 0x3FC; + f2 &= 0x3FC; + f3 &= 0x3FC; + + f0 = (int)(colors + f0); + f1 = (int)(colors + f1); + f2 = (int)(colors + f2); + f3 = (int)(colors + f3); + + LCOPY2( (void *)f0, &packs->r0, (void *)f1, &packs->r1 ); + LCOPY2( (void *)f2, &packs->r2, (void *)f3, &packs->r3 ); +} + +STATIC POLY_GT4 *DG_ShadePacks( unsigned int *nindices, POLY_GT4 *packs, int n_packs ) +{ + void *colors; + + for ( n_packs--; n_packs >= 0; n_packs-- ) + { + colors = getScratchAddr(8); + + if ( packs->tag & 0xFFFF ) + { + DG_ShadePack( nindices, packs, colors ); + } + + packs++; + nindices++; + } + + return packs; +} + +STATIC POLY_GT4 *DG_ShadePacksIndirect( unsigned int *nindices, POLY_GT4 *packs, int n_packs, unsigned int *vindices ) +{ + void *colors; + unsigned int mask; + unsigned int f0, f1, f2, f3; + unsigned int v0123; + int color; + + for ( n_packs--; n_packs >= 0; packs++, nindices++, vindices++, n_packs-- ) + { + mask = 0x80808080; + + colors = getScratchAddr(8); + + f0 = *nindices; + + if ( !( packs->tag & 0xFFFF ) && !( *nindices & mask ) ) continue; + + v0123 = *vindices; + f3 = *nindices; + f0 = *nindices << 2; + f1 = *nindices >> 6; + f2 = *nindices >> 22; + f3 = *nindices >> 14; + + f0 &= 0x1FC; + f1 &= 0x1FC; + f2 &= 0x1FC; + f3 &= 0x1FC; + + f0 += (unsigned int)colors; + f1 += (unsigned int)colors; + f2 += (unsigned int)colors; + f3 += (unsigned int)colors; + + if ( v0123 & mask ) + { +#ifdef __psyz + { + static int b = 0; + if (b > 0) { b--; printf("[shade] resolve %p\n", (void*)packs); } + } +#endif + if ( v0123 & 0x80 ) + { + color = **(int **)&packs->r0; + } + else + { + color = *(int *)f0; + } + v0123 >>= 8; + *(int *)&packs->r0 = color; + + if ( v0123 & 0x80 ) + { + color = **(int **)&packs->r1; + } + else + { + color = *(int *)f1; + } + v0123 >>= 8; + *(int *)&packs->r1 = color; + + if ( v0123 & 0x80 ) + { + color = **(int **)&packs->r3; + } + else + { + color = *(int *)f3; + } + v0123 >>= 8; + *(int *)&packs->r3 = color; + + if ( v0123 & 0x80 ) + { + color = **(int **)&packs->r2; + } + else + { + color = *(int *)f2; + } + v0123 >>= 8; + *(int *)&packs->r2 = color; + + } + else + { + LCOPY2( (void *)f0, &packs->r0, (void *)f1, &packs->r1 ); + LCOPY2( (void *)f2, &packs->r2, (void *)f3, &packs->r3 ); + } + } + + return packs; +} + +STATIC void DG_ShadeObj( DG_OBJ *obj, int idx ) +{ + typedef struct { int d0, d1, d2; } Unit; + + POLY_GT4 *packs; + DG_MDL *model; + Unit *normals; + int n_normals; + Unit *scratch; + + for ( packs = obj->packs[ idx ]; obj; obj = obj->extend ) + { + model = obj->model; + + gte_ldrgb( ( model->flags & DG_MODEL_TRANS ) ? + &DG_PacketCode[1] : + &DG_PacketCode[0] ); + + normals = (Unit *)model->normals; + n_normals = model->n_normals; + + *(Unit *)getScratchAddr(8) = normals[0]; + *(Unit *)getScratchAddr(11) = normals[1]; + + scratch = (Unit *)getScratchAddr(8); + while ( n_normals > 0 ) + { + gte_ldv3c( scratch ); + + n_normals -= 3; + normals += 2; + + gte_nct_b(); + scratch++; + + scratch[0] = normals[0]; + scratch[1] = normals[1]; + + gte_strgb3( &scratch[-1].d0, &scratch[-1].d1, &scratch[-1].d2 ); + } + + if ( !( model->flags & DG_MODEL_INDIRECT ) ) + { + packs = DG_ShadePacks( (unsigned int *)model->nindices, packs, obj->n_packs ); + } + else + { + packs = DG_ShadePacksIndirect( (unsigned int *)model->nindices, packs, obj->n_packs, (unsigned int *)model->vindices ); + } + } +} + +void DG_ShadeChanl( DG_CHANL *chanl, int idx ) +{ + DG_OBJS **queue; + int n_objs; + DG_OBJS *objs; + DG_OBJ *obj; + int n_models; + + queue = chanl->queue; + for ( n_objs = chanl->objs_index; n_objs > 0 ; n_objs-- ) + { + objs = *queue++; + + // TODO: figure out the values for bound_mode + if ( objs->bound_mode == 0 ) + { + continue; + } + + if ( !( objs->flag & DG_FLAG_SHADE ) ) + { + continue; + } + + gte_SetRotMatrix( objs->light ); + gte_SetColorMatrix( objs->light + 1 ); + + if ( objs->flag & DG_FLAG_AMBIENT ) + { + gte_SetBackColor( objs->light->t[0], objs->light->t[1], objs->light->t[2] ); + } + + obj = objs->objs; + for ( n_models = objs->n_models; n_models > 0 ; n_models-- ) + { + if ( obj->bound_mode != 0 ) + { + DG_MulRotMatrix0( &obj->world, getScratchAddr(0) ); + gte_SetLightMatrix( getScratchAddr(0) ); + + DG_ShadeObj( obj, idx ); + } + + obj++; + } + + if ( objs->flag & DG_FLAG_AMBIENT ) + { + gte_SetBackColor( DG_Ambient.vx, DG_Ambient.vy, DG_Ambient.vz ); + } + } +} + +void DG_ShadeEnd( void ) +{ + /* do nothing */ +} diff --git a/components/mgs/mgs/source/libdg/sort.c b/components/mgs/mgs/source/libdg/sort.c new file mode 100644 index 00000000..f935b31e --- /dev/null +++ b/components/mgs/mgs/source/libdg/sort.c @@ -0,0 +1,133 @@ +#include "libdg.h" + +typedef struct _SCRATCHPAD_UNK { + OT_TYPE *buf; /* the 256-bucket ordering table of the first radix pass */ + OT_TYPE *ot; + int len; +} SCRATCHPAD_UNK; + +extern OT_TYPE ptr_800B1400[256]; + +static inline SCRATCHPAD_UNK * get_scratch(void) +{ + return (SCRATCHPAD_UNK *)(SCRPAD_ADDR + 0x000); +} + +static inline OT_TYPE * get_buf(void) +{ + return ((SCRATCHPAD_UNK *)(SCRPAD_ADDR + 0x000))->buf; +} + +static inline int DG_GetCurrentGroupID(void) +{ + return DG_CurrentGroupID; +} + +void DG_SortChanl( DG_CHANL *chanl, int idx ) +{ + void *list; + void *list_next; + OT_TYPE *buf; + + OT_TYPE *ot; + OT_TYPE *ot2; + OT_TYPE *indexed_ot; + OT_TYPE *ot_ptr; + + int i; + + int index2; + unsigned int len; + + int group_id; + + int prim_size; + int prim_count; + + void **pQueue; + DG_PRIM *pPrim; + char *prim; + + SCRATCHPAD_UNK *pad = get_scratch(); + + pad->buf = ptr_800B1400; + pad->ot = chanl->ot[idx] + 1; + + buf = get_buf(); + ot = pad->ot; + + /* Second pass of a two-pass radix sort on the 16-bit depth. DG_DivideChanl + * bucketed by the low byte into `buf` and parked the high byte in the tag's + * length field; here each chain is re-bucketed by that high byte, and the + * length is finally set to the real POLY_GT4 payload size. + * + * On PSY-Q both halves shared one packed 32-bit tag (24-bit next pointer + + * 8-bit length). PSY-Z splits them into separate words because a host + * pointer does not fit in 24 bits, so the same operation is expressed as + * addPrim() plus setlen(). */ + for (i = 256; i > 0; i--) + { + list = (void *)getaddr(buf); + buf++; + + while (list != 0) + { + list_next = (void *)getaddr(list); + ot_ptr = &ot[getlen(list)]; + addPrim(ot_ptr, list); + setlen(list, 0x0c); + list = list_next; + } + } + + pQueue = (void **)&chanl->queue[chanl->prim_index]; + group_id = DG_GetCurrentGroupID(); + + for (i = chanl->queue_size - chanl->prim_index; i > 0; i--) + { + pPrim = *pQueue++; + + if (pPrim->type & DG_PRIM_INVISIBLE) + { + continue; + } + + if (pPrim->group_id && !(pPrim->group_id & group_id)) + { + continue; + } + + pad->len = pPrim->raise; + + prim_count = pPrim->prim_count; + prim = (char *)pPrim->packs[idx]; + prim_size = (short)pPrim->psize; + + /* both were read back through raw scratchpad addresses; they are the + * same two fields the struct already names */ + ot2 = pad->ot; + len = (unsigned int)pad->len; + + while (--prim_count >= 0) + { + index2 = *((u_short *)prim); + + if (index2 > 0) + { + index2 -= len; + + if (index2 < 0) + { + index2 = 0; + } + + index2 >>= 8; + + indexed_ot = &ot2[index2]; + addPrim(indexed_ot, prim); + } + + prim += prim_size; + } + } +} diff --git a/components/mgs/mgs/source/libdg/text.c b/components/mgs/mgs/source/libdg/text.c new file mode 100644 index 00000000..a87de658 --- /dev/null +++ b/components/mgs/mgs/source/libdg/text.c @@ -0,0 +1,314 @@ +#include "libdg.h" +#include "common.h" + +/*** bss ***/ +extern DG_TEX TexSets[DG_MAX_TEXTURES]; + +/*** data ***/ +STATIC DG_TEX DG_NullTexture = {}; + +/*** $gp ***/ +int SECTION(".sbss") DG_ResidentTextureCacheSize; +DG_TEX *SECTION(".sbss") DG_ResidentTextureCache; + +/*** sdata ***/ +STATIC int DG_LastMissingTexture = -1; + +/** + * @brief Searches the cache for an entry matching a given ID. + * Also returns an empty texture if one is found first. + * + * @param id The ID to search for in the cache. + * @param found The pointer to write the found texture pointer to. + */ +STATIC int FindTexture( int id, DG_TEX **found ) +{ + int start; + DG_TEX *record; + int index; + int record_id; + + start = id % DG_MAX_TEXTURES; + record = &TexSets[start]; + + index = start; + do + { + record_id = record->id; + + if (record_id == 0) + { + *found = record; + return 0; + } + + if (record_id == id) + { + *found = record; + return 1; + } + + index++; + record++; + + if (index == DG_MAX_TEXTURES) + { + record = TexSets; + index = 0; + } + } while(index != start); + + *found = NULL; + return 0; +} + +/** + * @brief Initialises the texture cache by marking all entries as unused. + */ +void DG_InitTextureSystem( void ) +{ + DG_TEX *tex; + int i; + + tex = TexSets; + for (i = DG_MAX_TEXTURES; i > 0; i--) + { + tex->id = 0; + tex->used = 0; + tex++; + } +} + +/** + * @brief Searches the cache for an entry matching a given ID and returns a + * matching entry if found. + * Returns an empty texture if no texture with a matching ID was found. + * Also sets DG_LastMissingTexture to the given ID if it was not found. + * + * @param id The ID to search for in the cache. + */ +DG_TEX *DG_GetTexture( int id ) +{ + DG_TEX *found; + + if (!FindTexture(id, &found)) + { + if (id != DG_LastMissingTexture) + { + DG_LastMissingTexture = id; + } + + found = &DG_NullTexture; + } + + return found; +} + +/** + * @brief Sets a texture given its ID and parameters. + * + * @param id The ID of the texture to set. + * @param tp The bitdepth of the texture (0: 4bpp, 1: 8bpp). + * @param abr The blend rate of the texture (see libgpu.h). + * @param tex The RECT describing the texture data. + * @param pal The RECT describing the palette data. + * @param col The number of palette entries. + */ +void DG_SetTexture( int id, int tp, int abr, RECT *img, RECT *pal, int col ) +{ + DG_TEX *tex; + int x, y, w, h; + int cx, cy; + int tpage; + int temp; + + if (FindTexture(id, &tex) && tex->used) + { + tex->id = 0; + } + + tex->id = id; + tex->col = col; + tex->used = 0; + + x = img->x; + y = img->y; + + cx = pal->x; + cy = pal->y; + + // They didn't use the LIBGPU macros :( + temp = x; + tpage = ((x / 64) + ((y / 256) << 4)) | ((tp << 7) | (abr << 5)); + + tex->tpage = tpage; + tex->clut = cy << 6 | cx >> 4; + + x %= 64; + + w = img->w; + h = img->h; + + if (tp == 0) + { + x *= 4; + w *= 4; + } + else + { + x *= 2; + w *= 2; + } + + tex->off_x = x; + tex->off_y = y % 256; + + tex->w = w - 1; + temp = h; + tex->h = temp - 1; +} + +/** + * @brief Gets the RECT describing the given texture's extents. + * + * @param tex The texture to get the RECT for. + * @param rect The RECT to write the texture's extents to. + */ +void DG_GetTextureRect( DG_TEX *tex, RECT *rect ) +{ + int tpage; + int x, y, w; + + tpage = tex->tpage; + x = ( tpage & 0x0F ) << 6; + y = ( tpage & 0x10 ) << 4; + + switch ( tpage & 0x180 ) + { + case 0: + rect->x = x + ( tex->off_x >> 2 ); + break; + case 0x80: + rect->x = x + ( tex->off_x >> 1 ); + break; + } + + rect->y = tex->off_y + y; + rect->h = tex->h + 1; + + w = tex->w + 1; + + switch ( tpage & 0x180 ) + { + case 0: + w /= 2; + case 0x80: + w /= 2; + break; + } + + rect->w = w; +} + +/** + * @brief Gets the RECT describing the given texture's palette extents. + * + * @param tex The texture to get the RECT for. + * @param rect The RECT to write the texture's palette extents to. + */ +void DG_GetClutRect( DG_TEX *tex, RECT *rect ) +{ + int clut; + int x, y, y2, w; + + clut = tex->clut; + + y = ( clut & 0x7FC0 ); + x = ( clut & 0x003F ) << 4; + y2 = y / 64; + + w = 0; + + switch ( tex->tpage & 0x180 ) + { + case 0: + w = 16; + break; + case 1: + w = 256; + break; + } + + rect->x = x; + rect->y = y2; + rect->w = w; + rect->h = 1; +} + +/** + * @brief Initialises the resident texture cache as empty. + */ +void DG_ClearResidentTexture( void ) +{ + DG_ResidentTextureCacheSize = 0; + DG_ResidentTextureCache = NULL; +} + +/** + * @brief Saves all texture cache entries to the resident texture cache. + */ +void DG_SaveResidentTextureCache( void ) +{ + DG_TEX *tex; + int size; + int i; + DG_TEX *resident; + + tex = TexSets; + size = 0; + for (i = DG_MAX_TEXTURES; i > 0; tex++, i--) + { + if (tex->id != 0) + { + size++; + } + } + + if (size) + { + DG_ResidentTextureCacheSize = size; + DG_ResidentTextureCache = resident = GV_AllocResidentMemory(size * sizeof(DG_TEX)); + + tex = TexSets; + for (i = DG_MAX_TEXTURES; i > 0; i--) + { + if (tex->id != 0) + { + *resident++ = *tex; + } + + tex++; + } + } +} + +/** + * @brief Loads all resident texture cache entries into the main texture cache. + */ +void DG_LoadResidentTextureCache( void ) +{ + DG_TEX *tex; + int i; + DG_TEX *found; + + if (DG_ResidentTextureCache) + { + tex = DG_ResidentTextureCache; + for (i = DG_ResidentTextureCacheSize; i > 0; i--) + { + FindTexture(tex->id, &found); + *found = *tex++; + found->used = 1; + } + } +} diff --git a/components/mgs/mgs/source/libdg/trans.c b/components/mgs/mgs/source/libdg/trans.c new file mode 100644 index 00000000..cefbee56 --- /dev/null +++ b/components/mgs/mgs/source/libdg/trans.c @@ -0,0 +1,480 @@ +#include "libdg.h" +#include "common.h" +#include "inline_n.h" +#include "inline_x.h" +#include "libgv/libgv.h" // for GV_Clock +#ifdef __psyz +unsigned mgs_dbg_objs_seen, mgs_dbg_objs_culled, mgs_dbg_models_culled, + mgs_dbg_models_drawn; +#endif + +typedef struct _SCRATCH +{ + DVECTOR xy[126]; + int area; + short bothface; + short flags; + DVECTOR zf[126]; + POLY_GT4 *parent_packs; + SVECTOR *vertices; +} SCRATCH; + +#define SPAD ((SCRATCH *)getScratchAddr(0)) + +void DG_TransStart( void ) +{ + /* do nothing */ +} + +STATIC unsigned int DG_WriteObjVerticesIndirect( unsigned int vidx, POLY_GT4 *packs ) +{ + int *pack_ptr; + int a2, a3, t3, t5, num, stride; + int i, temp; + unsigned int scrpd_idx, scrpd_idx2; + unsigned char t6; + + scrpd_idx = SCRPAD_ADDR; + t3 = *( int* )(scrpd_idx + 0x3F8); + if ( !t3 ) return 0; + + + t5 = *( int* )( scrpd_idx + 0x3FC ); + num = 0; + stride = 0; + + pack_ptr = ( int* )&packs->x0; + a3 = 8; + i = 4; + t6 = 32; + scrpd_idx2 = SCRPAD_ADDR; + + for ( ; i > 0; i-- ) + { + a2 = vidx & 0x7f; + + if ((vidx & 0x80) == 0) + { + a2 = (a2 * 4) + scrpd_idx2; + temp = ((unsigned int*)a2)[0x80]; + a2 = *( int* )a2; + num = num + temp; + stride++; + } + else + { + a2 = t5 + ( a2 << 3 ); + temp = ( ( unsigned short* )scrpd_idx2 )[0xFF]; + a2 = ( ( short* )a2 )[3]; + + if (temp & 4) + { + * (pack_ptr - 1 ) = (t3 + a2) - 4; +#ifdef __psyz + { + static int b = 0; + if (b > 0) { b--; printf("[shade] embed %p\n", (void*)packs); } + } +#endif + } + + a2 = *(int *)(t3 + a2); + } + + *pack_ptr = a2; + pack_ptr += 3; + vidx = ( vidx << ( t6 - a3 ) ) | ( vidx >> a3 ); + a3 = a3 + 8; + } + + if (stride == 2) + { + return num / 2; + } + else if (stride == 1) + { + return num; + } + else if (stride == 3) + { + return num / 3; + } + return 0; +} + +STATIC POLY_GT4 *DG_WriteObjVertices( unsigned int *vindices, POLY_GT4 *packs, int n_packs ) +{ + unsigned char bVar1; + int area; + unsigned int uVar7; + int count; + unsigned int scrpad_addr; + unsigned int uVar8; + unsigned int *n0, *n1, *n2, *n3; + + for (count = n_packs - 1; count >= 0; (packs++)->tag = uVar7, vindices++, count--) + { + scrpad_addr = SCRPAD_ADDR; + uVar7 = 0; + uVar8 = 0xfffe0000; + + n0 = (unsigned int *)*vindices; + + if (*vindices & 0x80808080) + { + uVar7 = DG_WriteObjVerticesIndirect(*vindices, packs); + continue; + } + + n1 = (unsigned int *)*vindices; + n2 = (unsigned int *)*vindices; + n3 = (unsigned int *)*vindices; + + n0 = (unsigned int *)((unsigned int)n0 << 2); + n1 = (unsigned int *)((unsigned int)n1 >> 6); + n2 = (unsigned int *)((unsigned int)n2 >> 22); + n3 = (unsigned int *)((unsigned int)n3 >> 14); + + n0 = (unsigned int *)((int)n0 & 0x1fc); + n1 = (unsigned int *)((int)n1 & 0x1fc); + n2 = (unsigned int *)((int)n2 & 0x1fc); + n3 = (unsigned int *)((int)n3 & 0x1fc); + + n0 = (unsigned int *)((int)n0 + scrpad_addr); + n1 = (unsigned int *)((int)n1 + scrpad_addr); + n2 = (unsigned int *)((int)n2 + scrpad_addr); + n3 = (unsigned int *)((int)n3 + scrpad_addr); + + + if ((*(unsigned short *)(scrpad_addr + 0x1fe) & 1)) + { + bVar1 = 0; + + if (n0[0x80] >> 24) + { + bVar1 = n0[0x80] >> 24 & n1[0x80] >> 24 & n2[0x80] >> 24 & n3[0x80] >> 24; + } + + if (bVar1) + { + continue; + } + } + + gte_ldsxy3(*n0, *n1, *n2); + gte_nclip(); + + LCOPY(n3, &packs->x3); + + gte_stopz((int *)(SCRPAD_ADDR + 0x1f8)); + gte_stsxy3_gt3(&packs->tag); + + area = *(int *)(scrpad_addr + 0x1f8); + + if (area <= 0) + { + if (!(*(short *)(scrpad_addr + 0x1fc)) || area == 0) + { + continue; + } + + uVar7 = (((-area) << 8) & uVar8) | 0x10000; + } + else + { + uVar7 = area << 8 & uVar8; + } + + uVar7 |= (((int) (n0[0x80] + n3[0x80])) / 2) & 0xffff; + } + + return packs; +} + +static inline void DG_BoundCheck( DVECTOR *vert ) +{ + int clip; + int val; + + clip = 0; + + val = vert->vx; + if ( val < -160 ) + { + clip = 1; + } + else if ( val > 160 ) + { + clip = 2; + } + + val = vert->vy; + if ( val < -112 ) + { + clip |= 4; + } + else if ( val > 112 ) + { + clip |= 8; + } + + *((char *)(vert + 128) + 3) = clip; +} + +static inline void DG_TransVerticesBound( DG_PVECTOR *verts, int n_verts ) +{ + SCRATCH *scratch; + DVECTOR *xy; + DVECTOR *zf; + int vert_count; + int xy0, z0; + int xy1, z1; + int xy2, z2; + + scratch = (SCRATCH *)getScratchAddr(0); + xy = scratch->xy; + zf = scratch->zf; + + gte_ldv3c(verts); + + verts += 3; + vert_count = n_verts - 3; + + gte_rtpt_b(); + + xy0 = verts[0].vxy; + z0 = verts[0].vz; + + xy1 = verts[1].vxy; + z1 = verts[1].vz; + + xy2 = verts[2].vxy; + z2 = verts[2].vz; + + gte_stsxy3c(xy); + gte_stsz3c(zf); + + xy += 3; + zf += 3; + + while ( vert_count > 0 ) + { + gte_ldVXYZ0(xy0, z0); + gte_ldVXYZ1(xy1, z1); + gte_ldVXYZ2(xy2, z2); + + verts += 3; + vert_count -= 3; + + gte_rtpt_b(); + + DG_BoundCheck( xy - 3 ); + DG_BoundCheck( xy - 2 ); + DG_BoundCheck( xy - 1 ); + + xy0 = verts[0].vxy; + z0 = verts[0].vz; + + xy1 = verts[1].vxy; + z1 = verts[1].vz; + + xy2 = verts[2].vxy; + z2 = verts[2].vz; + + gte_stsxy3c(xy); + gte_stsz3c(zf); + + xy += 3; + zf += 3; + } + + DG_BoundCheck( xy - 3 ); + DG_BoundCheck( xy - 2 ); + DG_BoundCheck( xy - 1 ); +} + +static inline void DG_TransVertices( DG_PVECTOR *verts, int n_verts ) +{ + SCRATCH *scratch; + DVECTOR *xy; + DVECTOR *zf; + int vert_count; + int xy0, z0; + int xy1, z1; + int xy2, z2; + + scratch = (SCRATCH *)getScratchAddr(0); + xy = scratch->xy; + zf = scratch->zf; + + gte_ldv3c(verts); + + verts += 3; + vert_count = n_verts; + + gte_rtpt_b(); + + xy0 = verts[0].vxy; + z0 = verts[0].vz; + + xy1 = verts[1].vxy; + z1 = verts[1].vz; + + xy2 = verts[2].vxy; + z2 = verts[2].vz; + + gte_stsxy3c(xy); + gte_stsz3c(zf); + + xy += 3; + zf += 3; + + vert_count -= 3; + + while ( vert_count > 0 ) + { + gte_ldVXYZ0(xy0, z0); + gte_ldVXYZ1(xy1, z1); + gte_ldVXYZ2(xy2, z2); + + verts += 3; + vert_count -= 3; + + gte_rtpt_b(); + + xy0 = verts[0].vxy; + z0 = verts[0].vz; + + xy1 = verts[1].vxy; + z1 = verts[1].vz; + + xy2 = verts[2].vxy; + z2 = verts[2].vz; + + gte_stsxy3c(xy); + gte_stsz3c(zf); + + xy += 3; + zf += 3; + } +} + +STATIC void DG_TransObj( DG_OBJ *obj, int idx ) +{ + POLY_GT4 *packs; + DG_MDL *model; + DG_PVECTOR *vertices; + + packs = obj->packs[idx]; + + while ( obj ) + { + model = obj->model; + vertices = (DG_PVECTOR *)model->vertices; + + if ( SPAD->flags & 0x1 ) + { + DG_TransVerticesBound( vertices, model->n_verts ); + } + else + { + DG_TransVertices( vertices, model->n_verts ); + } + + SPAD->bothface = model->flags & DG_MODEL_BOTHFACE; + + packs = DG_WriteObjVertices( ( unsigned int * ) model->vindices, packs, obj->n_packs ); + obj = obj->extend; + } +} + +void DG_TransChanl( DG_CHANL *chanl, int idx ) +{ + SCRATCH *work; + DG_OBJS **queue; + int n_objects; + DG_OBJS *objs; + DG_OBJ *obj; + int no_gbound; + int n_models; + DG_MDL *model; + DG_OBJ *parent; + + work = (SCRATCH *)getScratchAddr(0); + + DG_Clip(&chanl->clip_rect, chanl->clip_distance); + + queue = (DG_OBJS **)chanl->queue; +#ifdef __psyz + { + /* how many queued objects actually survive the bound tests? */ + extern unsigned mgs_dbg_objs_seen, mgs_dbg_objs_culled, + mgs_dbg_models_culled, mgs_dbg_models_drawn; + mgs_dbg_objs_seen += chanl->objs_index; + } +#endif + for (n_objects = chanl->objs_index; n_objects > 0; n_objects--) + { + objs = *queue++; + + if (objs->bound_mode == 0) + { +#ifdef __psyz + { extern unsigned mgs_dbg_objs_culled; mgs_dbg_objs_culled++; } +#endif + continue; + } + + obj = objs->objs; + no_gbound = !(objs->flag & DG_FLAG_GBOUND); + + for (n_models = objs->n_models; n_models > 0; obj++, n_models--) + { + if (obj->bound_mode == 0) + { +#ifdef __psyz + { extern unsigned mgs_dbg_models_culled; + mgs_dbg_models_culled++; } +#endif + continue; + } +#ifdef __psyz + { extern unsigned mgs_dbg_models_drawn; mgs_dbg_models_drawn++; } +#endif + + model = obj->model; + parent = &objs->objs[model->parent]; + + +#ifdef __psyz + /* The rest of the pipeline addresses packs by the pipeline's own + * buffer index; only this line used GV_Clock. They agree on the + * console because the flip and the render are locked in step, but + * here a skeleton child could read its parent's transformed + * vertices out of the OTHER buffer -- last frame's pose, or an + * arena already recycled -- which projects the child's bounding + * box somewhere wrong and the visibility test drops the limb. */ + work->parent_packs = parent->packs[idx]; +#else + work->parent_packs = parent->packs[GV_Clock]; +#endif + work->vertices = model->vertices; + + gte_SetRotMatrix(&obj->screen); + gte_SetTransMatrix(&obj->screen); + + work->flags = no_gbound && obj->bound_mode == 1; + if (objs->flag & DG_FLAG_SHADE) + { + work->flags |= 4; + } + + DG_TransObj(obj, idx); + } + } +} + +void DG_TransEnd( void ) +{ + /* do nothing */ +} diff --git a/components/mgs/mgs/source/libfs/cdbios.c b/components/mgs/mgs/source/libfs/cdbios.c new file mode 100644 index 00000000..9369bad5 --- /dev/null +++ b/components/mgs/mgs/source/libfs/cdbios.c @@ -0,0 +1,382 @@ +#include +#include +#include +#include + +/* __psyz_cd_rename: off-console there is no drive to read. port/virtual_cd.c + * supplies these over ordinary files; the disc versions keep compiling so the + * real ISO9660 layout stays documented, but nothing links against them. */ +#ifdef __psyz +#define CDBIOS_Reset CDBIOS_Reset_disc +#define CDBIOS_ReadRequest CDBIOS_ReadRequest_disc +#define CDBIOS_ReadSync CDBIOS_ReadSync_disc +#endif + + +#include "common.h" +#include "mts/mts.h" +#include "mts/taskid.h" +#include "libfs.h" + +extern CDBIOS_TASK cd_bios_task_800B4E58; + +#define CDBIOS_STACK_SIZE 1024 +extern char cd_bios_stack_800B4E88[CDBIOS_STACK_SIZE]; + +STATIC int cdbios_next_state = CDBIOS_STATE_INVALID; +STATIC int cdbios_start_flag = 0; +STATIC int cdbios_stop_flag = 0; + +static inline u_long loc_to_int( CdlLOC *loc ) +{ + return (btoi(loc->minute) * (60 * 75) + + btoi(loc->second) * 75 + + btoi(loc->sector)); +} + +static inline void int_to_loc( int pos, CdlLOC *loc ) +{ + int seconds; + + seconds = pos / 75; + loc->sector = itob(pos % 75); + loc->second = itob(seconds % 60); + loc->minute = itob(seconds / 60); +} + +int CDBIOS_Reset(void) +{ + int retries; + unsigned char params[8]; + + CDBIOS_TaskStart(); + + for (retries = 4; retries >= 0; retries--) + { + if (CdInit()) + { + goto success; + } + } + + return 0; + +success: + params[0] = CdlModeSpeed | CdlModeSize1; + while (!CdControl(CdlSetmode, params, 0)); + + mts_wait_vbl(3); + return 1; +} + +static void CDBIOS_Stop(void) +{ + CdReadyCallback(0); + CdSyncCallback(0); + CdFlush(); + CdControl(CdlPause, 0, 0); + cdbios_next_state = CDBIOS_STATE_IDLE; +} + +static void CDBIOS_Error(void) +{ + if (cd_bios_task_800B4E58.ticks == 0) + { + cd_bios_task_800B4E58.ticks = mts_get_tick_count(); + } + + cdbios_next_state = CDBIOS_STATE_ERROR; + CdReadyCallback(0); + CdSyncCallback(0); + CdFlush(); +} + +static void CDBIOS_SyncCallback(u_char status, u_char *result) +{ + /* do nothing */ +} + +static void CDBIOS_ReadyCallback(u_char status, u_char *result) +{ + int sector; + int callback_status; + unsigned int size; + CDBIOS_TASK *task; + CdlLOC loc[3]; + + if (cdbios_stop_flag != 0) + { + CDBIOS_Stop(); + return; + } + + if (status == 1) + { + task = &cd_bios_task_800B4E58; + + CdGetSector(loc, 3); + + sector = loc_to_int(&loc[0]); + + if (sector < task->sector) + { + return; + } + + if (sector > task->sector) + { + printf("CDFS: skip error %d %d %d\n", task->ticks, sector, task->sector); + } + else + { + size = 0x200; + + if (task->remaining <= 512u) + { + size = task->remaining; + } + + if (task->buffer) + { + CdGetSector(task->buffer, size); + } + + task->remaining -= size; + task->buffer_size = size; + + if (task->callback) + { + callback_status = task->callback(&cd_bios_task_800B4E58); + + if (callback_status == 0) + { + CDBIOS_Stop(); + } + else if (callback_status == 2) + { + size = 0; + } + } + + if (task->buffer) + { + task->buffer = (int *)task->buffer + size; + } + + task->sector++; + task->sectors_delivered++; + task->ticks = 0; + + if (task->remaining <= 0) + { + CDBIOS_Stop(); + } + + return; + } + } + + CDBIOS_Error(); +} + +static void CDBIOS_Main(void) +{ + CdlLOC loc; + u_char result[8]; + u_char param[3]; + + CDBIOS_TASK *task; + + int last_ticks; + int last_sector; + int failed_reads; + int ticks; + + mts_set_vsync_task(); + + task = &cd_bios_task_800B4E58; + + last_ticks = 0; + last_sector = 0; + failed_reads = 0; + + while (1) + { + ticks = mts_get_tick_count(); + + if (cdbios_stop_flag != 0) + { + cdbios_stop_flag = 0; + CDBIOS_Stop(); + } + + if (cdbios_next_state >= 0) + { + task->state = cdbios_next_state; + cdbios_next_state = CDBIOS_STATE_INVALID; + } + + if (cdbios_start_flag != 0) + { + task->state = CDBIOS_STATE_START; + cdbios_start_flag = 0; + cdbios_stop_flag = 0; + cdbios_next_state = CDBIOS_STATE_INVALID; + } + + switch (task->state) + { + case CDBIOS_STATE_IDLE: + break; + + case CDBIOS_STATE_START: + last_sector = task->sector; + failed_reads = 0; + + int_to_loc(task->sector, &loc); + + CdFlush(); + CdSyncCallback(&CDBIOS_SyncCallback); + CdReadyCallback(&CDBIOS_ReadyCallback); + + task->ticks = 0; + task->state = CDBIOS_STATE_READ; + + if (!CdControl(CdlReadN, (u_char *)&loc, result)) + { + CDBIOS_Error(); + } + + last_ticks = ticks; + break; + + case CDBIOS_STATE_READ: + if (last_sector != task->sector) + { + last_ticks = ticks; + last_sector = task->sector; + } + else if ((ticks - last_ticks) > 500) + { + CDBIOS_Error(); + printf("[T]"); + } + break; + + case CDBIOS_STATE_ERROR: + printf("."); + + CdFlush(); + CdReadyCallback(NULL); + CdSyncCallback(NULL); + + if ((ticks - task->ticks) > 800) + { + if (CdReset(0)) + { + param[0] = CdlModeSpeed | CdlModeSize1; + + if (CdControl(CdlSetmode, param, NULL)) + { + mts_wait_vbl(1); + mts_wait_vbl(3); + task->ticks = mts_get_tick_count(); + } + else + { + break; + } + } + else + { + break; + } + } + + CdFlush(); + + if ((failed_reads % 4) < 3) + { + int_to_loc(task->sector, &loc); + } + else + { + int_to_loc(task->sector - 4, &loc); + } + + CdFlush(); + CdSyncCallback(&CDBIOS_SyncCallback); + CdReadyCallback(&CDBIOS_ReadyCallback); + + if (cdbios_stop_flag == 0) + { + task->state = CDBIOS_STATE_READ; + + if (!CdControl(CdlReadN, (u_char *)&loc, NULL)) + { + failed_reads++; + task->state = CDBIOS_STATE_ERROR; + } + else + { + last_sector = task->sector; + last_ticks = mts_get_tick_count(); + } + } + break; + + default: + printf("[%d]", task->state); + break; + } + + mts_wait_vbl(1); + } +} + +void CDBIOS_TaskStart(void) +{ + cd_bios_task_800B4E58.state = CDBIOS_STATE_IDLE; + + cdbios_next_state = CDBIOS_STATE_INVALID; + cdbios_stop_flag = 0; + + mts_start_task(MTSID_CDBIOS, CDBIOS_Main, STACK_BOTTOM(cd_bios_stack_800B4E88), CDBIOS_STACK_SIZE); +} + +void CDBIOS_ReadRequest(void *buffer, unsigned int sector, unsigned int size, void *callback) +{ + cd_bios_task_800B4E58.buffer = buffer; + + if (size == 0) + { + size = 0x7fff0000; + } + + cd_bios_task_800B4E58.remaining = (size + 3) >> 2; + cd_bios_task_800B4E58.size = (size + 3) >> 2; + cd_bios_task_800B4E58.sector = sector; + cd_bios_task_800B4E58.callback = callback; + cd_bios_task_800B4E58.sectors_delivered = 0; + + cdbios_stop_flag = 0; + cdbios_start_flag = 1; +} + +int CDBIOS_ReadSync(void) +{ + return cd_bios_task_800B4E58.remaining * 4; +} + +void CDBIOS_ForceStop(void) +{ + if (cd_bios_task_800B4E58.state != CDBIOS_STATE_IDLE) + { + cdbios_stop_flag = 1; + } +} + +int CDBIOS_TaskState(void) +{ + return cd_bios_task_800B4E58.state; +} diff --git a/components/mgs/mgs/source/libfs/cdbios.h b/components/mgs/mgs/source/libfs/cdbios.h new file mode 100644 index 00000000..d889578b --- /dev/null +++ b/components/mgs/mgs/source/libfs/cdbios.h @@ -0,0 +1,35 @@ +#ifndef __MGS_CDBIOS_H__ +#define __MGS_CDBIOS_H__ + +struct _CDBIOS_TASK; +typedef int (*cdbios_task_pfn)(struct _CDBIOS_TASK *); + +typedef struct _CDBIOS_TASK { + int state; + int sector; + void *buffer; + int buffer_size; + int ticks; + int sectors_delivered; + int size; + int remaining; + cdbios_task_pfn callback; +} CDBIOS_TASK; + +enum CDBIOS_STATE { + CDBIOS_STATE_INVALID = -1, + CDBIOS_STATE_IDLE, + CDBIOS_STATE_START, + CDBIOS_STATE_READ, + CDBIOS_STATE_ERROR, +}; + +/* cdbios.c */ +int CDBIOS_Reset(void); +void CDBIOS_TaskStart(void); +void CDBIOS_ReadRequest(void *buffer, unsigned int sector, unsigned int size, void *callback); +int CDBIOS_ReadSync(void); +void CDBIOS_ForceStop(void); +int CDBIOS_TaskState(void); + +#endif // __MGS_CDBIOS_H__ diff --git a/components/mgs/mgs/source/libfs/cdstage.c b/components/mgs/mgs/source/libfs/cdstage.c new file mode 100644 index 00000000..1750eaf8 --- /dev/null +++ b/components/mgs/mgs/source/libfs/cdstage.c @@ -0,0 +1,159 @@ +#include "libfs.h" + +#include +#include +#include +#include + +#include "common.h" +#include "mts/mts.h" // for mts_wait_vbl +#include "libgv/libgv.h" // for GV_xxxMemory + +/*---------------------------------------------------------------------------*/ + +#define FS_DIRNAME_MAX 8 +#define FS_STAGEDIR_MAX 170 // ((FS_SECTOR_SIZE - 4) / sizeof(FS_DIR_ENTRY)) + +typedef struct _FS_DIR_ENTRY { + char name[FS_DIRNAME_MAX]; // ASCII stage name + int offset; // in number of sectors +} FS_DIR_ENTRY; + +typedef struct _FS_DIR_INFO { + int pos; // LBA position of the stage archive + int table_size; // size of the directory table + int n_stages; // number of stage directories + FS_DIR_ENTRY *table_buf; // in-memory copy of the dir table +} FS_DIR_INFO; + +STATIC FS_DIR_INFO fs_dir_info = {}; + +// NOTE: This code registers a single DIR-format archive and keeps it for +// the full duration of the game's runtime. This is only cleared at shutdown, +// since there are no deinitialization routines for LibFS's position tables. +// +// Theoretically, if there were more than one DIR-format archives present on +// the CD-ROM, the game could dynamically select which to register at startup, +// however, the program is currently hard-coded to load only "STAGE.DIR". + +/*---------------------------------------------------------------------------*/ + +static int CdStageReadCallback(CDBIOS_TASK *task) +{ + unsigned int size, rounded; + + if (task->sectors_delivered == 0) + { + size = *(unsigned int *)task->buffer; + rounded = (size + 3) / 4; + + task->size = rounded; + task->remaining = rounded - 512; + + fs_dir_info.table_size = size; + } + return 1; +} + +/** + * @brief Registers the DIR archive to read from. + * + * @note Because the dir entry table is expected to fit within + * the first 2048 bytes (1 sector) of the file, archives + * can contain a maximum of 170 stage entries. + * + * Therefore, the the maximum size of the entry table is + * 2040 bytes (sizeof(FS_DIR_ENTRY) * FS_STAGEDIR_MAX). + * + * @param[out] buffer temporary work buffer + * @param[in] sector starting LBA position of the DIR archive + */ +void FS_CdStageFileInit(void *buffer, int sector) +{ + int table_size; + + fs_dir_info.pos = sector; + CDBIOS_ReadRequest(buffer, sector, FS_SECTOR_SIZE, &CdStageReadCallback); + + while (CDBIOS_ReadSync() > 0) + { + mts_wait_vbl(1); + } + + table_size = fs_dir_info.table_size; + + if (fs_dir_info.table_buf == NULL) + { + fs_dir_info.table_buf = GV_AllocResidentMemory(table_size); + } + + printf("%X %X %d\n", + (unsigned int)buffer + 4, // src address + (unsigned int)fs_dir_info.table_buf, // dest address + table_size); // size + + GV_CopyMemory((char *)buffer + 4, fs_dir_info.table_buf, table_size); + + fs_dir_info.n_stages = table_size / sizeof(FS_DIR_ENTRY); + +#ifdef __psyz + { + /* The whole stage system hangs off this table: if it comes out empty + * every FS_LoadStageRequest answers "NOT FOUND" and the game idles + * forever with nothing to load. Print what was actually parsed. */ + int k; + printf("[dir] table_size %d -> %d stages, base sector %d\n", + table_size, fs_dir_info.n_stages, fs_dir_info.pos); + for (k = 0; k < fs_dir_info.n_stages && k < 8; k++) + { + printf("[dir] %-8.8s at %d\n", fs_dir_info.table_buf[k].name, + fs_dir_info.table_buf[k].offset); + } + } +#endif +} + +/** + * Gets the starting offset (logical block address) of a requested + * stage block within the registered DIR archive. + * + * @param dirname name of the requested stage + * + * @return LBA position of the requested stage block. + * Returns -1 if the stage is not found. + */ +int FS_CdGetStageFileTop(char *dirname) +{ + FS_DIR_ENTRY *dir; + int i; + + dir = fs_dir_info.table_buf; + +#ifdef __psyz + /* The table parses correctly at init time, so if a lookup still fails the + * interesting question is what the table looks like NOW -- the resident + * heap it lives in is rewound by the game manager after FS starts up. */ + { + int k; + if (0) printf("[dir?] want '%s' (%d,%d,%d,%d) among %d at %p\n", dirname, + dirname[0], dirname[1], dirname[2], dirname[3], + fs_dir_info.n_stages, (void*)dir); + for (k = 0; k < fs_dir_info.n_stages && k < 8; k++) + { + if (0) printf("[dir?] [%d] '%-8.8s' (%d,%d,%d,%d) at %d\n", k, + dir[k].name, dir[k].name[0], dir[k].name[1], dir[k].name[2], + dir[k].name[3], dir[k].offset); + } + } +#endif + + for (i = fs_dir_info.n_stages; i > 0; i--) + { + if (strncmp(dir->name, dirname, FS_DIRNAME_MAX) == 0) + { + return dir->offset + fs_dir_info.pos; + } + dir++; + } + return -1; +} diff --git a/components/mgs/mgs/source/libfs/datacnf.h b/components/mgs/mgs/source/libfs/datacnf.h new file mode 100644 index 00000000..9165a3f5 --- /dev/null +++ b/components/mgs/mgs/source/libfs/datacnf.h @@ -0,0 +1,29 @@ +#ifndef __MGS_DATACNF_H__ +#define __MGS_DATACNF_H__ + +#include + +typedef struct _DATACNF_TAG +{ + u_short id; // basename strcode + char mode; // memory region + char ext; // filename extension + int size; // size in bytes +} DATACNF_TAG; + +typedef struct _DATACNF +{ + char version; // always 0x01 + char pad; // + short size; // entire stage block (in number of CD-ROM sectors) + DATACNF_TAG tags[0]; +} DATACNF; + +typedef struct _DARFILE_TAG +{ + u_short id; // basename strcode + short ext; // filename extension + int size; // size in bytes +} DARFILE_TAG; + +#endif // __MGS_DATACNF_H__ diff --git a/components/mgs/mgs/source/libfs/file.cnf b/components/mgs/mgs/source/libfs/file.cnf new file mode 100644 index 00000000..6b9ea824 --- /dev/null +++ b/components/mgs/mgs/source/libfs/file.cnf @@ -0,0 +1,15 @@ +#ifndef __MGS_FILE_CNF__ +#define __MGS_FILE_CNF__ + +FS_FILE_INFO fs_file_info[] = { + { "STAGE.DIR", 0 }, // 0 + { "RADIO.DAT", 0 }, // 1 + { "FACE.DAT", 0 }, // 2 + { "ZMOVIE.STR", 0 }, // 3 + { "VOX.DAT", 0 }, // 4 + { "DEMO.DAT", 0 }, // 5 + { "BRF.DAT", 0 }, // 6 + { NULL, 0 } +}; + +#endif // __MGS_FILE_CNF__ diff --git a/components/mgs/mgs/source/libfs/fscd.c b/components/mgs/mgs/source/libfs/fscd.c new file mode 100644 index 00000000..cc3573aa --- /dev/null +++ b/components/mgs/mgs/source/libfs/fscd.c @@ -0,0 +1,84 @@ +#include "libfs.h" + +#include +#include "common.h" +#include "mts/mts.h" // for mts_wait_vbl +#include "libgv/libgv.h" // for GV_xxx_MEMORY_TOP + +extern int FS_DiskNum; + +#include "file.cnf" // defines fs_file_info + +int FS_ResetCdFilePosition(void *buffer) +{ + int disk_num = FS_CdMakePositionTable(buffer, fs_file_info); + printf("Position end\n"); + if (disk_num >= 0) + { + printf("DISK %d\n", disk_num); + FS_CdStageFileInit(buffer, fs_file_info[FS_FILEID_STAGE].pos); +#ifdef __psyz + /* With ZMOVIE.STR absent the index reads as zeroes, and the table + * FS_MovieFileInit then builds lands straight on top of the stage + * table -- its 8-byte records overwrote every other 12-byte stage + * entry, which is why "init" was in the table at boot and gone by the + * time anything looked for it. No movie file, no movie index. */ + { + extern int Mgs_CdFilePresent(int fileid); + if (Mgs_CdFilePresent(FS_FILEID_ZMOVIE)) + { + FS_MovieFileInit(buffer, fs_file_info[FS_FILEID_ZMOVIE].pos); + } + else + { + printf("[cd] no ZMOVIE.STR: skipping the movie index\n"); + } + } +#else + FS_MovieFileInit(buffer, fs_file_info[FS_FILEID_ZMOVIE].pos); +#endif + } + else + { + printf("illegal DISK\n"); + } + return disk_num; +} + +void FS_CDInit(void) +{ +#ifdef __psyz + printf( "[cd] CDBIOS_Reset\n" ); + CDBIOS_Reset(); + printf( "[cd] ResetCdFilePosition\n" ); + FS_DiskNum = FS_ResetCdFilePosition(MEM_ADDR); + printf( "[cd] disk %d, StreamCD\n", FS_DiskNum ); + FS_StreamCD(); + printf( "[cd] StreamTaskInit\n" ); + FS_StreamTaskInit(); + printf( "[cd] wait_vbl\n" ); + mts_wait_vbl(2); + printf( "[cd] done\n" ); +#else + CDBIOS_Reset(); + FS_DiskNum = FS_ResetCdFilePosition(MEM_ADDR); + FS_StreamCD(); + FS_StreamTaskInit(); + mts_wait_vbl(2); +#endif +} + +void FS_LoadFileRequest(int fileno, int offset, int size, void *buffer) +{ + CDBIOS_ReadRequest(buffer, fs_file_info[fileno].pos + offset, size, NULL); +} + +int FS_LoadFileSync(void) +{ + return CDBIOS_ReadSync(); +} + +void MakeFullPath(char *name, char *buffer) +{ + /* do nothing */ +} diff --git a/components/mgs/mgs/source/libfs/fshd.c b/components/mgs/mgs/source/libfs/fshd.c new file mode 100644 index 00000000..cfd21bb3 --- /dev/null +++ b/components/mgs/mgs/source/libfs/fshd.c @@ -0,0 +1,44 @@ +// This module isn't linked into CD-ROM builds, as it's an alternate +// implementation of fscd.c for hard disk builds (only used in development +// and the Win32 port by Digital Dialect). +// +// I'm assuming conditional compilation in select.c determines whether +// FS_CDInit or FS_HDInit is called by FS_StartDaemon and thus the linker +// discards either fshd.obj or fscd.obj, respectively. +// +// It's known to contain the following public functions: +// ------------------------------------------------------------- +// FS_LoadFileRequest +// FS_LoadFileSync +// FS_ResetCdFilePosition +// FS_HDInit +// +// NOTE: These labels were recovered from a memleak'd linker map fragment. +// Symbols were listed by address order, which is reflected above. +// + +#if 0 +#include "file.cnf" + +void FS_LoadFileRequest( int fileno, int offset, int size, void *buffer ) +{ + // todo: implement +} + +int FS_LoadFileSync( void ) +{ + // todo: implement + return 0; +} + +int FS_ResetCdFilePosition( void *ptr ) +{ + // todo: implement + return -1; +} + +void FS_HDInit( void ) +{ + // todo: implement +} +#endif diff --git a/components/mgs/mgs/source/libfs/libfs.h b/components/mgs/mgs/source/libfs/libfs.h new file mode 100644 index 00000000..831c33b6 --- /dev/null +++ b/components/mgs/mgs/source/libfs/libfs.h @@ -0,0 +1,122 @@ +#ifndef __MGS_LIBFS_H__ +#define __MGS_LIBFS_H__ + +#include +#include "cdbios.h" +#include "datacnf.h" + +#define FS_SECTOR_SIZE 2048 // x1 CD-ROM sector +#define FS_CDLOAD_BUF_SIZE ( FS_SECTOR_SIZE * 48 ) + +typedef struct _FS_STAGE_INFO // private to stageld.c +{ + int mode; + CDBIOS_TASK *task; + void *buffer; + DATACNF *datacnf; + void *tags; + DATACNF_TAG *tag_start1; // todo: rename this + DATACNF_TAG *tag_end1; // todo: rename this + DATACNF_TAG *tag_start2; // todo: rename this + DATACNF_TAG *tag_end2; // todo: rename this + int size; + int field_28; + DATACNF_TAG *tag; + void *current_ptr; + int remaining; +} FS_STAGE_INFO; + +typedef struct _FS_FILE_INFO +{ + const char *name; + u_int pos; +} FS_FILE_INFO; + +typedef struct _FS_MOVIE_FILE +{ + unsigned short id; + unsigned short frame; + int pos; +} FS_MOVIE_FILE; + +typedef struct _FS_MEMFILE +{ + int id; + int *ptr; + int size; +} FS_MEMFILE; + +typedef char * (*TFsCallback)(char *); +typedef void (*TFsSoundCallback)(void); + +/* select.c */ +void FS_StartDaemon(void); +void FS_CdStageProgBinFix(void); + +/* fscd.c */ +extern FS_FILE_INFO fs_file_info[]; /* in file.cnf */ + +// NOTE: fs_file_info actually has FS_MAX_FILEID+1 elements, +// but the last is a dummy entry to denote the end of the table. + +#define FS_FILEID_STAGE (0) // STAGE.DIR +#define FS_FILEID_RADIO (1) // RADIO.DAT +#define FS_FILEID_FACE (2) // FACE.DAT +#define FS_FILEID_ZMOVIE (3) // ZMOVIE.STR +#define FS_FILEID_VOX (4) // VOX.DAT +#define FS_FILEID_DEMO (5) // DEMO.DAT +#define FS_FILEID_BRF (6) // BRF.DAT +#define FS_MAX_FILEID (7) + +int FS_ResetCdFilePosition(void *buffer); +void FS_CDInit(void); +void FS_LoadFileRequest(int fileno, int offset, int size, void *buffer); +int FS_LoadFileSync(void); +void MakeFullPath(char *name, char *buffer); + +/* srchfile.c */ +int FS_CdMakePositionTable(char *buffer, FS_FILE_INFO *finfo); + +/* cdstage.c */ +void FS_CdStageFileInit(void *buffer, int sector); +int FS_CdGetStageFileTop(char *filename); + +/* stageld.c */ +void *FS_LoadStageRequest(const char *dirname); +int FS_LoadStageSync(void *info); +void FS_LoadStageComplete(void *info); + +/* movie.c */ +void FS_MovieFileInit(void *buffer, int sector); +FS_MOVIE_FILE *FS_GetMovieInfo( unsigned int to_find ); + +/* memfile.c */ +void FS_EnableMemfile( int read, int write ); +void FS_ClearMemfile( void ); +int FS_WriteMemfile( int id, int **buf_ptr, int size ); +int FS_ReadMemfile( int id, int **buf_ptr ); + +/* stream.c */ +void FS_StreamTaskStart(int sector); +int FS_StreamTaskState(void); +void FS_StreamTaskInit(void); +int FS_StreamSync(void); +void FS_StreamCD(void); +int FS_StreamGetTop(int is_demo); +int FS_StreamInit(void *pHeap, int heapSize); +void FS_StreamStop(void); +void FS_StreamOpen(void); +void FS_StreamClose(void); +int FS_StreamIsEnd(void); +void *FS_StreamGetData(int target_type); +int FS_StreamGetSize(void *stream); +void FS_StreamUngetData(void *stream); +void FS_StreamClear(void *stream); +void FS_StreamClearType( void *stream, int target_type ); +int FS_StreamGetEndFlag(void); +int FS_StreamIsForceStop(void); +void FS_StreamTickStart(void); +void FS_StreamSoundMode(void); +int FS_StreamGetTick(void); + +#endif // __MGS_LIBFS_H__ diff --git a/components/mgs/mgs/source/libfs/memfile.c b/components/mgs/mgs/source/libfs/memfile.c new file mode 100644 index 00000000..3023a6db --- /dev/null +++ b/components/mgs/mgs/source/libfs/memfile.c @@ -0,0 +1,94 @@ +#include "libfs.h" +#include +#include + +extern int dword_800BDFB8; +extern int dword_800BDFBC; +extern FS_MEMFILE *dword_800BDFC0; +extern int dword_800BDFC4; + +#define FS_MEMFILE_BASE ((FS_MEMFILE *)0x80700000) + +void FS_EnableMemfile( int read, int write ) +{ + dword_800BDFB8 = read; // cache_read_enable + dword_800BDFBC = write; // clear_cache_buffer + + if (write != 0) + { + printf("Cache Buffer Cleared\n"); + dword_800BDFC4 = 0x80700c00; + // Debug build address + FS_MEMFILE_BASE->id = 0; + dword_800BDFC0 = FS_MEMFILE_BASE; + } + + if (read) + { + printf("Cache Read Enable\n"); + } +} + +void FS_ClearMemfile( void ) +{ + // Debug build address + FS_MEMFILE_BASE->id = 0; +} + +int FS_WriteMemfile( int id, int **buf_ptr, int size ) +{ + int ptr; + int ptr_off; + FS_MEMFILE *memfile; + + *buf_ptr = NULL; + + if (dword_800BDFBC != 0) + { + memfile = dword_800BDFC0; + ptr = dword_800BDFC4; + + size = (size + 16) / 16 * 16; + ptr_off = ptr + size; + if (ptr_off <= 0x807FFFFF && memfile - FS_MEMFILE_BASE < 256) + { + memfile->id = id; + memfile->ptr = (int *)ptr; + memfile->size = size; + + *buf_ptr = (int *)ptr; + dword_800BDFC0 = memfile + 1; + dword_800BDFC0->id = 0; + dword_800BDFC4 = ptr_off; + return 1; + } + } + + return 0; +} + +int FS_ReadMemfile( int id, int **buf_ptr ) +{ + FS_MEMFILE *memfile; + int idtemp; + + *buf_ptr = NULL; + memfile = FS_MEMFILE_BASE; + + if (dword_800BDFB8 && memfile->id) + { + do + { + idtemp = memfile->id; + if (idtemp == id) + { + *buf_ptr = memfile->ptr; + return memfile->size; + } + memfile++; + idtemp = memfile->id; + } while (idtemp != 0); + } + + return 0; +} diff --git a/components/mgs/mgs/source/libfs/movie.c b/components/mgs/mgs/source/libfs/movie.c new file mode 100644 index 00000000..9cbc41f6 --- /dev/null +++ b/components/mgs/mgs/source/libfs/movie.c @@ -0,0 +1,75 @@ +#include "libfs.h" + +#include +#include "common.h" +#include "libgv/libgv.h" +#include "mts/mts.h" + +typedef struct _FS_MOVIE_INFO +{ + int tablenum; + FS_MOVIE_FILE files[8]; +} FS_MOVIE_INFO; + +STATIC FS_MOVIE_INFO *fs_movie_info = NULL; + +void FS_MovieFileInit(void *buffer, int sector) +{ + int count; + FS_MOVIE_FILE *file; + + if (sector < 150) + { + printf("movie file is wrong\n"); + fs_movie_info = 0; + return; + } + + CDBIOS_ReadRequest(buffer, sector, FS_SECTOR_SIZE, NULL); + + while (CDBIOS_ReadSync() > 0) + { + mts_wait_vbl(1); + } + + printf("MOVIE NUM %d\n", *(int *)buffer); + + if (!fs_movie_info) + { + fs_movie_info = GV_AllocResidentMemory(sizeof(FS_MOVIE_INFO)); + } + + GV_CopyMemory(buffer, fs_movie_info, sizeof(FS_MOVIE_INFO)); + printf("tablenum %d size %d\n", fs_movie_info->tablenum, sizeof(FS_MOVIE_INFO)); + + file = fs_movie_info->files; + + for (count = fs_movie_info->tablenum; count > 0; count--) + { + file->pos += sector; + printf("id %d frame %d pos %d\n", file->id, file->frame, file->pos); + file++; + } +} + +FS_MOVIE_FILE *FS_GetMovieInfo( unsigned int to_find ) +{ + FS_MOVIE_FILE *file; + int count; + + if ( fs_movie_info != 0 ) + { + count = fs_movie_info->tablenum; + file = fs_movie_info->files; + while ( count > 0 ) + { + count--; + if ( file->id == to_find ) + { + return file; + } + file++; + } + } + return NULL; +} diff --git a/components/mgs/mgs/source/libfs/select.c b/components/mgs/mgs/source/libfs/select.c new file mode 100644 index 00000000..50cde798 --- /dev/null +++ b/components/mgs/mgs/source/libfs/select.c @@ -0,0 +1,72 @@ +#include "libfs.h" + +#include +#include +#include +#include + +#include "common.h" +#include "libsio/isio.h" + +void FS_StartDaemon( void ) +{ +#ifdef __psyz + printf( "[fsd] SetMem\n" ); + SetMem(2); + printf( "[fsd] FS_CDInit\n" ); + FS_CDInit(); + printf( "[fsd] sio_output_stop\n" ); + sio_output_stop(); + printf( "[fsd] done\n" ); +#else + SetMem(2); // Set effective memory to 2MB (DTL-H2X00 has 8MB) + FS_CDInit(); // Init CD read system + sio_output_stop(); +#endif +} + +void FS_CdStageProgBinFix( void ) +{ + /* do nothing */ +} + +CdlCB CdReadCallback( CdlCB func ) +{ + return NULL; +} + +CdlCB CdReadMode( CdlCB func ) +{ + /* do nothing */ +} + +typedef void ( *DslCB )( u_char, u_char* ); +DslCB DsReadyCallback( DslCB func ) +{ + printf( "DsReadyCallback %x\n", (u_int)func ); +} + +void DsDataCallback( void (*func)() ) +{ + printf( "DsDataCallback %x\n", (u_int)func ); +} + +int PCinit(void) +{ + return -1; +} + +int PCopen(const char *name, int flags, int perms) +{ + return 0; +} + +int PCread(int fd, char *buff, int len) +{ + return 0; +} + +int PCclose(int fd) +{ + return 0; +} diff --git a/components/mgs/mgs/source/libfs/srchfile.c b/components/mgs/mgs/source/libfs/srchfile.c new file mode 100644 index 00000000..d5bef2a7 --- /dev/null +++ b/components/mgs/mgs/source/libfs/srchfile.c @@ -0,0 +1,311 @@ +#include "libfs.h" + +/* __psyz_cd_rename: off-console there is no drive to read. port/virtual_cd.c + * supplies these over ordinary files; the disc versions keep compiling so the + * real ISO9660 layout stays documented, but nothing links against them. */ +#ifdef __psyz +#define FS_CdMakePositionTable FS_CdMakePositionTable_disc +#endif + + +#include +#include +#include "common.h" +#include "mts/mts.h" // for mts_wait_vbl + +extern const char *MGS_DiskName[3]; /* in main.c */ + +/*---------------------------------------------------------------------------*/ + +/* Read (unaligned) big-endian words. */ +#define read_msb_ushort(p) \ + ((unsigned short)((p[0] << 8) | (p[1]))) + +#define read_msb_ulong(p) \ + ((unsigned int)((p[0] << 24) | (p[1] << 16) | (p[2] << 8) | (p[3]))) + +/* Read (unaligned) little-endian words. */ +#define read_lsb_ushort(p) \ + ((unsigned short)((p[0]) | (p[1] << 8))) + +#define read_lsb_ulong(p) \ + ((unsigned int)((p[0]) | (p[1] << 8) | (p[2] << 16) | (p[3] << 24))) + +/*---------------------------------------------------------------------------*/ +/** + * Copies ISO-9660 filename up until the first ';' character, trimming + * the version number from the path, or until the NULL terminator is hit. + * + * @param[out] dest output string pointer + * @param[in] src input string pointer + * @param[in] length input string length + */ +static void GetFileName(char *dest, char *src, int length) +{ + while (length > 0) + { + *dest++ = *src++; + if (*src == '\0') + { + break; + } + + --length; + if (*src == ';') + { + break; + } + } + *dest = 0; +} + +/*---------------------------------------------------------------------------*/ +/** + * Reads CD-ROM data from a given LBA (Logical Block Address). + * + * @param[out] buffer the memory location to write to + * @param[in] sector sector number from whence to read + * @param[in] size number of bytes to read + */ +static int ReadCdSector(void *buffer, int sector, int size) +{ + CDBIOS_ReadRequest(buffer, sector + 150, size, NULL); + + while (1) + { + if (CDBIOS_ReadSync() <= 0) + { + break; + } + mts_wait_vbl(1); + } + return 1; +} + +/*---------------------------------------------------------------------------*/ +/** + * Gets a pointer to the FS_FILE_INFO record by filename. + * + * @param[in] filename filename to query + * @param[in] finfo pointer to FS_FILE_INFO array + * + * @retval non-NULL pointer to FS_FILE_INFO record + * @retval NULL file not found + */ +static FS_FILE_INFO *GetFileInfo(char *filename, FS_FILE_INFO *finfo) +{ + FS_FILE_INFO *ip; + + for (ip = finfo; ip->name; ip++) + { + if (strcmp(filename, ip->name) == 0) + { + return ip; + } + } + return NULL; +} + +/*---------------------------------------------------------------------------*/ +// See: https://psx-spx.consoledev.net/cdromdrive/#cdrom-iso-file-and-directory-descriptors +static inline char GetXaAttribute(int dir_record, int name_length, int base_length) +{ + int xa_record; + int padding; + + xa_record = name_length; + xa_record += dir_record; + xa_record += base_length; + + padding = name_length & 1; + xa_record -= padding; + return *((char *)xa_record + 3); +} + +/*---------------------------------------------------------------------------*/ +/** + * @brief Scans the game data directory records. + * + * Interates over all files listed in the game's data directory and + * sets the LBA offset of each corresponding entry in fs_file_info. + * + * The LBA will be set to 0 for any filenames beginning with 'Z' that + * do not have proper CD-XA attributes in the "System Use" section + * of the ISO-9660 directory record. + * + * @param[in] buffer ISO-9660 directory record table + * @param[in,out] finfo FS_FILE_INFO table to initialize + * + * @retval -1 on failure + * @retval >= 0 disc number (0: Disc 1, 1: Disc 2) + */ +static int ReadCdDirectory(char *buffer, FS_FILE_INFO *finfo) +{ + int base_length; /* minimum record length */ + int name_length; + char name_buf[32]; + char *name_ptr; /* file identifier */ + char *dir_record; /* directory record */ + FS_FILE_INFO *file_info; + FS_FILE_INFO *found; + char *record_lba_ptr; + char *top_ptr, *size_ptr; + int record_lba; + int top, size; + int retval; /* disc id, -1 for error */ + int disk_no; + const char **disk_name; /* list of executable names */ + const char *exe_name; /* executable name (eg. SLPS_999.99) */ + + file_info = finfo; + dir_record = buffer; + retval = -1; + + /* iterate over the directory records */ + while (dir_record[0] != 0) + { + name_length = dir_record[32]; + + /* skip the "." ('\0') and ".." ('\1') entries */ + if (name_length != 1) + { + name_ptr = &dir_record[33]; + GetFileName(name_buf, name_ptr, name_length); + + /* check if the record is a file */ + if ((dir_record[25] & 2) == 0) + { + found = GetFileInfo(name_buf, file_info); + + /* write the position to the table entry */ + if (found) + { + base_length = 35; + + /* check XA data for correct attributes */ + if (name_buf[0] == 'Z' && + GetXaAttribute((int)dir_record, name_length, base_length) == 0x0D) + { + // if GetXaAttribute() returned 0x0D, it's a normal file (0x0D55). + // MGS's XA data (ZMOVIE.STR) typically has attribute 0x2555. + found->pos = 0; + } + else + { + /* get the file's position */ + record_lba_ptr = &dir_record[2]; + record_lba = read_lsb_ulong(record_lba_ptr); + found->pos = record_lba + 150; + } + } + else + { + /* check the hard-coded executable filename(s) */ + disk_name = MGS_DiskName; + exe_name = *disk_name; + disk_no = 0; + + while (exe_name != NULL) + { + if (strcmp(name_buf, *disk_name) == 0) + { + retval = disk_no; + } + + disk_name++; + exe_name = *disk_name; + disk_no++; + } + } + + top_ptr = &dir_record[2]; + top = read_lsb_ulong(top_ptr); + + size_ptr = &dir_record[10]; + size = read_lsb_ulong(size_ptr); + + printf("FILE %s : top %d size %d set %d\n", name_buf, top, size, found->pos); + } + } + + /* advance to the next record */ + dir_record += dir_record[0]; + } + + return retval; +} + +/*---------------------------------------------------------------------------*/ +/** + * @brief Initializes the CD-ROM file position table. + * + * @param[out] buffer temporary load buffer + * (needs to be at least 2048 bytes) + * @param[in,out] finfo FS_FILE_INFO table to initialize + * + * @retval -1 on failure + * @retval >= 0 disc number (0: Disc 1, 1: Disc 2) + */ +int FS_CdMakePositionTable(char *buffer, FS_FILE_INFO *finfo) +{ + char *path_table_ptr; + char *dir_lba_ptr; + int read_sector; + int path_table_size; /* in bytes */ + int retval; /* disc id, -1 for error */ + int path_entry_size; + int dir_name_length; /* maximum of 8 chars */ + char *dir_buffer; + char dir_name[16]; + + /* read ISO-9660 volume descriptor */ + ReadCdSector(buffer, 16, FS_SECTOR_SIZE); + + /* check system identifier */ + if (strncmp(buffer + 0x08, "PLAYSTATION", 11) != 0) + { + /* not a PlayStation-format disc */ + return -1; + } + + /* read ISO-9660 path table */ + path_table_size = *(int *)(buffer + 0x84); + ReadCdSector(buffer, *(int *)(buffer + 0x8C), path_table_size); + + /* align to the next multiple of 4 */ + dir_buffer = buffer + ((((unsigned int)path_table_size + 3) >> 2) << 2); + + retval = -1; + path_table_ptr = buffer; + + while (path_table_size > 0) + { + dir_name_length = *path_table_ptr; + path_entry_size = 8 + dir_name_length; + + GetFileName(dir_name, path_table_ptr + 8, dir_name_length); + + /* get directory LBA number */ + dir_lba_ptr = path_table_ptr + 2; + read_sector = read_lsb_ulong(dir_lba_ptr); + + /* found the game's data directory? */ + if (strcmp(dir_name, "MGS") == 0) + { + printf("MGS read_sector %d\n", read_sector); + ReadCdSector(dir_buffer, read_sector, FS_SECTOR_SIZE); + retval = ReadCdDirectory(dir_buffer, finfo); + } + + /* account for the padding field if the length is odd */ + if (path_entry_size & 1) + { + path_entry_size = 8 + dir_name_length + 1; + } + + path_table_size -= path_entry_size; + path_table_ptr += path_entry_size; + } + + return retval; +} diff --git a/components/mgs/mgs/source/libfs/stageld.c b/components/mgs/mgs/source/libfs/stageld.c new file mode 100644 index 00000000..a9ff9f04 --- /dev/null +++ b/components/mgs/mgs/source/libfs/stageld.c @@ -0,0 +1,519 @@ +#include "libfs.h" +#include "datacnf.h" + +#include +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" // for DG_FrameRate +#include "sound/sd_cli.h" + +extern FS_STAGE_INFO *fs_stage_info; +extern int gLoaderStartTime_800B528C; +extern int gOverlayBinSize_800B5290; +extern int FS_ResidentCacheDirty; +extern void *StageCharacterEntries; + +/*---------------------------------------------------------------------------*/ + +STATIC TFsCallback gFsCallback_8009D4F8 = NULL; +STATIC TFsSoundCallback gFsSoundCallback_8009D4FC = NULL; + +STATIC unsigned short dword_8009D500[] = {0, 0}; // *.wvx id +STATIC unsigned short word_8009D504 = 0; // *.wvx index +STATIC unsigned short word_8009D506 = 0; // *.mdx id +STATIC unsigned short word_8009D508 = 0; // *.efx id + +static int SetupNextFile( DATACNF_TAG *tag, CDBIOS_TASK *task ) +{ + unsigned char region; + FS_STAGE_INFO *info; + void *tmp; + DATACNF_TAG *tagptr; + DATACNF_TAG *looptag; + + info = fs_stage_info; + if ( gFsSoundCallback_8009D4FC ) + { + gFsSoundCallback_8009D4FC(); + gFsSoundCallback_8009D4FC = NULL; + } + gFsCallback_8009D4F8 = 0; + region = tag->mode; + + switch ( region ) + { + case 's': // .sound +#ifdef __psyz + /* Sound data is streamed straight into SPU RAM through the SD_* + * loaders, and those wait on transfer states the software SPU does + * not raise yet (sd_init's SpuMalloc already failed: + * spu_wave_start_ptr=ffffffff). The first wvx made PcmRead spin + * forever inside the load callback and the whole stage load froze at + * its final file. Until the audio phase, stream sound files to + * nowhere: NULL buffer means "consume the sectors, keep nothing", + * which is the same mechanism the dev build uses for the overlay. */ + if ( tag->ext != 'b' ) + { + task->buffer = NULL; + info->size = tag->size; + info->mode = tag->mode; + return 1; + } +#endif + switch ( tag->ext ) + { + case 'b': // *.bin + /* set the overlay's load address */ + task->buffer = StageCharacterEntries; + #if defined(DEV_EXE) || defined(__psyz) + /* MIPS machine code from the disc must never land on top of the + * native table: the stage code is linked into this executable */ + task->buffer = NULL; + #endif + break; + + case 'w': // *.wvx + if ( dword_8009D500[ word_8009D504 ] != tag->id ) + { + task->buffer = SD_WavDataLoadInit( tag->id ); + gFsSoundCallback_8009D4FC = SD_WavUnload; + gFsCallback_8009D4F8 = &SD_WavLoadBuf; + dword_8009D500[ word_8009D504 ] = tag->id; + } + else + { + task->buffer = 0; + } + word_8009D504 += 1; + break; + + case 'm': // *.mdx + if ( word_8009D506 != tag->id ) + { + task->buffer = SD_SngDataLoadInit(tag->id); + gFsSoundCallback_8009D4FC = SD_80083ED4; + word_8009D506 = tag->id; + } + else + { + task->buffer = 0; + } + break; + + case 'e': // *.efx + if ( word_8009D508 != tag->id ) + { + task->buffer = SD_SeDataLoadInit( tag->id ); + gFsSoundCallback_8009D4FC = SD_80083ED4; + word_8009D508 = tag->id; + } + else + { + task->buffer = 0; + } + break; + } + info->size = tag->size; + info->mode = tag->mode; + return 1; + + case 'r': // .resident + FS_ResidentCacheDirty = 1; + + case 'n': // .nocache + if ( tag->size <= 0x13FFF ) + { + info->tag_start2 = (DATACNF_TAG *)((int)info->tag_end1 + 0x19000); + } + else + { + info->tag_start2 = info->tag_end1; + } + tmp = info->tag_start2; + task->buffer = tmp; + info->tag_end2 = tmp; + info->size = tag->size; + break; + + case 'c': // .cache + if ( info->mode != region ) + { + tagptr = info->tag_start1; + if ( tagptr->mode == region ) + { + while ( tagptr->mode == 'c' ) + { + tagptr++; + } + } + if ( (tagptr - 1)->ext != 0xFF ) + { + looptag = (DATACNF_TAG *)info->tags; + while ( 1 ) + { + printf( "%d %c %c %d\n", + looptag->id, + looptag->mode, + looptag->ext, + looptag->size ); + if ( looptag == (tagptr - 1) ) + { + printf( "HERE !!\n" ); + } + looptag++; + } + } + tmp = info->tag_end1; + task->buffer = tmp; + info->tag_end2 = tmp; + info->size = (tagptr - 1)->size; + info->tag_start1 = tagptr - 1; + } + break; + + case 0: + return 0; + + default: + printf( "WRONG mode %02X (%c)\n", tag->mode, tag->mode); + break; + } + + return 1; +} + +/*---------------------------------------------------------------------------*/ + +static int StageFileReadyCallback(CDBIOS_TASK *task) +{ + FS_STAGE_INFO *info; + int result; + + info = fs_stage_info; + info->size -= FS_SECTOR_SIZE; + + if (info->size <= 0) + { + if (!SetupNextFile(info->tag_start1, task)) + { + if (task->remaining > 0) + { + printf("!!! WRONG %d !!!\n", task->remaining); + } + + result = 0; + } + else + { + result = 2; + } + + info->tag_start1++; + return result; + } + + if (gFsCallback_8009D4F8) + { + task->buffer = gFsCallback_8009D4F8(task->buffer + task->buffer_size * 4); + return 2; + } + + info->tag_end2 = task->buffer + task->buffer_size * 4; + return 1; +} + +static int StageConfigReadyCallback( CDBIOS_TASK *task ) +{ + FS_STAGE_INFO *info; + DATACNF *datacnf; + DATACNF_TAG *tag; + + info = fs_stage_info; + + if ( task->sectors_delivered == 0 ) + { + info->task = task; + + datacnf = (DATACNF *)info->buffer; + info->datacnf = datacnf; + + info->tags = (info->buffer + offsetof(DATACNF, tags)); + task->size = datacnf->size * 512; + task->remaining = task->size - 512; + } + + if ( --info->datacnf->version != 0 ) + { + return 1; + } + + tag = info->tags; + while ( ( tag->mode ) != 0 ) + { + tag++; + } + tag++; + + info->tag_end2 = info->tag_end1 = tag; + task->callback = &StageFileReadyCallback; + info->tag = info->tag_start1 = info->tags; + + SetupNextFile( info->tag_start1, task ); + info->tag_start1++; + + return 2; +} + +/*---------------------------------------------------------------------------*/ + +static inline int get_cache_id( DATACNF_TAG *tag ) +{ + return ( tag->ext - 'a' ) << 16 | tag->id; +} + +static int LoadCacheSection( FS_STAGE_INFO *info, int unused ) +{ + DATACNF_TAG *next_tag; + DATACNF_TAG *tag; + int size; + + if ( info->field_28 != 1 ) + { + info->field_28 = 1; + info->current_ptr = info->tag_end1; + } + + tag = info->tag; + + while(1) + { + next_tag = info->current_ptr + tag[1].size; + + if ( info->tag_start1[-1].mode != 'c' || info->tag_end2 >= next_tag ) + { + GV_LoadInit(info->current_ptr + tag->size, get_cache_id(tag), GV_INIT_CACHE); + } + else + { + return 0; + } + + tag++; + info->tag = tag; + + if (tag->ext == 0xFF) /* fake tag */ + { + size = (int)(info->current_ptr + tag->size) - (int)info->buffer; + GV_SplitMemory(GV_NORMAL_MEMORY, info->buffer, size); + break; + } + } + + info->tag++; + return 1; +} + +static int LoadDataArchives( FS_STAGE_INFO *info ) +{ + DARFILE_TAG *ntag; // "now tag"? + DARFILE_TAG *limit; + int region; + DARFILE_TAG *ntag2; // todo: rename + int size; + void *ptr; + + if ( info->field_28 != 0 ) + { + info->field_28 = 0; + info->current_ptr = info->tag_start2; + info->remaining = info->tag->size; + } + + ntag = info->current_ptr; + + if ( info->tag >= ( info->tag_start1 - 1 ) ) + { + limit = (DARFILE_TAG *)info->tag_end2; + } + else + { + limit = (DARFILE_TAG *)-1; + } + + region = ( ( info->tag->mode ) == 'r' ) ? GV_INIT_RESIDENT : GV_INIT_NOCACHE; + + while ( limit >= ( ntag + 1 ) ) + { + ntag2 = ntag; + + if ( ntag->size <= 0 ) + { + printf( "ntag %X size %d\n", (unsigned int)ntag, ntag->size ); + printf( "limit = %X\n", (unsigned int)limit ); + printf( "data %X %c, now %X\n", + (unsigned int)info->tag_start1, info->tag_start1->mode, + (unsigned int)info->tag_end2 ); + printf( "init %X %c, now %X\n", + (unsigned int)info->tag, info->tag->mode, + (unsigned int)info->current_ptr ); + } + + size = ntag->size + 8; + + if ( ( (char *)ntag + size ) > (char *)limit ) + { + break; + } + + if ( region != GV_INIT_NOCACHE ) + { + ptr = GV_AllocResidentMemory( ntag->size ); + GV_CopyMemory( ntag + 1, ptr, ntag->size ); + } + else + { + ptr = ntag + 1; + } + +#ifdef __psyz + { + static int dar_log = 0; + if (dar_log > 0) + { + dar_log--; + printf("[dar] ntag %p ext '%c'(%d) id %d size %d region %d rem %d\n", + (void *)ntag2, (ntag2->ext >= 32 && ntag2->ext < 127) ? ntag2->ext : '?', + ntag2->ext, ntag2->id, ntag2->size, region, info->remaining); + } + } +#endif + GV_LoadInit( ptr, ( ( ntag2->ext - 'a' ) << 16 ) | ntag2->id, region ); + info->remaining -= size; + + if ( info->remaining <= 0 ) + { + info->field_28 = 2; + info->tag++; + return -1; + } + + ntag = (DARFILE_TAG *)( (char *)ntag2 + size ); + info->current_ptr = ntag; + } + + return 0; +} + +static int LoadStageFiles( FS_STAGE_INFO *info ) +{ + int status; + + if ( !info->tag ) + { + return 1; + } + + status = -1; + + while ( ( status < 0 ) && ( info->tag <= ( info->tag_start1 - 1 ) ) ) + { + switch ( info->tag->mode & 0xff ) + { + case 'c': // cache + if ( LoadCacheSection( info, status ) ) + { + return 0; + } + goto exit; + + case 's': // sound + info->tag++; + break; + + case '\0': + return 0; + + default: + status = LoadDataArchives( info ); + break; + } + } + +exit: + return 1; +} + +/*---------------------------------------------------------------------------*/ + +void *FS_LoadStageRequest( const char *dirname ) +{ + int sector; + FS_STAGE_INFO *info; + void *buffer; + + DG_FrameRate = 1; + printf( "load %s\n", dirname ); + gLoaderStartTime_800B528C = VSync( -1 ); + FS_ResidentCacheDirty = 0; + sector = FS_CdGetStageFileTop( (char *)dirname ); + if ( sector < 0 ) + { + printf( "NOT FOUND %s\n", dirname ); + return NULL; /* esp-box-emu: not a virtual-CD read at a negative sector */ + } + + info = GV_Malloc( sizeof( FS_STAGE_INFO ) ); + if ( !info ) + { + printf( "no_mem\n" ); + return NULL; + } + + buffer = GV_GetMaxFreeMemory( GV_NORMAL_MEMORY ); + + do {} while ( 0 ); // TODO: Figure out what this was, a compiled out macro, checking mem alloc didn't fail ? + + info->buffer = buffer; + + info->field_28 = 2; + fs_stage_info = info; + info->mode = 0; + info->tag = NULL; + word_8009D504 = 0; + CDBIOS_ReadRequest( buffer, sector, FS_SECTOR_SIZE, StageConfigReadyCallback ); + + return (void *)info; +} + +int FS_LoadStageSync( void *info ) +{ +#ifdef __psyz + /* On the console the CD BIOS task delivered sectors on its own, so the || + * below could short-circuit ReadSync away without consequence. Here the + * delivery happens INSIDE CDBIOS_ReadSync, so short-circuiting it starves + * the transfer: the parser reports "waiting for data" forever and the data + * never arrives. Run both, in parse-then-deliver order, every poll. */ + int busy_parse = LoadStageFiles( info ); + int busy_read = CDBIOS_ReadSync(); + return ( busy_parse != 0 || busy_read > 0 ) ? 1 : 0; +#else + int ret = 0; + if ( LoadStageFiles( info ) != 0 || CDBIOS_ReadSync() > 0 ) + { + ret = 1; + } + return ret; +#endif +} + +void FS_LoadStageComplete( void *info ) +{ + int vblanks; + + vblanks = VSync( -1 ); + printf( "load complete time %d\n", vblanks - gLoaderStartTime_800B528C ); + if ( info ) /* a load that failed to start has nothing to free */ + GV_Free( info ); + FS_CdStageProgBinFix(); + DG_FrameRate = 2; +} diff --git a/components/mgs/mgs/source/libfs/stream.c b/components/mgs/mgs/source/libfs/stream.c new file mode 100644 index 00000000..a2315473 --- /dev/null +++ b/components/mgs/mgs/source/libfs/stream.c @@ -0,0 +1,575 @@ +#include "libfs.h" + +#include +#include +#include +#include +#include + +#include "common.h" +#include "mts/mts.h" +#include "sound/sd_cli.h" + +extern int fs_stream_ref_count; +extern int fs_stream_read; +extern int fs_stream_sector; +extern void *fs_stream_heap; +extern char *fs_stream_heap_end; +extern int fs_stream_heap_size; +extern void *fs_stream_unused; +extern char *fs_stream_top; +extern int *fs_stream_write_ptr; +extern char *fs_stream_bottom; +extern int fs_stream_task_state; + +STATIC int fs_stream_last_time = -1; +STATIC int fs_stream_next_time = 0; +STATIC int fs_stream_stop = 0; +STATIC int fs_stream_end = 1; +STATIC int *fs_stream_unread = NULL; + +static int StreamReadyCallback( CDBIOS_TASK *task ) +{ + int retval; + + char *charPtr1; + int size; + char *charPtr2; + + char *streamHeap, *streamHeapEnd; + int *memcpySrc, *memcpyDst; + int ptrDiff1, ptrDiff2, ptrDiff3; + + retval = 1; + + fs_stream_write_ptr = task->buffer + task->buffer_size * 4; + + if (fs_stream_stop == 0 && fs_stream_end == 0) + { + charPtr1 = fs_stream_bottom; + + for (;; fs_stream_bottom = charPtr1) + { + size = *(int *)charPtr1 >> 8; + charPtr2 = charPtr1 + size; + + if ((char *)&fs_stream_write_ptr[-1] >= charPtr2) + { + charPtr1 = charPtr2; + + if (*charPtr2 != 0xF0) + { + continue; + } + + fs_stream_end = 1; + fs_stream_bottom = charPtr2; + goto exit; + } + + if (fs_stream_heap_end >= charPtr2 && + fs_stream_heap_end >= (char *)(fs_stream_write_ptr + 2048 / sizeof(int))) + { + break; + } + + ptrDiff1 = (char *)fs_stream_heap + size - charPtr1; + if (ptrDiff1 < 0) + { + ptrDiff1 = ptrDiff1 + fs_stream_heap_size; + } + + ptrDiff2 = (char *)fs_stream_top - charPtr1; + if (ptrDiff2 < 0) + { + ptrDiff2 += fs_stream_heap_size; + } + + if (ptrDiff2 < ptrDiff1) + { + fs_stream_unread = (int *)charPtr1; + goto exit; + } + + memcpyDst = fs_stream_heap; + memcpySrc = (int *)charPtr1; + while (memcpySrc < fs_stream_write_ptr) + { + *memcpyDst = *memcpySrc; + memcpySrc++; + memcpyDst++; + } + + streamHeap = fs_stream_heap; + task->buffer = memcpyDst; + + *(int *)charPtr1 = -1; + retval = 2; + + fs_stream_write_ptr = memcpyDst; + fs_stream_heap_end = + (char *)memcpyDst + ((fs_stream_heap_size - ((char *)memcpyDst - streamHeap)) & ~0x7FF); + fs_stream_bottom = streamHeap; + goto skip_heap_end_adjustment; + } + + if ((char *)fs_stream_write_ptr >= fs_stream_heap_end) + { + streamHeapEnd = (char *)fs_stream_heap + fs_stream_heap_size; + if ((int)fs_stream_write_ptr < (unsigned)streamHeapEnd) + { + *fs_stream_write_ptr = -1; + } + + retval = 2; + fs_stream_heap_end = streamHeapEnd; + + task->buffer = fs_stream_heap; + fs_stream_bottom = fs_stream_heap; + fs_stream_write_ptr = fs_stream_heap; + } + + skip_heap_end_adjustment: + ptrDiff3 = -(int)fs_stream_write_ptr + (int)fs_stream_top; + if (ptrDiff3 < 0) + { + ptrDiff3 = ptrDiff3 + fs_stream_heap_size; + } + if (ptrDiff3 > 2048) + { + return retval; + } + } + +exit: + fs_stream_read = 0; + fs_stream_sector = task->sector + 1; + return 0; +} + +static void StartRead( void ) +{ + int temp_a0; + int var_v1; + int* temp_v0; + int* var_a1; + int* var_v1_2; + int new_var; + + if (fs_stream_unread != NULL) + { + temp_v0 = fs_stream_unread; + + var_v1 = (int)(fs_stream_heap + (temp_v0[0] >> 8)) - (int)temp_v0; + + if (var_v1 < 0) + { + var_v1 += fs_stream_heap_size; + } + + temp_a0 = (int)fs_stream_top - (int)temp_v0; + + if (temp_a0 < 0) + { + temp_a0 += fs_stream_heap_size; + } + + if (temp_a0 < var_v1) + { + return; + } + + SwEnterCriticalSection(); + + var_a1 = fs_stream_heap; + var_v1_2 = temp_v0; + + while (var_v1_2 < (int *)fs_stream_write_ptr) + { + *var_a1++ = *var_v1_2++; + } + + fs_stream_write_ptr = var_a1; + *temp_v0 = -1; + fs_stream_unread = NULL; + new_var = (fs_stream_heap_size - ((int)var_a1 - (int)fs_stream_heap)) & ~0x7FF; + fs_stream_heap_end = (char *)var_a1 + new_var; + fs_stream_bottom = fs_stream_heap; + + SwExitCriticalSection(); + } + + fs_stream_read = 1; + CDBIOS_ReadRequest(fs_stream_write_ptr, fs_stream_sector, 0, &StreamReadyCallback); +} + +void FS_StreamTaskStart( int sector ) +{ + fs_stream_end = 0; + fs_stream_stop = 0; + + fs_stream_sector = sector; + + fs_stream_last_time = -1; + fs_stream_next_time = 0; + + fs_stream_unread = NULL; + + fs_stream_task_state = -1; + + fs_stream_top = fs_stream_heap; + fs_stream_bottom = fs_stream_heap; + fs_stream_write_ptr = fs_stream_heap; + + StartRead(); +} + +int FS_StreamTaskState( void ) +{ + return fs_stream_task_state; +} + +void FS_StreamTaskInit( void ) +{ + /* do nothing */ +} + +int FS_StreamSync( void ) +{ + int stream_read; + int stream_end; + char *ptr; + int type; + int size; + int remaining; + + stream_read = fs_stream_read; + stream_end = fs_stream_end; + + if ((stream_end != 0) || (fs_stream_stop != 0)) + { + fs_stream_task_state = 0; + return 0; + } + + if (fs_stream_task_state == -1) + { + if (stream_read == 1) + { + return 1; + } + + fs_stream_task_state = 1; + fs_stream_unused = fs_stream_heap; + } + + ptr = fs_stream_top; + while (ptr != fs_stream_bottom) + { + type = *(int *)ptr & 0xFF; + size = *(int *)ptr >> 8; + + if (type == 0xFF) + { + ptr = fs_stream_heap; + } + else if (type == 0) + { + ptr += size; + + if (ptr == (fs_stream_heap + fs_stream_heap_size)) + { + ptr = fs_stream_heap; + } + } + else + { + break; + } + } + fs_stream_top = ptr; + + if (stream_read == 0 && stream_end == 0) + { + remaining = (char *)fs_stream_write_ptr - fs_stream_top; + + if (remaining < 0) + { + remaining += fs_stream_heap_size; + } + + if (remaining < ((fs_stream_heap_size * 2) / 3)) + { + StartRead(); + } + } + + return 1; +} + +void FS_StreamCD( void ) +{ + /* do nothing */ +} + +// warning/bug here is probably in OG code, unless this is an incorrect match +int FS_StreamGetTop( int is_demo ) +{ + int file_id; + +#ifndef NO_WARNING_WORKAROUNDS + file_id = *&file_id; +#endif + + switch (is_demo) + { + case 0: + file_id = FS_FILEID_VOX; + break; + case 1: + file_id = FS_FILEID_DEMO; + break; + } + + return fs_file_info[file_id].pos; +} + +int FS_StreamInit( void *heap, int size ) +{ + fs_stream_heap = heap; + fs_stream_heap_size = size; + fs_stream_heap_end = heap + size; + printf("stream init %X %X\n", (unsigned int)heap, size); + fs_stream_ref_count = 0; + return 1; +} + +void FS_StreamStop( void ) +{ + fs_stream_stop = 1; + fs_stream_end = 1; + fs_stream_task_state = 0; + CDBIOS_ForceStop(); +} + +void FS_StreamOpen( void ) +{ + mts_lock_sem(1); + ++fs_stream_ref_count; + mts_unlock_sem(1); +} + +void FS_StreamClose( void ) +{ + mts_lock_sem(1); + --fs_stream_ref_count; + mts_unlock_sem(1); +} + +int FS_StreamIsEnd( void ) +{ + return fs_stream_ref_count == 0; +} + +void *FS_StreamGetData( int target_type ) +{ + char *ptr; + int type; + int size; + + if (fs_stream_stop != 0) + { + return NULL; + } + + ptr = fs_stream_top; + while (ptr != fs_stream_bottom) + { + type = *(int *)ptr & 0xFF; + size = *(int *)ptr >> 8; + + if (type == 0xff) + { + ptr = fs_stream_heap; + } + else + { + if (type == target_type) + { + *ptr = type | 0x80; + return ptr + 4; + } + + ptr += size; + + if (ptr >= (char *)fs_stream_heap + fs_stream_heap_size) + { + ptr = fs_stream_heap; + } + } + } + + return NULL; +} + +int FS_StreamGetSize( void *stream ) +{ + int *tag; + + tag = (int *)((char *)stream - 4); + return *tag >> 8; +} + +void FS_StreamUngetData( void *stream ) +{ + int *tag; + int val; + + tag = (int *)((char *)stream - 4); + val = *tag; + + if (val & 0x80) + { + *(char *)tag = val & ~0x80; + } +} + +void FS_StreamClear( void *stream ) +{ + int *tag; + + tag = (int *)((char *)stream - 4); + *tag &= ~0xff; +} + +void FS_StreamClearType( void *stream, int target_type ) +{ + char *ptr; + char *end; + int size; + int type; + + if (fs_stream_stop != 0) + { + return; + } + + ptr = fs_stream_top; + end = (char *)stream - 4; + + while ((ptr != end) && (ptr != fs_stream_bottom)) + { + size = *(int *)ptr >> 8; + type = *(int *)ptr & 0xF; + + if (type == target_type) + { + ptr[0] = 0; + printf("clear %X\n", size); + } + + ptr += size; + + if (ptr == (fs_stream_heap + fs_stream_heap_size)) + { + ptr = fs_stream_heap; + } + } +} + +void FS_StreamDump( void ) +{ + char *ptr; + int type; + int size; + + ptr = fs_stream_top; + + printf("now_data_top %X loaded_header %X\n", (unsigned int)ptr, (unsigned int)fs_stream_bottom); + printf("Tick %d\n", FS_StreamGetTick()); + + while (ptr != fs_stream_bottom) + { + type = *(int *)ptr & 0xFF; + size = *(int *)ptr >> 8; + + if (type == 0xFF) + { + ptr = fs_stream_heap; + } + else + { + if (type == 5) + { + printf("%08X TYPE %X size %d time %d\n", (unsigned int)ptr, 5, size, *(int *)(ptr + 4)); + } + else + { + printf("%08X TYPE %X size %d\n", (unsigned int)ptr, type, size); + } + + ptr += size; + + if (ptr == (fs_stream_heap + fs_stream_heap_size)) + { + ptr = fs_stream_heap; + } + } + } +} + +int FS_StreamGetEndFlag( void ) +{ + return fs_stream_end; +} + +int FS_StreamIsForceStop( void ) +{ + return fs_stream_stop; +} + +// TODO: the var might be part of a struct and the code +// takes a ptr to that struct, unknown currently +static inline int *GetTicksPtr( void ) +{ + return &fs_stream_last_time; +} + +void FS_StreamTickStart( void ) +{ + *GetTicksPtr() = VSync(-1); +} + +// for some reason no ptr access here +void FS_StreamSoundMode( void ) +{ + fs_stream_last_time = -1; + fs_stream_next_time = 1; +} + +int FS_StreamGetTick( void ) +{ + int now = mts_get_tick_count(); + int ticks; + + if (fs_stream_next_time != 0) + { + ticks = get_str_counter(); + + if (ticks < 0) + { + if (fs_stream_last_time < 0) + { + return -1; + } + + return now - fs_stream_last_time + fs_stream_next_time; + } + + fs_stream_last_time = now; + fs_stream_next_time = (ticks * 64) / 105 + 1; + return fs_stream_next_time; + } + + return now - fs_stream_last_time; +} diff --git a/components/mgs/mgs/source/libgcl/basic.c b/components/mgs/mgs/source/libgcl/basic.c new file mode 100644 index 00000000..fc7fc8bc --- /dev/null +++ b/components/mgs/mgs/source/libgcl/basic.c @@ -0,0 +1,105 @@ +#include "libgcl.h" +#include "common.h" +#include "strcode.h" + +static int GCL_Command_if( char *top ) +{ + int type, res; + char *p, *block; + + p = top; + +EXEC_IF: + p = GCL_GetNextValue( p, &type, &res ); + +EXEC_ELSE: + p = GCL_GetNextValue( p, &type, (int *)&block ); + + if ( res ) + { + GCL_ExecBlock( block, NULL ); + } + else + { + p = GCL_GetNextValue( p, &type, &res ); + if ( p != NULL ) + { + type >>= 16; + p = (char *)res; + + if ( type == 'e' ) + { + res = 1; + goto EXEC_ELSE; + } + else if ( type == 'i' ) + { + goto EXEC_IF; + } + } + } + + return GCL_OK; +} + +static int GCL_Command_eval( char *top ) +{ + int type, res; + + GCL_GetNextValue( top, &type, &res ); + + return GCL_OK; +} + +static int GCL_Command_foreach( char *top ) +{ + long argbuf[ GCL_PROC_MAX_ARGS ]; + int i, type, value; + GCL_ARGS arg; + long *p; + char *block; + + p = argbuf; + for ( ;; ) + { + top = GCL_GetNextValue( top, &type, &value ); + if ( ( type & 0xFF ) == GCL_OPTION ) + { + break; + } + *p++ = value; + } + + top = GCL_GetNextValue( (char *)value, &type, &value ); + block = (char *)value; // "-do" parameter + + arg.argc = 1; + arg.argv = argbuf; + + for ( i = p - argbuf; i > 0; i-- ) + { + GCL_ExecBlock( block, &arg ); + arg.argv++; + } + + return GCL_OK; +} + +static int GCL_Command_return( char *top ) +{ + return GCL_RETURN; +} + +static GCL_COMMANDLIST commlist[] = { + { 0x0d86, GCL_Command_if }, // GV_StrCode("if") + { 0x64c0, GCL_Command_eval }, // GV_StrCode("eval") + { 0xcd3a, GCL_Command_return }, // GV_StrCode("return") + { 0x7636, GCL_Command_foreach }, // GV_StrCode("foreach") +}; + +static GCL_COMMANDDEF builtin_commands = GCL_COMMANDS( commlist ); + +void GCL_InitBasicCommands( void ) +{ + GCL_AddCommMulti( &builtin_commands ); +} diff --git a/components/mgs/mgs/source/libgcl/command.c b/components/mgs/mgs/source/libgcl/command.c new file mode 100644 index 00000000..e52b9ee3 --- /dev/null +++ b/components/mgs/mgs/source/libgcl/command.c @@ -0,0 +1,229 @@ +#include "libgcl.h" + +#include +#include "common.h" +#include "game/game.h" + +extern GCL_SCRIPT current_script; + +STATIC GCL_COMMANDDEF *commdef = NULL; + +/*---------------------------------------------------------------------------*/ + +int GCL_AddCommMulti( GCL_COMMANDDEF *def ) +{ + def->next = commdef; + commdef = def; + + return 0; +} + +static GCL_COMMANDLIST *FindCommand( int id ) +{ + GCL_COMMANDDEF *def; + + for ( def = commdef; def != NULL; def = def->next ) + { + int i; + GCL_COMMANDLIST *cl = def->commlist; + + for ( i = def->n_commlist; i > 0; i-- ) + { + if ( cl->id == id ) return cl; + cl++; + } + } + + printf( "command not found\n" ); + return NULL; +} + +int GCL_Command( char *ptr ) +{ + u_short id; + GCL_COMMANDLIST *cl; + int ret, ofs; + + id = GCL_GetShort( ptr ); + cl = FindCommand( id ); + ptr += sizeof(short); + + ofs = GCL_GetByte( ptr ); + GCL_SetCommandLine( ptr + ofs ); + ptr += sizeof(char); + + GCL_SetArgTop( ptr ); + + ret = ( *cl->func )( ptr ); + + GCL_UnsetCommandLine(); + + return ret; +} + +static char *set_proc_table( char *proc_table ) +{ + GCL_PROC_TABLE *pt; + + pt = (GCL_PROC_TABLE *)proc_table; + while ( *(int *)pt != 0 ) + { + pt->proc_id = GCL_GetShort( (char *)&pt->proc_id ); + pt->offset = GCL_GetShort( (char *)&pt->offset ); + pt++; + } + + return (char *)( pt + 1 ); +} + +static char *get_proc_block( int id ) +{ + GCL_PROC_TABLE *pt; + + pt = (GCL_PROC_TABLE *)current_script.proc_table; + while ( *(int *)pt != 0 ) + { + if ( pt->proc_id == id ) return current_script.proc_body + pt->offset; + pt++; + } + + printf( "PROC %X NOT FOUND\n", id ); + return NULL; +} + +void GCL_ForceExecProc( int proc_id, GCL_ARGS *arg ) +{ +#ifdef __psyz + /* get_proc_block returns NULL when the id is unknown, and the console + * happily read the resulting address 3 (BIOS space) as script bytes. + * A host faults instead, so skip the call. */ + char *blk = get_proc_block( proc_id ); + if ( !blk ) { return; } + GCL_ExecBlock( blk + 3, arg ); +#else + GCL_ExecBlock( get_proc_block( proc_id ) + 3, arg ); +#endif +} + +int GCL_ExecProc( int proc_id, GCL_ARGS *arg ) +{ + if ( GM_LoadRequest != 0 || ( GM_PlayerStatus & PLAYER_GAME_OVER ) ) + { + printf( "proc %d cancel\n", proc_id ); + return 0; + } + +#ifdef __psyz + { + char *blk = get_proc_block( proc_id ); + if ( !blk ) { return 0; } + return GCL_ExecBlock( blk + 3, arg ); + } +#else + return GCL_ExecBlock( get_proc_block( proc_id ) + 3, arg ); +#endif +} + +static int GCL_Proc( char *p ) +{ + long argbuf[ GCL_MAX_ARGS ]; + int i, id; + GCL_ARGS arg; + + id = (unsigned short)GCL_GetShort( p ); + p += sizeof(short); + + for ( i = 0; ; i++ ) + { + int type, value; + + p = GCL_GetNextValue( p, &type, &value ); + if ( type == GCL_END ) break; + if ( i >= 8 ) + { + printf( "TOO MANY ARGS PROC\n" ); + } + argbuf[ i ] = value; + } + + arg.argc = i; + arg.argv = argbuf; + + GCL_ExecProc( id, &arg ); + return 0; +} + +int GCL_LoadScript( char *datatop ) +{ + char *proc, *script, *font; + int proclen; + + proc = datatop + sizeof(int); + proclen = GCL_GetLong( datatop ); + current_script.proc_table = proc; + current_script.proc_body = set_proc_table( proc ); + + script = current_script.proc_table + proclen; + current_script.script_body = script + sizeof(int); + + font = current_script.script_body + GCL_GetLong( script ); + font_set_font_addr( 2, font + sizeof(int) ); + + return 0; +} + +int GCL_ExecBlock( char *top, GCL_ARGS *arg ) +{ + int value[ 2 ]; + void *org_stack; + + org_stack = GCL_SetArgStack( arg ); + while ( top != NULL ) + { + switch ( *top ) + { + case GCL_EXPR: + GCL_Expr( top + 2, value ); + top++; + top += *top; + break; + case GCL_COMMAND: + if (GCL_Command( top + 3 ) == GCL_RETURN) + { + return GCL_RETURN; + } + + top++; + top += GCL_GetShort( top ); + break; + case GCL_PROC: + GCL_Proc( top + 2 ); + top++; + top += *top; + break; + case GCL_END: + GCL_UnsetArgStack( org_stack ); + return 0; + default: + printf( "SCRIPT COMMAND ERROR %x\n", *top ); + } + } + + printf( "ERROR in script\n" ); + return 1; +} + +GCL_ARGS gcl_null_args = {}; + +void GCL_ExecScript( void ) +{ + unsigned char *datatop; + + datatop = (unsigned char *)current_script.script_body; + if ( *datatop != GCL_ARG ) + { + printf( "NOT SCRIPT DATA !!\n" ); + } + + GCL_ExecBlock( datatop + 3, &gcl_null_args ); +} diff --git a/components/mgs/mgs/source/libgcl/expr.c b/components/mgs/mgs/source/libgcl/expr.c new file mode 100644 index 00000000..b1cd7edf --- /dev/null +++ b/components/mgs/mgs/source/libgcl/expr.c @@ -0,0 +1,99 @@ +#include "libgcl.h" +#include "common.h" + +enum +{ + OP_END, + OP_MNS, OP_NOT, OP_NEG, + OP_ADD, OP_SUB, OP_MUL, OP_DIV, OP_MOD, + OP_EQ, OP_NE, OP_LT, OP_LE, OP_GT, OP_GE, + OP_OR, OP_AND, OP_XOR, + OP_OROR, OP_ANDAND, + OP_SET +}; + +static int calc( int op, int value1, int value2 ) +{ + switch ( op ) + { + case OP_MNS: return -value2; + case OP_NOT: return !value2; + case OP_NEG: return ~value2; + case OP_ADD: return ( value1 + value2 ); + case OP_SUB: return ( value1 - value2 ); + case OP_MUL: return ( value1 * value2 ); + case OP_DIV: return ( value1 / value2 ); + case OP_MOD: return ( value1 % value2 ); + case OP_EQ: return ( value1 == value2 ); + case OP_NE: return ( value1 != value2 ); + case OP_LT: return ( value1 < value2 ); + case OP_LE: return ( value1 <= value2 ); + case OP_GT: return ( value1 > value2 ); + case OP_GE: return ( value1 >= value2 ); + case OP_OR: return ( value1 | value2 ); + case OP_AND: return ( value1 & value2 ); + case OP_XOR: return ( value1 ^ value2 ); + case OP_OROR: return ( value1 || value2 ); + case OP_ANDAND: return ( value1 && value2 ); + } + + return 0; +} + +typedef struct { + int value; + char *ptr; +} EXPR_STACK; + +int GCL_Expr( char *data, int *value ) +{ + int type; + char *p; + EXPR_STACK *sp; + + sp = (EXPR_STACK *)SCRPAD_ADDR + 64; + + p = data; + + for ( ;; ) + { + type = *p; + if ( type == GCL_OP ) + { + int op; + + op = p[ 1 ]; + if ( op == OP_END ) + { + if ( value != NULL ) + { + *value = sp[ -1 ].value; + } + + return sp[ -1 ].value; + } + else if ( op == OP_SET ) + { + GCL_SetVar( sp[ -2 ].ptr, sp[ -1 ].value ); + sp[ -2 ].value = sp[ -1 ].value; + } + else + { + sp[ -2 ].value = calc( op, sp[ -2 ].value, sp[ -1 ].value ); + sp[ -2 ].ptr = NULL; + } + + sp--; + p += sizeof(short); + } + else + { + int val; + + sp->ptr = p; + p = GCL_GetNextValue( p, &type, &val ); + sp->value = val; + sp++; + } + } +} diff --git a/components/mgs/mgs/source/libgcl/gcl_init.c b/components/mgs/mgs/source/libgcl/gcl_init.c new file mode 100644 index 00000000..daf50705 --- /dev/null +++ b/components/mgs/mgs/source/libgcl/gcl_init.c @@ -0,0 +1,60 @@ +#include "libgcl.h" +#include "common.h" +#include "strcode.h" // for GCX_* defs +#include "libgv/libgv.h" // for GV_SetLoader + +int SECTION(".sbss") scenerio_code; +int SECTION(".sbss") dword_800AB994; // padding + +/** + * @brief GCX bytecode initialization handler + * + * If @p id is the same as @c scenerio_code the script will be loaded + * and set for execution, otherwise it will be skipped. + * + * @param[in] buf pointer to cached GCX script + * @param[in] id strcode of the script's basename + * + * @retval 1 on success + * @retval <= 0 on failure (but this can't happen) + */ +static int GCL_InitFunc( void *top, int id ) +{ + if ( id == scenerio_code ) + { + GCL_LoadScript( top ); + } + + return 1; +} + +/** + * @brief Sets which GCX script to load. + * + * If @p demo equals TRUE, demo.gcx will be set for execution + * upon loading a stage, otherwise it will default to the standard + * scenerio.gcx script. + * + * @param demo Sets "demo.gcx" if TRUE + */ +void GCL_ChangeSenerioCode( int demo ) +{ + scenerio_code = ( demo == TRUE ) + ? ( ( GCL_STRID << 16 ) | 0xa242 ) // GV_StrCode("demo") + : ( ( GCL_STRID << 16 ) | 0xea54 ); // GV_StrCode("scenerio") +} + +void GCL_StartDaemon( void ) +{ + GCL_ParseInit(); + GCL_InitVar(); + GCL_InitBasicCommands(); + GV_SetLoader( 'g', GCL_InitFunc ); + + GCL_ChangeSenerioCode( 0 ); +} + +void GCL_ResetSystem( void ) +{ + /* do nothing */ +} diff --git a/components/mgs/mgs/source/libgcl/libgcl.h b/components/mgs/mgs/source/libgcl/libgcl.h new file mode 100644 index 00000000..b592f7d7 --- /dev/null +++ b/components/mgs/mgs/source/libgcl/libgcl.h @@ -0,0 +1,215 @@ +#ifndef __MGS_LIBGCL_H__ +#define __MGS_LIBGCL_H__ + +#include +#include // for SVECTOR +#include +#include "common.h" + +/*---------------------------------------------------------------------------*/ + +typedef struct // private to libgcl/command.c +{ + unsigned short proc_id; + unsigned short offset; +} GCL_PROC_TABLE; + +typedef struct // private to libgcl/command.c +{ + char *proc_table; + char *proc_body; + char *script_body; +} GCL_SCRIPT; + +typedef int (*GCL_COMMANDFUNC)( char *top ); + +#define GCL_OK 0 +#define GCL_RETURN 1 +#define GCL_ERROR -1 + +typedef struct { + unsigned short id; + GCL_COMMANDFUNC func; +} GCL_COMMANDLIST; + +typedef struct _gcl_commanddef { + struct _gcl_commanddef *next; + int n_commlist; + GCL_COMMANDLIST *commlist; +} GCL_COMMANDDEF; + +#define GCL_COMMANDS(a) { NULL, countof(a), a } + +typedef struct { + u_short argc; + long *argv; +} GCL_ARGS; + +typedef struct GCL_Vars +{ + short var[1024]; +} GCL_Vars; + +// Radio codes (radio.dat files) +#define RDCODE_NULL 0 +#define RDCODE_TALK 1 +#define RDCODE_VOICE 2 +#define RDCODE_ANIM 3 +#define RDCODE_ADD_CONTACT 4 +#define RDCODE_MEMSAVE 5 +#define RDCODE_SOUND 6 +#define RDCODE_PROMPT 7 +#define RDCODE_VARSAVE 8 +#define RDCODE_IF 0x10 +#define RDCODE_ELSE 0x11 +#define RDCODE_ELSEIF 0x12 +#define RDCODE_SWITCH 0x20 +#define RDCODE_SWITCH_CASE 0x21 +#define RDCODE_SWITCH_DEFAULT 0x22 +#define RDCODE_RANDSWITCH 0x30 +#define RDCODE_RANDSWITCH_CASE 0x31 +#define RDCODE_EVAL 0x40 +#define RDCODE_SCRIPT 0x80 +#define RDCODE_ENDLINE 0xFF + +/* ----- Types ----- */ + +#define GCL_END 0x00 + +#define GCL_SHORT 0x01 // $w: +#define GCL_BYTE 0x02 // $b: +#define GCL_CHAR 0x03 // $c: +#define GCL_BOOL 0x04 // $f: +#define GCL_VECTOR 0x05 // $v: +#define GCL_STRID 0x06 // $s: +#define GCL_STRING 0x07 // +#define GCL_PROCID 0x08 // $p: +#define GCL_INT 0x09 // $i: +#define GCL_LONG GCL_INT +#define GCL_SYMBOL 0x0a // $t: + +#define GCL_TYPEMASK 0x0f +#define GCL_TYPE(a) ((a) & GCL_TYPEMASK) + +#define GCL_CONST 0x00 +#define GCL_VAR 0x10 +#define GCL_ARRAY 0x20 + +#define GCL_EXPR 0x30 +#define GCL_OP 0x31 + +#define GCL_ARG 0x40 +#define GCL_OPTION 0x50 +#define GCL_COMMAND 0x60 +#define GCL_PROC 0x70 + +#define GCL_TAGMASK 0xf0 +#define GCL_TAG(a) ((a) & GCL_TAGMASK) + +#define GCL_VAR_TYPE_GLOBAL 0x000000 +#define GCL_VAR_TYPE_LOCAL 0x100000 +#define GCL_VAR_TYPE_SYSTEM 0x400000 +#define GCL_VAR_TYPE_LINK 0x800000 + +#define GCL_GetVarTypeCode(gcl_var) (((gcl_var << 1) >> 25) & 0xF) +#define GCL_GetVarOffset(gcl_var) (gcl_var & 0xFFFF) +#define GCL_IsGameStateVar(gcl_var) ((gcl_var & 0xF00000) == GCL_VAR_TYPE_LINK) +#define GCL_GetFlagBitFlag(gcl_var) (char)(1 << (((gcl_var << 1) >> 17) & 0xF)) + +#define GCL_MAX_ARGS 8 +#define GCL_PROC_MAX_ARGS 16 + +#define MAX_LINKVARBUF 96 + +/*---------------------------------------------------------------------------*/ +// clang-format off + +static inline long GCL_GetLong( char *ptr ) +{ + unsigned char *p; + p = ( unsigned char * )ptr; + return ( p[ 0 ] << 24 ) | ( p[ 1 ] << 16 ) | ( p[ 2 ] << 8 ) | ( p[ 3 ] ); +} + +static inline long GCL_GetShort( char *ptr ) +{ + unsigned char *p; + p = ( unsigned char * )ptr; + return ( signed short )( ( p[ 0 ] << 8 ) | ( p[ 1 ] ) ); +} + +static inline char GCL_GetByte( char *ptr ) +{ + return *ptr; +} + +// clang-format on +/*---------------------------------------------------------------------------*/ + +extern GCL_ARGS gcl_null_args; + +/* gcl_init.c */ +void GCL_ChangeSenerioCode( int demo ); +void GCL_StartDaemon( void ); +void GCL_ResetSystem( void ); + +/* command.c */ +int GCL_AddCommMulti( GCL_COMMANDDEF *def ); +int GCL_Command( char *ptr ); +void GCL_ForceExecProc( int proc_id, GCL_ARGS *arg ); +int GCL_ExecProc( int proc_id, GCL_ARGS *arg ); +int GCL_LoadScript( char *datatop ); +int GCL_ExecBlock( char *top, GCL_ARGS *arg ); +void GCL_ExecScript( void ); + +/* basic.c */ +void GCL_InitBasicCommands( void ); + +/* expr.c */ +int GCL_Expr( char *data, int *value ); + +/* parse.c */ +void GCL_SetArgTop( char *top ); +char *GCL_GetNextValue( char *top, int *type_p, int *value_p ); +void *GCL_SetArgStack( GCL_ARGS *args ); +void GCL_UnsetArgStack( void *stack ); +int GCL_GetArgs( int argno ); +void GCL_SetCommandLine( char *argtop ); +void GCL_UnsetCommandLine( void ); +char *GCL_GetOption( char c ); +int GCL_StrToInt( char *ptr ); +int GCL_StrToSV( char *ptr, short *vec ); +char *GCL_GetString( char *ptr ); +char *GCL_NextStr( void ); +int GCL_GetNextInt( void ); +void GCL_GetNextSV( short *vec ); +void GCL_SkipCommand( char *ptr ); +void GCL_ParseInit( void ); + +/* variable.c */ +void GCL_SaveLinkVar( short *ptr ); +int GCL_MakeSaveFile( char *ptr ); +int GCL_SetLoadFile( char *ptr ); +void GCL_InitVar( void ); +void GCL_InitClearVar( void ); +void GCL_SaveVar( void ); +void GCL_RestoreVar( void ); +char *GCL_GetVar( char *top, int *type_p, int *value_p ); +char *GCL_SetVar( char *top, int value ); +char *GCL_VarSaveBuffer( char *top ); + + +#ifdef __psyz +/* MGS tells a script-block POINTER apart from a 16-bit proc id by testing the + * sign bit: every PSX RAM address is 0x8xxxxxxx, so a pointer always read as + * negative. Host addresses are not -- PSRAM here is 0x3Cxxxxxx, which is + * POSITIVE, so a stored block pointer was taken for a proc id, looked up, + * missed ("PROC 3C1987D2 NOT FOUND"), and the NULL it returned was walked at + * offset 3. Proc ids come from GCL_GetShort and are 16-bit, so anything above + * that range is a pointer no matter which memory map we are on. */ +#define GCL_IS_BLOCK_PTR(v) ((unsigned int)(v) > 0xFFFFu) +#else +#define GCL_IS_BLOCK_PTR(v) ((int)(v) < 0) +#endif + +#endif // __MGS_LIBGCL_H__ diff --git a/components/mgs/mgs/source/libgcl/parse.c b/components/mgs/mgs/source/libgcl/parse.c new file mode 100644 index 00000000..d61df687 --- /dev/null +++ b/components/mgs/mgs/source/libgcl/parse.c @@ -0,0 +1,240 @@ +#include "libgcl.h" + +#include +#include "common.h" + +/*** $gp ***/ +int *SECTION(".sbss") argstack_p; +char **SECTION(".sbss") commandline_p; +char *SECTION(".sbss") next_str_ptr; + +/*** bss ***/ +extern int argbuffer[32]; +extern char *commandlines[8]; + +void GCL_SetArgTop( char *top ) +{ + next_str_ptr = top; +} + +char *GCL_GetNextValue( char *top, int *type_p, int *value_p ) +{ + int type, size; + unsigned char *p; + + p = (unsigned char *)top; + type = *p; + p++; + + if ( GCL_TAG( type ) == GCL_VAR ) + { + return GCL_GetVar( top, type_p, value_p ); + } + + *type_p = type; + + switch ( type ) + { + case GCL_END: + p = NULL; + break; + case GCL_SHORT: + *value_p = GCL_GetShort( p ); + p += 2; + break; + case GCL_INT: + case GCL_SYMBOL: + *value_p = GCL_GetLong( p ); + p += 4; + break; + case GCL_STRID: + case GCL_PROCID: + *value_p = (unsigned short)GCL_GetShort( p ); + p += 2; + break; + case GCL_BYTE: + case GCL_CHAR: + case GCL_BOOL: + *value_p = (unsigned char)GCL_GetByte( p ); + p++; + break; + case GCL_STRING: + *value_p = (int)( p + 1 ); + p += *p + 1; + break; + case GCL_ARRAY: + *value_p = GCL_GetArgs( *p ); + *type_p = GCL_SHORT; + p++; + break; + case GCL_ARG: + *value_p = (int)( p + 2 ); + size = GCL_GetShort( p ); + p += size; + break; + case GCL_EXPR: + *value_p = GCL_Expr( p + 1, value_p ); + p += *p; + break; + case GCL_OPTION: + *type_p |= *p << 16; + *value_p = (int)( p + 2 ); + p += p[ 1 ] + 1; + break; + default: + printf( "GCL:WRONG CODE %x\n", type ); + break; + } + + next_str_ptr = p; + return p; +} + +static void GCL_InitArgStack(void) +{ + argstack_p = argbuffer; +} + +void *GCL_SetArgStack( GCL_ARGS *args ) +{ + int *org, *argv; + int i; + + if ( args == NULL ) + { + return NULL; + } + + org = argstack_p; + argv = (int *)( args->argv + ( args->argc - 1 ) ); + + for ( i = args->argc; i > 0; i-- ) + { + *argstack_p++ = *argv--; + } + + *argstack_p++ = args->argc; + return org; +} + +void GCL_UnsetArgStack( void *stack ) +{ + if ( stack != NULL ) + { + argstack_p = stack; + } +} + +int GCL_GetArgs( int argno ) +{ + return argstack_p[ -( argno + 1 ) ]; +} + +static void GCL_InitCommandLineBuffer( void ) +{ + commandline_p = commandlines; +} + +void GCL_SetCommandLine( char *argtop ) +{ + *commandline_p++ = argtop; +} + +void GCL_UnsetCommandLine( void ) +{ + commandline_p--; +} + +char *GCL_GetOption( char c ) +{ + char *p; + + p = commandline_p[ -1 ]; + + for ( ;; ) + { + int type, value; + + p = GCL_GetNextValue( p, &type, &value ); + if ( type == GCL_END ) return NULL; + if ( ( type & 0xFF ) == GCL_OPTION ) + { + if ( ( type >> 16 ) == c ) + { + next_str_ptr = (char *)value; + return (char *)value; + } + } + } +} + +int GCL_StrToInt( char *ptr ) +{ + int type, value; + + next_str_ptr = GCL_GetNextValue( ptr, &type, &value ); + return value; +} + +int GCL_StrToSV( char *ptr, short *vec ) +{ + int i, type, value; + + for ( i = 0; i < 3; i++ ) + { + ptr = GCL_GetNextValue( ptr, &type, &value ); + vec[ i ] = value; + } + + next_str_ptr = ptr; + return 0; +} + +char *GCL_GetString( char *ptr ) +{ + int type, value; + + if ( ( next_str_ptr = GCL_GetNextValue( ptr, &type, &value ) ) == NULL ) + { + return NULL; + } + + return (char *)value; +} + +char *GCL_NextStr( void ) +{ + if ( *next_str_ptr == GCL_END || *next_str_ptr == GCL_OPTION ) + { + return NULL; + } + + return next_str_ptr; +} + +int GCL_GetNextInt( void ) +{ + return GCL_StrToInt( GCL_NextStr() ); +} + +void GCL_GetNextSV( short *vec ) +{ + GCL_StrToSV( GCL_NextStr(), vec ); +} + +void GCL_SkipCommand( char *ptr ) +{ + for ( ;; ) + { + int type, value; + + ptr = GCL_GetNextValue( ptr, &type, &value ); + if ( type == GCL_END ) break; + } +} + +void GCL_ParseInit( void ) +{ + GCL_InitArgStack(); + GCL_InitCommandLineBuffer(); +} diff --git a/components/mgs/mgs/source/libgcl/variable.c b/components/mgs/mgs/source/libgcl/variable.c new file mode 100644 index 00000000..6572605f --- /dev/null +++ b/components/mgs/mgs/source/libgcl/variable.c @@ -0,0 +1,347 @@ +#include +#include "common.h" +#include "libgcl.h" + +#include "game/game.h" // for AreaHistory +#include "menu/menuman.h" // for RadioMemory +#include "linkvar.h" + +extern short linkvarbuf[ MAX_LINKVARBUF ]; +extern short sv_linkvarbuf[ MAX_LINKVARBUF ]; + +extern GCL_Vars gGcl_vars_800B3CC8; +extern GCL_Vars gGcl_memVars_800b4588; +extern char gStageName_800B4D88[16]; +extern RadioMemory gRadioMemory_800BDB38[RADIO_MEMORY_COUNT]; + +#define SAVE_LINKVAR(buf, var) (buf[((short*)&var - (short*)&linkvarbuf)]) + +/*---------------------------------------------------------------------------*/ + +#define SAVE_VERSION 0x60 +#define SAVE_VERSION2 0x800 + +typedef struct SAVE_DATA +{ + int version; + int version2; + int totalFrameTime; + int padding[3]; + char stage_name[16]; + AreaHistory area_history; + short varbuf[0x60]; + GCL_Vars gcl_vars; + RadioMemory radio_memory[RADIO_MEMORY_COUNT]; +} SAVE_DATA; // size 0xA38 + +typedef struct SAVE_FILE +{ + int size; + int checksum; + SAVE_DATA data; +} SAVE_FILE; + +/*---------------------------------------------------------------------------*/ + +void GCL_SaveLinkVar(short *gameVar) +{ + char *addr; + int offset; + + addr = (char *)sv_linkvarbuf; + offset = (char *)gameVar - (char *)linkvarbuf; + *(short *)(addr + offset) = *gameVar; +} + +// Used for save files +static unsigned int crc32(int len, unsigned char *ptr) +{ + unsigned int crc; + int counter; + + crc = 0xffffffff; + if (len != 0) + { + do { + crc ^= *ptr++; + counter = 8; + + do { + if ((crc & 1) != 0) + { + crc = crc >> 1 ^ 0xedb88320; + } + else + { + crc = crc >> 1; + } + } while (--counter != 0); + } while (--len != 0); + } + return ~crc; +} + +int GCL_MakeSaveFile(char *save_buf) +{ + typedef struct { short f[sizeof(gRadioMemory_800BDB38) / sizeof(short)]; } RdMem; + + SAVE_FILE *save_file; + SAVE_DATA *save_data; + + save_file = (SAVE_FILE *)save_buf; + save_data = &save_file->data; + + save_data->version = SAVE_VERSION; + save_data->version2 = SAVE_VERSION2; + save_data->totalFrameTime = gTotalFrameTime; + + GM_LastSaveHours = GM_TotalHours; + GM_LastSaveSeconds = GM_TotalSeconds; + SAVE_LINKVAR(sv_linkvarbuf, GM_LastSaveHours) = GM_TotalHours; + SAVE_LINKVAR(sv_linkvarbuf, GM_LastSaveSeconds) = GM_TotalSeconds; + SAVE_LINKVAR(sv_linkvarbuf, GM_TotalSaves) = GM_TotalSaves; + + save_data->padding[0] = 0; + save_data->padding[1] = 0; + save_data->padding[2] = 0; + + strcpy(save_data->stage_name, gStageName_800B4D88); + GM_GetAreaHistory(&save_data->area_history); + + memcpy(save_data->varbuf, sv_linkvarbuf, 0xC0); + save_data->gcl_vars = gGcl_memVars_800b4588; + *(RdMem *)&save_data->radio_memory = *(RdMem *)&gRadioMemory_800BDB38; + + save_file->size = (void *)save_data + sizeof(SAVE_DATA) - (void *)save_buf; + save_file->checksum = crc32(sizeof(SAVE_DATA), (char *)save_data); // size 0xA38 + + return save_file->size; +} + +int GCL_SetLoadFile(char *save_buf) +{ + typedef struct { short f[sizeof(gRadioMemory_800BDB38) / sizeof(short)]; } RdMem; + + SAVE_FILE *save_file; + SAVE_DATA *save_data; + + save_file = (SAVE_FILE *)save_buf; + save_data = &save_file->data; + + if ((save_data->version != SAVE_VERSION) || (save_data->version2 != SAVE_VERSION2)) + { + printf("SAVE DATA VERSION ERROR!!\n"); + } + if (save_file->checksum != crc32(sizeof(SAVE_DATA), (char *)save_data)) + { + printf("CRC ERROR !!\n"); + return 0; + } + + gTotalFrameTime = save_data->totalFrameTime; + strcpy(gStageName_800B4D88, save_data->stage_name); + GM_SetAreaHistory(&save_data->area_history); + + memcpy(sv_linkvarbuf, save_data->varbuf, 0xC0); + gGcl_memVars_800b4588 = save_data->gcl_vars; + memcpy(linkvarbuf, save_data->varbuf, 0xC0); + gGcl_vars_800B3CC8 = save_data->gcl_vars; + *(RdMem *)&gRadioMemory_800BDB38 = *(RdMem *)&save_data->radio_memory; + + return 1; +} + +/*---------------------------------------------------------------------------*/ + +void GCL_InitVar( void ) +{ + int option; + int difficulty; + + option = GM_OptionFlag; + difficulty = GM_DifficultyFlag; + + gGcl_vars_800B3CC8 = ( GCL_Vars ){{ 0 }}; + memset( linkvarbuf, 0, MAX_LINKVARBUF * sizeof(short) ); + + GM_DifficultyFlag = difficulty; + GM_OptionFlag = option; +} + +void GCL_InitClearVar( void ) +{ + gGcl_vars_800B3CC8 = ( GCL_Vars ){{ 0 }}; + memset( &GM_CurrentStageFlag, 0, 0xb4 ); + + GCL_SaveVar(); +} + +void GCL_SaveVar( void ) +{ + memcpy( sv_linkvarbuf, linkvarbuf, MAX_LINKVARBUF * sizeof(short) ); + gGcl_memVars_800b4588 = gGcl_vars_800B3CC8; + strcpy( gStageName_800B4D88, GM_GetArea( 0 ) ); +} + +void GCL_RestoreVar( void ) +{ + memcpy( linkvarbuf, sv_linkvarbuf, 0x9C ); + gGcl_vars_800B3CC8 = gGcl_memVars_800b4588; + + GM_SetArea( GV_StrCode( gStageName_800B4D88 ), gStageName_800B4D88 ); +} + +// This function takes a GCL variable and return the associated C variable +// +// gcl_variable example: 0x11800002 (difficulty_level) +// 1 = type variable +// 1 = type short +// 80 = linkvarbuf +// 0002 = offset (not index) into linkvarbuf + +char *GCL_GetVar( char *top, int *type_p, int *value_p ) +{ + int gcl_var; + int gcl_code; + char *ptr; + + gcl_var = GCL_GetLong( top ); + gcl_code = GCL_GetVarTypeCode(gcl_var); + + *type_p = gcl_code; + if (GCL_IsGameStateVar(gcl_var)) + { + ptr = (char *)linkvarbuf; + } + else + { + ptr = (char *)&gGcl_vars_800B3CC8; + } + ptr += GCL_GetVarOffset(gcl_var); + switch (gcl_code) + { + case GCL_SHORT: // $w: + case GCL_STRID: // $s: + case GCL_PROCID: + if (gcl_code == GCL_SHORT) + { + *value_p = *(short *)ptr; + } + else + { + *value_p = *(unsigned short *)ptr; + } + break; + + case GCL_BYTE: // $b: + case GCL_CHAR: // $c: + *value_p = (unsigned char)*ptr; + break; + + case GCL_BOOL: // $f: + *value_p = (*ptr & GCL_GetFlagBitFlag(gcl_var)) != 0; + break; + + default: + break; + } + return top + sizeof(gcl_var); +} + +char *GCL_SetVar( char *top, int value ) +{ + int gcl_var, gcl_code; + char *ptr; + char bitFlag; + + gcl_var = GCL_GetLong(top); + gcl_code = GCL_GetVarTypeCode(gcl_var); + if (GCL_IsGameStateVar(gcl_var)) + { + ptr = (char *)linkvarbuf; + } + else + { + ptr = (char *)&gGcl_vars_800B3CC8; + } + ptr += GCL_GetVarOffset(gcl_var); + switch (gcl_code) + { + case GCL_SHORT: // $w: + case GCL_STRID: // $s: + case GCL_PROCID: + *(unsigned short *)ptr = value; + break; + + case GCL_BYTE: // $b: + case GCL_CHAR: // $c: + *ptr = value; + break; + + case GCL_BOOL: // $f: + bitFlag = GCL_GetFlagBitFlag(gcl_var); + if (value) + { + *ptr |= bitFlag; + } + else + { + *ptr &= ~(char)bitFlag; + } + break; + + default: + break; + } + return top + sizeof(gcl_var); +} + +char *GCL_VarSaveBuffer( char *top ) +{ + int gcl_var, gcl_code, value; + char *ptr; + char bitFlag; + + GCL_GetVar(top, &gcl_code, &value); + gcl_var = GCL_GetLong(top); + gcl_code = GCL_GetVarTypeCode(gcl_var); + if (GCL_IsGameStateVar(gcl_var)) + { + ptr = (char *)sv_linkvarbuf; + } + else + { + ptr = (char *)&gGcl_memVars_800b4588; + } + ptr += GCL_GetVarOffset(gcl_var); + switch (gcl_code) + { + case GCL_SHORT: // $w: + case GCL_STRID: // $s: + case GCL_PROCID: + *(short *)ptr = (short)value; + break; + + case GCL_BYTE: // $b: + case GCL_CHAR: // $c: + *ptr = (char)value; + break; + + case GCL_BOOL: // $f: + bitFlag = GCL_GetFlagBitFlag(gcl_var); + if (value) + { + *ptr |= bitFlag; + } + else + { + *ptr &= ~bitFlag; + } + + case GCL_VECTOR: + case GCL_STRING: + default: + break; + } + return top + sizeof(gcl_var); +} diff --git a/components/mgs/mgs/source/libgv/actor.c b/components/mgs/mgs/source/libgv/actor.c new file mode 100644 index 00000000..f0340e7d --- /dev/null +++ b/components/mgs/mgs/source/libgv/actor.c @@ -0,0 +1,347 @@ +/** + * @file actor.c + * @brief This file contains the implementation of the actor system. + * + * The actor system manages a list of actors, allowing for initialization, + * execution, and destruction of a generic actors. Actors can be added + * to different lists, and each list can have its own pause and kill levels. + * The system also supports deferred destruction of actors and provides + * functions for memory allocation and initialization. + */ + +#define __LIBGV_ACTOR_C__ +#include +#include +#include "libgv.h" +#include "mts/mts.h" // for cprintf + +// 0x0 No pause +// 0x1 codec +// 0x2 game paused +int SECTION(".sbss") GV_PauseLevel; +STATIC int SECTION(".sbss") dword_800AB92C; //maybe unused + +extern AList ActorList[ GV_ACTOR_LEVEL ]; + +// initialization structure for the actor lists +// the pause and kill levels are set for each list +static struct +{ + short pause; + short kill; +} levels[ GV_ACTOR_LEVEL ] = +{ + { GV_PAUSE_NOSTOP, GV_KILL_LEVEL_MANAGER }, // GV_ACTOR_DAEMON + { GV_PAUSE_NOSTOP, GV_KILL_LEVEL_MANAGER }, // GV_ACTOR_MANAGER + { GV_LEVEL_STOP, GV_KILL_LEVEL_NORMAL }, // GV_ACTOR_ASSIST + { GV_LEVEL_STOP, GV_KILL_LEVEL_NORMAL }, // GV_ACTOR_PREV2 + { GV_LEVEL_NORMAL, GV_KILL_LEVEL_NORMAL }, // GV_ACTOR_PREV + { GV_LEVEL_NORMAL, GV_KILL_LEVEL_NORMAL }, // GV_ACTOR_USER + { GV_LEVEL_NORMAL, GV_KILL_LEVEL_NORMAL }, // GV_ACTOR_AFTER + { GV_LEVEL_STOP, GV_KILL_LEVEL_NORMAL }, // GV_ACTOR_AFTER2 + { GV_PAUSE_NOSTOP, GV_KILL_LEVEL_MANAGER } // GV_ACTOR_DAEMON2 +}; + +/*---------------------------------------------------------------------------*/ + +/** + * @brief Initialize the actors lists and set the pause and kill levels. + */ +void GV_InitActorSystem( void ) +{ + AList *list; + int i; + + list = ActorList; + for ( i = 0; i < GV_ACTOR_LEVEL; i++ ) + { + GV_ACT *start, *end; + + start = &list->start; + end = &list->end; + start->prev = NULL; + start->next = end; + end->prev = start; + end->next = NULL; + + start->act = start->die = NULL; + end->act = end->die = NULL; + + list->pause_level = levels[ i ].pause; + list->kill_level = levels[ i ].kill; + list++; + } + GV_PauseLevel = GV_PAUSE_NOSTOP; +} + +/** + * @brief Set the pause and kill levels for an actor list. + * + * @param level of the actor list + * @param pause level to set for the pause flag + * @param kill level to set for the kill flag + */ +void GV_ConfigActorSystem( int level, short pause, short kill ) +{ + AList *list; + + list = &ActorList[ level ]; + list->pause_level = pause; + list->kill_level = kill; +} + +/** + * @brief (Unused) Dump the actor system to the console. + * Iterate over all actors in all actor lists and print their name and update + * function. + */ +void GV_DumpActorSystem( void ) +{ + AList *list; + int i; + + list = ActorList; + cprintf("--DumpActorSystem--\n"); + + for ( i = 0; i < GV_ACTOR_LEVEL; i++ ) + { + cprintf( "Lv %d Pause %d Kill %d\n", i, list->pause_level, list->kill_level ); + { + GV_ACT *this, *next; + + this = &list->start; + do + { + next = this->next; + if ( this->act != NULL ) + { + int c; + if ( this->count > 0 ) + { + c = this->runtime * 100 / this->count; + } + else + { + c = 0; + } + cprintf("Lv%d %04d.%02d %08X %s\n", + i, + c / 100, + c % 100, + this->act, + this->filename); + this->runtime = this->count = 0; + } + } while ( ( this = next ) != NULL ); + } + list++; + } +} + +/** + * @brief Execute all actors in the actor system. + * Iterate over all actors in all actor lists and call their update function. + * This function is invoked continuously and represent the main game loop. + */ +void GV_ExecActorSystem( void ) +{ + AList *list; + int i, pause; +#ifdef __psyz + int dbg_ran = 0, dbg_seen = 0; + static unsigned dbg_beat; +#endif + + list = ActorList; + for ( i = GV_ACTOR_LEVEL; i > 0; i-- ) + { + void ( *act )( GV_ACT * ); + GV_ACT *this, *next; + + pause = GV_PauseLevel; + if ( ( list->pause_level & pause ) == 0 ) + { + this = &list->start; + do + { + extern int GM_CurrentMap; + + next = this->next; +#ifdef __psyz + dbg_seen++; +#endif + if ( ( act = this->act ) != NULL ) + { +#ifdef __psyz + dbg_ran++; +#endif + act( this ); + } + GM_CurrentMap = 0; + } while ( ( this = next ) != NULL ); + } + list++; + } +#ifdef __psyz + if ((dbg_beat++ % 1200u) == 0u) + { + printf("[act] pass %u: ran %d of %d, pause %08x" "\n", + dbg_beat, dbg_ran, dbg_seen, (unsigned)GV_PauseLevel); + } +#endif +} + +/** + * @brief Deferred destruction of actors in the actor system. + * + * @param kill The level at which to destroy actors. + */ +void GV_DestroyActorSystem( int kill ) +{ + AList *list; + int i; + + list = ActorList; + for ( i = GV_ACTOR_LEVEL; i > 0; i-- ) + { + GV_ACT *this, *next; + + if ( list->kill_level <= kill ) + { + this = &list->start; + do + { + next = this->next; + if ( this->act || this->die ) + { + GV_DestroyActor( this ); + } + } while ( ( this = next ) != NULL ); + } + list++; + } +} + +/** + * @brief Initialize an actor and add it at the end of the selected list + * + * @param level The id of the list where the actor will be added. + * @param this The actor to add. + * @param free The function to call when freeing the actor. + */ +void GV_InitActor( int level, void *this, GV_FREEFUNC free ) +{ + GV_ACT *actor, *next, *prev; + + actor = (GV_ACT *)this; + next = &ActorList[ level ].end; + prev = next->prev; + + next->prev = actor; + prev->next = actor; + actor->next = next; + actor->prev = prev; + + actor->act = actor->die = NULL; + actor->free = free; +} + +/** + * @brief Allocate memory for an actor and initialize it. + * + * @param level The id of the execution list where the actor will be added. + * @param size The size of the actor. + * @return GV_ACT* The allocated actor. + */ +void *GV_NewActor( int level, int size ) +{ + GV_ACT *this; + + this = GV_Malloc( size ); + if ( this != NULL ) + { + GV_ZeroMemory( this, size ); + GV_InitActor( level, this, GV_Free ); + } + return (void *)this; +} + +void GV_SetNamedActor( void *this, void *act, void *die, const char *name ) +{ + GV_ACT *actor; + + actor = (GV_ACT *)this; + actor->act = (GV_ACTFUNC)act; + actor->die = (GV_ACTFUNC)die; + actor->filename = name; + actor->runtime = actor->count = 0; +} + +// Removes from linked list and calls shutdown/free funcs +/** + * @brief Destroys an actor quickly by removing it from the actor list. + * If the actor has a destructor and memory freeing functions, they are called. + * + * @param this Pointer to the actor to be destroyed. + */ +void GV_DestroyActorQuick( void *this ) +{ + GV_ACT *actor, *next, *prev; + void ( *die )( GV_ACT * ) ; + void ( *free )( void * ) ; + + actor = (GV_ACT *)this; + next = actor->next; + prev = actor->prev; + + next->prev = prev; + prev->next = next; + actor->next = actor->prev = NULL; + + if ( ( die = actor->die ) != NULL ) + { + die( actor ); + } + + if ( ( free = actor->free ) != NULL ) + { + free( actor ); + } +} + +/** + * @brief Schedule the destruction of an actor for the next activation cycle + * by setting its update function. + * + * @param actor The actor to destroy. + */ +void GV_DestroyActor( void *this ) +{ + ( (GV_ACT *)this )->act = (GV_ACTFUNC)GV_DestroyActorQuick; +} + +void GV_DestroyOtherActor( void *target ) +{ + AList *list; + int i; + + list = ActorList; + for ( i = GV_ACTOR_LEVEL; i > 0; i-- ) + { + GV_ACT *this, *next; + + this = &list->start; + do + { + next = this->next; + if ( this == target ) + { + GV_DestroyActor( this ); + return; + } + } while ( ( this = next ) != NULL ); + list++; + } + + printf("#"); +} diff --git a/components/mgs/mgs/source/libgv/cache.c b/components/mgs/mgs/source/libgv/cache.c new file mode 100644 index 00000000..acb18de3 --- /dev/null +++ b/components/mgs/mgs/source/libgv/cache.c @@ -0,0 +1,334 @@ +#include "libgv.h" +#include +#include + +#define CACHEID_RESIDENT 0x01000000 + +#define SET_SPEC( _id, _spec ) ( (_id) + ( (_spec) << 16 ) ) +#define GET_SPEC( _id ) ( (_id & ~CACHEID_RESIDENT) >> 16 ) + +#define NEVER_USED (0) + +extern GV_LOADFUNC Loaders[ MAX_LOADERS ]; + +extern CACHE Caches[ MAX_CACHES ]; +static CACHE *SECTION(".sbss") EmptyCache; + +STATIC CACHE *SECTION(".sbss") GV_ResidentCache; +STATIC int SECTION(".sbss") GV_ResidentCacheSize; + +/*---------------------------------------------------------------------------*/ + +/** + * @brief Searches the cache for an entry matching a given ID and returns the + * tag of a matching entry if found. + * EmptyCache is also set to the first free cache entry if one is found. + * + * @param id The ID to search for in the cache. + */ +static CACHE *FindCache( int id ) +{ + CACHE *cp, *empty; + int mod, this; + int i, j; + + empty = NULL; + mod = id % MAX_CACHES; + cp = Caches + mod; + j = MAX_CACHES - mod; + + for ( i = MAX_CACHES; i > 0; i-- ) + { + this = 0xFFFFFF & cp->id; + if ( this == NEVER_USED ) + { + if ( empty == NULL ) empty = cp; + break; + } + if ( this == id ) + { + return cp; + } + cp++; + if ( --j == 0 ) cp = Caches; + } + + EmptyCache = empty; + return NULL; +} + +/* --------------------------------------------------------- */ + +/** + * @brief Returns the cache ID of a file using its hashed name and extension. + * + * @param root_id strcode hash of the filename (without extension). + * @param spec The first character of the file extension. + */ +int GV_CacheID( int root_id, int spec ) +{ + spec -= 'a'; + if ( spec > 25 ) spec += 32; + return SET_SPEC( root_id, spec ); +} + +/** + * @brief Returns the cache ID of a file using its name and extension. + * + * @param root_name The name of the file (without extension). + * @param spec The first character of the file extension. + */ +int GV_CacheID2( const char *root_name, int spec ) +{ + return GV_CacheID( GV_StrCode( root_name ), spec ); +} + +/** + * @brief Returns the cache ID of a file using its full file name as a string. + * The character found after the first '.' is considered the extension. + * + * @param name The full name of the file with extension. + */ +int GV_CacheID3( char *name ) +{ + char root[ 32 ]; + char c, *cp, *cp2; + + cp = name; + cp2 = root; + do + { + if ( ( c = *cp++ ) == '.' ) + { + *cp2 = '\0'; + break; + } + *cp2++ = c; + } while ( c != '\0' ); + + return GV_CacheID2( root, cp[0] ); +} + +/** + * @brief Returns the data pointer of a cache entry given its ID. + * + * @param id The ID to search for in the cache. + * + * @returns NULL if the file couldn't be found. + */ +void *GV_GetCache( int id ) +{ + CACHE *cp; + + if ( ( cp = FindCache( id ) ) != NULL ) return cp->buf; + return NULL; +} + +/** + * @brief Sets the data pointer of a cache entry given its ID. + * + * @param id The ID to search for in the cache. + * @param buf The data pointer to be written into the cache entry. + */ +int GV_SetCache( int id, void *buf ) +{ + if ( FindCache(id) == NULL && EmptyCache != NULL ) + { + EmptyCache->id = id; + EmptyCache->buf = buf; + return 0; + } + return -1; +} + +/** + * @brief Sets the loader callback for a given file extension. + * + * @param spec The file extension to set a loader callback for. + * @param init The function pointer to the loader callback. + */ +void GV_SetLoader( int spec, GV_LOADFUNC init ) +{ + int type; + + type = spec - 'a'; + Loaders[ type ] = init; +} + +/** + * @brief Initialises the loader callbacks by setting them all to NULL. + */ +void GV_ResetLoader( void ) +{ + GV_LOADFUNC *func; + int i; + + func = Loaders; + for ( i = MAX_LOADERS; i > 0; i-- ) + { + *func = NULL; + func++; + } +} + +/** + * @brief Initialises the cache system by marking all entries as unused. + * Also initialises the resident cache as empty. + */ +void GV_InitCacheSystem( void ) +{ + CACHE *cp; + int i; + + cp = Caches; + for ( i = MAX_CACHES; i > 0; i-- ) + { + cp->id = NEVER_USED; + cp++; + } + + GV_ResidentCache = NULL; + GV_ResidentCacheSize = 0; +} + +/** + * @brief Saves all cache entries marked as resident to GV_ResidentCache. + */ +void GV_SaveResidentFileCache( void ) +{ + CACHE *cp; + int i, size; + + cp = Caches; + size = 0; + for ( i = MAX_CACHES; i > 0; i-- ) + { + if ( cp->id & CACHEID_RESIDENT ) size++; + cp++; + } + + if ( size != 0 ) + { + CACHE *res; + + res = GV_AllocResidentMemory( size * sizeof(CACHE) ); + GV_ResidentCache = res; + GV_ResidentCacheSize = size; + cp = Caches; + for ( i = MAX_CACHES; i > 0; i-- ) + { + if ( cp->id & CACHEID_RESIDENT ) *res++ = *cp; + cp++; + } + } +} + +/** + * @brief Frees the cache system by marking all entries as unused. + * Then loads all resident entries from GV_ResidentCache. + */ +void GV_FreeCacheSystem( void ) +{ + CACHE *cp; + int i; + + cp = Caches; + for ( i = MAX_CACHES; i > 0; i-- ) + { + cp->id = NEVER_USED; + cp++; + } + + cp = GV_ResidentCache; + if ( cp != NULL ) + { + for ( i = GV_ResidentCacheSize; i > 0; i-- ) + { + GV_SetCache( cp->id, cp->buf ); + cp++; + } + } +} + +/** + * @brief Sets the current free cache tag. + * + * @param data The data pointer of the file to write. + * @param name The ID of the file to write. + * @param cache_mode The region of the file to write. + */ +static inline void SetCurrentTag( void *data, int name, int cache_mode ) +{ + if ( cache_mode != GV_INIT_CACHE ) + { + name |= CACHEID_RESIDENT; + } + EmptyCache->id = name; + EmptyCache->buf = data; +} + +/** + * @brief Loads a given file by calling its loader function. + * Also inserts the file into the file cache depending on the region. + * + * @param data The data pointer of the file being loaded. + * @param name The ID of the file being loaded. + * @param cache_mode The region of the file being loaded. + */ +int GV_LoadInit( void *data, int name, int cache_mode ) +{ + GV_LOADFUNC func; + int ret; + + if ( cache_mode == GV_INIT_NOCACHE ) + { + { + int lidx = name / 65536; + if (lidx < 0 || lidx >= 26) { + printf("[gv] BAD loader ext %d id %d data %p" "\n", + lidx, name & 0xFFFF, data); + return -1; + } + func = Loaders[ lidx ]; + } + if ( func != NULL ) + { + ret = func( data, name ); + if ( ret <= 0 ) + { + return ret; + } + } + } + else + { + CACHE *cp; + + if ( FindCache( name ) != NULL || EmptyCache == NULL ) + { + printf( "id conflict\n" ); + return -1; + } + cp = EmptyCache; + SetCurrentTag( data, name, cache_mode ); + { + int lidx = name / 65536; + if (lidx < 0 || lidx >= 26) { + printf("[gv] BAD loader ext %d id %d data %p" "\n", + lidx, name & 0xFFFF, data); + return -1; + } + func = Loaders[ lidx ]; + } + if ( func != NULL ) + { + ret = func( data, name ); + if ( ret <= 0 ) + { + cp->id = NEVER_USED; + return ret; + } + } + } + return 1; +} diff --git a/components/mgs/mgs/source/libgv/debug.c b/components/mgs/mgs/source/libgv/debug.c new file mode 100644 index 00000000..e54d9d86 --- /dev/null +++ b/components/mgs/mgs/source/libgv/debug.c @@ -0,0 +1,21 @@ +#include "libgv.h" +#include + +const char *GV_DebugMes = "none"; + +void GV_Assert( char *file, int line ) +{ + printf( "Assertion failed: %s, line %d\n", file, line ); + + HANGUP(); +} + +void GV_Warning( char *format, int id ) +{ + printf( format, id ); +} + +void GV_Error( char *format, int id ) +{ + GV_Warning( format, id ); +} diff --git a/components/mgs/mgs/source/libgv/gvd.c b/components/mgs/mgs/source/libgv/gvd.c new file mode 100644 index 00000000..64aefaa1 --- /dev/null +++ b/components/mgs/mgs/source/libgv/gvd.c @@ -0,0 +1,105 @@ +#define __LIBGV_GVD_C__ +#include "libgv.h" + +#include +#include +#include "common.h" +#include "mts/mts.h" +#include "libdg/libdg.h" // for DG_HikituriFlag + +extern unsigned char *GV_ResidentMemoryBottom; + +/*---------------------------------------------------------------------------*/ + +char SECTION(".sbss") *GM_StageName; +short *SECTION(".sbss") GM_CurrentPadData; +int SECTION(".sbss") GV_Clock; +int SECTION(".sbss") GV_PassageTime; + +int GV_Time = 0; +STATIC int GV_LastTickCount = 0; + +typedef struct { + GV_ACT actor; +} Work; + +extern Work GV_Work; + +/*---------------------------------------------------------------------------*/ + +static void Excep_callback( void ) +{ + printf( "HANGUP: %s\n", GV_DebugMes ); +} + +static void Act( Work *work ) +{ + int ticks; + + GV_Time++; + + ticks = mts_get_tick_count(); + + GV_PassageTime = ticks - GV_LastTickCount; + GV_LastTickCount = ticks; + + if ( DG_HikituriFlag == 0 ) + { + // Flip active buffer + GV_Clock ^= 1; + } + + if ( GV_PauseLevel == 0 ) + { + GV_ClearMessageSystem(); + } +} + +void GV_ResetPacketMemory( void ) +{ + GV_InitMemorySystem( GV_PACKET_MEMORY0, GV_MEMORY_DYNAMIC, PACK_ADDR0, PACK_SIZE ); + GV_InitMemorySystem( GV_PACKET_MEMORY1, GV_MEMORY_DYNAMIC, PACK_ADDR1, PACK_SIZE ); +} + +void GV_SetPacketTempMemory( void ) +{ + GV_InitMemorySystem( GV_PACKET_MEMORY0, GV_MEMORY_STATIC, PACK_ADDR0, PACK_SIZE * 2 ); + GV_InitMemorySystem( GV_PACKET_MEMORY1, GV_MEMORY_STATIC, NULL, 0 ); +} + +static void ResetMemorySystem( void ) +{ + GV_InitMemorySystemAll(); + GV_ResetPacketMemory(); + GV_InitMemorySystem( GV_NORMAL_MEMORY, GV_MEMORY_STATIC, MEM_ADDR, MEM_SIZE ); + printf( "RESIDENT TOP %X\n", (unsigned int)GV_ResidentMemoryBottom ); +} + +void GV_ResetSystem( void ) +{ + GV_InitMessageSystem(); + GV_InitPadSystem(); +} + +void GV_ResetMemory( void ) +{ + GV_FreeCacheSystem(); + ResetMemorySystem(); +} + +void GV_StartDaemon( void ) +{ + GV_InitActorSystem(); + GV_InitResidentMemory(); + GV_ResetLoader(); + GV_InitCacheSystem(); + GV_ResetSystem(); + + GV_InitActor( GV_ACTOR_DAEMON, &GV_Work.actor, NULL ); + GV_SetNamedActor( &GV_Work.actor, Act, NULL, "gvd.c" ); + + GV_Clock = 0; + GV_Time = 0; + + mts_set_exception_func( Excep_callback ); +} diff --git a/components/mgs/mgs/source/libgv/libgv.h b/components/mgs/mgs/source/libgv/libgv.h new file mode 100644 index 00000000..86743526 --- /dev/null +++ b/components/mgs/mgs/source/libgv/libgv.h @@ -0,0 +1,511 @@ +/** + * Main include header for the GV library. + * + * LibGV is the core of the MGS "system" library group and provides various + * services, including memory management, resource loading, input processing, + * actor creation/execution control, message-passing, and linear algebra. + */ + +#ifndef __MGS_LIBGV_H__ +#define __MGS_LIBGV_H__ + +#include "common.h" +#include +#include +#include + +/* gvd.c */ +extern int GV_Time; +#ifndef __LIBGV_GVD_C__ +extern int GV_Clock; +extern int GV_PassageTime; +#endif + +void GV_StartDaemon(void); +void GV_ResetSystem(void); + +void GV_SetPacketTempMemory(void); +void GV_ResetPacketMemory(void); + +/* strcode.c */ +int GV_StrCode( const char *string ); + +/*---------------------------------------------------------------------------*/ + +/* debug.c */ +extern const char *GV_DebugMes; + +void GV_Assert( char *, int ); +void GV_Warning( char *, int ); +void GV_Error( char *, int ); + +#ifdef DEV_EXE // #ifdef DEBUG +#define ASSERT(c) if (!(c)) { GV_Assert(__FILE__, __LINE__); } +#define WARNING(f,v) GV_Warning(f,v) +#define ERROR(f,v) GV_Error(f,v) +#define OPERATOR() +#define MARK(n) (GV_DebugMes = (char *)n) +#define HANGUP() (*(int *)1 = 0) +#else +// NOTE: Assertions were disabled for INTEGRAL, but the US release +// (and possibly others) were compiled with assertions still enabled. +#define ASSERT(c) +#define WARNING(f,v) +#define ERROR(f,v) +#define OPERATOR() +#define MARK(n) +#define HANGUP() +#endif + +/*------ Actor Management ---------------------------------------------------*/ + +struct _GV_ACT; + +typedef void (*GV_ACTFUNC)(struct _GV_ACT *); +typedef void (*GV_FREEFUNC)(void *); + +/** + * @brief Linked list of game actors. + * A game actor is an entity with a name and an update function. + */ +typedef struct _GV_ACT +{ + struct _GV_ACT *prev; + struct _GV_ACT *next; + GV_ACTFUNC act; // update callback + GV_ACTFUNC die; // shutdown callback + GV_FREEFUNC free; // free callback + const char *filename; // source filename + int runtime; + int count; +} GV_ACT; + +typedef struct // private to libgv/actor.c +{ + GV_ACT start; + GV_ACT end; + short pause_level; + short kill_level; +} AList; + +enum { + GV_ACTOR_DAEMON, // 0 + GV_ACTOR_MANAGER, // 1 + GV_ACTOR_ASSIST, // 2 + + GV_ACTOR_PREV2, // 3 + GV_ACTOR_PREV, // 4 + + GV_ACTOR_USER, // 5 + + GV_ACTOR_AFTER, // 6 + GV_ACTOR_AFTER2, // 7 + + GV_ACTOR_DAEMON2, // 8 + GV_ACTOR_LEVEL // 9 +}; + +#define GV_KILL_LEVEL_NORMAL (0x04) +#define GV_KILL_LEVEL_MANAGER (0x07) + +#define GV_PAUSE_NOSTOP 0x00 +#define GV_PAUSE_STOP 0x01 +#define GV_PAUSE_PAUSE 0x02 +#define GV_PAUSE_MENU 0x04 +#define GV_PAUSE_READERROR 0x08 + +#define GV_LEVEL_NORMAL (GV_PAUSE_STOP|GV_PAUSE_PAUSE|GV_PAUSE_MENU|GV_PAUSE_READERROR) +#define GV_LEVEL_STOP (GV_PAUSE_STOP|GV_PAUSE_READERROR) + +/* actor.c */ +#ifndef __LIBGV_ACTOR_C__ +extern int GV_PauseLevel; +#endif + +void GV_InitActorSystem( void ); +void GV_ConfigActorSystem( int level, short pause, short kill ); +void GV_DumpActorSystem( void ); +void GV_ExecActorSystem( void ); +void GV_DestroyActorSystem( int kill ); +void GV_InitActor( int level, void *this, GV_FREEFUNC free ); +void *GV_NewActor( int level, int size ); +void GV_SetNamedActor( void *this, void *act, void *die, const char *name ); +void GV_DestroyActor( void *this ); +void GV_DestroyActorQuick( void *this ); +void GV_DestroyOtherActor( void *target ); + +#define GV_SetActor( _this, _act, _die ) GV_SetNamedActor( _this, _act, _die, __FILE__ ) + +/*------ Cache System -------------------------------------------------------*/ + +enum GV_CACHE_REGION +{ + GV_INIT_NOCACHE, + GV_INIT_CACHE, + GV_INIT_RESIDENT, +}; + +typedef struct CACHE +{ + int id; + void *buf; +} CACHE; + +#define MAX_CACHES 128 + +#define MAX_LOADERS 26 // 'a'~'z' + +typedef int ( *GV_LOADFUNC )( void *data, int id ); + +/* cache.c */ +int GV_CacheID( int root_id, int spec ); +int GV_CacheID2( const char *root_name, int spec ); +int GV_CacheID3( char *name ); +void *GV_GetCache( int id ); +int GV_SetCache( int id, void *buf ); +void GV_SetLoader( int spec, GV_LOADFUNC init ); +void GV_ResetLoader( void ); +void GV_InitCacheSystem( void ); +void GV_SaveResidentFileCache( void ); +void GV_FreeCacheSystem( void ); +int GV_LoadInit( void *data, int name, int cache_mode ); + +/*------ Memory Management --------------------------------------------------*/ + +#define PACK_SIZE (188 * 1024) /* 188KiB */ +#define MEM_SIZE (428 * 1024) /* 428KiB */ + +/* The game's entire main-RAM map is derived by subtraction from this one + * address, so relocating it moves everything: the two packet arenas, the 428 KB + * general heap, and the load buffer libfs reads sectors into. On the console + * 0x801E0000 was the top of the 2 MB the game was allowed; here it has to be + * memory we actually own, or the first fread() into MEM_ADDR stores to + * 0x80117000 and faults. See port/psyz_port.c for the reservation. */ +#ifdef __psyz +#define MGS_MAIN_RAM_SIZE 0xC9000 /* MEM_SIZE + PACK_SIZE * 2 */ +/* The game also uses the 128 KB ABOVE MEM_BOTTOM (0x801E0000..0x80200000 on + * the console): sng_data at +0, the CD stream buffer at +0x7800 and its + * header at +0x1F800. The reservation covers it so those pointers stay + * inside owned memory. */ +#define MGS_TOP_RAM_SIZE 0x20000 +/* The resident heap grows DOWN from MEM_ADDR (see libgv/resident.c). On the + * console it descended into the space between the executable's bss and the + * heaps; here it needs its own headroom below MEM_ADDR or every resident + * allocation lands BEFORE the reservation, silently trampling the PSRAM + * heap (the virtual CD's disc data lives there). */ +#define MGS_RESIDENT_HEADROOM 0x80000 +#ifdef MGS_ESPBOX +extern unsigned char* mgs_main_ram; /* allocated at start (esp-box-emu) */ +#else +extern unsigned char mgs_main_ram[]; +#endif +#define MEM_BOTTOM ((void *)(mgs_main_ram + MGS_RESIDENT_HEADROOM + MGS_MAIN_RAM_SIZE)) +#else +#define MEM_BOTTOM ((void *)0x801E0000) +#endif + +#define PACK_ADDR0 (MEM_BOTTOM - PACK_SIZE * 2) +#define PACK_ADDR1 (MEM_BOTTOM - PACK_SIZE) + +#define MEM_ADDR (PACK_ADDR0 - MEM_SIZE) +#define RESIDENT_BOTTOM (MEM_ADDR) + +#define GV_MEMORY_STATIC 0 +#define GV_MEMORY_DYNAMIC 1 + +#define GV_PACKET_MEMORY0 0 +#define GV_PACKET_MEMORY1 1 +#define GV_NORMAL_MEMORY 2 // known memleak constant +#define MAX_MEMSYS 3 + +enum GV_ALLOC_STATE +{ + GV_ALLOC_STATE_FREE = 0, + GV_ALLOC_STATE_VOID = 1, + GV_ALLOC_STATE_USED = 2, +}; + +typedef struct GV_ALLOC +{ + void *start; + unsigned int state; // pointer to start of memory for dynamic allocations +} GV_ALLOC; + +enum GV_HEAP_FLAG +{ + GV_HEAP_FLAG_DYNAMIC = 0x1, + GV_HEAP_FLAG_VOIDED = 0x2, + GV_HEAP_FLAG_FAILED = 0x4, +}; + +#define MAX_ALLOC_UNITS 512 + +typedef struct GV_HEAP +{ + int flags; + void *start; + void *end; + int used; + GV_ALLOC units[MAX_ALLOC_UNITS]; +} GV_HEAP; + +/* memory.c */ +void GV_InitMemorySystemAll(void); +void GV_InitMemorySystem(int, int, void *, int); +void GV_ResetMemory(void); +void *GV_AllocMemory(int, int); +void *GV_AllocMemory2(int, int, void **); +void GV_FreeMemory(int, void *); +void GV_FreeMemory2(int, void **); +void GV_ClearMemorySystem(int); +void GV_CheckMemorySystem(int); +void GV_DumpMemorySystem(int); +void GV_CopyMemory(void *, void *, int); +void GV_ZeroMemory(void *, int); +void *GV_Malloc(int); +void GV_Free(void *); +void GV_DelayedFree(void *); + +void *GV_GetMaxFreeMemory(int which); +void *GV_SplitMemory(int which, void *addr, int size); + +/* resident.c */ +void GV_InitResidentMemory( void ); +void GV_SaveResidentTop( void ); +void *GV_AllocResidentMemory( long size ); + +/*------ Message Handling ---------------------------------------------------*/ + +typedef struct // from memleak +{ + u_short address; + u_short _len; + u_short message[7]; + u_short message_len; +} GV_MSG; + +#define MAX_MESSAGES 16 + +typedef struct // private to libgv/message.c +{ + int num; + GV_MSG messages[ MAX_MESSAGES ]; +} MESSAGE_LIST; + +/* message.c */ +void GV_InitMessageSystem( void ); +void GV_ClearMessageSystem( void ); +int GV_SendMessage( GV_MSG *send ); +int GV_ReceiveMessage( int address, GV_MSG **msg_ptr ); + +/*------ Input Processing ---------------------------------------------------*/ + +/* SNES-style button layout */ +#define PAD_U PADLup // 0x1000 ↑ +#define PAD_D PADLdown // 0x4000 ↓ +#define PAD_L PADLleft // 0x8000 ← +#define PAD_R PADLright // 0x2000 → +#define PAD_A PADRright // 0x0020 ○ +#define PAD_B PADRdown // 0x0040 × +#define PAD_X PADRup // 0x0010 △ +#define PAD_Y PADRleft // 0x0080 □ +#define PAD_L1 PADL1 // 0x0004 L1 +#define PAD_L2 PADL2 // 0x0001 L2 +#define PAD_R1 PADR1 // 0x0008 R1 +#define PAD_R2 PADR2 // 0x0002 R2 +#define PAD_STA PADstart // 0x0800 START +#define PAD_SEL PADselect // 0x0100 SELECT +#define PAD_AL PADi // 0x0200 L3 +#define PAD_AR PADj // 0x0400 R3 + +/* button masks */ +#define PAD_UDLR (PAD_U | PAD_D | PAD_L | PAD_R) +#define PAD_ABXY (PAD_A | PAD_B | PAD_X | PAD_Y) +#define PAD_LR (PAD_L1 | PAD_L2 | PAD_R1 | PAD_R2) + +/* button aliases */ +#define PAD_UP PAD_U +#define PAD_DOWN PAD_D +#define PAD_LEFT PAD_L +#define PAD_RIGHT PAD_R +#define PAD_CIRCLE PAD_A +#define PAD_CROSS PAD_B +#define PAD_TRIANGLE PAD_X +#define PAD_SQUARE PAD_Y +#define PAD_START PAD_STA +#define PAD_SELECT PAD_SEL +#define PAD_L3 PAD_AL +#define PAD_R3 PAD_AR + +typedef struct +{ + u_short status; + u_short press; + u_short release; + u_short quick; + short dir; + + short analog; + unsigned char right_dx; + unsigned char right_dy; + unsigned char left_dx; + unsigned char left_dy; +} GV_PAD; + +enum +{ + GV_PAD_DIGITAL = 0, + GV_PAD_ANAJOY = 1, + GV_PAD_ANALOG = 2 +}; + +/* pad.c */ +#ifndef __LIBGV_PAD_C__ +extern u_short GV_DemoPadStatus[2]; +extern u_long GV_DemoPadAnalog; +#endif +#ifndef __BSSDEFINE__ +extern GV_PAD GV_PadData[4]; +#endif +extern int GV_PadMask; + +void GV_InitPadSystem(void); +void GV_UpdatePadSystem(void); +void GV_OriginPadSystem(int); +int GV_GetPadOrigin(void); +int GV_GetPadDirNoPadOrg(unsigned int); + +/*------ Math Operations ----------------------------------------------------*/ + +/* util.c */ +extern void GV_AddVec3( SVECTOR *vec1, SVECTOR *vec2, SVECTOR *vec3 ); +extern void GV_SubVec3( SVECTOR *vec1, SVECTOR *vec2, SVECTOR *vec3 ); +extern int GV_VecLen3( SVECTOR *vec ); +extern void GV_LenVec3( SVECTOR *vec1, SVECTOR *vec2, int len1, int len2 ); +extern int GV_DiffVec3( SVECTOR *vec1, SVECTOR *vec2 ); +extern int GV_VecDir2( SVECTOR *vec ); +extern void GV_DirVec2( int dir, int len, SVECTOR *vec ); +extern void GV_DirVec3( SVECTOR *dir, int len, SVECTOR *vec ); +extern int GV_DiffDirU( int from, int to ); +extern int GV_DiffDirS( int from, int to ); +extern int GV_DiffDirAbs( int from, int to ); +extern int GV_RandU( int input ); +extern int GV_RandS( int input ); + +/* near.c */ +extern int GV_NearExp2( int from, int to ); +extern int GV_NearExp4( int from, int to ); +extern int GV_NearExp8( int from, int to ); +extern int GV_NearPhase( int from, int to ); +extern int GV_NearRange( int from, int to, int range ); +extern int GV_NearSpeed( int from, int to, int speed ); +extern int GV_NearTime( int from, int to, int interp ); + +extern int GV_NearExp2P( int from, int to ); +extern int GV_NearExp4P( int from, int to ); +extern int GV_NearExp8P( int from, int to ); +extern int GV_NearTimeP( int from, int to, int interp ); + +extern void GV_NearExp2V( void *vfrom, void *vto, int n ); +extern void GV_NearExp4V( void *vfrom, void *vto, int n ); +extern void GV_NearExp8V( void *vfrom, void *vto, int n ); +extern void GV_NearPhaseV( void *vfrom, void *vto, int n ); +extern void GV_NearRangeV( void *vfrom, void *vto, int range, int n ); +extern void GV_NearSpeedV( void *vfrom, void *vto, int speed, int n ); +extern void GV_NearTimeV( void *vfrom, void *vto, int interp, int n ); + +extern void GV_NearExp2PV( void *vfrom, void *vto, int n ); +extern void GV_NearExp4PV( void *vfrom, void *vto, int n ); +extern void GV_NearExp8PV( void *vfrom, void *vto, int n ); +extern void GV_NearExp2SV( void *vfrom, void *vto, int speed, int n ); +extern void GV_NearExp4SV( void *vfrom, void *vto, int speed, int n ); +extern void GV_NearExp8SV( void *vfrom, void *vto, int speed, int n ); +extern void GV_NearTimePV( void *vfrom, void *vto, int interp, int n ); + +/* quat.c */ +typedef struct { /* short word type quaternion */ + short x, y; + short z, w; +} SQUAT; + +extern void GV_QuatToMat( SQUAT *quat, MATRIX *mat ); +extern void GV_EulerToQuat( SVECTOR *rot, SQUAT *quat ); +extern void GV_MatToQuat( MATRIX *mat, SQUAT *quat ); +extern void GV_QuatSlerp( SQUAT *from, SQUAT *to, int t, SQUAT *res ); +extern void GV_QuatNormalize( SQUAT *quat ); +extern void GV_QuatMul( SQUAT *q1, SQUAT *q2, SQUAT *res ); + +/*---------------------------------------------------------------------------*/ + +static inline short FP_Extend(short value) +{ + value &= 0x0fff; + + if (value > 2048) + { + value -= 4096; + } + return value; +} + +static inline int FP_Extend2(int value) +{ + value &= 0x0fff; + + if (value >= 2048) + { + value -= 4096; + } + return value; +} + +static inline int FP_ExtendN(int value) +{ + value |= 0xf000; + + if (value < -2048) + { + value += 4096; + } + return value; +} + +static inline int FP_Subtract(int fp, int toSub) +{ + short value = fp - toSub; + + value &= 0x0fff; + + if (value > 2048) + { + value -= 4096; + } + return value; +} + +static inline int FP_Subtract_2(int a, int b) +{ + int value = a - b; + + if (value >= 0) + { + value &= 0x0fff; + + if (value >= 2048) + { + value -= 4096; + } + } + else + { + value |= 0xf000; + + if (value < -2048) + { + value += 4096; + } + } + return value; +} + +#endif // __MGS_LIBGV_H__ diff --git a/components/mgs/mgs/source/libgv/memory.c b/components/mgs/mgs/source/libgv/memory.c new file mode 100644 index 00000000..8043d9b4 --- /dev/null +++ b/components/mgs/mgs/source/libgv/memory.c @@ -0,0 +1,872 @@ +#include "libgv.h" +#include +#include "common.h" + +extern GV_HEAP MemorySystems_800AD2F0[MAX_MEMSYS]; + +/* Unused, does it really belong to memory.c ? */ +STATIC short SECTION(".sbss") dword_800AB93C; + +/*---------------------------------------------------------------------------*/ + +/** + * @brief Find in a specific heap the allocation that contains the address. + * + * @param heap heap to search in + * @param addr address to search for + * + * @return the allocation block that contains the address + */ +void *GV_FindAllocation(GV_HEAP *heap, void *addr) +{ + int i; + GV_ALLOC *start; + GV_ALLOC *end; + + // return if the address is not within the heap + if (addr < heap->start || addr >= heap->end) + return NULL; + + start = heap->units; + + // search for the allocation that contains the address + for (i = heap->used; i >= 2; i /= 2) + { + end = &start[i / 2]; + + // the address is at the start of the allocation + if (addr == end->start) + return end; + + if (end->start < addr) + { + i--; + start = end + 1; + } + } + + // if the address is not found return NULL + if (i <= 0 || start->start != addr) + return NULL; + + return start; +} + +/** + * @brief Finds a free memory allocation in the heap that can accommodate + * a specified size. + * + * This function iterates through the memory allocations in the specified heap + * to find a free block that is large enough to accommodate the requested size. + * If such a block is found, it returns a pointer to the memory allocation. + * Otherwise, it returns NULL. + * + * @param heap pointer to the heap structure + * @param size size of the memory block to find + * + * @return Pointer to the found memory allocation, + * or NULL if no suitable block is found. + */ +GV_ALLOC *GV_FindFreeMemory(GV_HEAP *heap, int size) +{ + GV_ALLOC *alloc; + void *start; + void *next; + int i; + int bytes; + + // Initialize j with the start address of the first allocation + alloc = heap->units; + start = alloc[0].start; + + // Iterate through the heap + for (i = heap->used; i > 0; i--) + { + // Get the start address of the next allocation + next = alloc[1].start; + + // Calculate the size of the current block + bytes = (char *)next - (char *)start; + + // Find a large enough free block + if (bytes >= size && alloc->state == GV_ALLOC_STATE_FREE) + { + return alloc; + } + + start = next; + alloc++; + } + + // Return NULL if no suitable block is found + return NULL; +} + +/** + * @brief Inserts a new memory allocation into the heap. + * + * This function inserts a new memory allocation into the specified heap by + * shifting existing allocations to make space for the new allocation. + * It updates the heap's unit count accordingly. + * + * @param heap pointer to the heap structure + * @param alloc pointer to the memory allocation to be inserted + */ +void GV_SplitAllocation(GV_HEAP *heap, GV_ALLOC *alloc) +{ + int used; + GV_ALLOC *last; + int size; + int i; + + used = heap->used; + + // Pointer to the last allocation in the heap + last = &heap->units[used]; + + // Calculate the size of the new allocation + size = alloc - heap->units; + + // Shift existing allocations to make space for the new allocation + for (i = used - size; i >= 0; i--) + { + last[1] = last[0]; + last--; + } + + heap->used++; +} + +/** + * @brief Merges adjacent free memory blocks in the heap. + * + * This function merges adjacent free memory blocks in the specified heap by + * shifting memory allocations and updating the heap's unit count. + * + * @param heap pointer to the heap structure + * @param alloc pointer to the memory allocation to start merging from + * @param n_unit number of units to merge + */ +void GV_MergeMemory(GV_HEAP *heap, GV_ALLOC *alloc, int n_unit) +{ + GV_ALLOC *next; + int size; + int shift; + int i; + + next = &alloc[n_unit]; + + // Calculate the number of units to shift + size = alloc - heap->units; + shift = heap->used - size - n_unit; + + // Shift memory allocations to merge free blocks + for (i = shift; i > -1; i--) + { + *alloc = *next; + alloc++; + next++; + } + + // Update the heap's unit count to reflect the merged blocks + heap->used -= n_unit; +} + +/** + * @brief Resets the heap by compacting voided memory allocations. + * + * This function iterates through the memory allocations in the specified heap + * and compacts voided (free) memory blocks. + * + * It ensures that all valid allocations are moved to the start of the heap, + * and any voided blocks are merged into a single free block. + * + * Updates the heap's unit count. + * + * @param heap pointer to the heap structure + */ +void GV_ResetVoidedMemorySystem(GV_HEAP *heap) +{ + GV_ALLOC *alloc; + GV_ALLOC *new; + int voided; + int i; + + alloc = heap->units; + new = alloc; + + voided = 0; + + for (i = heap->used; i > 0; i--) + { + if (alloc->state != GV_ALLOC_STATE_FREE && alloc->state != GV_ALLOC_STATE_VOID) + { + *new++ = *alloc; + voided = 0; + } + else if (!voided) + { + voided = 1; + new->start = alloc->start; + new->state = GV_ALLOC_STATE_FREE; + new++; + } + + alloc++; + } + + // Mark the end of the heap + new->start = heap->end; + new->state = GV_ALLOC_STATE_USED; + + heap->used = new - heap->units; +} + +/** + * @brief Resets the dynamic memory allocations in the specified heap. + * + * This function compacts the memory allocations in the heap by moving them to + * the start address of the heap. + * + * It iterates through the memory allocations, copying them to the new location + * if necessary, and updates the allocation metadata. + * + * @param heap Pointer to the heap structure. + */ +void GV_ResetDynamicMemorySystem(GV_HEAP *heap) +{ + GV_ALLOC *alloc; + GV_ALLOC *new; + char *addr; + int i; + int state; + int size; + void *start; + + alloc = heap->units; + new = alloc; + + addr = heap->start; + + // Iterate through all memory allocations in the heap + for (i = heap->used; i > 0; i--) + { + state = alloc->state; + + if (state != GV_ALLOC_STATE_FREE && state != GV_ALLOC_STATE_VOID) + { + start = alloc->start; + size = alloc[1].start - start; + + if (start != addr) + { + new->start = addr; + new->state = state; + + // Write the new start address to the userdata pointer + *(void **)state = addr; + GV_CopyMemory(start, addr, size); + } + + addr += size; + new++; + } + + alloc++; + } + + // If the last allocation is not at the end of the heap, add a new one + if (addr != heap->end) + { + new->start = addr; + new->state = GV_ALLOC_STATE_FREE; + new++; + } + + // Add a final allocation to mark the end of the heap + new->start = heap->end; + new->state = GV_ALLOC_STATE_USED; + + heap->used = new - heap->units; +} + +void GV_InitMemorySystemAll(void) +{ + int i; + + for (i = 0; i < MAX_MEMSYS; i++) + { + GV_InitMemorySystem(i, 0, NULL, 0); + } +} + +/** + * @brief Initializes a specific heap in the memory system. + * + * This function initializes an heap the specified index. It sets up the + * structure, aligns the end pointer, and marks the initial memory allocations + * as free and used. + * + * @param which index of the heap to initialize + * @param dynamic flag indicating whether the heap is dynamic + * @param memory pointer to the start of the memory block + * @param size size of the memory block + */ +void GV_InitMemorySystem(int which, int dynamic, void *memory, int size) +{ + GV_HEAP *heap; + GV_ALLOC *alloc; + unsigned char *end; + + heap = &MemorySystems_800AD2F0[which]; + alloc = heap->units; + + // Align the end to 16 bytes + end = (char *)memory + (size & ~15); + + heap->flags = dynamic != 0; + heap->start = memory; + + heap->end = end; + heap->used = 1; + + // First entry is free + alloc[0].start = memory; + alloc[0].state = GV_ALLOC_STATE_FREE; + + // Second is used and is the entire space + alloc[1].start = end; + alloc[1].state = GV_ALLOC_STATE_USED; +} + +/** + * @brief Clears the memory for the specified heap. + * + * This function checks the flags of the specified heap and performs the + * necessary reset operations if the heap has failed or contains voided memory + * blocks. It resets the heap's flags accordingly. + * + * @param which index of the heap to clear + */ +void GV_ClearMemorySystem(int which) +{ + GV_HEAP *heap; + int flags; + + heap = &MemorySystems_800AD2F0[which]; + flags = heap->flags; + + if (flags & (GV_HEAP_FLAG_FAILED | GV_HEAP_FLAG_VOIDED)) + { + if (flags & GV_HEAP_FLAG_FAILED) + { + if (flags & GV_HEAP_FLAG_DYNAMIC) + { + GV_ResetDynamicMemorySystem(heap); + heap->flags &= ~(GV_HEAP_FLAG_FAILED | GV_HEAP_FLAG_VOIDED); + } + } + + if (flags & GV_HEAP_FLAG_VOIDED) + { + GV_ResetVoidedMemorySystem(heap); + heap->flags &= ~GV_HEAP_FLAG_VOIDED; + } + } + + heap->flags &= ~(GV_HEAP_FLAG_FAILED | GV_HEAP_FLAG_VOIDED); +} + +/** + * @brief Checks and prints the status of the specified heap. + * + * This function prints detailed information about the specified heap, + * It also calculates and prints the total free memory, total voided memory, + * and the size of the largest free block. + * + * @param which index of the heap to check + */ +void GV_CheckMemorySystem(int which) +{ + GV_HEAP *heap; + int total; + int voided; + int max_free; + int free; + GV_ALLOC *alloc; + int i; + int state; + void *start; + void *next; + int size; + + heap = &MemorySystems_800AD2F0[which]; + + printf("system %d ( ", which); + + if (heap->flags & GV_HEAP_FLAG_DYNAMIC) + { + printf("dynamic "); + } + + if (heap->flags & GV_HEAP_FLAG_VOIDED) + { + printf("voided "); + } + + if (heap->flags & GV_HEAP_FLAG_FAILED) + { + printf("failed "); + } + + printf(")\n"); + + printf(" addr = %08x - %08x, units = %d\n", + (unsigned int)heap->start, (unsigned int)heap->end, heap->used); + + total = heap->end - heap->start; + + free = 0; + voided = 0; + max_free = 0; + + alloc = &heap->units[0]; + for (i = heap->used; i > 0; i--) + { + state = alloc->state; + + start = alloc->start; + next = alloc[1].start; + size = next - start; + + if (state == GV_ALLOC_STATE_FREE) + { + free += size; + + if (size > max_free) + { + max_free = size; + } + } + else if (state == GV_ALLOC_STATE_VOID) + { + voided += size; + } + + alloc++; + } + + printf(" free = %d / %d, voided = %d, max_free = %d\n", + free, total, voided, max_free); +} + +/** + * @brief Dumps the memory system information for the specified heap. + * + * Helper function to print information about the memory allocations in the + * specified heap. + * + * @param which index of the heap to dump + */ +void GV_DumpMemorySystem(int which) +{ + GV_HEAP *heap; + GV_ALLOC *alloc; + int i; + int state; + void *start; + void *next; + int size; + + heap = &MemorySystems_800AD2F0[which]; + + printf("system %d ( ", which); + + if (!(heap->flags & GV_HEAP_FLAG_DYNAMIC)) + { + printf("static "); + } + else + { + printf("dynamic "); + } + + if (heap->flags & GV_HEAP_FLAG_VOIDED) + { + printf("voided "); + } + + if (heap->flags & GV_HEAP_FLAG_FAILED) + { + printf("failed "); + } + + printf(")\n"); + + alloc = &heap->units[0]; + + for (i = heap->used; i > 0; i--) + { + state = alloc->state; + + start = alloc->start; + next = alloc[1].start; + size = next - start; + + if (state == GV_ALLOC_STATE_FREE) + { + printf("---- %8d bytes ( from %08x free )\n", + size, (unsigned int)alloc->start); + } + else if (state == GV_ALLOC_STATE_VOID) + { + printf("==== %8d bytes ( from %08x void )\n", + size, (unsigned int)alloc->start); + } + else if (state == GV_ALLOC_STATE_USED) + { + printf("++++ %8d bytes ( from %08x used )\n", + size, (unsigned int)alloc->start); + } + else + { + printf("**** %8d bytes ( from %08x user %08x )\n", + size, (unsigned int)alloc->start, state); + } + + alloc++; + } + + printf("\n"); +} + +void *GV_AllocMemory(int which, int size) +{ + return GV_AllocMemory2(which, size, (void **)GV_ALLOC_STATE_USED); +} + +/** + * @brief Allocates memory from the specified heap with a start pointer + * for dynamic allocations. + * + * This function allocates a block of memory from the specified heap. It aligns + * the requested size to a 16-byte boundary, finds a suitable free block, and + * updates the heap's memory allocation structures. If the allocation is + * successful, it returns a pointer to the start of the allocated memory block. + * If the allocation fails, it sets the heap's failed flag and returns NULL. + * + * @param which index of the heap to allocate memory from + * @param size size of the memory block to allocate + * @param pstart GV_ALLOC_STATE_USED for GV_AllocMemory, or a pointer to + * receive the start address otherwise + * + * @return pointer to the start of the allocated memory block, + * or NULL if the allocation fails + */ +void *GV_AllocMemory2(int which, int size, void **pstart) +{ + GV_HEAP *heap; + int normal; + GV_ALLOC *alloc; + void *start; + + heap = &MemorySystems_800AD2F0[which]; + normal = GV_ALLOC_STATE_USED; + + // Check if there is space for more allocations + if (heap->used >= (MAX_ALLOC_UNITS - 1)) + { + return NULL; + } + + // Align size to 16-byte boundary + size = (size + 15) & ~15; + + // Find a free block that can accommodate the requested size + alloc = GV_FindFreeMemory(heap, size); + + if (!alloc) + { + // Set the heap's failed flag if no suitable block is found + heap->flags |= GV_HEAP_FLAG_FAILED; + } + else + { + start = alloc->start; + + // If the free block is larger than the requested size, split it + if ((alloc[1].start - start) > size) + { + GV_SplitAllocation(heap, alloc); + alloc[1].start = start + size; + alloc[1].state = GV_ALLOC_STATE_FREE; + } + + // Mark the allocation as used + alloc->state = (int)pstart; + + if ((int)pstart != normal) + { + *pstart = start; + } + + // Return the start address of the allocation + return start; + } + + // Return NULL if the allocation fails + return NULL; +} + +/** + * @brief Frees a memory allocation in the specified heap. + * + * This function finds the memory allocation corresponding to the given address + * in the specified heap and marks it as free. It also handles merging adjacent + * free blocks and updating the heap's state. + * + * @param which index of the heap + * @param addr address of the memory block to be freed + */ +void GV_FreeMemory(int which, void *addr) +{ + GV_HEAP *heap; + GV_ALLOC *alloc; + GV_ALLOC *merge; + int units; + + heap = &MemorySystems_800AD2F0[which]; + alloc = GV_FindAllocation(heap, addr); + + // Return if the allocation is not found or already free + if (!alloc || alloc->state == GV_ALLOC_STATE_FREE) + return; + + alloc->state = GV_ALLOC_STATE_FREE; + merge = alloc; + + units = 0; + + // Check if the previous allocation is also free + if (alloc != heap->units && alloc[-1].state == GV_ALLOC_STATE_FREE) + { + units++; + } + else + { + merge++; + } + + // Check if the next allocation is also free + if (alloc[1].state == GV_ALLOC_STATE_FREE) + { + units++; + } + + // If there are adjacent free blocks, merge them + if (units) + { + GV_MergeMemory(heap, merge, units); + } +} + +/** + * @brief Marks a memory allocation as voided in the specified heap. + * + * This function finds the memory allocation corresponding to the given address + * in the specified heap and marks it as voided. It also sets the heap's flag to + * indicate that it contains voided allocations. + * + * @param which index of the heap. + * @param addr pointer to the address of the memory block to be voided + */ +void GV_FreeMemory2(int which, void **addr) +{ + GV_HEAP *heap; + GV_ALLOC *alloc; + + heap = &MemorySystems_800AD2F0[which]; + alloc = GV_FindAllocation(heap, *addr); + + if (alloc) + { + alloc->state = GV_ALLOC_STATE_VOID; + heap->flags |= GV_HEAP_FLAG_VOIDED; + } +} + +/** + * @brief Copies a block of memory from one location to another. + * + * This function copies a block of memory from the source address to the + * destination address. It optimizes the copying process by using a + * structure to copy multiple bytes at a time when possible. + * + * @param[in] from pointer to the source memory block + * @param[out] to pointer to the destination memory block + * @param[in] size size of the memory block in bytes + */ +void GV_CopyMemory( void *from, void *to, int size ) +{ + typedef struct { long d0, d1, d2, d3; } Unit; + int i, i2; + Unit *u0; + Unit *u1; + char *c0; + char *c1; + + c0 = (char *)from; + c1 = (char *)to; + + // Calculate the alignment of the source and destination addresses + i = 3 & (long)from; + i2 = 3 & (long)to; + + // If the alignments are different, copy byte by byte + if (i2 != i) { + for (i = size; i > 0; --i) { + *(c1++) = *(c0++); + } + } else { + // Adjust size to account for initial unaligned bytes + size = size - i; + for (; i > 0; --i) { + *(c1++) = *(c0++); + } + // Copy using the Unit structure for larger chunks + u0 = (Unit *)c0; + u1 = (Unit *)c1; + for (i = size / sizeof(Unit); i > 0; --i) { + *(u1++) = *(u0++); + } + // Copy any remaining bytes + c0 = (char *)u0; + c1 = (char *)u1; + for (i = (sizeof(Unit) - 1) & size; i > 0; --i) { + *(c1++) = *(c0++); + } + } +} + +/** + * @brief Sets a block of memory to zero. + * + * This function sets a block of memory to zero, optimizing the process by + * using a structure to zero out multiple bytes at a time when possible. + * + * @param[out] to pointer to the memory block to be zeroed + * @param[in] size size of the memory block in bytes + */ +// clang-format off +void GV_ZeroMemory( void *to, int size ) +{ + typedef struct { long d0, d1, d2, d3; } Unit; + Unit *u; + char *c; + int i; + + c = (char *)to; + i = 3 & (long)to; + size -= i; + for (; i > 0; --i) { + *(c++) = 0; + } + u = (Unit *)c; + for (i = size / sizeof(Unit); i > 0; --i) { + u->d0 = 0; + u->d1 = 0; + u->d2 = 0; + u->d3 = 0; + u++; + } + c = (char *)u; + for (i = (sizeof(Unit) - 1) & size; i > 0; --i) { + *(c++) = 0; + } +} + +void *GV_Malloc( int size ) +{ + return GV_AllocMemory( GV_NORMAL_MEMORY, size ); +} + +void GV_Free( void *addr ) +{ + GV_FreeMemory( GV_NORMAL_MEMORY, addr ); +} + +void GV_DelayedFree( void *addr ) +{ + GV_FreeMemory2( GV_NORMAL_MEMORY, &addr ); +} +// clang-format on + +/** + * @brief Finds and allocates the largest contiguous free memory block + * in the specified heap. + * + * @param which index of the heap to search + * + * @return pointer to the allocated memory block + */ +void *GV_GetMaxFreeMemory(int which) +{ + int max; + GV_HEAP *heap; + GV_ALLOC *alloc; + int i; + int size; + + max = 0; + + heap = &MemorySystems_800AD2F0[which]; + alloc = heap->units; + + for (i = heap->used; i > 0; i--) + { + if (alloc->state == GV_ALLOC_STATE_FREE) + { + size = alloc[1].start - alloc->start; + if (size > max) + { + // keep track of the largest free block + max = size; + } + } + + alloc++; + } + + return GV_AllocMemory(which, max); +} + +/** + * @brief Resizes an existing memory allocation in the specified heap. + * + * @param which index of the heap + * @param addr address of the memory block to resize + * @param size new size of the memory block + * + * @return pointer to the resized memory block, or NULL if resizing fails + */ +void *GV_SplitMemory(int which, void *addr, int size) +{ + GV_HEAP *heap; + GV_ALLOC *alloc; + + heap = &MemorySystems_800AD2F0[which]; + alloc = GV_FindAllocation(heap, addr); + + if (!alloc || alloc->state != GV_ALLOC_STATE_USED) + return NULL; + + if ((alloc[1].start - alloc->start) == size) + return NULL; + + GV_SplitAllocation(heap, alloc); + + alloc[1].start = alloc->start + size; + alloc[1].state = GV_ALLOC_STATE_FREE; + + return alloc[1].start; +} diff --git a/components/mgs/mgs/source/libgv/message.c b/components/mgs/mgs/source/libgv/message.c new file mode 100644 index 00000000..a61de9e0 --- /dev/null +++ b/components/mgs/mgs/source/libgv/message.c @@ -0,0 +1,99 @@ +#include "libgv.h" + +/* ---------------------------------------------------------- */ + +extern MESSAGE_LIST message_list[ 2 ]; +STATIC int SECTION(".sbss") which_buffer; + +// TODO: remove padding +STATIC int SECTION(".sbss") dword_800AB94C; + +/* ---------------------------------------------------------- */ + +static void slide_message_forward( GV_MSG *ptr, int n_ptr ) +{ + ptr += n_ptr; + + while ( --n_ptr >= 0 ) + { + *ptr = *(ptr - 1); + ptr--; + } +} + +void GV_InitMessageSystem( void ) +{ + message_list[ 0 ].num = 0; + message_list[ 1 ].num = 0; + which_buffer = 0; +} + +void GV_ClearMessageSystem( void ) +{ + MESSAGE_LIST *list; + + which_buffer = 1 - which_buffer; + + list = message_list + ( 1 - which_buffer ); + list->num = 0; +} + +int GV_SendMessage( GV_MSG *send ) +{ + MESSAGE_LIST *list; + int num; + + { + list = message_list + ( 1 - which_buffer ); + num = list->num; + if ( num >= MAX_MESSAGES ) return -1; + list->num = num + 1; + } + + { + GV_MSG *msg; + int address, length; + + msg = list->messages; + address = send->address; + length = 0; + for ( ; num > 0; num-- ) + { + if ( msg->address == address ) + { + length = msg->_len; + slide_message_forward( msg, num ); + break; + } + msg++; + } + + *msg = *send; + msg->_len = length + 1; + } + + return 0; +} + +int GV_ReceiveMessage( int address, GV_MSG **msg_ptr ) +{ + MESSAGE_LIST *list; + GV_MSG *msg; + int num; + + if ( GV_PauseLevel != 0 ) return 0; + + list = message_list + which_buffer; + if ( ( num = list->num ) == 0 ) return 0; + msg = list->messages; + while ( --num >= 0 ) + { + if ( msg->address == address ) + { + *msg_ptr = msg; + return msg->_len; + } + msg++; + } + return 0; +} diff --git a/components/mgs/mgs/source/libgv/near.c b/components/mgs/mgs/source/libgv/near.c new file mode 100644 index 00000000..5f9104b1 --- /dev/null +++ b/components/mgs/mgs/source/libgv/near.c @@ -0,0 +1,391 @@ +#include "libgv.h" + +/*----------------------------------------------------------------*/ + +static short TimeInv[16] = { + 0, 2048, 1365, 1024, 819, 682, 585, 512, + 455, 409, 372, 341, 315, 292, 273, 256 +}; + +/*----------------------------------------------------------------*/ + +static inline int NearExp2( int from, int to ) +{ + int diff; + + diff = to - from; + if ( diff > -2 && diff < 2 ) return to; + return from + diff / 2; +} + +static inline int NearExp4( int from, int to ) +{ + int diff; + + diff = to - from; + if ( diff > -4 && diff < 4 ) return to; + return from + diff / 4; +} + +static inline int NearExp8( int from, int to ) +{ + int diff; + + diff = to - from; + if ( diff > -8 && diff < 8 ) return to; + return from + diff / 8; +} + +static inline int NearPhase( int from, int to ) +{ + short diff; + + diff = 4095 & ( from - to ); + if ( diff > 2048 ) diff -= 4096; + return to + diff; +} + +static inline int NearRange( int from, int to, int range ) +{ + int border; + + border = to - range; + if ( from < border ) return border; + border = to + range; + if ( from > border ) return border; + return from; +} + +static inline int NearSpeed( int from, int to, int speed ) +{ + int next; + + if ( from < to ) + { + next = from + speed; + if ( next >= to ) return to; + } + else + { + next = from - speed; + if ( next <= to ) return to; + } + return next; +} + +static inline int NearTime( int from, int to, int interp ) +{ + if ( interp == 0 ) return to; + return ( to - from ) * interp / 4096 + from; +} + +/*----------------------------------------------------------------*/ + +static inline int NearExp2P( int from, int to ) +{ + return NearExp2( NearPhase( from, to ), to ); +} + +static inline int NearExp4P( int from, int to ) +{ + return NearExp4( NearPhase( from, to ), to ); +} + +static inline int NearExp8P( int from, int to ) +{ + return NearExp8( NearPhase( from, to ), to ); +} + +static inline int NearExp2S( int from, int to, int speed ) +{ + int diff; + + diff = ( to - from ) / 2; + if ( diff > -speed && diff < speed ) return to; + return from + diff; +} + +static inline int NearExp4S( int from, int to, int speed ) +{ + int diff; + + diff = ( to - from ) / 4; + if ( diff > -speed && diff < speed ) return to; + return from + diff; +} + +static inline int NearExp8S( int from, int to, int speed ) +{ + int diff; + + diff = ( to - from ) / 8; + if ( diff > -speed && diff < speed ) return to; + return from + diff; +} + +/*----------------------------------------------------------------*/ + +int GV_NearExp2( int from, int to ) +{ + return NearExp2( from, to ); +} + +int GV_NearExp4( int from, int to ) +{ + return NearExp4( from, to ); +} + +int GV_NearExp8( int from, int to ) +{ + return NearExp8( from, to ); +} + +int GV_NearPhase( int from, int to ) +{ + return NearPhase( from, to ); +} + +int GV_NearRange( int from, int to, int range ) +{ + return NearRange( from, to, range ); +} + +int GV_NearSpeed( int from, int to, int speed ) +{ + return NearSpeed( from, to, speed ); +} + +int GV_NearTime( int from, int to, int interp ) +{ + if ( interp > 15 ) interp = 15; + interp = TimeInv[ interp ]; + return NearTime( from, to, interp ); +} + +/*----------------------------------------------------------------*/ + +int GV_NearExp2P( int from, int to ) +{ + return NearExp2P( from, to ); +} + +int GV_NearExp4P( int from, int to ) +{ + return NearExp4P( from, to ); +} + +int GV_NearExp8P( int from, int to ) +{ + return NearExp8P( from, to ); +} + +int GV_NearTimeP( int from, int to, int interp ) +{ + if ( interp > 15 ) interp = 15; + interp = TimeInv[ interp ]; + return NearTime( NearPhase( from, to ), to, interp ); +} + +/*----------------------------------------------------------------*/ + +void GV_NearExp2V( void *vfrom, void *vto, int n ) +{ + short *from, *to; + + from = (short *)vfrom; + to = (short *)vto; + while ( --n >= 0 ) + { + *from = NearExp2( *from, *to ); + from++; + to++; + } +} + +void GV_NearExp4V( void *vfrom, void *vto, int n ) +{ + short *from, *to; + + from = (short *)vfrom; + to = (short *)vto; + while ( --n >= 0 ) + { + *from = NearExp4( *from, *to ); + from++; + to++; + } +} + +void GV_NearExp8V( void *vfrom, void *vto, int n ) +{ + short *from, *to; + + from = (short *)vfrom; + to = (short *)vto; + while ( --n >= 0 ) + { + *from = NearExp8( *from, *to ); + from++; + to++; + } +} + +void GV_NearPhaseV( void *vfrom, void *vto, int n ) +{ + short *from, *to; + + from = (short *)vfrom; + to = (short *)vto; + while( --n >= 0 ) + { + *from = NearPhase( *from, *to ); + from++; + to++; + } +} + +void GV_NearRangeV( void *vfrom, void *vto, int range, int n ) +{ + short *from, *to; + + from = (short *)vfrom; + to = (short *)vto; + while ( --n >= 0 ) + { + *from = NearRange( *from, *to, range ); + from++; + to++; + } +} + +void GV_NearSpeedV( void *vfrom, void *vto, int speed, int n ) +{ + short *from, *to; + + from = (short *)vfrom; + to = (short *)vto; + while ( --n >= 0 ) + { + *from = NearSpeed( *from, *to, speed ); + from++; + to++; + } +} + +void GV_NearTimeV( void *vfrom, void *vto, int interp, int n ) +{ + short *from, *to; + + if ( interp > 15 ) interp = 15; + interp = TimeInv[ interp ]; + from = (short *)vfrom; + to = (short *)vto; + while ( --n >= 0 ) + { + *from = NearTime( *from, *to, interp ); + from++; + to++; + } +} + +/*----------------------------------------------------------------*/ + +void GV_NearExp2PV( void *vfrom, void *vto, int n ) +{ + short *from, *to; + + from = (short *)vfrom; + to = (short *)vto; + while ( --n >= 0 ) + { + *from = NearExp2P( *from, *to ); + from++; + to++; + } +} + +void GV_NearExp4PV( void *vfrom, void *vto, int n ) +{ + short *from, *to; + + from = (short *)vfrom; + to = (short *)vto; + while ( --n >= 0 ) + { + *from = NearExp4P( *from, *to ); + from++; + to++; + } +} + +void GV_NearExp8PV( void *vfrom, void *vto, int n ) +{ + short *from, *to; + + from = (short *)vfrom; + to = (short *)vto; + while ( --n >= 0 ) + { + *from = NearExp8P( *from, *to ); + from++; + to++; + } +} + +void GV_NearExp2SV( void *vfrom, void *vto, int speed, int n ) +{ + short *from, *to; + + from = (short *)vfrom; + to = (short *)vto; + while ( --n >= 0 ) + { + *from = NearExp2S( *from, *to, speed ); + from++; + to++; + } +} + +void GV_NearExp4SV( void *vfrom, void *vto, int speed, int n ) +{ + short *from, *to; + + from = (short *)vfrom; + to = (short *)vto; + while ( --n >= 0 ) + { + *from = NearExp4S( *from, *to, speed ); + from++; + to++; + } +} + +void GV_NearExp8SV( void *vfrom, void *vto, int speed, int n ) +{ + short *from, *to; + + from = (short *)vfrom; + to = (short *)vto; + while ( --n >= 0 ) + { + *from = NearExp8S( *from, *to, speed ); + from++; + to++; + } +} + +void GV_NearTimePV( void *vfrom, void *vto, int interp, int n ) +{ + short *from, *to; + + if ( interp > 15 ) interp = 15; + interp = TimeInv[ interp ]; + from = (short *)vfrom; + to = (short *)vto; + while ( --n >= 0 ) + { + *from = NearTime( NearPhase( *from, *to ) , *to, interp ); + from++; + to++; + } +} diff --git a/components/mgs/mgs/source/libgv/pad.c b/components/mgs/mgs/source/libgv/pad.c new file mode 100644 index 00000000..c030bbdb --- /dev/null +++ b/components/mgs/mgs/source/libgv/pad.c @@ -0,0 +1,424 @@ +#define __LIBGV_PAD_C__ +#include "libgv.h" + +#include "mts/mts.h" +#include "mts/mts_pad.h" +#include "linkvar.h" +#include "game/game.h" + +/***bss****************************************************************/ +extern int dword_800B05A8[6]; +/*********************************************************************/ + +int GV_PadMask = 0; +STATIC int GV_PadOrigin = 0; +STATIC int GV_800AB37C = 0; + +STATIC int SECTION(".sbss") dword_800AB950; +STATIC int SECTION(".sbss") dword_800AB954; +u_long SECTION(".sbss") GV_DemoPadStatus; +u_long SECTION(".sbss") GV_DemoPadAnalog; + +STATIC short dir_table[16] = { + 0x0000, /* 0: (NONE) */ + 0x0800, /* 1: UP */ + 0x0400, /* 2: RIGHT */ + 0x0600, /* 3: RIGHT + UP */ + 0x0000, /* 4: DOWN */ + 0x0000, /* 5: DOWN + UP (INVALID) */ + 0x0200, /* 6: DOWN + RIGHT */ + 0x0000, /* 7: DOWN + RIGHT + UP (INVALID) */ + 0x0C00, /* 8: LEFT */ + 0x0A00, /* 9: LEFT + UP */ + 0x0000, /* 10: LEFT + RIGHT (INVALID) */ + 0x0000, /* 11: LEFT + RIGHT + UP (INVALID) */ + 0x0E00, /* 12: LEFT + DOWN */ + 0x0000, /* 13: LEFT + DOWN + UP (INVALID) */ + 0x0000, /* 14: LEFT + DOWN + RIGHT (INVALID) */ + 0x0000 /* 15: LEFT + DOWN + RIGHT + UP (INVALID) */ +}; + +#ifdef VR_EXE +static void sub_800165B0(MTS_PAD *data) +{ + unsigned short status = GV_DemoPadStatus; + if (status & 0x400) + { + data->flag = MTS_PAD_ANALOG; + data->lx = GV_DemoPadAnalog; + data->ly = (GV_DemoPadAnalog & 0xFF00) >> 8; + } + else + { + data->flag = MTS_PAD_DIGITAL; + } +} +#endif + +static int GV_SwapButtons(int button, int a, int b) +{ + int swapped; + int i; + + swapped = button; + + for (i = 1; i >= 0; --i) + { + swapped &= ~(a | b); + + if (button & a) + { + swapped |= b; + } + + if (button & b) + { + swapped |= a; + } + + a <<= 16; + b <<= 16; + } + + return swapped; +} + +static int GV_ConvertButtonMode(int button) +{ + switch (GM_OptionFlag & OPTION_BUTTON_MASK) + { + case OPTION_BUTTON_TYPE_B: + return GV_SwapButtons(button, PAD_CIRCLE, PAD_CROSS); + + case OPTION_BUTTON_TYPE_C: + return GV_SwapButtons(button, PAD_CIRCLE, PAD_SQUARE); + + default: // Button Mode A + return button; + } +} + +static void GV_AnalogToDirection(int *button, MTS_PAD *data) +{ + char lx, rx; + int dir; + + *button &= ~PAD_UDLR; + + lx = data->lx; + rx = data->ly; + dir = 0; + + if (lx < 64) + { + dir = PAD_LEFT; + } + else if (lx > 192) + { + dir = PAD_RIGHT; + } + + if (rx < 64) + { + dir |= PAD_UP; + } + else if (rx > 192) + { + dir |= PAD_DOWN; + } + + *button |= dir; +} + +void GV_InitPadSystem(void) +{ + int i; + GV_PAD *pad; + + pad = GV_PadData; + + for (i = 2; i > 0; --i) + { + pad->release = 0; + pad->press = 0; + pad->status = 0; + pad->dir = -1; + pad++; + } + + dword_800AB950 = 0; + GV_PadOrigin = 0; + + for (i = 5; i > -1; --i) + { + dword_800B05A8[i] = 0; + } +} + +void GV_UpdatePadSystem(void) +{ + int chan, prev; + unsigned int t0, t1, t2, t3, t4, t5; + unsigned long button, ret; // button = var_20, ret = s2 + GV_PAD *pad; + MTS_PAD data; + SVECTOR svector; + int s3, var; + + // loc_8001682C + ret = mts_PadRead(0); + button = GV_ConvertButtonMode(ret); + + if (DG_UnDrawFrameCount > 0) + { + button = 0; + ret = 0; + } + else + { + // loc_80016870 + if (GM_GameStatus >= 0) + { + if (GM_GameStatus & STATE_PADDEMO) + { + ret = GV_DemoPadStatus & ~0x06000600; + button = ret; + } + else if (GM_GameStatus & STATE_PADRELEASE) + { + button = 0; + ret = 0; + } + else if (GM_GameStatus & STATE_PADMASK) + { + ret &= GV_PadMask; + button &= GV_PadMask; + } + } + } + + // loc_800168E0 + pad = GV_PadData; + s3 = 0; + chan = 2; + + // loc_800168FC + for (; chan > 0; --chan) + { + if (mts_get_pad((chan % 2) + 1, &data) || (GM_GameStatus & STATE_PADDEMO && chan == 2)) + { + // loc_80016944 + // int local_gamestatus = GM_GameStatus & 0x40000000; + if (GM_GameStatus & STATE_PADDEMO) + { + #ifndef VR_EXE + data.flag = MTS_PAD_DIGITAL; + #else + if (chan == 2) + { + sub_800165B0(&data); + } + else + { + data.flag = MTS_PAD_DIGITAL; + } + #endif + } + + // loc_80016960 + pad->analog = data.flag - 1; + + // if ( pad->analog > 0 && ( GM_GameStatus & 0x90000000 && local_gamestatus ) ) + if (pad->analog > 0 && (!(GM_GameStatus & (STATE_PADRELEASE | STATE_DEMO)) || GM_GameStatus & STATE_PADDEMO)) + { + // loc_8001698C + if (button & PAD_UDLR) + { + // loc_800169A0 + int v0 = dir_table[(button & PAD_UDLR) >> 12]; + v0 += GV_PadOrigin; + pad->dir = v0 & 0x0FFF; + pad->analog = 0; + } + else + { + // loc_800169C0 + MTS_PAD *local_data = &data; + int lx, ly, dir, temp; + + // analog x dir + temp = local_data->lx - 0x80; + if (temp < 0) + { + temp = local_data->lx - 0x79; + } + temp >>= 3; + lx = temp << 8; + svector.vx = lx; + + // analog y dir + temp = local_data->ly - 0x80; + if (temp < 0) + { + temp = local_data->ly - 0x79; + } + // this is all nonsense and clearly not the way it would have been originally programmed + ly = temp >> 3; + temp = ly << 8; + svector.vz = temp; + + temp = lx + 0x07FF; + temp &= 0xFFFF; + + ly <<= 8; + + if (((unsigned int)temp < 0x0FFF) && (ly >= -0x07FF) && (ly < 0x800)) + { + dir = -1; + } + else + { + // loc_80016A2C: + dir = (GV_VecDir2(&svector) + GV_PadOrigin); + } + // loc_80016A40: + pad->dir = dir; + GV_AnalogToDirection((int *)&button, &data); + } + // loc_80016A50: + *((unsigned long *)&pad->right_dx) = *((unsigned long *)(&data.rx)); + if (GM_GameStatus & STATE_PADMASK) + { + if (!(GV_PadMask & PAD_UDLR)) + { + pad->analog = 0; + pad->dir = -1; + } + } + } + else + { + // loc_80016A94 + int val, check; + pad->analog = 0; + check = button & PAD_UDLR; + if (!(check)) + { + val = -1; + } + else + { + check >>= 12; + val = (dir_table[check] + GV_PadOrigin) & 0x0FFF; + } + pad->dir = val; + } + // loc_80016AD8 + var = 1; + GV_800AB37C |= var << chan; + } + else + { + // loc_80016AF0/loc_80016AF4 + pad->analog = 0; + pad->dir = -1; + } + + // loc_80016B00: + pad++; + s3 |= button << ((2 - chan) << 4); + button >>= 16; + } + + // loc_80016B28 + ret |= s3 & 0xF000F000; + button = s3; + GV_CopyMemory(GV_PadData, &GV_PadData[2], sizeof(GV_PAD)*2); +#ifdef __psyz + { + static int budget = 20; + if (budget > 0 && (GV_PadData[0].press || GV_PadData[0].status)) + { + budget--; + if (0) printf("[gvpad] status %04x press %04x\n", + (unsigned)GV_PadData[0].status, + (unsigned)GV_PadData[0].press); + } + } +#endif + + prev = dword_800AB954; + dword_800AB954 = ret; + + t0 = button & ~dword_800AB950; + t1 = 0; + t2 = ~button & dword_800AB950; + t4 = ret & ~prev; + t3 = ~ret & prev; + + dword_800AB950 = button; + + if (t0) + { + for (chan = t1; chan < 6; chan++) + { + t1 |= dword_800B05A8[chan]; + } + t1 &= t0; + } + + // loc_80016BB4 + chan = 4; + t5 = 2; + dword_800B05A8[(GV_Time % 6)] = t0; + + pad = GV_PadData; + + for (; chan > 0; --chan) + { + if (chan == t5) + { + // loc_80016C14 + button = ret; + t0 = t4; + t2 = t3; + } + // loc_80016C20 + pad->status = button; + + pad->press = t0; + t0 >>= 16; // max value + 1; + + pad->release = t2; + t2 >>= 16; + + pad->quick = t1; + t1 >>= 16; + + pad++; + button = (button >> 16) & 0xFFFF; + } +} + +void GV_OriginPadSystem(int org) +{ + GV_PadOrigin = org; +} + +int GV_GetPadOrigin(void) +{ + return GV_PadOrigin; +} + +int GV_GetPadDirNoPadOrg(unsigned int button) +{ + int value; + + value = -1; + if (button & PAD_UDLR) + { + value = (dir_table[(button & PAD_UDLR) >> 12] + GV_PadOrigin) & ~PAD_UDLR & 0xFFFF; + } + + return value - GV_PadOrigin; +} diff --git a/components/mgs/mgs/source/libgv/quat.c b/components/mgs/mgs/source/libgv/quat.c new file mode 100644 index 00000000..80bc3114 --- /dev/null +++ b/components/mgs/mgs/source/libgv/quat.c @@ -0,0 +1,205 @@ +#include "libgv.h" + +#include +#include +#include +#include "inline_n.h" + +// The functions below are based on code listings from the article +// "Rotating Objects Using Quaternions" by Nick Bobick. +// +// This article originally appeared in the February 1998 issue of +// Game Developer Magazine. A digital version can be found here: +// https://media.gdcvault.com/GD_Mag_Archives/GDM_February_1998.pdf +// +// Game Developer Code, Feb.1998: +// https://media.gdcvault.com/GD_Mag_Archives/feb98.zip +// +// Online mirrors: +// https://archive.org/details/GDM_February_1998/page/n20/mode/1up +// https://github.com/dwilliamson/GDMagArchive/tree/master/feb98 + +// clang-format off + +#define DELTA 0 // error tolerance + +void GV_QuatToMat( SQUAT *quat, MATRIX *mat ) +{ + int wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2; + + /* calculate coefficients */ + x2 = quat->x + quat->x; + y2 = quat->y + quat->y; + z2 = quat->z + quat->z; + xx = quat->x * x2; xy = quat->x * y2; xz = quat->x * z2; + yy = quat->y * y2; yz = quat->y * z2; zz = quat->z * z2; + wx = quat->w * x2; wy = quat->w * y2; wz = quat->w * z2; + + mat->m[0][0] = (4096 * 4096 - (yy + zz)) / 4096; + mat->m[0][1] = (xy - wz) / 4096; + mat->m[0][2] = (xz + wy) / 4096; + + mat->m[1][0] = (xy + wz) / 4096; + mat->m[1][1] = (4096 * 4096 - (xx + zz)) / 4096; + mat->m[1][2] = (yz - wx) / 4096; + + mat->m[2][0] = (xz - wy) / 4096; + mat->m[2][1] = (yz + wx) / 4096; + mat->m[2][2] = (4096 * 4096 - (xx + yy)) / 4096; + + mat->t[0] = 0; + mat->t[1] = 0; + mat->t[2] = 0; +} + +void GV_EulerToQuat( SVECTOR *rot, SQUAT *quat ) +{ + int cr, cp, cy, sr, sp, sy, cpcy, spsy; + + /* calculate trig identities */ + cr = rcos( rot->vx / 2 ); + cp = rcos( rot->vy / 2 ); + cy = rcos( rot->vz / 2 ); + + sr = rsin( rot->vx / 2 ); + sp = rsin( rot->vy / 2 ); + sy = rsin( rot->vz / 2 ); + + cpcy = (cp * cy) / 32; + spsy = (sp * sy) / 32; + + quat->w = (cr * cpcy + sr * spsy) / (4096 * 4096 / 32); + quat->x = (sr * cpcy - cr * spsy) / (4096 * 4096 / 32); + quat->y = (cr * sp / 32 * cy + sr * cp / 32 * sy) / (4096 * 4096 / 32); + quat->z = (cr * cp / 32 * sy - sr * sp / 32 * cy) / (4096 * 4096 / 32); +} + +void GV_MatToQuat( MATRIX *mat, SQUAT *quat ) +{ + int tr, s, q[4]; + int i, j, k; + + static int nxt[3] = {1, 2, 0}; + + tr = mat->m[0][0] + mat->m[1][1] + mat->m[2][2]; + + /* check the diagonal */ + if ( tr > 0 ) { + s = SquareRoot12( tr + 4096 ); + + quat->w = s / 2; + + s = 2048 * 4096 / s; + + quat->x = ( mat->m[2][1] - mat->m[1][2] ) * s / 4096 ; + quat->y = ( mat->m[0][2] - mat->m[2][0] ) * s / 4096 ; + quat->z = ( mat->m[1][0] - mat->m[0][1] ) * s / 4096 ; + + } else { + + /* diagonal is negative */ + i = 0; + + if ( mat->m[1][1] > mat->m[0][0] ) i = 1; + if ( mat->m[2][2] > mat->m[i][i] ) i = 2; + + j = nxt[i]; + k = nxt[j]; + + s = SquareRoot12( ( mat->m[i][i] - ( mat->m[j][j] + mat->m[k][k] ) ) + 4096 ); + + q[i] = s / 2 ; + + if ( s != 0 ) s = 2048 * 4096 / s; + + q[3] = ( mat->m[k][j] - mat->m[j][k] ) * s / 4096 ; + q[j] = ( mat->m[j][i] + mat->m[i][j] ) * s / 4096 ; + q[k] = ( mat->m[k][i] + mat->m[i][k] ) * s / 4096 ; + + quat->x = q[0]; + quat->y = q[1]; + quat->z = q[2]; + quat->w = q[3]; + } + +} + +void GV_QuatSlerp( SQUAT *from, SQUAT *to, int t, SQUAT *res) +{ + int to1[4]; + int omega, cosom, sinom; + int scale0, scale1; + + /* calc cosine */ + cosom = ( from->x * to->x + from->y * to->y + from->z * to->z + from->w * to->w ) / 4096 ; + + /* adjust signs (if necessary) */ + if ( cosom < 0 ){ + cosom = -cosom; + to1[0] = - to->x; + to1[1] = - to->y; + to1[2] = - to->z; + to1[3] = - to->w; + } else { + to1[0] = to->x; + to1[1] = to->y; + to1[2] = to->z; + to1[3] = to->w; + } + + /* calculate coefficients */ + if ( (4096 - cosom) > DELTA ){ + /* standard case (slerp) */ + sinom = SquareRoot12(4096 - ( cosom * cosom ) / 4096); + omega = ratan2(sinom, cosom); + sinom = 4096 * 4096 / sinom; + scale0 = (rsin(((4096 - t) * omega) / 4096) * sinom) >> 12; + scale1 = (rsin((t * omega) / 4096) * sinom) >> 12; + } else { + /* "from" and "to" quaternions are very close */ + /* ... so we can do a linear interpolation */ + scale0 = 4096 - t; + scale1 = t; + } + + /* calculate final values */ + res->x = ( scale0 * from->x + scale1 * to1[0] ) / 4096 ; + res->y = ( scale0 * from->y + scale1 * to1[1] ) / 4096 ; + res->z = ( scale0 * from->z + scale1 * to1[2] ) / 4096 ; + res->w = ( scale0 * from->w + scale1 * to1[3] ) / 4096 ; +} + +void GV_QuatNormalize( SQUAT *quat ) +{ + VECTOR vec; + int ww, len; + + gte_ldsv(quat); + gte_sqr0(); + ww = quat->w * quat->w; + gte_stlvnl(&vec); + + len = ww; + len += vec.vx + vec.vy + vec.vz; + + len = SquareRoot0( len ); + if ( len > 0 ){ + len = 4096 * 4096 / len; + quat->x = ( quat->x * len ) >> 12 ; + quat->y = ( quat->y * len ) >> 12 ; + quat->z = ( quat->z * len ) >> 12 ; + quat->w = ( quat->w * len ) >> 12 ; + } +} + +void GV_QuatMul( SQUAT *q1, SQUAT *q2, SQUAT *res ) +{ + res->x = ( q1->w * q2->x + q1->x * q2->w + q1->y * q2->z - q1->z * q2->y ) / 4096 ; + res->y = ( q1->w * q2->y + q1->y * q2->w + q1->z * q2->x - q1->x * q2->z ) / 4096 ; + res->z = ( q1->w * q2->z + q1->z * q2->w + q1->x * q2->y - q1->y * q2->x ) / 4096 ; + res->w = ( q1->w * q2->w - q1->x * q2->x - q1->y * q2->y - q1->z * q2->z ) / 4096 ; + + GV_QuatNormalize( res ); +} + +// clang-format on diff --git a/components/mgs/mgs/source/libgv/resident.c b/components/mgs/mgs/source/libgv/resident.c new file mode 100644 index 00000000..ae43b530 --- /dev/null +++ b/components/mgs/mgs/source/libgv/resident.c @@ -0,0 +1,79 @@ +#include "libgv.h" +#include +#include "common.h" + +extern unsigned char *StageCharacterEntries; + +unsigned char *SECTION(".sbss") GV_ResidentMemoryBottom; +STATIC int SECTION(".sbss") dword_800AB944; + +#ifdef MGS_ESPBOX +void *GV_ResidentAreaBottom; /* mgs_main_ram is a pointer here: set on init */ +#else +void *GV_ResidentAreaBottom = RESIDENT_BOTTOM; +#endif + +void GV_InitResidentMemory( void ) +{ +#ifdef MGS_ESPBOX + if ( !GV_ResidentAreaBottom ) GV_ResidentAreaBottom = RESIDENT_BOTTOM; +#endif + GV_ResidentMemoryBottom = GV_ResidentAreaBottom; +} + +void GV_SaveResidentTop( void ) +{ + GV_ResidentAreaBottom = GV_ResidentMemoryBottom; +} + +/** + * @brief Reserves a block of memory from the bottom + * of the resident memory area. + * + * @param size The size of the memory block to reserve. + * + * @return pointer to the new resident memory bottom + */ +void *GV_AllocResidentMemory( long size ) +{ +#ifdef DEV_EXE + // linker-defined symbol + extern unsigned char _bss_orgend[]; +#endif + + // Align the size to 4 bytes + size = (size + 3) & ~3; + + // decrement the bottom of the resident memory + GV_ResidentMemoryBottom -= size; + +#if defined(__psyz) + { + /* the reservation's floor is the real limit; StageCharacterEntries is a + * native link-time address here and comparing against it is noise */ +#ifdef MGS_ESPBOX + extern unsigned char* mgs_main_ram; /* allocated at start (esp-box-emu) */ +#else + extern unsigned char mgs_main_ram[]; +#endif + if (GV_ResidentMemoryBottom < mgs_main_ram) + { + printf("Resident Memory Over !!\n"); + } + } + return GV_ResidentMemoryBottom; +#endif +#ifdef DEV_EXE + // dev_exe has to compare to _bss_orgend since the overlay base pointer + // used by the OG code will be pointing somewhere in the .data section. + if (GV_ResidentMemoryBottom < _bss_orgend) +#else + // BUG: the overlay can potentially be alloc'd over with no warning. + if (GV_ResidentMemoryBottom < StageCharacterEntries) +#endif + { + printf("Resident Memory Over !!\n"); + } + + return GV_ResidentMemoryBottom; +} diff --git a/components/mgs/mgs/source/libgv/strcode.c b/components/mgs/mgs/source/libgv/strcode.c new file mode 100644 index 00000000..79a16181 --- /dev/null +++ b/components/mgs/mgs/source/libgv/strcode.c @@ -0,0 +1,47 @@ +#include + +#define BIT_LEN 16 + +/** + * @brief Generate a simple hash from a string. + * + * Given that the digest is only 16 bits wide, this algorithm is highly + * prone to collisions. Check for collisions after precalculating a set + * of hashes intended for use at runtime. (e.g. stage data files) + * + * @li Passing an empty string will return 0. + * @li Passing a single-char string will return the value of the char. + * @li Mutations of strings differing in only the last character will + * result in the lowest byte of the hash mirroring that difference. + * + * Input/Output Examples: + * + * @li GV_StrCode("cb_box11") returns @c 0xADDB + * @li GV_StrCode("cb_box12") returns @c 0xADDC + * @li GV_StrCode("op_kcej_l") returns @c 0x4349 + * @li GV_StrCode("op_kcej_r") returns @c 0x434F + * + * @param string NULL-terminated string. + * + * @retval 0 if @p string is empty. + * @retval non-zero 16-bit hash of @p string. + */ +int GV_StrCode( const char *string ) +{ + u_char c; + u_char *p = (u_char *)string; + u_short id = 0; + + while (( c = *p++ )) { + id = ((id << 5) | (id >> (BIT_LEN - 5))); + id += c; + } + return id; +} + +// This algorithm appears to be derived from "djb2" (aka. DJBX33A), +// first described by Daniel J. Bernstein on Usenet's comp.lang.c in 1991. +// +// Reference implementation: +// http://www.cse.yorku.ca/~oz/hash.html +// diff --git a/components/mgs/mgs/source/libgv/util.c b/components/mgs/mgs/source/libgv/util.c new file mode 100644 index 00000000..f017b63f --- /dev/null +++ b/components/mgs/mgs/source/libgv/util.c @@ -0,0 +1,200 @@ +#include "libgv.h" + +#include +#include +#include +#include +#include "common.h" + +/** + * @brief Adds two 3D vectors. + * + * This function adds the components of two 3D vectors and stores the result + * in the destination vector. + * + * @param vec1 Pointer to the first vector. + * @param vec2 Pointer to the second vector. + * @param vec3 Pointer to the destination vector where the result will be stored. + */ +void GV_AddVec3( SVECTOR *vec1, SVECTOR *vec2, SVECTOR *vec3 ) +{ + vec3->vx = vec1->vx + vec2->vx; + vec3->vy = vec1->vy + vec2->vy; + vec3->vz = vec1->vz + vec2->vz; +} + +/** + * @brief Subtracts one 3D vector from another. + * + * This function subtracts the components of the second vector from the first + * vector and stores the result in the destination vector. + * + * @param vec1 Pointer to the first vector. + * @param vec2 Pointer to the second vector. + * @param vec3 Pointer to the destination vector where the result will be stored. + */ +void GV_SubVec3( SVECTOR *vec1, SVECTOR *vec2, SVECTOR *vec3 ) +{ + vec3->vx = vec1->vx - vec2->vx; + vec3->vy = vec1->vy - vec2->vy; + vec3->vz = vec1->vz - vec2->vz; +} + +/** + * @brief Calculates the length of a 3D vector. + * + * This function calculates the Euclidean length (magnitude) of a 3D vector. + * + * @param vec Pointer to the vector. + * @return The length of the vector. + */ +int GV_VecLen3( SVECTOR *vec ) +{ + VECTOR tmp; + int len; + + tmp.vx = vec->vx; + tmp.vy = vec->vy; + tmp.vz = vec->vz; + Square0( &tmp, &tmp ); + len = SquareRoot0( tmp.vx + tmp.vy + tmp.vz ); + return len; +} + +/** + * @brief Scales a 3D vector by a ratio. + * + * This function scales the components of a 3D vector by a ratio defined by the + * numerator and denominator. + * + * @param vec1 Pointer to the input vector. + * @param vec2 Pointer to the output vector where the result will be stored. + * @param len1 The denominator of the scaling ratio. + * @param len2 The numerator of the scaling ratio. + */ +void GV_LenVec3( SVECTOR *vec1, SVECTOR *vec2, int len1, int len2 ) +{ + int len; + + len = len2 * 4096; + if ( len1 == 0 ) len1 = 1; + len /= len1; + vec2->vx = vec1->vx * len / 4096; + vec2->vy = vec1->vy * len / 4096; + vec2->vz = vec1->vz * len / 4096; +} + +/** + * @brief Calculates the difference between two 3D vectors. + * + * This function calculates the Euclidean distance between two 3D vectors. + * + * @param vec1 Pointer to the first vector. + * @param vec2 Pointer to the second vector. + * @return The distance between the two vectors. + */ +int GV_DiffVec3( SVECTOR *vec1, SVECTOR *vec2 ) +{ + VECTOR tmp; + + tmp.vx = vec1->vx - vec2->vx; + tmp.vy = vec1->vy - vec2->vy; + tmp.vz = vec1->vz - vec2->vz; + + Square0( &tmp, &tmp ); + return SquareRoot0( tmp.vx + tmp.vy + tmp.vz ); +} + +/** + * @brief Calculates the direction of a 2D vector. + * + * This function calculates the direction (angle) of a 2D vector in + * the x-z plane. + * + * @param vec Pointer to the vector. + * @return The direction of the vector in the x-z plane. + */ +int GV_VecDir2( SVECTOR *vec ) +{ + return 4095 & ratan2( vec->vx, vec->vz ); +} + +/** + * @brief Creates a 2D vector from an angle and radius. + * + * This function creates a 2D vector in the x-z plane from a given angle + * and radius. + * + * @param dir The angle in the x-z plane. + * @param len The radius (length) of the vector. + * @param vec Pointer to the output vector where the result will be stored. + */ +void GV_DirVec2( int dir, int len, SVECTOR *vec ) +{ + vec->vx = len * rsin( dir ) / 4096; + vec->vy = 0; + vec->vz = len * rcos( dir ) / 4096; +} + +/** + * @brief Creates a 3D vector from angles and length. + * + * This function creates a 3D vector from given angles and length using a + * rotation matrix. + * + * @param dir Pointer to the vector containing the angles. + * @param len The length of the vector. + * @param vec Pointer to the output vector where the result will be stored. + */ +void GV_DirVec3( SVECTOR *dir, int len, SVECTOR *vec ) +{ + MATRIX m0; + + RotMatrixYXZ( dir, &m0 ); + vec->vx = len * m0.m[ 0 ][ 2 ] / 4096; + vec->vy = len * m0.m[ 1 ][ 2 ] / 4096; + vec->vz = len * m0.m[ 2 ][ 2 ] / 4096; +} + +int GV_DiffDirU( int from, int to ) +{ + return 4095 & ( to - from ); +} + +int GV_DiffDirS( int from, int to ) +{ + int diff; + + diff = 4095 & ( to - from ); + return ( diff <= 2048 ) ? diff : diff - 4096; +} + +int GV_DiffDirAbs( int from, int to ) +{ + int diff; + + diff = 4095 & ( to - from ); + return ( diff <= 2048 ) ? diff : 4096 - diff; +} + +/** + * @brief Generates a random unsigned integer within a range [0, input). + * + * @param input The upper bound of the range. + * @return The random unsigned integer. + */ +int GV_RandU( int input ) +{ + return ( input - 1 ) & rand(); +} + +/** + * @brief Generates a random signed integer within a range [-input, input). + * + * @param input The -lower/upper bound of the range. + * @return The random signed integer. + */ +int GV_RandS( int input ) +{ + return ( ( input * 2 - 1 ) & rand() ) - input; +} diff --git a/components/mgs/mgs/source/libhzd/bind.c b/components/mgs/mgs/source/libhzd/bind.c new file mode 100644 index 00000000..a7a8449e --- /dev/null +++ b/components/mgs/mgs/source/libhzd/bind.c @@ -0,0 +1,431 @@ +#define __LIBHZD_EVENT_C__ +#include "libhzd.h" + +#include "common.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "game/delay.h" +#include "linkvar.h" +#include "strcode.h" + +STATIC int dword_8009D548[] = {0, 0x20, 0x40, 8, 0x800, 0x10000, 0x20000, 0x1000, 4, 1}; +STATIC int dword_8009D570[] = {0x20, 0x80, 0x40, 0x10}; + +// TODO: These two shouldn't belong in libhzd... +unsigned short GM_WeaponTypes[] = { + 0x0110, // 0: NONE + 0x9042, // 1: SOCOM + 0x9043, // 2: FAMAS + 0x414C, // 3: GRENADE + 0x20C0, // 4: NIKITA + 0x2220, // 5: STINGER + 0x4110, // 6: CLAYMORE + 0x4110, // 7: C4 + 0x414C, // 8: STUN G. + 0x414C, // 9: CHAFF G. + 0x9220 // 10: PSG1 +}; +unsigned short GM_ItemTypes[] = { + 0x0000, // 0: NONE + 0x8000, // 1: CIGS + 0x8003, // 2: SCOPE + 0x8001, // 3: C.BOX A + 0x8001, // 4: C.BOX B + 0x8001, // 5: C.BOX C + 0x8000, // 6: N.V.G. + 0x8000, // 7: THERM G. + 0x8000, // 8: GASMASK + 0x8000, // 9: B.ARMOR + 0x8000, // 10: KETCHUP + 0x8000, // 11: STEALTH + 0x8000, // 12: BANDANA + 0x8003, // 13: CAMERA + 0x2000, // 14: RATION + 0x2000, // 15: MEDICINE + 0x2000, // 16: DIAZEPAM + 0, // 17: PAL KEY + 0, // 18: CARD + 0, // 19: TIMER B. + 0, // 20: MINE D. + 0, // 21: DISC + 0, // 22: ROPE + 0, // 23: SCARF + 0, // 24: SUPPR + 0 // 25: ??? +}; + +extern int N_Binds; +extern HZD_BND *Bind; + +int SECTION(".sbss") N_Binds; +HZD_BND *SECTION(".sbss") Bind; + +/* +possible function names + HZD_ReExecEventSubXQ + HZD_ExecEventRCM + Binds + HZD_BindMapChangep + HZD_SetEvent + HZD_ExecBind + HZD_ExecEventRCM + HZD_ExecEventL + HZD_ReExecEventSub + HZD_FlashEvent + ExecLeaveEvent + HZD_EnterTrap +*/ + +void HZD_SetBind( int param_1, HZD_BND *bind, int n_bind ) +{ + N_Binds = n_bind; + Bind = bind; + return; +} + +void HZD_BindMapChange( int map ) +{ + HZD_BND *bind; + int i; + + bind = Bind; + for ( i = N_Binds; i > 0; i-- ) + { + if ( !( bind->map & map ) ) + { + bind->field_8_param_i_c_flags &= ~0x80; + } + + bind++; + } +} + +void HZD_SetEvent( HZD_EVT *event, int name ) +{ + unsigned short *tmp; + int i; + + event->name = name; + event->n_inside = 0; + event->object = 0; + event->type = 0; + + tmp = event->inside; + + for ( i = 6 ; i > 0 ; i-- ) + { + *tmp++ = 0; + } + + event->mov.vz = 0; + event->mov.vy = 0; + event->mov.vx = 0; +} + +void HZD_ExecBind( HZD_BND *bnd, HZD_EVT *ev, int event, int type ) +{ + GCL_ARGS args; + long buf[8], *p; + + p = buf; + *p++ = ev->object; + *p++ = ev->name; + *p++ = event; + *p++ = ev->mov.vx; + *p++ = ev->mov.vy; + *p++ = ev->mov.vz; + *p++ = type; + + args.argc = 7; + args.argv = buf; + + if ( bnd->time != 0 ) + { + GM_DelayedExecCommand( bnd->command, &args, bnd->time ); + } + else if ( bnd->field_B_param_e & 0x80 ) + { + GCL_ExecProc( (int)bnd->command, &args ); + } + else + { + GCL_ExecBlock( (u_char *)bnd->command, &args ); + } +} + +static inline int HashMatch(unsigned short value, unsigned int hash) +{ + return (value == 0) || (value == hash); +} + +static void HZD_ExecEventRCM( HZD_HDL *hzd, HZD_EVT *event, int mode ) +{ + HZD_BND *pBind; + unsigned int hash; + unsigned int name; + unsigned int trigger; + int count; + unsigned int one; + unsigned int name2; + + pBind = Bind; + hash = event->name; + name = event->type; + trigger = event->object; + + count = N_Binds; + for (count--; count >= 0; pBind++, count--) + { + one = 1; + + if (!HashMatch(pBind->field_4, trigger)) + { + continue; + } + + if (!(pBind->map & hzd->map)) + { + continue; + } + + if (!HashMatch(pBind->field_0, hash)) + { + if (!(pBind->field_8_param_i_c_flags & 0x2)) + { + continue; + } + + if (!HashMatch(pBind->field_0, SNAKE_NAME)) + { + continue; + } + } + + name2 = name; + + if (pBind->field_B_param_e & 0xF) + { + continue; + } + + if ((mode != 0) && (one != 0)) + { + if ((mode != one) && !(pBind->field_B_param_e & 0x40)) + { + continue; + } + } + else + { + name2 = HASH_LEAVE; + pBind->field_8_param_i_c_flags &= ~0x80; + } + + if (HashMatch(pBind->field_2_param_m, name2)) + { + HZD_ExecBind(pBind, event, name2, 2); + } + } +} + +static inline int HZD_helper2_80029D50(HZD_BND *pBind, HZD_EVT *event) +{ + int diff; + int mask; + + if (!(pBind->field_B_param_e & 0xF)) + { + return 1; + } + + if (pBind->field_B_param_e & 0x1) + { + diff = (-pBind->field_C_param_d + event->mov.pad) & 0xFFF; + + if (diff > 2048) + { + diff = 4096 - diff; + } + + if (pBind->field_E_param_d_or_512 < diff) + { + return 0; + } + } + + if (pBind->field_B_param_e & 0x2) + { + if (dword_8009D548[pBind->field_9_param_s]) + { + if (!(GM_PlayerStatus & dword_8009D548[pBind->field_9_param_s])) + { + return 0; + } + } + else if (GM_PlayerStatus != 0) + { + if ((GM_UnkFlagBE != 0) && !(GM_PlayerStatus & 0x2)) + { + mask = 0xFFF4C011; + } + else + { + mask = 0xFFF44010; + } + + if (GM_PlayerStatus & ~mask) + { + return 0; + } + } + } + + if (pBind->field_B_param_e & 0x4) + { + if (GM_PlayerStatus & PLAYER_NOT_PLAYABLE) + { + return 0; + } + + if (!(GV_PadData[0].press & dword_8009D570[pBind->field_A_param_b])) + { + return 0; + } + } + + return 1; +} + +void HZD_ExecEvent(HZD_HDL *hzd, HZD_EVT *event, int mode) +{ + HZD_BND *pBind; + unsigned int hash, hash2; + unsigned int name; + unsigned int trigger; + int count; + unsigned char flag; + + if (event->name == RCM_NAME) + { + HZD_ExecEventRCM(hzd, event, mode); + return; + } + + pBind = Bind; + + hash = event->name; + name = event->type; + trigger = event->object; + + count = N_Binds; + for (count--; count >= 0; pBind++, count--) + { + if (!HashMatch(pBind->field_4, trigger) || !(pBind->map & hzd->map)) + { + continue; + } + + if (!HashMatch(pBind->field_0, hash)) + { + continue; + } + + hash2 = name; + + if ((mode != 0) && HZD_helper2_80029D50(pBind, event)) + { + if (!(pBind->field_B_param_e & 0xF)) + { + if ((mode != 1) && !(pBind->field_B_param_e & 0x40)) + { + continue; + } + } + else if (pBind->field_8_param_i_c_flags & 0x80) + { + continue; + } + else if (!(pBind->field_B_param_e & 0x40)) + { + pBind->field_8_param_i_c_flags |= 0x80; + } + } + else + { + flag = pBind->field_B_param_e; + if ((flag & 0xF) && !(flag & 0x40) && !(pBind->field_8_param_i_c_flags & 0x80)) + { + continue; + } + + hash2 = HASH_LEAVE; + pBind->field_8_param_i_c_flags &= ~0x80; + } + + if (HashMatch(pBind->field_2_param_m, hash2)) + { + HZD_ExecBind(pBind, event, hash2, 0); + } + } +} + +//HZD_ExecEventSub ? +STATIC void HZD_8002A090(HZD_HDL *hzd, HZD_EVT *event, int flags, int hash) +{ + HZD_BND *pBinds; + int bindCount; + int msgType; + unsigned short *pArray; + int count; + + pBinds = Bind; + bindCount = N_Binds; + + msgType = event->name; + + for (bindCount--; bindCount >= 0; pBinds++, bindCount--) + { + if (!(pBinds->map & hzd->map)) + { + continue; + } + + if (flags != 0 && !(pBinds->field_8_param_i_c_flags & flags)) + { + continue; + } + + if (!HashMatch(pBinds->field_0, msgType)) + { + continue; + } + + pArray = event->inside; + + for (count = event->n_inside; count > 0; count--, pArray++) + { + if (HashMatch(pBinds->field_4, *pArray) && HashMatch(pBinds->field_2_param_m, hash)) + { + HZD_ExecBind(pBinds, event, hash, 1); + } + } + } +} + +void HZD_ReExecEvent(HZD_HDL *hzd, HZD_EVT *event, unsigned int flags) +{ + if (flags & 0x200) + { + HZD_8002A090(hzd, event, flags, HASH_LEAVE); + } + if (flags & 0x100) + { + HZD_8002A090(hzd, event, flags, HASH_ENTER); + } +} + +void HZD_ExecLeaveEvent(HZD_HDL *hzd, HZD_EVT *event) +{ + HZD_8002A090(hzd, event, 0, HASH_LEAVE); +} diff --git a/components/mgs/mgs/source/libhzd/dynamic.c b/components/mgs/mgs/source/libhzd/dynamic.c new file mode 100644 index 00000000..cc5cd2a3 --- /dev/null +++ b/components/mgs/mgs/source/libhzd/dynamic.c @@ -0,0 +1,165 @@ +#include "libhzd.h" +#include "common.h" + +int HZD_QueueDynamicSegment2(HZD_HDL *hdl, HZD_SEG *seg, int flag) +{ + int idx = hdl->dynamic_queue_index; + if ( idx >= hdl->max_dynamic_segments ) + { + return -1; + } + hdl->dynamic_segments[idx] = seg; + hdl->dynamic_flags[idx] = flag; + hdl->dynamic_flags[hdl->max_dynamic_segments + idx] = flag >> 8; + hdl->dynamic_queue_index = idx + 1; + return 0; +} + +void HZD_DequeueDynamicSegment(HZD_HDL *hdl, HZD_SEG *seg) +{ + HZD_SEG **var_a2; + char *var_t0; + int temp_a3; + char *var_t1; + int var_v1; + + var_a2 = hdl->dynamic_segments; + var_t0 = hdl->dynamic_flags; + temp_a3 = hdl->dynamic_queue_index; + var_t1 = var_t0 + hdl->max_dynamic_segments; + var_v1 = temp_a3; + + while (var_v1 > 0) + { + var_v1--; + + if (*var_a2 == seg) + { + goto found; + } + + var_a2++; + var_t0++; + var_t1++; + } + + return; + +found: + temp_a3--; + *var_a2 = hdl->dynamic_segments[temp_a3]; + *var_t0 = hdl->dynamic_flags[temp_a3]; + *var_t1 = hdl->dynamic_flags[temp_a3 + hdl->max_dynamic_segments]; + hdl->dynamic_queue_index = temp_a3; +} + +void HZD_SetDynamicSegment(HZD_SEG *a1, HZD_SEG *a2) +{ + int sVar1; + int sVar2; + + sVar2 = a1->p1.x; + sVar1 = a1->p2.x; + + if (sVar2 >= sVar1) + { + if (sVar2 <= sVar1) + { + sVar2 = a1->p1.z; + sVar1 = a1->p2.z; + + if (sVar2 >= sVar1) + { + if (sVar2 <= sVar1) + { + a1->p2.x++; + + if (a1 != a2) + { + *a2 = *a1; + } + + return; + } + } + else + { + if (a1 != a2) + { + *a2 = *a1; + } + + return; + } + } + } + else + { + if (a1 != a2) + { + *a2 = *a1; + } + + return; + } + + // Are those SCOPYL2 (or similar) inlines? + sVar2 = a1->p1.x; + sVar1 = a1->p2.x; + a2->p2.x = sVar2; + a2->p1.x = sVar1; + sVar2 = a1->p1.y; + sVar1 = a1->p2.y; + a2->p2.y = sVar2; + a2->p1.y = sVar1; + sVar2 = a1->p1.z; + sVar1 = a1->p2.z; + a2->p2.z = sVar2; + a2->p1.z = sVar1; + sVar2 = a1->p1.h; + sVar1 = a1->p2.h; + a2->p2.h = sVar2; + a2->p1.h = sVar1; +} + +int HZD_QueueDynamicFloor(HZD_HDL *hdl, HZD_FLR *floor) +{ + int count; + + if (hdl->dynamic_floor_index >= hdl->max_dynamic_floors) + { + return -1; + } + + count = hdl->dynamic_floor_index; + hdl->dynamic_floors[count] = floor; + hdl->dynamic_floor_index = count + 1; + floor->p4.h |= 0x01; + + return 0; +} + +void HZD_DequeueDynamicFloor(HZD_HDL *hdl, HZD_FLR *floor) +{ + HZD_FLR **ptr; + int count; + int i; + + ptr = hdl->dynamic_floors; + count = hdl->dynamic_floor_index; + + for (i = count; i > 0; i--, ptr++) + { + if (*ptr == floor) + { + goto found; + } + } + + return; + +found: + count--; + *ptr = hdl->dynamic_floors[count]; + hdl->dynamic_floor_index = count; +} diff --git a/components/mgs/mgs/source/libhzd/hzdd.c b/components/mgs/mgs/source/libhzd/hzdd.c new file mode 100644 index 00000000..0196010b --- /dev/null +++ b/components/mgs/mgs/source/libhzd/hzdd.c @@ -0,0 +1,153 @@ +#define __LIBHZD_HZDD_C__ +#include "libhzd.h" + +#include +#include "libgv/libgv.h" + +int SECTION(".sbss") dword_800AB9A4; // unused +int SECTION(".sbss") HZD_CurrentGroup; +int SECTION(".sbss") dword_800AB9AC; // unused + +//------------------------------------------------------------------------------ + +void HZD_StartDaemon(void) +{ + GV_SetLoader('h', HZD_LoadInitHzd); +} + +STATIC void HZD_ProcessTraps(HZD_TRG *trap, int n_traps) +{ + int i; + char *new_var; + int j; + char *s; + + for (i = n_traps - 1; i > (-1); i--) + { + s = trap->trap.name; + j = (sizeof(trap->trap.name)) + 1; + + if (s[0xd] == ((char) (-1))) + { + break; + } + for (; (j > 0) && ((*s) != ' '); j--) + { + s++; + } + + *s = '\0'; + trap->trap.name_id = GV_StrCode(trap->trap.name); + trap++; + new_var = trap->trap.name; + s = new_var; + } +} + +STATIC void HZD_ProcessRoutes(HZD_PAT *routes, int n_routes, HZD_DEF *hzm) +{ + HZD_PTP *points; + int i; + + points = (HZD_PTP *)&routes->points; + for (i = n_routes - 1; i > -1; i--) + { + OFFSET_TO_PTR(hzm, points); + points++; + } +} + +int HZD_LoadInitHzd(void *buf, int id) +{ + HZD_DEF *hzm; + HZD_GRP *area; + int i; + + hzm = (HZD_DEF *)buf; + if (hzm->version < 2) + { + printf("Warning:old version hzm\n"); + } + + hzm->ptr_access[0] = 0; + + OFFSET_TO_PTR(hzm, &hzm->groups); + OFFSET_TO_PTR(hzm, &hzm->zones); + OFFSET_TO_PTR(hzm, &hzm->routes); + + HZD_ProcessRoutes(hzm->routes, hzm->n_routes, hzm); + + area = hzm->groups; + for (i = hzm->n_groups; i > 0; i--) + { + OFFSET_TO_PTR(hzm, &area->walls); + OFFSET_TO_PTR(hzm, &area->floors); + OFFSET_TO_PTR(hzm, &area->triggers); + OFFSET_TO_PTR(hzm, &area->wallsFlags); + + HZD_ProcessTraps((HZD_TRG *)area->triggers, area->n_triggers); + area++; + } + + return 1; +} + +HZD_HDL *HZD_MakeHandler(HZD_DEF *hzd, int areaIndex, int dynamic_segments, int dynamic_floors) +{ + short n_zones; + void *zones; + HZD_HDL *hzdMap; + int i; + HZD_TRG *trig; + + if (*(int *)hzd == 0) + { + n_zones = hzd->n_zones; + if (n_zones > 1) + { + zones = GV_Malloc((n_zones - 1) * (n_zones - 2) / 2 + (n_zones - 1)); + HZD_MakeRoute(hzd, zones); + *(int *)hzd = (int)zones; + } + } + + hzdMap = (HZD_HDL *)GV_Malloc((4 * dynamic_floors) + sizeof(HZD_HDL) + (4 * dynamic_segments) + (2 * dynamic_segments)); + if (hzdMap) + { + hzdMap->dynamic_floors = (void *)&hzdMap[1]; + hzdMap->dynamic_segments = (void *)&hzdMap->dynamic_floors[dynamic_floors]; + hzdMap->dynamic_flags = (char*)&hzdMap->dynamic_segments[dynamic_segments]; + + hzdMap->max_dynamic_segments = dynamic_segments; + hzdMap->max_dynamic_floors = dynamic_floors; + hzdMap->def = hzd; + hzdMap->grp = &hzd->groups[areaIndex]; + hzdMap->dynamic_queue_index = 0; + hzdMap->dynamic_floor_index = 0; + hzdMap->route = *(u_char **)hzd; + + trig = hzdMap->grp->triggers; + for (i = hzdMap->grp->n_triggers; i > 0; i--) + { + // stop when we find a camera (traps are stored after cameras) + if (trig->trap.id2 == (char)-1) + { + break; + } + trig++; + } + hzdMap->n_cameras = i; + hzdMap->traps = (HZD_TRP *)trig; + } + + return hzdMap; +} + +void HZD_FreeHandler(HZD_HDL *hdl) +{ + if (hdl != NULL) + { + GV_Free(hdl); + } + return; +} diff --git a/components/mgs/mgs/source/libhzd/level.c b/components/mgs/mgs/source/libhzd/level.c new file mode 100644 index 00000000..7590cac3 --- /dev/null +++ b/components/mgs/mgs/source/libhzd/level.c @@ -0,0 +1,331 @@ +#include "libhzd.h" + +#include "common.h" +#include "libdg/libdg.h" +#include "libgv/libgv.h" +#include "inline_n.h" + +/*possible funcs + HZD_GetLevelHazard + HZD_GetLevelHeight + HZD_LevelHazardCheckOne + HZD_GetLevelAtr +*/ + +static void CopyVector(SVECTOR *src, HZD_VEC *dst) +{ + dst->x = src->vx; + dst->y = src->vy; + dst->z = src->vz; +} + +typedef struct { + char unused1[0x8]; // 00 + int side; // 08 + HZD_VEC point; // 0C + char unused2[0x20]; // 14 + HZD_VEC f34; // 34 + HZD_FLR *max_floor; // 3C + HZD_FLR *min_floor; // 40 + int max_level; // 44 + int min_level; // 48 +} SCRPAD_DATA; + +#define SCRPAD ((SCRPAD_DATA *)SCRPAD_ADDR) + +#define SIDE (*(int *)(SCRPAD_ADDR + 0x8)) +#define POINT (*(HZD_VEC *)(SCRPAD_ADDR + 0xC)) +#define MAX_FLOOR (*(HZD_FLR **)(SCRPAD_ADDR + 0x3C)) +#define MIN_FLOOR (*(HZD_FLR **)(SCRPAD_ADDR + 0x40)) + +STATIC int HZD_LevelCheckInPoint(HZD_FLR *floor) +{ + int p0, p1, p2, p3, p4; + + p0 = POINT.long_access[0]; + p1 = floor->p1.long_access[0]; + p2 = floor->p2.long_access[0]; + + gte_ldsxy3( p1, p2, p0 ); + gte_nclip(); + p3 = floor->p3.long_access[0]; + gte_stopz( &SIDE ); + + if ( SIDE >= 0 ) + { + gte_ldsxy3( p2, p3, p0 ); + gte_nclip(); + p4 = floor->p4.long_access[0]; + gte_stopz( &SIDE ); + + if ( SIDE < 0 ) return 0; + + gte_ldsxy3( p3, p4, p0 ); + gte_nclip(); + gte_stopz( &SIDE ); + + if ( SIDE < 0 ) return 0; + + gte_ldsxy3( p4, p1, p0 ); + gte_nclip(); + gte_stopz( &SIDE ); + + return SIDE >= 0; + } + else + { + gte_ldsxy3( p2, p3, p0 ); + gte_nclip(); + p4 = floor->p4.long_access[0]; + gte_stopz( &SIDE ); + + if ( SIDE > 0 ) return 0; + + gte_ldsxy3( p3, p4, p0 ); + gte_nclip(); + gte_stopz( &SIDE ); + + if ( SIDE > 0 ) return 0; + + gte_ldsxy3( p4, p1, p0 ); + gte_nclip(); + gte_stopz( &SIDE ); + + return SIDE <= 0; + } + +} + +static inline void HZD_LevelPointHeight_helper(void) +{ + // what were the original parameters for this crap? trying to make + // the source and destination two arguments swaps the registers + short *scratch2 = ( short * )SCRPAD_ADDR; + + scratch2[3] = *(short *)(SCRPAD_ADDR + 0x038); + scratch2[2] = -*(short *)(SCRPAD_ADDR + 0x03a); +} + +static inline void assign_subtract( int idx, short idx2, short idx3, short *val ) +{ + ((short*)SCRPAD_ADDR)[idx] = ((short*)SCRPAD_ADDR)[idx2] - val[idx3]; +} + +STATIC int SlopeFloorLevel(HZD_FLR *floor) +{ + short *test; + int x, y; + + assign_subtract( 26, 6, 0, ( short * )&floor->p1 ); + assign_subtract( 27, 7, 1, ( short * )&floor->p1 ); + + //todo: fix below, probably some inline + test = ( short * )(SCRPAD_ADDR + 0x038); + test[0] = floor->p1.h; + do {} while(0); + test[y = 1] = floor->p2.h; + + HZD_LevelPointHeight_helper(); + + gte_ldsxy3(0, *( int * )(SCRPAD_ADDR + 0x034), *( int* )(SCRPAD_ADDR + 0x004)); + gte_nclip(); + gte_stopz( (SCRPAD_ADDR + 0x008) ); + + x = *(int * )(SCRPAD_ADDR + 0x008); +#ifdef __psyz + { + /* The floor height Snake stands on comes out of this one division, and + * it is coming out wrong: his slope probe 500 units ahead reports a + * 742-unit step on a flat dock, which then feeds SquareRoot0 a + * negative sum of squares. Everything here rides on scratchpad offsets + * and on HZD_FLR's layout, both of which this port has already had to + * correct elsewhere, so show the operands rather than guess which. */ + static int budget = 12; + int div = floor->p3.h; + int res; + + if (div == 0) + { + if (budget > 0) + { + budget--; + printf("[hzd] p3.h is 0 -- floor %p p1.y %d x %d\n", + (void *)floor, floor->p1.y, x); + } + return floor->p1.y; + } + res = floor->p1.y - x / div; + if (budget > 0 && (res - floor->p1.y > 512 || res - floor->p1.y < -512)) + { + budget--; + printf("[hzd] slope p1.y %d p2.h %d p3.h %d x %d -> %d\n", + floor->p1.y, floor->p2.h, div, x, res); + } + return res; + } +#else + return floor->p1.y - x / floor->p3.h; +#endif +} + +STATIC void HZD_LevelTest(HZD_FLR *floor) +{ + int y, h; + SCRPAD_DATA *scrpad; + + h = floor->b1.h; // TODO: What's "h"? + if ((h & 1) || HZD_LevelCheckInPoint(floor)) + { + if (h & 2) + { + y = floor->b1.y; + } + else + { + y = SlopeFloorLevel(floor); + } + + scrpad = (SCRPAD_DATA *)SCRPAD_ADDR; + if (POINT.y >= y) + { + if (y > scrpad->max_level) + { + scrpad->max_level = y; + scrpad->max_floor = floor; + } + } + else + { + if (y < scrpad->min_level) + { + scrpad->min_level = y; + scrpad->min_floor = floor; + } + } + } +} + +static inline int HZD_PointInBounds(HZD_FLR *floor, HZD_VEC *point) +{ + if (floor->b1.z > point->z || floor->b2.z < point->z || + floor->b1.x > point->x || floor->b2.x < point->x) + { + return 0; + } + + return 1; +} + +int HZD_LevelHazardCheck( HZD_HDL *hzd, SVECTOR *from, int chk_flag ) +{ + HZD_GRP *pArea; + int *pScr; + HZD_FLR *floor; + int count; + HZD_FLR **ppFloors; + int *pScr2; + + pArea = hzd->grp; + + CopyVector(from, &POINT); + + pScr = (int *)getScratchAddr(0); + pScr[16] = 0; + pScr[15] = 0; + pScr[17] = -1000000; + pScr[18] = 1000000; + + if ( chk_flag & HZD_CHK_F_FLOOR ) + { + floor = pArea->floors; + + for (count = pArea->n_floors; count > 0; count--, floor++) + { + if (HZD_PointInBounds(floor, (HZD_VEC *)getScratchAddr(3))) + { + HZD_LevelTest(floor); + } + } + } + + if ( chk_flag & HZD_CHK_D_FLOOR ) + { + ppFloors = hzd->dynamic_floors; + + for (count = hzd->dynamic_floor_index; count > 0; count--, ppFloors++) + { + if (HZD_PointInBounds(*ppFloors, (HZD_VEC *)getScratchAddr(3))) + { + HZD_LevelTest(*ppFloors); + } + } + } + + pScr2 = (int *)getScratchAddr(0); + if (pScr2[16] == 0) + { + return pScr2[15] != 0; + } + + return (pScr2[15] == 0) ? 2 : 3; +} + +void HZD_GetLevelHazard( HZD_FLR **flr ) +{ + SCRPAD_DATA *scrpad = (SCRPAD_DATA *)SCRPAD_ADDR; + + flr[0] = scrpad->max_floor; + flr[1] = scrpad->min_floor; +} + +void HZD_GetLevelHeight( int *lvl_ptr ) +{ + SCRPAD_DATA *scrpad = (SCRPAD_DATA *)SCRPAD_ADDR; + + lvl_ptr[0] = scrpad->max_level; + lvl_ptr[1] = scrpad->min_level; +} + +int HZD_SlopeFloorLevel( SVECTOR *mov, HZD_FLR *flr ) +{ + CopyVector(mov, &POINT); + return SlopeFloorLevel(flr); +} + +int HZD_GetFloorHit( HZD_FLR *flr, SVECTOR *mov ) +{ + SCRPAD_DATA *scrpad; + SCRPAD_DATA *scrpad2; + + CopyVector(mov, &POINT); + + scrpad = (SCRPAD_DATA *)SCRPAD_ADDR; + scrpad->min_floor = NULL; + scrpad->max_floor = NULL; + scrpad->max_level = -1000000; + scrpad->min_level = 1000000; + + if (HZD_PointInBounds(flr, &POINT)) + { + HZD_LevelTest(flr); + } + + scrpad2 = (SCRPAD_DATA *)SCRPAD_ADDR; + + if (!scrpad2->min_floor) + { + return scrpad2->max_floor != NULL; + } + + return (!scrpad2->max_floor) ? 2 : 3; +} + +int HZD_GetFloorLevel( void ) +{ + if (!MAX_FLOOR) + { + return 0; + } + + return MAX_FLOOR->b1.h >> 8; +} diff --git a/components/mgs/mgs/source/libhzd/libhzd.h b/components/mgs/mgs/source/libhzd/libhzd.h new file mode 100644 index 00000000..b41bfe88 --- /dev/null +++ b/components/mgs/mgs/source/libhzd/libhzd.h @@ -0,0 +1,208 @@ +#ifndef __MGS_LIBHZD_H__ +#define __MGS_LIBHZD_H__ + +#include +#include +#include + +#include "common.h" +#include "fmt_hzd.h" + +#define OFFSET_TO_PTR(ptr, offset) (*(int *)offset = (int)ptr + *(int *)offset) + +typedef struct { + HZD_DEF *def; + HZD_GRP *grp; + short map; + short dynamic_queue_index; + short dynamic_floor_index; + short n_cameras; + short max_dynamic_floors; + short max_dynamic_segments; + u_char *route; + HZD_TRP *traps; + HZD_FLR **dynamic_floors; + HZD_SEG **dynamic_segments; + char *dynamic_flags; +} HZD_HDL; + +typedef struct { + u_short name; + u_short type; + u_short object; + short n_inside; + u_short inside[6]; + SVECTOR mov; +} HZD_EVT; + +typedef struct { + short field_0; // Name of entity entering/leaving the trap + short field_2_param_m; // Mask of event type enter/leave to accept + short field_4; // Name of the trap being hit + u_short map; + u_char field_8_param_i_c_flags; + u_char field_9_param_s; + u_char field_A_param_b; + u_char field_B_param_e; + u_short field_C_param_d; + u_short field_E_param_d_or_512; + int time; + int command; +} HZD_BND; + +/*----------------------------------------------------------------*/ + +#define HZD_SEG_NO_COLLIDE (0x01) /* collisions */ +#define HZD_SEG_NO_NAVIGATE (0x02) /* enemy navigation */ +#define HZD_SEG_NO_PLAYER (0x04) /* player sight */ +#define HZD_SEG_NO_MISSILE (0x08) /* missile collisions */ +#define HZD_SEG_NO_HARITSUKI (0x10) /* attach c4 */ +#define HZD_SEG_NO_BULLET (0x20) /* bullet collisions */ +#define HZD_SEG_NO_BEHIND (0x40) /* player lean */ +#define HZD_SEG_NO_DISP_RADAR (0x80) /* radar draw */ + +#define HZD_CHK_F_FLOOR (0x1) +#define HZD_CHK_D_FLOOR (0x2) +#define HZD_CHK_F_SEGMENT (0x4) +#define HZD_CHK_D_SEGMENT (0x8) +#define HZD_CHK_ALL ( HZD_CHK_F_SEGMENT | HZD_CHK_F_FLOOR | HZD_CHK_D_SEGMENT | HZD_CHK_D_FLOOR ) +#define HZD_CHK_FIX ( HZD_CHK_F_FLOOR | HZD_CHK_F_SEGMENT ) +#define HZD_CHK_DYNAMIC ( HZD_CHK_D_FLOOR | HZD_CHK_D_SEGMENT ) +#define HZD_CHK_SEGMENT ( HZD_CHK_F_SEGMENT | HZD_CHK_D_SEGMENT ) +#define HZD_CHK_FLOOR ( HZD_CHK_F_FLOOR | HZD_CHK_D_FLOOR ) + +#define HZD_NO_ZONE (0xFF) + +enum +{ + HZD_REACH, + HZD_DIRECT_REACH, + HZD_INDIRECT_REACH, + HZD_UNREACH +}; + +/*----------------------------------------------------------------*/ + +static inline int HZD_Address( int zone1, int zone2 ) +{ + if ( zone2 < zone1 ) + { + SWAP( zone1, zone2 ); + } + + return ( 255 & zone1 ) | ( ( 255 & zone2 ) << 8 ); +} + +static inline int HZD_ZoneAddress( int address ) +{ + return ( 255 & address ) | ( ( 255 & address ) << 8 ); +} + +static inline int HZD_Zone1( int address ) +{ + return 255 & address; +} + +static inline int HZD_Zone2( int address ) +{ + return 255 & ( address >> 8 ); +} + +/*----------------------------------------------------------------*/ + +/* hzdd.c */ +#ifndef __LIBHZD_HZDD_C__ +extern int HZD_CurrentGroup; +#endif + +void HZD_StartDaemon( void ); +int HZD_LoadInitHzd( void *buf, int id ); +HZD_HDL *HZD_MakeHandler( HZD_DEF *hzd, int id, int n_d_seg, int n_d_flr ); +void HZD_FreeHandler( HZD_HDL *hdl ) ; + +/* route.c */ +void HZD_MakeRoute( HZD_DEF *hzd, u_char *routes ); + +/* dynamic.c */ +int HZD_QueueDynamicSegment2( HZD_HDL *hzd, HZD_SEG *seg, int flag ); +void HZD_DequeueDynamicSegment( HZD_HDL *hzd, HZD_SEG *seg ); +void HZD_SetDynamicSegment( HZD_SEG *a1, HZD_SEG *a2 ); +int HZD_QueueDynamicFloor( HZD_HDL *hzd, HZD_FLR *flr ); +void HZD_DequeueDynamicFloor( HZD_HDL *hzd, HZD_FLR *flr ); + +/* bind.c */ +void HZD_SetBind(int, HZD_BND *bnd, int); +void HZD_BindMapChange( int mask ); +void HZD_SetEvent( HZD_EVT *ev, int name ); +void HZD_ExecBind( HZD_BND *bnd, HZD_EVT *ev, int, int ); +void HZD_ReExecEvent(HZD_HDL *hzd, HZD_EVT *ev, unsigned int flags); +void HZD_ExecLeaveEvent(HZD_HDL *hzd, HZD_EVT *ev); +void HZD_ExecEvent(HZD_HDL *hzd, HZD_EVT *ev, int type); + +/* trap.c */ +void HZD_EnterTrap( HZD_HDL *hzd, HZD_EVT *ev ); +HZD_TRP *HZD_CheckBehindTrap( HZD_HDL *hzd, SVECTOR *pos ); + +/* level.c */ +int HZD_LevelHazardCheck( HZD_HDL *hzd, SVECTOR *from, int chk_flag ); +void HZD_GetLevelHazard( HZD_FLR **flr ); +void HZD_GetLevelHeight( int *lvl_ptr ); +int HZD_SlopeFloorLevel( SVECTOR *mov, HZD_FLR *flr ); + +/* Integer division that cannot take the board down. + * + * This library divides by quantities derived from geometry -- squared segment + * lengths, coordinate differences, interpolation denominators -- and every one + * of them reaches zero for a degenerate case: a point exactly on an endpoint, + * two identical heights, a segment with no extent in the XZ plane. On the + * R3000 that was survivable: MIPS leaves a divide-by-zero result undefined and + * keeps running, so the frame produced a meaningless normal and the next one + * was fine. Xtensa raises IntegerDivideByZero, and the panic reboots the whole + * board -- which is what happened while walking Snake around the dock, inside + * HZD_NearHazardCheck. + * + * Returning zero keeps the console's "garbage but alive" behaviour without the + * garbage being unbounded. Only needed for divisors computed at runtime; + * constants like /2 or /256 are fine as they are. */ +#ifdef __psyz +static inline int HZD_DIV( int num, int den ) +{ + return den ? num / den : 0; +} +#else +#define HZD_DIV( num, den ) ( ( num ) / ( den ) ) +#endif +int HZD_GetFloorHit( HZD_FLR *flr, SVECTOR *mov ); +int HZD_GetFloorLevel( void ); + +/* navigate.c */ +int HZD_GetAddress( HZD_HDL *hzd, SVECTOR *pos, int address ); +int HZD_ReachTo( HZD_HDL *hzd, int addr1, int addr2 ); +int HZD_Navigate( HZD_HDL *hzd, int addr1, int addr2, SVECTOR *pos ); +int HZD_Navigate2( HZD_HDL *hzd, int addr1, int addr2, SVECTOR *pos ); +int HZD_ZoneDistance( HZD_HDL *hzd, int zone_from, int zone_to ); +int HZD_OverDistanceZone( HZD_HDL *hzd, int zone_from, int zone_to, int dis_th ); +int HZD_BoundOutZone( HZD_HDL *hzd, int zone_from, int zone_to, int dis_th ); +int HZD_InsideZone( HZD_HDL *hzd, SVECTOR *pos, int zone ); +int HZD_NearZones( HZD_HDL *hzd, int zone, int *near ); +int HZD_FarZoneNavigate( HZD_HDL *hzd, int zone1, int zone2, int *rout ); +int HZD_GetRoute( HZD_HDL *hzd, int zone1, int zone2 ); + +/* online.c */ +int HZD_OnlineHazardCheck( HZD_HDL *hzd, SVECTOR *from, SVECTOR *to, int chk_flag, int seg_flag ); +void *HZD_GetOnlineHazard( void ); +int HZD_GetOnlineHazardAtr( void ); +void HZD_GetOnlineVector( SVECTOR *vect_ptr ); +void HZD_GetOnlinePoint( SVECTOR *ptp_ptr ); + +/* near.c */ +int HZD_NearHazardCheck( HZD_HDL *hzd, SVECTOR *from, int range, int chk_flag, int seg_flag ); +void HZD_GetNearHazard( HZD_SEG **segs ); +void HZD_GetIsEdge( signed char *ie ); +void HZD_GetNearVector( SVECTOR *vect_ptr ); + +/* vector.c */ +int HZD_HazardReaction( SVECTOR *vects, int n_vects, int range, SVECTOR *react ); +void HZD_GetNormal( void *hzd, SVECTOR *norm ); + +#endif // __MGS_LIBHZD_H__ diff --git a/components/mgs/mgs/source/libhzd/navigate.c b/components/mgs/mgs/source/libhzd/navigate.c new file mode 100644 index 00000000..2a31fbf0 --- /dev/null +++ b/components/mgs/mgs/source/libhzd/navigate.c @@ -0,0 +1,624 @@ +#include "libhzd.h" +#include "private.h" + +#include +#include "common.h" +#include "libdg/libdg.h" // for DG_PointCheck + +#define MAX_MIN_DISTANCE (16777216) + +static u_char ROUTE( u_char *route, int z1, int z2, int n_zone ) +{ + int index; + + if ( z2 < z1 ) + { + SWAP( z2, z1 ); + } + else if ( z1 == z2 ) + { + return 0; + } + + /* triangular matrix index */ + index = ( z1 * ( 2 * n_zone - z1 - 3 ) ) / 2 + z2 - 1; + return route[ index ]; +} + +static int InsideZone( HZD_ZON *zone, SVECTOR *pos ) +{ + int value, center, width; + + value = pos->vy - zone->y; + if ( value < -ZONE_HEIGHT || value > ZONE_HEIGHT ) return 0; + value = pos->vx; + center = zone->x; width = zone->w; + if ( value < center - width || value > center + width ) return 0; + value = pos->vz; + center = zone->z; width = zone->h; + if ( value < center - width || value > center + width ) return 0; + return 1; +} + +static int BetweenZone( HZD_ZON *zone1, HZD_ZON *zone2, SVECTOR *pos ) +{ + int value; + int center1, width1; + int center2, width2; + + /* Y check */ + value = pos->vy; + center1 = zone1->y; + center2 = zone2->y; + if ( value < center1 && value < center2 ) return 0; + value -= ZONE_HEIGHT; + if ( value > center1 && value > center2 ) return 0; + + /* X check */ + value = pos->vx; + center1 = zone1->x; width1 = zone1->w; + center2 = zone2->x; width2 = zone2->w; + if ( value < center1 - width1 && value < center2 - width2 ) return 0; + if ( value > center1 + width1 && value > center2 + width2 ) return 0; + + /* Z check */ + value = pos->vz; + center1 = zone1->z; width1 = zone1->h; + center2 = zone2->z; width2 = zone2->h; + if ( value < center1 - width1 && value < center2 - width2 ) return 0; + if ( value > center1 + width1 && value > center2 + width2 ) return 0; + return 1; +} + +static int InsideNearZones( HZD_HDL *hzd, HZD_ZON *zone, SVECTOR *pos ) +{ + u_char *nears; + int i, near; + + nears = zone->nears; + for ( i = 6; i > 0; i-- ) + { + if ( ( near = *nears++ ) == HZD_NO_ZONE ) break; + if ( InsideZone( hzd->def->zones + near, pos ) ) return near; + } + return -1; +} + +static int BetweenNearZones( HZD_HDL *hzd, HZD_ZON *zone, SVECTOR *pos ) +{ + u_char *nears; + int i, near; + + nears = zone->nears; + for ( i = 6; i > 0; i-- ) + { + if ( ( near = *nears++ ) == HZD_NO_ZONE ) break; + if ( BetweenZone( zone, hzd->def->zones + near, pos ) ) return near; + } + return -1; +} + +static int Distance( HZD_ZON *zone, SVECTOR *pos, int minlen ) +{ + int sum, len; + + sum = 0; + + len = pos->vx - zone->x; + if ( len < 0 ) len = -len; + len -= zone->w; + if ( len > 0 ) sum += len; + if ( sum > minlen ) return minlen; + + len = pos->vz - zone->z; + if ( len < 0 ) len = -len; + len -= zone->h; + if ( len > 0 ) sum += len; + if ( sum > minlen ) return minlen; + + len = zone->y - pos->vy; + if ( len < 0 ) len = -len; + if ( len > ZONE_HEIGHT ) sum += len; + + return sum; +} + +static int NearestZone( HZD_HDL *hzd, SVECTOR *pos ) +{ + HZD_DEF *def; + HZD_ZON *zone; + int i, min_i, n_zones; + int len, min_len; + + def = hzd->def; + zone = def->zones; + n_zones = def->n_zones; + min_i = -1; + min_len = MAX_MIN_DISTANCE; + for ( i = n_zones; i > 0; i-- ) + { + len = Distance( zone, pos, min_len ); + if ( len < min_len ) + { + min_len = len; + min_i = i; + } + zone++; + } + if ( min_i < 0 ) return 0; + return def->n_zones - min_i; +} + +static int ExistRoute( HZD_HDL *hzd, int zone1, int zone2 ) +{ + HZD_ZON *zone; + u_char *nears; + int i, near; + + zone = hzd->def->zones + zone1; + nears = zone->nears; + for ( i = 6; i > 0; i-- ) + { + if ( ( near = *nears++ ) == HZD_NO_ZONE ) break; + if ( near == zone2 ) return 1; + } + return 0; +} + +static int GetRoute( HZD_HDL *hzd, int zone1, int zone2 ) +{ + if ( zone1 == HZD_NO_ZONE || zone2 == HZD_NO_ZONE ) return MAX_ROUTE; + return ROUTE( hzd->route, zone1, zone2, hzd->def->n_zones ); +} + +static int NearZones( HZD_HDL *hzd, int zone, int *near ) +{ + u_char *nears; + int i; + + nears = hzd->def->zones[ zone ].nears; + for ( i = 0; i < 6; i++ ) + { + if ( ( near[ i ] = *nears++ ) == HZD_NO_ZONE ) break; + } + return i; +} + +static int ZoneNavigate( HZD_HDL *hzd, int zone1, int zone2 ) +{ + u_char *nears; + int n_zones, i, near; + int route, min_route, min_near; + + if ( zone1 == zone2 ) return zone1; + + nears = hzd->def->zones[ zone1 ].nears; + n_zones = hzd->def->n_zones; + min_route = MAX_ROUTE; + min_near = zone1; + for ( i = 6; i > 0; i-- ) + { + if ( ( near = *nears++ ) == HZD_NO_ZONE ) break; + route = ROUTE( hzd->route, near, zone2, n_zones ); + if ( route < min_route ) + { + min_route = route; + min_near = near; + } + } + return min_near; +} + +static int ZoneNavigate2( HZD_HDL *hzd, int zone1, int zone2 ) +{ + u_char *nears; + int n_zones, i, near; + int route, min_route, min_near; + + if ( zone1 == zone2 ) return zone1; + + nears = hzd->def->zones[ zone1 ].nears; + n_zones = hzd->def->n_zones; + min_route = MAX_ROUTE; + min_near = zone1; + for ( i = 6; i > 0; i-- ) + { + if ( ( near = *nears++ ) == HZD_NO_ZONE ) break; + route = ROUTE( hzd->route, near, zone2, n_zones ); + if ( route <= min_route ) + { + min_route = route; + min_near = near; + } + } + return min_near; +} + +int HZD_GetAddress( HZD_HDL *hzd, SVECTOR *pos, int address ) +{ + HZD_ZON *zone1, *zone2; + int z1, z2, near; + + z1 = HZD_Zone1( address ); + z2 = HZD_Zone2( address ); + + if ( z2 == HZD_NO_ZONE ) z2 = z1; + + if ( z1 == z2 ) + { + if ( z1 != HZD_NO_ZONE ) + { + zone1 = &hzd->def->zones[ z1 ]; + if ( InsideZone( zone1, pos ) ) goto end; + if ( ( near = BetweenNearZones( hzd, zone1, pos ) ) >= 0 ) { z2 = near; goto end; } + } + } + else + { + zone1 = &hzd->def->zones[ z1 ]; + if ( InsideZone( zone1, pos ) ) { z2 = z1; goto end; } + zone2 = &hzd->def->zones[ z2 ]; + if ( InsideZone( zone2, pos ) ) { z1 = z2; goto end; } + + if ( ( near = InsideNearZones( hzd, zone1, pos ) ) >= 0 ) { z1 = z2 = near; goto end; } + if ( ( near = InsideNearZones( hzd, zone2, pos ) ) >= 0 ) { z1 = z2 = near; goto end; } + + if ( BetweenZone( zone1, zone2, pos ) ) return address; + + if ( ( near = BetweenNearZones( hzd, zone1, pos ) ) >= 0 ) { z2 = near; goto end; } + if ( ( near = BetweenNearZones( hzd, zone2, pos ) ) >= 0 ) { z1 = near; goto end; } + } + + z1 = NearestZone( hzd, pos ); + z2 = HZD_NO_ZONE; + +end: + return HZD_Address( z1, z2 ); +} + +int HZD_ReachTo( HZD_HDL *hzd, int addr1, int addr2 ) +{ + int z1, z2, z3, z4; + + z1 = HZD_Zone1( addr1 ); + z2 = HZD_Zone2( addr1 ); + z3 = HZD_Zone1( addr2 ); + z4 = HZD_Zone2( addr2 ); + + if ( z2 == HZD_NO_ZONE ) z2 = z1; + if ( z4 == HZD_NO_ZONE ) z4 = z3; + + if ( z1 == z2 ) + { + if ( z3 == z4 ) + { + if ( z1 == z3 ) + { + return HZD_REACH; + } + else if ( ExistRoute( hzd, z1, z3 ) ) + { + return HZD_INDIRECT_REACH; + } + } + else + { + if ( z2 == z3 || z2 == z4 ) return HZD_DIRECT_REACH; + } + } + else + { + if ( z3 == z4 ) + { + if ( z1 == z3 || z2 == z3 ) return HZD_DIRECT_REACH; + } + else + { + if ( z1 == z3 && z2 == z4 ) + { + return HZD_REACH; + } + else if ( z1 == z3 || z1 == z4 || z2 == z3 || z2 == z4 ) + { + return HZD_INDIRECT_REACH; + } + } + } + return HZD_UNREACH; +} + +int HZD_Navigate( HZD_HDL *hzd, int addr1, int addr2, SVECTOR *pos ) +{ + int z1, z2, z3, z4; + int route1, route2, route3, route4; + + z1 = HZD_Zone1( addr1 ); + z2 = HZD_Zone2( addr1 ); + z3 = HZD_Zone1( addr2 ); + z4 = HZD_Zone2( addr2 ); + + if ( z2 == HZD_NO_ZONE ) return z1; + + if ( z1 == z2 ) + { + if ( z1 == z3 || z1 == z4 ) return z1; + + if ( z4 != z3 && z3 != HZD_NO_ZONE ) + { + route3 = GetRoute( hzd, z1, z3 ); + route4 = GetRoute( hzd, z1, z4 ); + if ( route4 < route3 ) z3 = z4; + } + + return ZoneNavigate( hzd, z1, z3 ); + } + + route1 = GetRoute( hzd, z1, z3 ); + route2 = GetRoute( hzd, z2, z3 ); + + if ( z3 != z4 && z4 != HZD_NO_ZONE ) + { + route3 = GetRoute( hzd, z1, z4 ); + route4 = GetRoute( hzd, z2, z4 ); + if ( route3 < route1 ) route1 = route3; + if ( route4 < route2 ) route2 = route4; + } + + if ( route1 == route2 ) + { + route1 = Distance( &hzd->def->zones[ z1 ], pos, 0x7f000000 ); + route2 = Distance( &hzd->def->zones[ z2 ], pos, 0x7f000000 ); + } + + if ( route2 < route1 ) z1 = z2; + return z1; +} + +int HZD_Navigate2( HZD_HDL *hzd, int addr1, int addr2, SVECTOR *pos ) +{ + int z1, z2, z3, z4; + int route1, route2, route3, route4; + + z1 = HZD_Zone1( addr1 ); + z2 = HZD_Zone2( addr1 ); + z3 = HZD_Zone1( addr2 ); + z4 = HZD_Zone2( addr2 ); + + if ( z2 == HZD_NO_ZONE ) return z1; + + if ( z1 == z2 ) + { + if ( z1 == z3 || z1 == z4 ) return z1; + + if ( z4 != z3 && z3 != HZD_NO_ZONE ) + { + route3 = GetRoute( hzd, z1, z3 ); + route4 = GetRoute( hzd, z1, z4 ); + if ( route4 < route3 ) z3 = z4; + } + + return ZoneNavigate2( hzd, z1, z3 ); + } + + route1 = GetRoute( hzd, z1, z3 ); + route2 = GetRoute( hzd, z2, z3 ); + + if ( z3 != z4 && z4 != HZD_NO_ZONE ) + { + route3 = GetRoute( hzd, z1, z4 ); + route4 = GetRoute( hzd, z2, z4 ); + if ( route3 < route1 ) route1 = route3; + if ( route4 < route2 ) route2 = route4; + } + + if ( route1 == route2 ) + { + route1 = Distance( &hzd->def->zones[ z1 ], pos, 0x7f000000 ); + route2 = Distance( &hzd->def->zones[ z2 ], pos, 0x7f000000 ); + } + + if ( route2 < route1 ) z1 = z2; + return z1; +} + +int HZD_ZoneDistance( HZD_HDL *hzd, int zone_from, int zone_to ) +{ + HZD_DEF *def; + int n_zones, dist; + + def = hzd->def; + n_zones = def->n_zones; + dist = 0; + while ( zone_from != zone_to ) + { + int i, min_route, min_near, min_dist; + HZD_ZON *zp; + + min_route = MAX_ROUTE; + min_near = zone_from; + min_dist = 0; + zp = def->zones + zone_from; + + for ( i = 0; i < 6; i++ ) + { + int near, route; + + if ( ( near = zp->nears[ i ] ) == HZD_NO_ZONE ) break; + route = ROUTE( hzd->route, near, zone_to, n_zones ); + if ( route < min_route ) + { + min_route = route; + min_near = near; + min_dist = zp->dists[ i ]; + } + } + dist += min_dist; + if ( min_near == zone_from ) + { + printf( " no reach zone from %d to %d \n", zone_from, zone_to ); + dist = 100000; + break; + } + zone_from = min_near; + } + return dist; +} + +int HZD_OverDistanceZone( HZD_HDL *hzd, int zone_from, int zone_to, int dis_th ) +{ + HZD_DEF *def; + int n_zones, dist; + + def = hzd->def; + n_zones = def->n_zones; + dist = 0; + while ( zone_from != zone_to ) + { + int i, min_route, min_near, min_dist; + HZD_ZON *zp; + + min_route = MAX_ROUTE; + min_near = zone_from; + min_dist = 0; + zp = def->zones + zone_from; + + for ( i = 0; i < 6; i++ ) + { + int near, route; + + if ( ( near = zp->nears[ i ] ) == HZD_NO_ZONE ) break; + route = ROUTE( hzd->route, near, zone_to, n_zones ); + if ( route < min_route ) + { + min_route = route; + min_near = near; + min_dist = zp->dists[ i ]; + } + } + dist += min_dist; + if ( dist > dis_th ) return zone_from; + if ( min_near == zone_from ) + { + printf( "over no reach zone from %d to %d \n", zone_from, zone_to ); + return zone_from; + } + zone_from = min_near; + } + return zone_from; +} + +int HZD_BoundOutZone( HZD_HDL *hzd, int zone_from, int zone_to, int dis_th ) +{ + HZD_DEF *def; + int n_zones, dist; + + def = hzd->def; + n_zones = def->n_zones; + dist = 0; + while ( zone_from != zone_to ) + { + int i, min_route, min_near, min_dist; + HZD_ZON *zp; + + min_route = MAX_ROUTE; + min_near = zone_from; + min_dist = 0; + zp = def->zones + zone_from; + for ( i = 0; i < 6; i++ ) + { + int near, route; + + if ( ( near = zp->nears[ i ] ) == HZD_NO_ZONE ) break; + route = ROUTE( hzd->route, near, zone_to, n_zones ); + if ( route < min_route ) + { + min_route = route; + min_near = near; + min_dist = zp->dists[ i ]; + } + } + + dist += min_dist; + if ( dist > dis_th ) + { + SVECTOR pos; + + pos.vx = zp->x; + pos.vy = zp->y; + pos.vz = zp->z; + + DG_PointCheck( &pos, 1 ); + + if ( pos.pad == 0 ) + { + return zone_from; + } + } + if ( min_near == zone_from ) + { + printf( "bound no reach zone from %d to %d \n", zone_from, zone_to ); + return zone_from; + } + zone_from = min_near; + } + return zone_from; +} + +int HZD_InsideZone( HZD_HDL *hzd, SVECTOR *pos, int zone ) +{ + return InsideZone( &hzd->def->zones[ zone ], pos ); +} + +int HZD_NearZones( HZD_HDL *hzd, int zone, int *near ) +{ + return NearZones( hzd, zone, near ); +} + +int HZD_FarZoneNavigate( HZD_HDL *hzd, int zone1, int zone2, int *rout ) +{ + u_char *nears; + int n_zones, i, near; + int route, max_route, max_near; + + if ( zone1 == zone2 ) return zone1; + + nears = hzd->def->zones[ zone1 ].nears; + n_zones = hzd->def->n_zones; + max_route = 0; + max_near = zone1; + for ( i = 6; i > 0; i-- ) + { + if ( ( near = *nears++ ) == HZD_NO_ZONE ) break; + route = ROUTE( hzd->route, near, zone2, n_zones ); + if ( route > max_route ) + { + max_route = route; + max_near = near; + } + } + *rout = max_route - 1; + return max_near; +} + +int HZD_GetRoute( HZD_HDL *hzd, int zone1, int zone2 ) +{ + u_char *nears; + int n_zones, i, near; + int route, min_route; + + if ( zone1 == zone2 ) return zone1; + + nears = hzd->def->zones[ zone1 ].nears; + n_zones = hzd->def->n_zones; + min_route = MAX_ROUTE; + for ( i = 6; i > 0; i-- ) + { + if ( ( near = *nears++ ) == HZD_NO_ZONE ) break; + route = ROUTE( hzd->route, near, zone2, n_zones ); + if ( route < min_route ) + { + min_route = route; + } + } + return min_route; +} diff --git a/components/mgs/mgs/source/libhzd/near.c b/components/mgs/mgs/source/libhzd/near.c new file mode 100644 index 00000000..5ef315d6 --- /dev/null +++ b/components/mgs/mgs/source/libhzd/near.c @@ -0,0 +1,386 @@ +#include "libhzd.h" +#include "private.h" + +#include "common.h" +#include "inline_n.h" +#include "inline_x.h" +#include "psxdefs.h" // for getScratchAddr2 +#include "libdg/libdg.h" + +static void CopyVector(SVECTOR *src, HZD_VEC *dst) +{ + dst->x = src->vx; + dst->y = src->vy; + dst->z = src->vz; +} + +static void CreateBoundingBox(HZD_VEC *vec, int range) +{ + SVECTOR *min; + SVECTOR *max; + int comp; + + min = (SVECTOR *)(SCRPAD_ADDR + 0x14); + max = (SVECTOR *)(SCRPAD_ADDR + 0x1C); + + comp = vec->x; + min->vx = comp - range; + max->vx = comp + range; + + comp = vec->z; + min->vz = comp - range; + max->vz = comp + range; + + comp = vec->y; + max->vy = comp; + min->vy = comp; +} + +STATIC int HZD_80028930(void) +{ + int lzcnt; + int num; + int opz, opz2, opz3; + + short *ptr1; + short *ptr2; + + Sub2D((SVECTOR *)(SCRPAD_ADDR + 0x038), (SVECTOR *)(SCRPAD_ADDR + 0x02C), (SVECTOR *)(SCRPAD_ADDR + 0x024)); + Sub2D((SVECTOR *)(SCRPAD_ADDR + 0x034), (SVECTOR *)(SCRPAD_ADDR + 0x00C), (SVECTOR *)(SCRPAD_ADDR + 0x024)); + + opz = Dot2D((SVECTOR *)(SCRPAD_ADDR + 0x038), (SVECTOR *)(SCRPAD_ADDR + 0x034)); + + *(int *)(SCRPAD_ADDR + 0x04C) = 1; + *(int *)(SCRPAD_ADDR + 0x0AC) = 1; + + if (opz < 0) + { + *(int *)(SCRPAD_ADDR + 0x0A8) = 0; + Sub2D((SVECTOR *)(SCRPAD_ADDR + 0x05C), (SVECTOR *)(SCRPAD_ADDR + 0x024), (SVECTOR *)(SCRPAD_ADDR + 0x00C)); + } + else + { + opz2 = Dot2D((SVECTOR *)(SCRPAD_ADDR + 0x038), (SVECTOR *)(SCRPAD_ADDR + 0x038)); + + if (opz2 < opz) + { + *(int *)(SCRPAD_ADDR + 0x0A8) = 1; + Sub2D((SVECTOR *)(SCRPAD_ADDR + 0x05C), (SVECTOR *)(SCRPAD_ADDR + 0x02C), (SVECTOR *)(SCRPAD_ADDR + 0x00C)); + } + else + { + opz3 = Det2D((SVECTOR *)(SCRPAD_ADDR + 0x038), (SVECTOR *)(SCRPAD_ADDR + 0x034)); + + gte_ldlzc(opz2); + gte_stlzc((SCRPAD_ADDR + 0x0A4)); + + lzcnt = 16 - *(int *)(SCRPAD_ADDR + 0x0A4); + + if (lzcnt > 0) + { + opz >>= lzcnt; + opz3 >>= lzcnt; + opz2 >>= lzcnt; + } + + *(int *)(SCRPAD_ADDR + 0x0A8) = opz; + *(int *)(SCRPAD_ADDR + 0x0AC) = opz2; + + num = *(short *)(SCRPAD_ADDR + 0x03A) * opz3; + + ptr1 = (short *)(SCRPAD_ADDR + 0x04C); + /* opz2 is a squared 2D length and reaches zero when the point sits + * exactly on a segment endpoint, or when the segment is degenerate + * in the XZ plane. MIPS shrugs: R3000 divide-by-zero leaves the + * result undefined and execution carries on, so on the console + * this frame just produced a meaningless normal and the next one + * was fine. Xtensa raises IntegerDivideByZero and takes the board + * down -- which is the reboot seen while walking Snake around the + * dock. Answer zero and let the caller's own "was it zero?" fixup + * below pick the sign, which is the same shape of recovery the + * original code already applies. */ + ptr1[8] = opz2 ? (short)(num / opz2) : 0; + + if ((ptr1[8] == 0) && (num != 0)) + { + ptr1[8] = (num > 0) ? 1 : -1; + } + + num = -*(short *)(SCRPAD_ADDR + 0x038) * opz3; + + ptr2 = (short*)(SCRPAD_ADDR + 0x04C); + ptr2[9] = opz2 ? (short)(num / opz2) : 0; + + if ((ptr2[9] == 0) && (num != 0)) + { + ptr2[9] = (num > 0) ? 1 : -1; + } + + *(int *)(SCRPAD_ADDR + 0x04C) = 0; + *(int *)(SCRPAD_ADDR + 0x060) = *(int *)(SCRPAD_ADDR + 0x024); + *(int *)(SCRPAD_ADDR + 0x064) = *(int *)(SCRPAD_ADDR + 0x02C); + } + } + + *(int *)(SCRPAD_ADDR + 0x050) = Dot2D((SVECTOR *)(SCRPAD_ADDR + 0x05C), (SVECTOR *)(SCRPAD_ADDR + 0x05C)); + return *(int *)(SCRPAD_ADDR + 0x050); +} + +STATIC void HZD_80028CF8(void) +{ + /* same divisor as HZD_80028930 stored at 0x0AC, same reason it can be + * zero, and the same fatal difference on this CPU */ + { + int den = *(int *)(SCRPAD_ADDR + 0x0AC); + gte_lddp(den ? (*(int *)(SCRPAD_ADDR + 0x0A8) * 4096) / den : 0); + } + gte_ld_intpol_sv0((SVECTOR *)(SCRPAD_ADDR + 0x030)); + gte_ldopv2SV((SVECTOR *)(SCRPAD_ADDR + 0x028)); + gte_intpl(); + gte_stsv((SVECTOR *)(SCRPAD_ADDR + 0x028)); + + return; +} + +static inline int PointTestSegment_inline(HZD_SEG *wall) +{ + int z1, z2; + int tmp; + int height; + int y1, y2; + + if ((wall->p1.x > *(short *)(SCRPAD_ADDR + 0x01C)) || (wall->p2.x < *(short *)(SCRPAD_ADDR + 0x014))) + { + return 0; + } + + z1 = wall->p1.z; + z2 = wall->p2.z; + + if (z2 < z1) + { + tmp = z1; + z1 = z2; + z2 = tmp; + } + + if ((z1 > *(short *)(SCRPAD_ADDR + 0x020)) || (z2 < *(short *)(SCRPAD_ADDR + 0x018))) + { + return 0; + } + + height = *(short *)(SCRPAD_ADDR + 0x016); + + y1 = wall->p1.y; + y2 = wall->p2.y; + + if (height < y1 && height < y2) + { + return 0; + } + + height = *(short *)(SCRPAD_ADDR + 0x01E); + + y1 += wall->p1.h; + y2 += wall->p2.h; + + if (height > y1 && height > y2) + { + return 0; + } + + return 1; +} + +STATIC void PointTestSegment(HZD_SEG *wall, int index, int flags) +{ + int *ptr; + int opz; + int height; + int *ptr1, *ptr2, *ptr3; + + if (!PointTestSegment_inline(wall)) + { + return; + } + + *(HZD_SEG *)(SCRPAD_ADDR + 0x024) = *wall; + + ptr = (int *)(SCRPAD_ADDR + 0x084); + opz = HZD_80028930(); + + if (opz >= ptr[1]) + { + return; + } + + if (index > *(int *)(SCRPAD_ADDR + 0x044)) + { + HZD_80028CF8(); + + height = *(short *)(SCRPAD_ADDR + 0x010) - ((HZD_SEG *)(SCRPAD_ADDR + 0x024))->p1.y; + + if (height < 0 || height > ((HZD_SEG *)(SCRPAD_ADDR + 0x024))->p1.h) + { + return; + } + } + + ptr1 = (int *)(SCRPAD_ADDR + 0x04C); + ptr2 = (int *)(SCRPAD_ADDR + 0x068); + ptr3 = (int *)(SCRPAD_ADDR + 0x000); + + ptr1[2] = (int)wall; + ptr1[3] = (flags & 0x7F) | (*(int *)(ptr3 + 0x2C)) | (*(*(char **)(ptr3 + 0x2D) - index) << 8); + + if (opz < ptr2[1]) + { + memcpy(ptr, ptr2, 28); + memcpy(ptr2, ptr1, 28); + } + else if (*(int *)(SCRPAD_ADDR + 0x05C) != *(int *)(SCRPAD_ADDR + 0x078)) + { + memcpy(ptr, ptr1, 28); + } + else + { + return; + } + + *(int *)(SCRPAD_ADDR + 0x048) += 1; +} + +static inline void sub_helper_80029098(void) +{ + if (*(int *)(SCRPAD_ADDR + 0x084) == 0) + { + return; + } + + if (*(int *)(SCRPAD_ADDR + 0x068) != 0) + { + if (*(int *)(SCRPAD_ADDR + 0x078) != *(int *)(SCRPAD_ADDR + 0x094)) + { + return; + } + } + else + { + Add2D((SVECTOR *)(SCRPAD_ADDR + 0x0A0), (SVECTOR *)(SCRPAD_ADDR + 0x00C), (SVECTOR *)(SCRPAD_ADDR + 0x094)); + + if (*(int *)(SCRPAD_ADDR + 0x0A0) != *(int *)(SCRPAD_ADDR + 0x07C) && *(int *)(SCRPAD_ADDR + 0x0A0) != *(int *)(SCRPAD_ADDR + 0x080)) + { + return; + } + } + + *(int *)(SCRPAD_ADDR + 0x048) = 1; +} + +int HZD_NearHazardCheck(HZD_HDL *hzd, SVECTOR *from, int range, int chk_flag, int seg_flag) +{ + HZD_GRP *pArea; + int n_unknown; + HZD_SEG *pWalls; + char *pFlags; + int wall_count; + char **ptr; + char **ptr2; + int i; + HZD_SEG **ppWalls; + int idx; + int queue_size; + + pArea = hzd->grp; + + CopyVector(from, (HZD_VEC *)(SCRPAD_ADDR + 0x00C)); + CreateBoundingBox((HZD_VEC *)(SCRPAD_ADDR + 0x00C), range); + + *(int *)(SCRPAD_ADDR + 0x048) = 0; + + if (chk_flag & HZD_CHK_F_SEGMENT) + { + n_unknown = pArea->n_flat_walls; + + *(int *)(SCRPAD_ADDR + 0x088) = range * range; + *(int *)(SCRPAD_ADDR + 0x06C) = range * range; + + do {} while (0); + + pWalls = pArea->walls; + pFlags = pArea->wallsFlags; + wall_count = pArea->n_walls; + + ptr = (char **)SCRPAD_ADDR; + ptr[0x2C] = (char *)0; + ptr[0x2D] = pFlags + wall_count * 2; + + *(int *)(SCRPAD_ADDR + 0x044) = n_unknown; + + for (i = pArea->n_walls; i > 0; i--, pWalls++, pFlags++) + { + if ((*pFlags & seg_flag) == 0) + { + PointTestSegment(pWalls, i, *pFlags); + } + } + } + + if (chk_flag & HZD_CHK_D_SEGMENT) + { + ppWalls = hzd->dynamic_segments; + pFlags = hzd->dynamic_flags; + queue_size = hzd->max_dynamic_segments; + idx = hzd->dynamic_queue_index; + + ptr2 = (char **)SCRPAD_ADDR; + ptr2[0x2C] = (char *)0x80; + ptr2[0x2D] = pFlags + queue_size + idx; + + *(int *)(SCRPAD_ADDR + 0x044) = 0; + + for (i = hzd->dynamic_queue_index; i > 0; i--, ppWalls++, pFlags++) + { + if ((*pFlags & seg_flag) == 0) + { + PointTestSegment(*ppWalls, i, *pFlags); + } + } + } + + if (*(int *)(SCRPAD_ADDR + 0x048) > 1) + { + *(int *)(SCRPAD_ADDR + 0x048) = 2; + sub_helper_80029098(); + } + + return *(int *)(SCRPAD_ADDR + 0x048); +} + +void HZD_GetNearHazard(HZD_SEG **segs) +{ + segs[0] = *(HZD_SEG **)(SCRPAD_ADDR + 0x70); + segs[1] = *(HZD_SEG **)(SCRPAD_ADDR + 0x8c); +} + +void HZD_GetIsEdge(signed char *ie) +{ + ie[0] = *getScratchAddr2(char, 0x74); + ie[1] = *getScratchAddr2(char, 0x90); +} + +void HZD_GetNearVector(SVECTOR *vect_ptr) +{ + HZD_SEG *wall1; + HZD_SEG *wall2; + + wall1 = getScratchAddr2(HZD_SEG, 0x68); + vect_ptr[0].vx = wall1[1].p1.x; + vect_ptr[0].vy = 0; + vect_ptr[0].vz = wall1[1].p1.z; + + wall2 = getScratchAddr2(HZD_SEG, 0x84); + vect_ptr[1].vx = wall2[1].p1.x; + vect_ptr[1].vy = 0; + vect_ptr[1].vz = wall2[1].p1.z; +} diff --git a/components/mgs/mgs/source/libhzd/online.c b/components/mgs/mgs/source/libhzd/online.c new file mode 100644 index 00000000..ce10e864 --- /dev/null +++ b/components/mgs/mgs/source/libhzd/online.c @@ -0,0 +1,795 @@ +#include "libhzd.h" +#include "private.h" + +#include "common.h" +#include "libdg/libdg.h" +#include "libgv/libgv.h" +#include "inline_n.h" +#include "inline_x.h" +#include "game/map.h" // for GM_IterHazard +#include "psxdefs.h" // for getScratchAddr2 + +typedef struct SPAD_DATA +{ + char pad[4]; + HZD_VEC vec[4]; +} SPAD_DATA; + +#define SPAD ((SPAD_DATA *)getScratchAddr(0)) + +STATIC int ComputeDirection(void) +{ + HZD_VEC *pVec1 = &SPAD->vec[3]; + HZD_VEC *pVec2 = &SPAD->vec[2]; + HZD_VEC *pVec3 = &SPAD->vec[1]; + int area; + + pVec1->x = pVec2->x - pVec3->x; + pVec1->y = pVec2->y - pVec3->y; + pVec1->z = pVec2->z - pVec3->z; + + area = Len2D((SVECTOR *)(SCRPAD_ADDR + 0x01C)); + if (area == 0) + { + return 0; + } + + pVec1->x = (pVec1->x * 256) / area; + pVec1->y = (pVec1->y * 256) / area; + pVec1->z = (pVec1->z * 256) / area; + + return area; +} + +STATIC void ComputeBounds(SVECTOR *svec1, SVECTOR *svec2) +{ + SVECTOR *scratchvec1, *scratchvec2; + int coord1, coord2, coord1_copy; + + coord1 = svec1->vx; + coord2 = svec2->vx; + if (coord2 < coord1) + { + coord1_copy = coord1; + coord1 = coord2; + coord2 = coord1_copy; + } + scratchvec1 = (SVECTOR *)getScratchAddr(0x9); + scratchvec2 = (SVECTOR *)getScratchAddr(0xB); + + scratchvec1->vx = coord1; + scratchvec2->vx = coord2; + + coord1 = svec1->vz; + coord2 = svec2->vz; + if (coord2 < coord1) + { + coord1_copy = coord1; + coord1 = coord2; + coord2 = coord1_copy; + } + + scratchvec1->vy = coord1; + scratchvec2->vy = coord2; + + coord1 = svec1->vy; + coord2 = svec2->vy; + if (coord2 < coord1) + { + coord1_copy = coord1; + coord1 = coord2; + coord2 = coord1_copy; + } + + scratchvec1->vz = coord1; + scratchvec2->vz = coord2; +} + +STATIC int CheckWallBounds(void) +{ + int z1, z2; + int y1, y2; + int cmp; + + if (getScratchAddr2(HZD_SEG, 0x34)->p1.x > getScratchAddr2(SVECTOR, 0x2C)->vx || + getScratchAddr2(HZD_SEG, 0x34)->p2.x < getScratchAddr2(SVECTOR, 0x24)->vx) + { + return 0; + } + + z1 = getScratchAddr2(HZD_SEG, 0x34)->p1.z; + z2 = getScratchAddr2(HZD_SEG, 0x34)->p2.z; + + if (z1 > z2) + { + SWAP( z1, z2 ); + } + + if (z1 > getScratchAddr2(SVECTOR, 0x2C)->vz || z2 < getScratchAddr2(SVECTOR, 0x24)->vz) + { + return 0; + } + + y1 = getScratchAddr2(HZD_SEG, 0x34)->p1.y; + y2 = getScratchAddr2(HZD_SEG, 0x34)->p2.y; + + cmp = getScratchAddr2(SVECTOR, 0x2C)->vy; + if (y1 > cmp && y2 > cmp) + { + return 0; + } + + y1 += getScratchAddr2(HZD_SEG, 0x34)->p1.h; + y2 += getScratchAddr2(HZD_SEG, 0x34)->p2.h; + + cmp = getScratchAddr2(SVECTOR, 0x24)->vy; + if (y1 < cmp && y2 < cmp) + { + return 0; + } + + return 1; +} + +STATIC int CalculateHitTime(void) +{ + long a; + + int opz_b; + int opz_a; + + SVECTOR *ptr; + SVECTOR *pa; + SVECTOR *pb; + + // Can't get the code to generate a useless absolute load without this +#ifdef __psyz + /* pinning a MIPS register only serves the matching build; elsewhere this is + * an ordinary local */ + long *t0; +#else + register long *t0 asm("t0"); +#endif + + Sub2D((SVECTOR *)(SCRPAD_ADDR + 0x048), (SVECTOR *)(SCRPAD_ADDR + 0x03C), (SVECTOR *)(SCRPAD_ADDR + 0x034)); + + a = *(long *)(SCRPAD_ADDR + 0x048); + + t0 = 0; + gte_ldsxy3(t0, a, *(long *)(SCRPAD_ADDR + 0x01C)); + gte_nclip(); + + ptr = (SVECTOR *)(SCRPAD_ADDR + 0x044); + pa = (SVECTOR *)(SCRPAD_ADDR + 0x00C); + pb = (SVECTOR *)(SCRPAD_ADDR + 0x034); + + Sub2D(ptr, pa, pb); + ptr = 0; + + gte_read_opz(opz_a); + + t0 = (long *)(SCRPAD_ADDR + 0x044); + opz_b = *t0; + opz_a /= 16; + + asm("" :: "r"(t0)); + + if (opz_a == 0) + { + return 0xF4240; + } + + gte_ldsxy3(0 , opz_b, a); + gte_nclip(); + gte_read_opz(opz_b); + + if (opz_b < 0) + { + opz_b = -opz_b; + opz_a = -opz_a; + } + + if (opz_b >= 0x9000000) + { + opz_a = HZD_DIV(opz_b, opz_a / 16); + } + else + { + opz_a = HZD_DIV(opz_b * 16, opz_a); + } + + if (opz_a < 0) + { + return 0xF4240; + } + + return opz_a; +} + +STATIC int CalculateHitPoint(int mult) +{ + short x, y, z; + short *scratch1, *scratch2, *scratch3, *scratch4; + + scratch1 = (short *)(SCRPAD_ADDR + 0x01C); + scratch2 = (short *)(SCRPAD_ADDR + 0x04C); + scratch3 = (short *)(SCRPAD_ADDR + 0x00C); + + x = scratch2[0] = scratch3[0] + (scratch1[0] * mult) / 256; + z = scratch2[2] = scratch3[2] + (scratch1[2] * mult) / 256; + y = scratch2[1] = scratch3[1] + (scratch1[1] * mult) / 256; + + if (*(short *)(SCRPAD_ADDR + 0x048) != 0) + { + scratch4 = (short *)(SCRPAD_ADDR + 0x034); + if (x < scratch4[0] - 32 || scratch4[4] + 32 < x) + { + return 0; + } + else + { + return 1; + } + } + else + { + scratch4 = (short *)(SCRPAD_ADDR + 0x034); + if (y < scratch4[1] - 32 || scratch4[5] + 32 < y) + { + return 0; + } + else + { + return 2; + } + } +} + +STATIC void CalculateSegmentHeight(int a0) +{ + int v1; + int v0; + + if (a0 == 1) + { + v0 = *(short *)(SCRPAD_ADDR + 0x04C); + v1 = *(short *)(SCRPAD_ADDR + 0x034); + v0 -= v1; + v1 = *(short *)(SCRPAD_ADDR + 0x048); + } + else + { + v0 = *(short *)(SCRPAD_ADDR + 0x04E); + v1 = *(short *)(SCRPAD_ADDR + 0x036); + v0 -= v1; + v1 = *(short *)(SCRPAD_ADDR + 0x04A); + } + + v0 *= 4096; + v1 = HZD_DIV(v0, v1); + + gte_lddp(v1); + gte_ld_intpol_sv0((SVECTOR *)(SCRPAD_ADDR + 0x040)); + gte_ldopv2SV((SVECTOR *)(SCRPAD_ADDR + 0x038)); + gte_intpl(); + gte_stsv((SVECTOR *)(SCRPAD_ADDR + 0x038)); +} + +STATIC void TestSegment(HZD_SEG *seg, int a2, int a3) +{ + struct copier + { + int a, b; + }; + + short *scratch1; + HZD_SEG *scratch2; + char *scratch3; + + int tmp1; + int tmp2; + int tmp3; + int tmp4; + char *tmp5; + short tmp6; + + *((HZD_SEG *)(SCRPAD_ADDR + 0x034)) = *seg; + if (CheckWallBounds()) + { + tmp1 = CalculateHitTime(); + tmp4 = CalculateHitPoint(tmp1); + if (tmp4) + { + if (*(int *)(SCRPAD_ADDR + 0x060) < a2) + { + CalculateSegmentHeight(tmp4); + } + scratch1 = (short *)(SCRPAD_ADDR + 0x04C); + scratch2 = (HZD_SEG *)(SCRPAD_ADDR + 0x034); + tmp2 = scratch1[2] - scratch2->p1.y; + if (tmp2 >= 0 && scratch2->p1.h >= tmp2) + { + *(int *)(SCRPAD_ADDR + 0x06C) += 1; + if (*(int *)(SCRPAD_ADDR + 0x05C) >= tmp1) + { + scratch3 = (char *)SCRPAD_ADDR; + + *(struct copier *)(SCRPAD_ADDR + 0x054) = *(struct copier *)scratch1; + do {} while (0); + + *(int *)(SCRPAD_ADDR + 0x05C) = tmp1; + tmp5 = *(char **)(scratch3 + 0x70); + tmp6 = *(short *)(scratch3 + 0x6A); + tmp4 = a3 & 127; + do + { + } while (0); + + *(HZD_SEG **)(SCRPAD_ADDR + 0x064) = seg; + tmp3 = *(tmp5 - a2); + tmp3 <<= 8; + *(short *)(SCRPAD_ADDR + 0x068) = tmp6 | tmp4 | tmp3; + } + } + } + } +} + +STATIC int HZD_80027BF8(SVECTOR *svec) +{ + int z; + int y; + int x; + + SVECTOR * scr = getScratchAddr2(SVECTOR, 0xC); + + x = svec->vx - scr->vx; + if (x < 0) + { + x = -x; + } + + z = svec->vz - scr->vz; + if (z < 0) + { + z = -z; + } + + x += z; + + y = svec->vy - scr->vy; + if (y < 0) + { + y = -y; + } + + return x + y; +} + +STATIC int HZD_80027C64(void) +{ + int dividend; + int val; + + val = *(short *)getScratchAddr(0x0E); + + if (val == *getScratchAddr(0x1D)) + { + return *getScratchAddr(0x1E); + } + + dividend = (val - *(short *)getScratchAddr(0x4)) * 4096; + gte_lddp(HZD_DIV(dividend, *(short *)getScratchAddr(0x6) - + *(short *)getScratchAddr(0x4))); + gte_ld_intpol_sv0((SVECTOR *)getScratchAddr(0x5)); + gte_ldopv2SV((SVECTOR *)getScratchAddr(0x3)); + gte_intpl(); + gte_stsv((SVECTOR *)getScratchAddr(0x13)); + *getScratchAddr(0x1D) = val; + *getScratchAddr(0x1E) = HZD_80027BF8((SVECTOR *)getScratchAddr(0x13)); + return *getScratchAddr(0x1E); +} + +STATIC int HZD_80027D80(HZD_FLR *floor) +{ + long sxy_0; + long sxy_1; + long sxy_2; + long sxy_3; + long sxy_4; + long *pZ; + + sxy_1 = *(long *)getScratchAddr(19); + sxy_3 = floor->p1.long_access[0]; + sxy_0 = floor->p2.long_access[0]; + + gte_ldsxy3(sxy_3, sxy_0, sxy_1); + gte_nclip(); + sxy_2 = floor->p3.long_access[0]; + gte_stopz(getScratchAddr(2)); + + pZ = (long *)getScratchAddr(2); + + if (*pZ >= 0) + { + gte_ldsxy3(sxy_0, sxy_2, sxy_1); + gte_nclip(); + sxy_4 = floor->p4.long_access[0]; + gte_stopz(getScratchAddr(2)); + + if (*pZ < 0) + { + return 0; + } + + gte_NormalClip(sxy_2, sxy_4, sxy_1, getScratchAddr(2)); + if(*pZ < 0) + { + return 0; + } + + gte_NormalClip(sxy_4, sxy_3, sxy_1, getScratchAddr(2)); + return *pZ >= 0; + } + else + { + gte_ldsxy3(sxy_0, sxy_2, sxy_1); + gte_nclip(); + sxy_4 = floor->p4.long_access[0]; + gte_stopz(getScratchAddr(2)); + + if (*pZ > 0) + { + return 0; + } + + gte_NormalClip(sxy_2, sxy_4, sxy_1, getScratchAddr(2)); + if (*pZ > 0) + { + return 0; + } + + gte_NormalClip(sxy_4, sxy_3, sxy_1, getScratchAddr(2)); + return *pZ <= 0; + } +} + +static inline void GetFloorHeight(SVECTOR *dst, HZD_FLR *a, HZD_VEC *b) +{ + dst->vx = a->p1.x - b->x; + dst->vy = a->p1.y - b->y; + dst->vz = a->p1.z - b->z; +} + +static inline int GetScratch(int offset) +{ + int *ptr = (int *)SCRPAD_ADDR; + return ptr[offset]; +} + +static inline void SetScratch(int offset, int value) +{ + int *ptr = (int *)SCRPAD_ADDR; + ptr[offset] = value; +} + +static inline int sub_helper_80027F10(void) +{ + if ((*(short *)(SCRPAD_ADDR + 0x036) > *(short *)(SCRPAD_ADDR + 0x030)) || + (*(short *)(SCRPAD_ADDR + 0x03E) < *(short *)(SCRPAD_ADDR + 0x028)) || + (*(short *)(SCRPAD_ADDR + 0x034) > *(short *)(SCRPAD_ADDR + 0x02C)) || + (*(short *)(SCRPAD_ADDR + 0x03C) < *(short *)(SCRPAD_ADDR + 0x024)) || + (*(short *)(SCRPAD_ADDR + 0x038) > *(short *)(SCRPAD_ADDR + 0x02E)) || + (*(short *)(SCRPAD_ADDR + 0x040) < *(short *)(SCRPAD_ADDR + 0x026))) + { + return 0; + } + + return 1; +} + +static inline int sub_helper2_80027F10(void) +{ + if ((*(short *)(SCRPAD_ADDR + 0x04C) < *(short *)(SCRPAD_ADDR + 0x034)) || + (*(short *)(SCRPAD_ADDR + 0x03C) < *(short *)(SCRPAD_ADDR + 0x04C)) || + (*(short *)(SCRPAD_ADDR + 0x04E) < *(short *)(SCRPAD_ADDR + 0x036)) || + (*(short *)(SCRPAD_ADDR + 0x03E) < *(short *)(SCRPAD_ADDR + 0x04E))) + { + return 0; + } + + return 1; +} + +//todo: include proper +#define UNTAG_PTR(_type, _ptr) (_type *)((unsigned int)_ptr & 0x7fffffff) + +STATIC void TestFloor(HZD_FLR *floor) +{ + int flags; + int length; + int n, d; + + *(HZD_SEG *)(SCRPAD_ADDR + 0x034) = *(HZD_SEG *)floor; + do {} while (0); + + if (!sub_helper_80027F10()) + { + return; + } + + flags = *(short *)(SCRPAD_ADDR + 0x03A); + + if ((flags & 2) != 0) + { + if (*(short *)(SCRPAD_ADDR + 0x010) == *(short *)(SCRPAD_ADDR + 0x018)) + { + return; + } + + length = HZD_80027C64(); + } + else + { + if (GetScratch(0x23) == 0) + { + gte_ReadRotMatrix((SCRPAD_ADDR + 0x090)); + SetScratch(0x23, 1); + } + + SetScratch(0x1F, floor->p1.h); + SetScratch(0x20, floor->p3.h); + SetScratch(0x21, floor->p2.h); + gte_ldlvl((SCRPAD_ADDR + 0x07C)); + + GetFloorHeight((SVECTOR *)(SCRPAD_ADDR + 0x0B0), (HZD_FLR *)(SCRPAD_ADDR + 0x004), (HZD_VEC *)(SCRPAD_ADDR + 0x00C)); + GetFloorHeight((SVECTOR *)(SCRPAD_ADDR + 0x0B6), floor, (HZD_VEC *)(SCRPAD_ADDR + 0x00C)); + + gte_SetRotMatrix((SCRPAD_ADDR + 0x0B0)); + gte_rtir(); + gte_stlvnl((SCRPAD_ADDR + 0x07C)); + + n = *(int *)(SCRPAD_ADDR + 0x080); + d = *(int *)(SCRPAD_ADDR + 0x07C); + + if (((d < 0) && (n < 0)) || ((d > 0) && (n > 0))) + { + *(int *)(SCRPAD_ADDR + 0x074) = 0xF4240; + *(short *)(SCRPAD_ADDR + 0x04C) = *(short *)(SCRPAD_ADDR + 0x00C) + (*(short *)(SCRPAD_ADDR + 0x0B0) * n) / d; + *(short *)(SCRPAD_ADDR + 0x050) = *(short *)(SCRPAD_ADDR + 0x010) + (*(short *)(SCRPAD_ADDR + 0x0B2) * n) / d; + *(short *)(SCRPAD_ADDR + 0x04E) = *(short *)(SCRPAD_ADDR + 0x00E) + (*(short *)(SCRPAD_ADDR + 0x0B4) * n) / d; + + length = HZD_80027BF8((SVECTOR *)(SCRPAD_ADDR + 0x04C)); + } + else + { + length = 0xF4240; + } + } + + if (length >= *(int *)(SCRPAD_ADDR + 0x05C)) + { + return; + } + + if (!sub_helper2_80027F10()) + { + return; + } + + if ((flags & 1) || HZD_80027D80(floor)) + { + *(int *)(SCRPAD_ADDR + 0x06C) += 1; + *(HZD_VEC *)(SCRPAD_ADDR + 0x054) = *(HZD_VEC *)(SCRPAD_ADDR + 0x04C); + *(int *)(SCRPAD_ADDR + 0x05C) = length; + *(HZD_FLR **)(SCRPAD_ADDR + 0x064) = UNTAG_PTR(HZD_FLR, floor); + } +} + +static inline void CopySvector(SVECTOR *dst, SVECTOR *src) +{ + struct copy_struct + { + int a, b; + }; + *(struct copy_struct *)dst = *(struct copy_struct *)src; +} + +static inline void CopySvectorToSpad(int offset, SVECTOR *svec) +{ + short *spad_top; + spad_top = (short *)SCRPAD_ADDR; + + spad_top[offset + 0] = svec->vx; + spad_top[offset + 2] = svec->vy; + spad_top[offset + 1] = svec->vz; +} + +int HZD_OnlineHazardCheck(HZD_HDL *hzd, SVECTOR *from, SVECTOR *to, int chk_flag, int seg_flag) +{ + int count; + int n_areas, n_areas2; + int bit1, bit2; + HZD_GRP *pArea; + int current_group; + HZD_FLR *pFloor; + HZD_SEG *pWall; + HZD_FLR **ppFloor; + HZD_SEG **ppWall; + char *pFlags; + int n_unknown; + char *pFlagsEnd; + int queue_size, idx; + char *pFlagsEnd2; + HZD_HDL *pNextMap; + + current_group = HZD_CurrentGroup; + + CopySvectorToSpad(6, from); + + *((int *)(SCRPAD_ADDR + 0x064)) = (*((int *)(SCRPAD_ADDR + 0x06C)) = 0); + + CopySvectorToSpad(10, to); + CopySvector((SVECTOR *)(SCRPAD_ADDR + 0x054), (SVECTOR *)(SCRPAD_ADDR + 0x014)); + + *((int *)(SCRPAD_ADDR + 0x08C)) = 0; + + ComputeBounds((SVECTOR *)(SCRPAD_ADDR + 0x00C), (SVECTOR *)(SCRPAD_ADDR + 0x054)); + + *((int *)(SCRPAD_ADDR + 0x05C)) = ComputeDirection(); + + if (!(*(int *)(SCRPAD_ADDR + 0x05C))) + { + return 0; + } + + if (chk_flag & HZD_CHK_F_SEGMENT) + { + char *scratchpad; + + bit2 = 1; + pArea = hzd->def->groups; + for (n_areas2 = hzd->def->n_groups; n_areas2 > 0; n_areas2--, bit2 <<= 1, pArea++) + { + if (current_group & bit2) + { + do + { + pWall = pArea->walls; + pFlags = pArea->wallsFlags; + do {} while (0); + n_unknown = pArea->n_flat_walls; + pFlagsEnd = pFlags + 2 * pArea->n_walls; + scratchpad = (char *)SCRPAD_ADDR; + *((short *)(scratchpad + 0x6A)) = 0; + } while (0); + + *((char **)(scratchpad + 0x70)) = pFlagsEnd; + *((int *)(SCRPAD_ADDR + 0x060)) = n_unknown; + + for (count = pArea->n_walls; count > 0; count--, pWall++, pFlags++) + { + if (!((*pFlags) & seg_flag)) + { + TestSegment(pWall, count, *pFlags); + } + } + } + } + } + + if (chk_flag & HZD_CHK_D_SEGMENT) + { + char *scratchpad; + + pNextMap = NULL; + while ((pNextMap = GM_IterHazard(pNextMap))) + { + scratchpad = (char *)SCRPAD_ADDR; + do + { + ppWall = pNextMap->dynamic_segments; + pFlags = pNextMap->dynamic_flags; + queue_size = pNextMap->max_dynamic_segments; + idx = pNextMap->dynamic_queue_index; + *((short *)(scratchpad + 0x6A)) = 0x80; + do + { + } while (0); + + pFlagsEnd2 = (pFlags + queue_size) + idx; + *((char **)(scratchpad + 0x70)) = pFlagsEnd2; + } while (0); // TODO: Is it the same macro as above in "if (chk_flag & HZD_CHK_F_SEGMENT)" case? + + count = pNextMap->dynamic_queue_index; + *((int *)(SCRPAD_ADDR + 0x060)) = 0; + + for (; count > 0; count--, ppWall++, pFlags++) + { + if (!((*pFlags) & seg_flag)) + { + TestSegment(*ppWall, count, *pFlags); + } + } + } + } + ComputeBounds((SVECTOR *)(SCRPAD_ADDR + 0x00C), (SVECTOR *)(SCRPAD_ADDR + 0x054)); + *((int *)(SCRPAD_ADDR + 0x05C)) = HZD_80027BF8((SVECTOR *)(SCRPAD_ADDR + 0x054)); + *((int *)(SCRPAD_ADDR + 0x074)) = 0xF4240; + + if (chk_flag & HZD_CHK_F_FLOOR) + { + bit1 = 1; + pArea = hzd->def->groups; + for (n_areas = hzd->def->n_groups; n_areas > 0; n_areas--, bit1 <<= 1, pArea++) + { + if (current_group & bit1) + { + pFloor = pArea->floors; + for (count = pArea->n_floors; count > 0; count--) + { + TestFloor(pFloor); + pFloor++; + } + } + } + } + + if (chk_flag & HZD_CHK_D_FLOOR) + { + pNextMap = NULL; + while ((pNextMap = GM_IterHazard(pNextMap))) + { + ppFloor = pNextMap->dynamic_floors; + for (count = pNextMap->dynamic_floor_index; count > 0; count--, ppFloor++) + { + TestFloor(*ppFloor); + } + } + } + + if (*(int *)(SCRPAD_ADDR + 0x08C) != 0) + { + gte_SetRotMatrix((SCRPAD_ADDR + 0x090)); + } + + if (*(int *)(SCRPAD_ADDR + 0x064) != 0) + { + return *(int *)(SCRPAD_ADDR + 0x06C); + } + return 0; +} + +void *HZD_GetOnlineHazard(void) +{ + return *getScratchAddr2(void **, 0x64); +} + +int HZD_GetOnlineHazardAtr(void) +{ + return *getScratchAddr2(short, 0x68); +} + +void HZD_GetOnlineVector(SVECTOR *vect_ptr) +{ + HZD_VEC *cross; + HZD_VEC *from; + + cross = getScratchAddr2(HZD_VEC, 0x54); + from = getScratchAddr2(HZD_VEC, 0x0c); + + vect_ptr->vx = cross->x - from->x; + vect_ptr->vy = cross->y - from->y; + vect_ptr->vz = cross->z - from->z; +} + +void HZD_GetOnlinePoint(SVECTOR *ptp_ptr) +{ + HZD_VEC *cross; + + cross = getScratchAddr2(HZD_VEC, 0x54); + + ptp_ptr->vx = cross->x; + ptp_ptr->vy = cross->y; + ptp_ptr->vz = cross->z; +} diff --git a/components/mgs/mgs/source/libhzd/private.h b/components/mgs/mgs/source/libhzd/private.h new file mode 100644 index 00000000..830078a1 --- /dev/null +++ b/components/mgs/mgs/source/libhzd/private.h @@ -0,0 +1,63 @@ +#ifndef __MGS_LIBHZD_PRIVATE_H__ +#define __MGS_LIBHZD_PRIVATE_H__ + +#include +#include +#include "inline_n.h" +#include +#include "fmt_hzd.h" // for HZD_VEC + +#define ZONE_HEIGHT (2000) + +#define MAX_ROUTE (255) + +#define CopyToHzdVec(dst, src) \ +{ \ + ((HZD_VEC *)dst)->x = ((SVECTOR *)src)->vx; \ + ((HZD_VEC *)dst)->z = ((SVECTOR *)src)->vz; \ +} + +#define CopyFromHzdVec(dst, src) \ +{ \ + ((SVECTOR *)dst)->vx = ((HZD_VEC *)src)->x; \ + ((SVECTOR *)dst)->vz = ((HZD_VEC *)src)->z; \ +} + +static inline void Add2D(SVECTOR *out, SVECTOR *v1, SVECTOR *v2) +{ + out->vx = v1->vx + v2->vx; + out->vy = v1->vy + v2->vy; +} + +static inline void Sub2D(SVECTOR *out, SVECTOR *v1, SVECTOR *v2) +{ + out->vx = v1->vx - v2->vx; + out->vy = v1->vy - v2->vy; +} + +static inline void Mul2D(SVECTOR *out, SVECTOR *in, int num, int denom) +{ + out->vx = (in->vx * num) / denom; + out->vy = (in->vy * num) / denom; +} + +static inline long Dot2D(SVECTOR *v1, SVECTOR *v2) +{ + *(short *)(SCRPAD_ADDR + 0x004) = -v2->vy; + *(short *)(SCRPAD_ADDR + 0x006) = v2->vx; + gte_NormalClip(0, *(long *)v1, *(long *)(SCRPAD_ADDR + 0x004), (SCRPAD_ADDR + 0x008)); + return *(long *)(SCRPAD_ADDR + 0x008); +} + +static inline long Det2D(SVECTOR *v1, SVECTOR *v2) +{ + gte_NormalClip(0, *(long *)v1, *(long *)v2, (SCRPAD_ADDR + 0x008)); + return *(long *)(SCRPAD_ADDR + 0x008); +} + +static inline long Len2D(SVECTOR *vec) +{ + return SquareRoot0(Dot2D(vec, vec)); +} + +#endif // __MGS_LIBHZD_PRIVATE_H__ diff --git a/components/mgs/mgs/source/libhzd/route.c b/components/mgs/mgs/source/libhzd/route.c new file mode 100644 index 00000000..1310c8f4 --- /dev/null +++ b/components/mgs/mgs/source/libhzd/route.c @@ -0,0 +1,100 @@ +#include "libhzd.h" + +#include "libgv/libgv.h" + +/*----------------------------------------------------------------*/ + +typedef struct +{ + int buffer[16]; +} List; + +/*----------------------------------------------------------------*/ + +static void MakeRoutes(HZD_ZON *zones, int n_zones, int to, char *routes) +{ + int i, j, k, t1, t4; //t0, t2, t3, t1 + //int zone_buf[32]; + List zone_buf[2]; + int *p; //t6 + int *p2; // t5 + char *nears; + int val; + int near; + char *temp_ptr; + unsigned char *idx; + + for ( i = 0 ; i < n_zones ; i++ ) + { + routes[ i ] = 0xFF; + } + + //loc_80021C8C + p = ( int* )zone_buf; //t6 + p2 = ( int* )&zone_buf[ 1 ]; //t5 + + routes[ to ] = 0; + zone_buf[ 0 ].buffer[ 0 ] = 1; + *( ( char* )&zone_buf[ 0 ].buffer[ 1 ] ) = to; + + k = 1; + for ( t4 = 1 ; t4 > 0 ; k++ ) + { + for ( i = t1 = 0 ; i < t4 ; i++ ) + { + idx = ((char*)p + i); + nears = zones[ idx[ 4 ] ].nears; //a2 + + for ( j = 0 ; j < 6 ; j++ ) + { + near = nears[ 0 ]; + nears++; + if ( near == 0xFF ) break; + if ( k < routes[ near ] ) + { + //loc_80021D08 + temp_ptr = &(( char* )p2)[ t1 ]; + t1++; + routes[ near ] = k; + temp_ptr[ 4 ] = near; + } + } + } + + val = k & 1; + p = ( int* )&zone_buf[ val ]; + p2[ 0 ] = t1; + p2 = ( int* )&zone_buf[ ( 1 - val ) ]; + t4 = p[ 0 ]; + } +} + +void HZD_MakeRoute(HZD_DEF *hzd, u_char *routes) +{ + HZD_ZON *zones; + int i, n_zones; + + char *tmp_routes, *r; + + int j, n; + + n_zones = hzd->n_zones; + zones = hzd->zones; + n = n_zones; + if (n_zones > 0) + { + if ((tmp_routes = GV_Malloc(n_zones))) + { + r = routes; + for (i = 0; i < n_zones; i++) + { + MakeRoutes(zones, n_zones, i, tmp_routes); + for (j = i + 1; j < n_zones; j++) + { + *r++ = tmp_routes[j]; + } + } + GV_Free(tmp_routes); + } + } +} diff --git a/components/mgs/mgs/source/libhzd/trap.c b/components/mgs/mgs/source/libhzd/trap.c new file mode 100644 index 00000000..ac250969 --- /dev/null +++ b/components/mgs/mgs/source/libhzd/trap.c @@ -0,0 +1,265 @@ +#include "libhzd.h" + +#include "common.h" +#include "strcode.h" +#include "game/game.h" + +typedef struct +{ + HZD_VEC from; + HZD_SEG current; + u_int n_inside; + u_short inside[6]; +} ScrPad; + +#define SCRPAD ((ScrPad *)SCRPAD_ADDR) + +#define FROM (&(SCRPAD->from)) +#define CURRENT (&(SCRPAD->current)) +#define N_INSIDE (&(SCRPAD->n_inside)) +#define INSIDE (SCRPAD->inside) + +/*----------------------------------------------------------------*/ + +static inline void CopyInsideList(HZD_EVT *ev) +{ + u_short *from, *to; + int i; + + from = ev->inside; + to = INSIDE; + *N_INSIDE = ev->n_inside; + for (i = ev->n_inside; i > 0; i--) + { + *to++ = *from++; + } +} + +// TODO: remove in argument +static inline int DeleteInsideList(u_short *in, int name) +{ + u_short *inside; + int i; + + inside = INSIDE; + for (i = *N_INSIDE; i > 0; i--) + { + if (*inside++ == name) + { + (*N_INSIDE)--; + inside[-1] = (in + 0x0E)[*N_INSIDE]; + return 1; + } + } + return 0; +} + +static inline int AppendInsideList(u_short *inside, int n_inside, int name) +{ + int i; + + for (i = n_inside; i > 0; i--) + { + if (*inside++ == name) + { + return n_inside; + } + } + + *inside = name; + return n_inside + 1; +} + +/*----------------------------------------------------------------*/ + +#ifdef __psyz +/* Counted at the hit site further down and printed by the periodic scan + * report; defined in port/psyz_port.c so both blocks see the same one. */ +extern int mgs_dbg_trap_hits; +#endif + +static inline int InsideTrap(void) +{ + int d; + + d = FROM->x; + if (d < CURRENT->p1.x || d >= CURRENT->p2.x) return 0; + d = FROM->z; + if (d < CURRENT->p1.z || d >= CURRENT->p2.z) return 0; + d = FROM->y; + if (d < CURRENT->p1.y || d >= CURRENT->p2.y) return 0; + return 1; +} + +static void ExecEnterEvent(HZD_HDL *hzd, HZD_EVT *ev) +{ + HZD_GRP *grp; + HZD_TRP *trp; + void *scr; + int i, n_inside; + int name; + + grp = hzd->grp; + trp = (HZD_TRP *)grp->triggers; + + ev->type = HASH_ENTER; + +#ifdef __psyz + { + /* Triggers are how MGS grows the world: crossing one runs a script + * that does "add map N". Only map 1 was ever added on this board, so + * the rest of the dock has no geometry and no collision -- black to + * the sides, and nowhere to walk to. Either no trigger is ever being + * entered, or the list being scanned is empty. Say which, once every + * few seconds, and report the first hit so a working one is + * distinguishable from none at all. */ + static int beat; + if ((++beat % 600) == 0) + { + printf("[trap] scanning %d triggers (%d cameras), %d entered so far\n", + grp->n_triggers - hzd->n_cameras, hzd->n_cameras, + mgs_dbg_trap_hits); + } + } +#endif + + n_inside = 0; + for (i = grp->n_triggers - hzd->n_cameras; i > 0; i--, trp++) + { + scr = SCRPAD; + *CURRENT = *(HZD_SEG *)trp; + + if (!InsideTrap()) + { + continue; + } +#ifdef __psyz + { + /* Report each trigger the FIRST time it is crossed, rather than + * the first twelve crossings. The ones around the spawn point fire + * at once and used to eat the whole budget, so walking somewhere + * interesting -- the elevator -- produced nothing at all. With a + * seen-list the log stays quiet until new ground is covered, and + * the name that appears when the player reaches something that + * does not respond IS the answer to why it does not. */ + static short seen[64]; + static int n_seen; + int k, fresh = 1; + + mgs_dbg_trap_hits++; + for (k = 0; k < n_seen; k++) + { + if (seen[k] == (short)trp->name_id) { fresh = 0; break; } + } + if (fresh) + { + if (n_seen < (int)(sizeof(seen) / sizeof(seen[0]))) + { + seen[n_seen++] = (short)trp->name_id; + } + printf("[trap] NEW trigger '%.12s' id %d at %d,%d,%d\n", + trp->name, trp->name_id, ev->mov.vx, ev->mov.vy, + ev->mov.vz); + } + } +#endif + + name = trp->name_id; + ev->object = name; + + if (!DeleteInsideList(scr, name)) + { + HZD_ExecEvent(hzd, ev, 1); + } + else + { + HZD_ExecEvent(hzd, ev, 2); + } + + n_inside = AppendInsideList(ev->inside, n_inside, name); + } + + ev->n_inside = n_inside; +} + +static void ExecLeaveEvent(HZD_HDL *hzd, HZD_EVT *ev) +{ + u_short *inside; + int i; + + ev->type = HASH_LEAVE; + + inside = INSIDE; + for (i = *N_INSIDE; i > 0; i--) + { + ev->object = *inside++; + HZD_ExecEvent(hzd, ev, 0); + } +} + +void HZD_EnterTrap(HZD_HDL *hzd, HZD_EVT *ev) +{ + SVECTOR *mov; + short tmp; + u_short *from, *to; + int i; + + mov = &ev->mov; + + *(short *)(SCRPAD_ADDR + 0x000) = mov->vx; + do {} while (0); + + *(short *)(SCRPAD_ADDR + 0x004) = mov->vy; + from = ev->inside; + do {} while (0); + + tmp = mov->vz; + to = INSIDE; + + do {} while (0); + + *(short *)(SCRPAD_ADDR + 0x002) = tmp; + *N_INSIDE = ev->n_inside; + + for (i = ev->n_inside; i > 0; i--) + { + *to++ = *from++; + } + + ExecEnterEvent(hzd, ev); + ExecLeaveEvent(hzd, ev); +} + +// TODO: move + +#define HZD_COPY_ELEM(dst, src) \ +do { \ + *(dst) = (src); \ +} while (0) + +#define HZD_COPY_VEC(dst, src) \ +do { \ + HZD_COPY_ELEM(&((HZD_VEC *)(dst))->x, (src)->vx); \ + HZD_COPY_ELEM(&((HZD_VEC *)(dst))->y, (src)->vy); \ + HZD_COPY_ELEM(&((HZD_VEC *)(dst))->z, (src)->vz); \ +} while (0) + +HZD_TRP *HZD_CheckBehindTrap(HZD_HDL *hzd, SVECTOR *pos) +{ + int i; + HZD_TRP *trap; + + HZD_COPY_VEC(FROM, pos); + + for (i = hzd->n_cameras, trap = hzd->traps; i > 0; i--, trap++) + { + *CURRENT = *(HZD_SEG *)trap; + + if (InsideTrap()) + { + return trap; + } + } + + return NULL; +} diff --git a/components/mgs/mgs/source/libhzd/vector.c b/components/mgs/mgs/source/libhzd/vector.c new file mode 100644 index 00000000..96109260 --- /dev/null +++ b/components/mgs/mgs/source/libhzd/vector.c @@ -0,0 +1,122 @@ +#include "libhzd.h" +#include "private.h" + +#include "common.h" +#include "inline_n.h" +#include "inline_x.h" +#include "psxdefs.h" // for getScratchAddr2 +#include "libdg/libdg.h" +#include "libgv/libgv.h" // for GV_VecLen3 + +int HZD_HazardReaction( SVECTOR *vects, int n_vects, int range, SVECTOR *react ) +{ + int area; + int area2; + int area4; + int area3; + int area5; + int area6; + int len; + + SVECTOR *pVec1; + SVECTOR *pVec2; + + int temp; + + react->vz = 0; + react->vy = 0; + react->vx = 0; + + if ( n_vects == 0 ) + { + return 1; + } + + CopyToHzdVec((SCRPAD_ADDR + 0x00C), vects); + area = Len2D((SVECTOR *)(SCRPAD_ADDR + 0x00C)); + + if ( area >= range ) + { + return 1; + } + + if ( area == 0 ) + { + return 0; + } + + if ( n_vects == 2 ) + { + CopyToHzdVec((SCRPAD_ADDR + 0x010), &vects[1]); + area2 = Len2D((SVECTOR *)(SCRPAD_ADDR + 0x010)); + + if ( area2 < range ) + { + Mul2D((SVECTOR *)(SCRPAD_ADDR + 0x014), (SVECTOR *)(SCRPAD_ADDR + 0x00C), range, area); + Mul2D((SVECTOR *)(SCRPAD_ADDR + 0x018), (SVECTOR *)(SCRPAD_ADDR + 0x010), range, area2); + + Sub2D((SVECTOR *)(SCRPAD_ADDR + 0x014), (SVECTOR *)(SCRPAD_ADDR + 0x014), (SVECTOR *)(SCRPAD_ADDR + 0x00C)); + Sub2D((SVECTOR *)(SCRPAD_ADDR + 0x018), (SVECTOR *)(SCRPAD_ADDR + 0x018), (SVECTOR *)(SCRPAD_ADDR + 0x010)); + + area3 = Dot2D((SVECTOR *)(SCRPAD_ADDR + 0x014), (SVECTOR *)(SCRPAD_ADDR + 0x014)); + area4 = Dot2D((SVECTOR *)(SCRPAD_ADDR + 0x018), (SVECTOR *)(SCRPAD_ADDR + 0x018)); + area5 = Dot2D((SVECTOR *)(SCRPAD_ADDR + 0x014), (SVECTOR *)(SCRPAD_ADDR + 0x018)); + + if ((area5 < area3) && (area5 < area4)) + { + area6 = Det2D((SVECTOR *)(SCRPAD_ADDR + 0x014), (SVECTOR *)(SCRPAD_ADDR + 0x018)); + + if ( area6 != 0 ) + { + pVec1 = (SVECTOR *)(SCRPAD_ADDR + 0x014); + + temp = pVec1->vy * area4; + + pVec2 = (SVECTOR *)(SCRPAD_ADDR + 0x018); + + react->vx = (temp - pVec2->vy * area3) / area6; + + react->vz = (pVec2->vx * area3 - pVec1->vx * area4) / area6; + + len = GV_VecLen3( react ); + + if ( len > (range << 2) ) + { + GV_LenVec3( react, react, len, range << 2 ); + } + } + + return 1; + } + } + } + + Mul2D((SVECTOR *)(SCRPAD_ADDR + 0x014), (SVECTOR *)(SCRPAD_ADDR + 0x00C), range, area); + Sub2D((SVECTOR *)(SCRPAD_ADDR + 0x014), (SVECTOR *)(SCRPAD_ADDR + 0x00C), (SVECTOR *)(SCRPAD_ADDR + 0x014)); + + CopyFromHzdVec(react, (SVECTOR *)(SCRPAD_ADDR + 0x014)); + return 1; +} + +void HZD_GetNormal(void *hzd, SVECTOR *norm) +{ + HZD_SEG *seg; + + seg = hzd; + if (seg->p1.h >= 0) // Wall + { + norm->vx = seg->p2.z - seg->p1.z; + norm->vy = 0; + norm->vz = seg->p1.x - seg->p2.x; + GV_LenVec3(norm, norm, GV_VecLen3(norm), 4096); + } + else // Floor + { + HZD_FLR *flr; + + flr = hzd; + norm->vx = flr->p1.h * 16; + norm->vy = flr->p3.h * 16; + norm->vz = flr->p2.h * 16; + } +} diff --git a/components/mgs/mgs/source/libsio/dummy.c b/components/mgs/mgs/source/libsio/dummy.c new file mode 100644 index 00000000..f98bb8a8 --- /dev/null +++ b/components/mgs/mgs/source/libsio/dummy.c @@ -0,0 +1,58 @@ +void sio_output_start(void) +{ +} + +void sio_output_stop(void) +{ +} + +void init_sio(void) +{ +} + +void sio_print(void) +{ +} + +void sio_printf(void) +{ +} + +int sio_getchar2(void) +{ +#ifdef MGS_ESPBOX + /* The mts idle task polls this in a tight loop and would hold core 0 at + * the game's priority for as long as every game thread is waiting on the + * vblank; the emulator menu, gamepad and idle tasks share that core. + * Blocking a tick here hands it to them; the vblank tick wakes a game + * thread by suspending this task, which works just as well from a delay. */ + extern void Mgs_IdleYield(void); + Mgs_IdleYield(); +#endif + return -1; /* EOF */ +} + +int getchar(void) +{ + return 0; +} + +void sio_putchar(void) +{ +} + +void sio_getchar(void) +{ +} + +void sio_puts(void) +{ +} + +void sio_setup(void) +{ +} + +void sio_dump(void) +{ +} diff --git a/components/mgs/mgs/source/libsio/isio.h b/components/mgs/mgs/source/libsio/isio.h new file mode 100644 index 00000000..ceb56074 --- /dev/null +++ b/components/mgs/mgs/source/libsio/isio.h @@ -0,0 +1,22 @@ +/* + * ref."beatmania APPEND 5thMIX" + * work.5th/include/isio.h (Apr.06,1998) + */ +#ifndef __ISIO_H__ +#define __ISIO_H__ + +extern void sio_output_start(void); +extern void sio_output_stop(void); + +extern void sio_putchar(unsigned char); +extern unsigned char sio_getchar(void); +extern int sio_getchar2(void); +//extern int sio_chkbuf(void); +//extern void sio_flush(void); +extern void sio_puts(unsigned char *); +extern int sio_setup(int); +extern void sio_dump(unsigned char *, unsigned long); +//extern int sio_get_dsr(void); +//extern int sio_get_cts(void); + +#endif diff --git a/components/mgs/mgs/source/main/main.c b/components/mgs/mgs/source/main/main.c new file mode 100644 index 00000000..d5b0f8ff --- /dev/null +++ b/components/mgs/mgs/source/main/main.c @@ -0,0 +1,147 @@ +#include +#include +#include +#include +#include +#include "common.h" + +#include "mts/mts.h" +#include "mts/taskid.h" +#include "sound/sd_cli.h" + +#include "libgv/libgv.h" +#include "libfs/libfs.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "libhzd/libhzd.h" +#include "memcard/memcard.h" +#include "game/game.h" + +unsigned int _ramsize = 0x200000; // 2MB RAM size +unsigned int _stacksize = 0x8000; // 32KB stack size + +/*--------------------------------------------------------------------------*/ + +#define DECLARE_NEWCHARA_PROTOS +#include "charalst.h" + +CHARA MainCharacterEntries[] = { + CHARA_SNAKE, /* chara スネーク */ + CHARA_ITEM, /* chara アイテム */ + CHARA_DOOR, /* chara ドア */ + CHARA_END +}; + +const char *MGS_DiskName[] = { +#if defined(__psyz) + /* the port runs off the USA release the user supplied; the file layout and + * STAGE.DIR format are identical to Integral's */ + "SLUS_005.94", // 0: DISC 1 +#elif defined(INTEGRAL) +#ifdef VR_EXE + "SLPM_862.49", // 0: VR-DISC +#else + "SLPM_862.47", // 0: DISC 1 + "SLPM_862.48", // 1: DISC 2 +#endif +#endif + NULL // end-of-list +}; +const char *MGS_MemoryCardName = { +#ifdef INTEGRAL + "BISLPM-86247" +#endif +}; + +/*--------------------------------------------------------------------------*/ + +//static long GameStack[512]; +//static long SdStack[512]; + +#define GAME_STACK_SIZE (2048) +#define SD_STACK_SIZE (2048) + +extern char GameStack[GAME_STACK_SIZE]; +extern char SdStack[SD_STACK_SIZE]; + +static void Main(void) +{ + RECT rect; + +#ifdef __psyz +/* Boot trace: on the PSX every one of these returned promptly. Off it, any of + * them can block forever on hardware that is not there, and a silent stop + * looks identical to a crash. */ +#define BOOT_STEP(x) do { printf("[boot] " #x "\n"); x; } while (0) +#else +#define BOOT_STEP(x) x +#endif + + BOOT_STEP(ResetGraph(0)); + BOOT_STEP(SetGraphDebug(0)); + BOOT_STEP(CdInit()); + BOOT_STEP(SetDispMask(0)); + + setRECT(&rect, 0, 0, 1024, 511); + BOOT_STEP(ClearImage(&rect, 0, 0, 0)); + + BOOT_STEP(DrawSync(0)); + BOOT_STEP(SetDispMask(1)); + BOOT_STEP(InitGeom()); + + BOOT_STEP(mts_init_vsync()); + BOOT_STEP(mts_set_vsync_task()); + printf("[boot] subsystems next\n"); + + printf("mem:"); + memcard_init(); + + printf("pad:"); + mts_init_controller(); + + printf("gv:"); + GV_StartDaemon(); + + printf("fs:"); + FS_StartDaemon(); + + printf("dg:"); + DG_StartDaemon(); + + printf("gcl:"); + GCL_StartDaemon(); + + printf("hzd:"); + HZD_StartDaemon(); + + printf("sound:"); + mts_start_task(MTSID_SOUND_MAIN, SdMain, STACK_BOTTOM(SdStack), SD_STACK_SIZE); + + while (!sd_task_active()) + { + mts_wait_vbl(1); + } + + printf("gm:"); + GM_StartDaemon(); + + printf("start\n"); + + for (;;) + { + GV_ExecActorSystem(); + } +} + +static inline void START_GAME( void (*proc)(void) ) +{ + // the game task stack was originally declared static here + // ...or at least it is in 5thMix's work.5th/main/bm.c + + mts_boot_task( MTSID_GAME, proc, STACK_BOTTOM(GameStack), GAME_STACK_SIZE ); +} + +int main() +{ + START_GAME( Main ); +} diff --git a/components/mgs/mgs/source/memcard/memcard.c b/components/mgs/mgs/source/memcard/memcard.c new file mode 100644 index 00000000..d5597667 --- /dev/null +++ b/components/mgs/mgs/source/memcard/memcard.c @@ -0,0 +1,737 @@ +#include "memcard.h" +#ifdef __psyz +#ifndef O_NOWAIT +#define O_NOWAIT 0x8000 /* PSY-Q non-blocking open; no-op on a hosted FS */ +#endif +#endif + +#include +#include +#include +#include +#include "psxdefs.h" + +#include "common.h" +#include "mts/mts.h" + +STATIC int memcard_initialized = FALSE; + +extern long gHardware_end_io; +extern long gHardware_end_write; +extern long gHardware_timeout; +extern long gHardware_new_device; +extern long gSoftware_end_io; +extern long gSoftware_end_write; +extern long gSoftware_timeout; +extern long gSoftware_new_device; +extern TMemCardFunc gHwCard_do_op; +extern TMemCardFunc gSwCard_do_op; +extern volatile int gSwCardLastOp; +extern volatile int gHwCardLastOp; +extern MEM_CARD gMemCards[2]; +extern volatile long gMemCard_io_size; + +static void memcard_hwcard_do_op(int op); +static void memcard_swcard_do_op(int op); +static int memcard_dummy(int state); + +static inline void memcard_access_wait(void) +{ + printf("[R]"); + + while ((gHwCard_do_op != memcard_hwcard_do_op) || + (gSwCard_do_op != memcard_swcard_do_op)) + { + printf("ACCESS WAIT..\n"); + mts_wait_vbl(2); + } + + gHwCardLastOp = 0; + gSwCardLastOp = 0; +} + +static void memcard_hwcard_do_op(int op) +{ + gHwCardLastOp = op; +} + +static void memcard_swcard_do_op(int op) +{ + gSwCardLastOp = op; +} + +static void memcard_hwcard_end_io() +{ + gHwCard_do_op(1); +} + +static void memcard_hwcard_end_write() +{ + printf("*** hw card error\n"); + gHwCard_do_op(2); +} + +static void memcard_hwcard_timeout() +{ + printf("[C.H.T.O]"); + gHwCard_do_op(3); +} + +static void memcard_hwcard_new() +{ + printf("*** hw card new\n"); + gHwCard_do_op(4); +} + +static void memcard_swcard_end_io() +{ + gSwCard_do_op(1); +} + +static void memcard_swcard_end_write() +{ + printf("*** sw card error\n"); + gSwCard_do_op(2); +} + +static void memcard_swcard_timeout() +{ + printf("[C.S.T.O]"); + gSwCard_do_op(3); +} + +static void memcard_swcard_new() +{ + printf("*** sw card new\n"); + gSwCard_do_op(4); +} + +static void memcard_set_sw_hw_card_fns(void) +{ + gHwCard_do_op = memcard_hwcard_do_op; + gSwCard_do_op = memcard_swcard_do_op; +} + +/** + * @brief Determines whether the memory card in the specified port is formatted. + * + * This function reads the first sector of the first block from the specified + * memory card. If the first two bytes are 'M' and 'C', then the card is deemed + * formatted, unformatted otherwise. + * + * @param port The port to check: 0 for port 1, 1 for port 2. + * + * @bug When passing 1 as argument, the function does not check port 2, but tries + * to access port B of MultiTap on port 1 instead, which results in an error. + * + * @return + * - 1 if the memory card is formatted. + * - 5 if the memory card is unformatted. + * - 2 if an error occurred. + */ +static int memcard_easy_format_test(int port) +{ + char buffer[128]; + +#ifdef __psyz + /* Also a blocking hardware probe (memcard_access_wait waits on a BIOS card + * event). No slot, no card: report an error, which the caller turns into + * last_op = 2 and the game treats as "no memory card in this port". */ + (void)port; + return 2; +#endif + + printf("easy_format_test\n"); + memset(buffer, 0, sizeof(buffer)); + + memcard_access_wait(); + + // BUG: the first parameter is the channel, not the port, so the correct + // value to pass is port * 16 + cardIndex. Therefore the function works + // correctly when port is 0, but when port is 1, it tries to access + // port B of MultiTap on port 1, which results in an error. + // However, this function is probably never executed by the game. + // See also the comment in memcard_init(). + _card_read(port, 0, buffer); + + do { + mts_wait_vbl(1); + } while (!gHwCardLastOp); + + if (gHwCardLastOp != 1) + { + printf("card_error\n"); + return 2; + } + else + { + if (buffer[0] == 'M' && buffer[1] == 'C') + { + printf("card_normal\n"); + return 1; + } + else + { + printf("card_unformat\n"); + return 5; + } + } +} + +/** + * Gets information about all the files contained in a memory card. + * + * For each file in the memory card, this function fills + * gMemCards[port].files[i] fields by setting the file name + * and size, and MEM_CARD_FILE::field_14 to 0 (unknown meaning). + * + * @param port The memory card port: 0 for port 1, 1 for port 2. + * @param pUsedBlocksCount When the function returns, contains the number of + * blocks that are in use in the memory card. + * + * @return The number of files contained in the memory card. + */ +static int memcard_loaddir(int port, int *pUsedBlocksCount) +{ + struct DIRENTRY dir; + char name[32]; + int files; + int blocks; + + blocks = 0; + + sprintf(name, "bu%02X:*", port * 16); + printf("load_dir %s start\n", name); + + if (firstfile(name, &dir)) + { + files = 0; + + do { + memcpy(gMemCards[port].files[files].name, dir.name, sizeof(dir.name)); + gMemCards[port].files[files].field_14 = 0; + gMemCards[port].files[files].field_18_size = dir.size; + blocks += (dir.size + 8191) / MC_BLOCK_SIZE; + files++; + } + while (nextfile(&dir)); + + printf("TOTAL %d FILES used %d block\n", files, blocks); + *pUsedBlocksCount = blocks; + return files; + } + + printf("NO FILE\n"); + *pUsedBlocksCount = 0; + return 0; +} + +/** + * Helper function for memcard_get_files(). + * + * This function fills all gMemCards[port] fields by setting the card + * index, the number of files contained in the card, the number of free blocks, + * and MEM_CARD::last_op to 1 (unknown meaning). It then calls + * memcard_loaddir() to fill the array MEM_CARD::files. + * + * @param port The memory card port: 0 for port 1, 1 for port 2. + */ +static void memcard_load_files(int port) +{ + int pUsedBlocksCount; + gMemCards[port].card_idx = port; + gMemCards[port].last_op = 1; + gMemCards[port].file_count = memcard_loaddir(port, &pUsedBlocksCount); + gMemCards[port].free_blocks = 15 - pUsedBlocksCount; +} + +// Pure function whose return value is never used +// (as of the current decompilation status). +static int memcard_dummy(int state) +{ + switch (state) + { + case 1: + return 0; + case 4: + return 0x01000000; + case 2: + return 0xc0000002; + case 3: + return 0xc0000003; + default: + return 0x80000000; + } +} + +void memcard_reset_status(void) +{ + gMemCards[0].last_op = 2; + gMemCards[1].last_op = 2; +} + +int memcard_check(int port) +{ + int chan; + int retries; + int sw_card_op; + int hw_card_op; + +#ifdef __psyz + /* This polls the BIOS card driver and waits on events the hardware raises. + * There is no card slot here, so the loop below never terminates and the + * whole boot stops inside memcard_init(). Report "no card present", which + * is a state the game already handles -- it is also simply true. */ + gMemCards[port].last_op = 2; /* error / absent */ + return 0; +#endif + + chan = port * 16; + retries = 0; + + if ((gMemCards[port].last_op == 5) || (gMemCards[port].last_op == 2)) + { + goto loop_24; + } + + while (1) + { + memcard_access_wait(); + _card_info(chan); + + if ((retries++) > 10) + { + printf("MEMCARD:RETRY OVER!!\n"); + return 0x80000002; + } + + // FIXME: why does THIS need a goto while the others need a do while? + retry1: + mts_wait_vbl(1); + + if ((sw_card_op = !gSwCardLastOp)) + goto retry1; + + sw_card_op = gSwCardLastOp; + switch (sw_card_op) + { + case 1: + if (gMemCards[port].last_op == 5) + { + return 0x80000001; + } + + if (gMemCards[port].last_op == 4) + { + goto exit; + } + + sw_card_op = 1; + gMemCards[port].last_op = sw_card_op; + return 0; + + case 2: + + case 3: + gMemCards[port].last_op = sw_card_op; + memcard_retry(port); + return; + + case 4: + break; + + default: + return; // FIXME: Return without value in function that returns long??? + } + + loop_24: + memcard_access_wait(); + _card_clear(chan); + + do { + mts_wait_vbl(1); + } while ((hw_card_op = !gHwCardLastOp)); + hw_card_op = gHwCardLastOp; + + if (hw_card_op == 1) + { + gMemCards[port].last_op = 4; + + memcard_access_wait(); + _card_load(chan); + + do { + mts_wait_vbl(1); + } while ((sw_card_op = !gSwCardLastOp)); + + sw_card_op = gSwCardLastOp; + + if (sw_card_op == 4) + { + gMemCards[port].last_op = 5; + + { +#ifdef __psyz + /* pinning $v0 only serves the matching build */ + int ret; +#else + register int ret asm("v0"); +#endif + ret = 0x80000000; + asm("" :: "r"(ret)); + } + + return 0x80000001; // or "return ret | 1;" (with ret variable outside block) + } + + if (sw_card_op == 1) + { + gMemCards[port].last_op = 4; + break; + } + + printf("RETRY(new)\n"); + } + else + { + printf("RETRY\n"); + } + } + +exit: + return 0x1000000; +} + +void memcard_init(void) +{ + int port; + + if (!memcard_initialized) + { + memcard_initialized = !memcard_initialized; + gHwCardLastOp = 1; + + gSwCardLastOp = 1; + memcard_set_sw_hw_card_fns(); + + EnterCriticalSection(); + + gHardware_end_io = OpenEvent(HwCARD, EvSpIOE, EvMdINTR, (openevent_cb_t)memcard_hwcard_end_io); + gHardware_end_write = OpenEvent(HwCARD, EvSpERROR, EvMdINTR, (openevent_cb_t)memcard_hwcard_end_write); + gHardware_timeout = OpenEvent(HwCARD, EvSpTIMOUT, EvMdINTR, (openevent_cb_t)memcard_hwcard_timeout); + gHardware_new_device = OpenEvent(HwCARD, EvSpNEW, EvMdINTR, (openevent_cb_t)memcard_hwcard_new); + + gSoftware_end_io = OpenEvent(SwCARD, EvSpIOE, EvMdINTR, (openevent_cb_t)memcard_swcard_end_io); + gSoftware_end_write = OpenEvent(SwCARD, EvSpERROR, EvMdINTR, (openevent_cb_t)memcard_swcard_end_write); + gSoftware_timeout = OpenEvent(SwCARD, EvSpTIMOUT, EvMdINTR, (openevent_cb_t)memcard_swcard_timeout); + gSoftware_new_device = OpenEvent(SwCARD, EvSpNEW, EvMdINTR, (openevent_cb_t)memcard_swcard_new); + + EnableEvent(gHardware_end_io); + EnableEvent(gHardware_end_write); + EnableEvent(gHardware_timeout); + EnableEvent(gHardware_new_device); + + EnableEvent(gSoftware_end_io); + EnableEvent(gSoftware_end_write); + EnableEvent(gSoftware_timeout); + EnableEvent(gSoftware_new_device); + + ExitCriticalSection(); + + port = 0; + + InitCARD(0); + StartCARD(); + _bu_init(); + mts_set_vsync_task(); + memcard_reset_status(); + + for (port = 0; port < 2; port++) + { + if (!memcard_check(port)) + { + // I couldn't make the game enter this path without forcing it, + // but I think it is never executed because the function below + // is bugged with regard to port 2. + long memCardType = memcard_easy_format_test(port); + if (memCardType == 5) // Unformatted card. + { + gMemCards[port].last_op = 5; + } + else if (memCardType == 1) // Formatted card. + { + gMemCards[port].last_op = 1; + } + else // Error. + { + gMemCards[port].last_op = 2; + } + } + else + { + gMemCards[port].last_op = 5; + } + } + } +} + +void memcard_exit(void) +{ + StopCARD(); + EnterCriticalSection(); + CloseEvent(gHardware_end_io); + CloseEvent(gHardware_end_write); + CloseEvent(gHardware_timeout); + CloseEvent(gHardware_new_device); + CloseEvent(gSoftware_end_io); + CloseEvent(gSoftware_end_write); + CloseEvent(gSoftware_timeout); + CloseEvent(gSoftware_new_device); + ExitCriticalSection(); + memcard_initialized = FALSE; +} + +void memcard_retry(int port) +{ + int op; + int count; + int i; + + switch (gMemCards[port].last_op) + { + case 1: + case 4: + // Dead code path. + op = gMemCards[port].last_op; + memcard_dummy(op); + return; + + case 3: + count = 1; + break; + + case 2: + count = 20; + break; + } + + for (i = 0; i < count; i++) + { + memcard_access_wait(); + _card_info(port * 16); + + do { + mts_wait_vbl(1); + } + while (!gSwCardLastOp); + + op = gSwCardLastOp; + + if (op == 1 || op == 4) + { + break; + } + + mts_wait_vbl(3); + } + + memcard_dummy(op); +} + +/** + * Gets the number of files, their name and size, and the number of free blocks + * for the memory card in the specified port. + * + * This function calls other internal functions to fill all + * gMemCards[port] fields, but only if such card is in a suitable + * status to perform the operation, i.e., if its last_op + * is 1 or 4 (unknown meaning). + * + * @param port The memory card port: 0 for port 1, 1 for port 2. + * + * @return A pointer to gMemCards[port] containing the requested + * information if the operation was performed, otherwise 0. + */ +MEM_CARD *memcard_get_files(int port) +{ + MEM_CARD *pCardBase = gMemCards; + MEM_CARD *pCard = &pCardBase[port]; + + if (pCard->last_op == 1 || pCard->last_op == 4) + { + memcard_load_files(port); + pCard->last_op = 1; + return pCard; + } + return 0; +} + +/** + * Tries to delete the specified file from the memory card in the specified port. + * + * @param port The memory card port: 0 for port 1, 1 for port 2. + * @param filename The name of the file to delete. + * + * @return 1 if the file was deleted, 0 otherwise. + */ +int memcard_delete(int port, const char *filename) +{ + char tmp[32]; + + MEM_CARD *pCardBase = gMemCards; + MEM_CARD *pCard = &pCardBase[port]; + + if (pCard->last_op == 1) + { + sprintf(tmp, "bu%02X:%s", 16 * port, filename); + if (erase(tmp)) + { + printf("Deleted File %s", filename); + return 1; + } + printf("ERROR : can't delete %s\n", filename); + } + return 0; +} + +static void memcard_hwcard_read_write_handler(int op) +{ + if (op == 1) + { + gMemCard_io_size -= 128; + if (!gMemCard_io_size) + { + gHwCard_do_op = memcard_hwcard_do_op; + } + } + else + { + gMemCard_io_size = -1; + memcard_set_sw_hw_card_fns(); + } +} + +static void memcard_swcard_read_write_handler(int op) +{ + if (op == 1) + { + printf("MEMCARD READ/WRITE end\n"); + } + else + { + printf("ERROR : MEMCARD READ/WRITE\n"); + } + gSwCard_do_op = (TMemCardFunc)memcard_swcard_do_op; +} + +static void memcard_set_read_write(int fileSize) +{ + gHwCard_do_op = memcard_hwcard_read_write_handler; + gSwCard_do_op = memcard_swcard_read_write_handler; + gMemCard_io_size = fileSize; +} + +#define ROUND_UP(val,rounding) (((val) + (rounding) - 1) / (rounding) * (rounding)) + +void memcard_write(int port, const char *filename, int offset, char *buffer, int size) +{ + int blocks = ROUND_UP(size, MC_BLOCK_SIZE) / MC_BLOCK_SIZE; + int fd; + char name[32]; + + sprintf(name, "bu%02X:%s", port * 16, filename); + + fd = open(name, (blocks << 16) | O_CREAT); + if (fd < 0) + { + printf("Warning : MEMCARD create error ... overwrite\n"); + } + close(fd); + + fd = open(name, O_NOWAIT | O_WRONLY); + if (fd < 0) + { + printf("MEMCARD WRITE ERROR FD %d\n", fd); + gMemCard_io_size = -1; + return; + } + + printf("MEMCARD WRITE %s FD %d SIZE %d\n", filename, fd, size); + size = ROUND_UP(size, 128); + if (offset > 0) + { + lseek(fd, offset, SEEK_SET); + } + memcard_set_read_write(size); + write(fd, buffer, size); + close(fd); + printf("WRITING FILE %s...\n", filename); +} + +void memcard_read(int port, const char *filename, int offset, char *buffer, int size) +{ + char name[32]; + int fd; + + sprintf(name, "bu%02x:%s", port * 16, filename); + fd = open(name, FREAD | FASYNC); + if (fd < 0) + { + printf("MEMCARD READ ERROR FD %d\n", fd); + gMemCard_io_size = -1; + return; + } + size = ROUND_UP(size, 128); + printf("MEMCARD READ %s FD %d SIZE %d\n", filename, fd, size); + if (offset > 0) + { + lseek(fd, offset, SEEK_SET); + } + memcard_set_read_write(size); + read(fd, buffer, size); + close(fd); + printf("READING FILE %s...\n", filename); +} + +int memcard_get_status(void) +{ + return gMemCard_io_size; +} + +/** + * Tries to format the memory card in the specified port. + * + * @param port The memory card port: 0 for port 1, 1 for port 2. + * + * @retval 1 memory card was formatted. + * @retval 0 memory card was not formatted. + */ +int memcard_format(int port) +{ + int retries; + char cardPath[32]; + + retries = 4; + sprintf(cardPath, "bu%02x:", port * 16); + + if (gMemCards[port].last_op == 5) + { + retry: + if (format(cardPath) != 0) + { + printf("FORMATED %d\n", port); + gMemCards[port].last_op = 1; + return 1; + } + retries--; + if (retries <= 0) + { + printf("ERROR : MEMCARD FORMAT\n"); + return 0; + } + goto retry; // TODO: find a match without goto + } + + printf("ERROR : MEMCARD FORMATED CARD\n"); + return 0; +} diff --git a/components/mgs/mgs/source/memcard/memcard.h b/components/mgs/mgs/source/memcard/memcard.h new file mode 100644 index 00000000..b612e1ba --- /dev/null +++ b/components/mgs/mgs/source/memcard/memcard.h @@ -0,0 +1,40 @@ +#ifndef __MGS_MEMCARD_H__ +#define __MGS_MEMCARD_H__ + +// 8 KB +#define MC_BLOCK_SIZE 8192 + +typedef struct MEM_CARD_FILE +{ + char name[20]; + char field_14; + char field_15; + unsigned short field_16; + int field_18_size; +} MEM_CARD_FILE; + +typedef struct MEM_CARD +{ + unsigned char card_idx; + unsigned char last_op; + char file_count; + char free_blocks; + MEM_CARD_FILE files[15]; +} MEM_CARD; + +typedef void (*TMemCardFunc)(int); + +void memcard_reset_status(void); +int memcard_check(int port); +void memcard_init(void); +void memcard_exit(void); +void memcard_retry(int port); +MEM_CARD *memcard_get_files(int port); +int memcard_delete(int port, const char *filename); + +void memcard_write(int port, const char *filename, int offset, char *buffer, int size); +void memcard_read(int port, const char *filename, int offset, char *buffer, int size); +int memcard_get_status(void); +int memcard_format(int port); + +#endif // __MGS_MEMCARD_H__ diff --git a/components/mgs/mgs/source/menu/datasave.c b/components/mgs/mgs/source/menu/datasave.c new file mode 100644 index 00000000..58c9bd6f --- /dev/null +++ b/components/mgs/mgs/source/menu/datasave.c @@ -0,0 +1,2372 @@ +#include "menuman.h" + +#include +#include +#include + +#include "common.h" +#include "game/game.h" +#include "menu/radio.h" +#include "linkvar.h" +#include "libdg/libdg.h" +#include "libgv/libgv.h" +#include "libgcl/libgcl.h" +#include "memcard/memcard.h" +#include "mts/mts.h" +#include "mts/taskid.h" +#include "sound/g_sound.h" + +//------------------------------------------------------------------------------ +// gp +int SECTION(".sbss") dword_800ABB44; +int SECTION(".sbss") dword_800ABB48; +DATA_INFO *SECTION(".sbss") data_info; +void *SECTION(".sbss") stack_800ABB50; +int SECTION(".sbss") dword_800ABB54; +int SECTION(".sbss") dword_800ABB58; +int SECTION(".sbss") dword_800ABB5C; +int SECTION(".sbss") mcd_last_check_800ABB60[2]; +MEM_CARD *SECTION(".sbss") mcd_last_file_800ABB68[2]; +SELECT_INFO *SECTION(".sbss") dword_800ABB70; +SELECT_INFO *SECTION(".sbss") dword_800ABB74; +SELECT_INFO *SECTION(".sbss") dword_800ABB78; +RadioFileModeStru_800ABB7C *SECTION(".sbss") stru_800ABB7C; +int SECTION(".sbss") dword_800ABB80; +int SECTION(".sbss") dword_800ABB84; +SELECT_INFO *SECTION(".sbss") dword_800ABB88; +int SECTION(".sbss") dword_800ABB8C; +int SECTION(".sbss") dword_800ABB90; +int SECTION(".sbss") dword_800ABB94; +//------------------------------------------------------------------------------ + +extern char *MGS_MemoryCardName; /* in main.c */ + +extern char aRequestX[]; +extern char aResultX[]; + +int clutsAndIcons_8009E774[6][40] = { + { + // CLUT for icon 1 (8 * 4 bytes = 32 bytes) + 0x24A51421, 0x45AD3529, 0x88A5631, 0x217414EF, 0x363929D6, 0x10750C13, 0x195A14D8, 0xEEB19BC, + // Icon 1 (32 * 4 bytes = 128 bytes): Snake + 0x12211, 0xEA00310, 0x20000000, 0xEC00331, 0x23210012, 0xEB20243, 0x33332213, 0xEA10134, + 0x44422233, 0xEA10123, 0x34322343, 0xED11212, 0x24444443, 0xEE02221, 0x24444343, 0xEE30132, + 0x23444233, 0xEE00133, 0x22334301, 0xEB00123, 0x21224420, 0xD000113, 0x22100100, 0xA100202, + 0x32112344, 0x1100201, 0x23233200, 0x1000200, 0x2212222, 0x1000220, 0, 0, + }, + { + // CLUT for icon 2 + 0x24A51421, 0x45AD3529, 0x88A5631, 0x217414EF, 0x363929D6, 0x10750C13, 0x195A14D8, 0xEEB19BC, + // Icon 2: Meryl + 0x11124444, 0x2221131, 0x21000134, 0x2210222, 0x1114, 0x1230, 0x10000124, 0x241, + 0x11123234, 0x20234, 0x43334444, 0x10234, 0x44444444, 0x221233, 0x44444444, 0x1011133, + 0x34444444, 0x2100123, 0x34444334, 0x1110023, 0x33444421, 0x1000012, 0x33433443, 0x100002, + 0x23320211, 0xB001001, 0x13333233, 0xAC10000, 0x1333322, 0xCB10100, 0, 0, + }, + { + // CLUT for icon 3 + 0x24A51421, 0x45AD3529, 0x88A5631, 0x217414EF, 0x363929D6, 0x10750C13, 0x195A14D8, 0xEEB19BC, + // Icon 3: Otacon + 0x1343023, 0x1102021, 0x13444312, 0x1001020, 0x3444301, 0x120, 0x22343113, 0x10021, + 0x32211213, 0x10012, 0x33334433, 0x21002, 0x24444433, 0x1002002, 0x24444334, 0x2210001, + 0x23444314, 0xB100010, 0x12444302, 0xDA10011, 0x22344421, 0xEBA0002, 0x22212333, 0xEEC1001, + 0x21310100, 0xEC10100, 0x12132344, 0xC312200, 0x2112222, 0x4233101, 0, 0, + }, + { + // CLUT for icon 4 + 0x2CA51C21, 0x456B3908, 0x5E3151CE, 0x217414EF, 0x363929D6, 0x10550C13, 0x151814B7, 0x19BC195A, + // Icon 4: Ninja + 0x34455555, 0xFA12022, 0x255300, 0xFA12300, 0x241CD, 0xFA12240, 0x21000BDA, 0xFA11234, + 0x445521CD, 0xFB11223, 0x34455300, 0xFDA1123, 0x33445552, 0xFFA1122, 0x33445551, 0xFFA1112, + 0x12344551, 0xFFB1221, 0x31344551, 0xFFDA122, 0x31334451, 0xFFFA112, 0x22014451, 0xFFFB012, + 0x24213451, 0xFFFB111, 0x22313441, 0xFFFB201, 0x22312341, 0xFFFA310, 0, 0, + }, + { + // CLUT for icon 5 + 0x14A90425, 0x35B1252D, 0x88A4635, 0x217414EF, 0x363929D6, 0x49035844, 0x2A4139A2, 0x1E8316E0, + // Icon 5: Mei Ling + 0x10013444, 0xE021020, 0x12221234, 0xA010031, 0x12224, 0x32, 0x10001224, 0x200030, + 0x10022324, 0x410332, 0x32233434, 0x210333, 0x44444444, 0x110233, 0x44444444, 0xA010133, + 0x44444434, 0xCA10133, 0x34444432, 0xECBA023, 0x34433443, 0xEEDD013, 0x33421211, 0xEEED102, + 0x23333222, 0xEEEC101, 0x12334310, 0xEED0100, 0x1233444, 0xEEA1002, 0, 0, + }, + { + // CLUT for icon 6 + 0x14A90425, 0x35B1252D, 0x88A4635, 0x217414EF, 0x363929D6, 0x44693427, 0x60CD508B, 0x64896CEF, + // Icon 6: Campbell + 0x11001123, 0xED00210, 0x10000003, 0xEB01100, 0x11201013, 0xB200210, 0x12212124, 0xB020221, + 0x22322234, 0xC211123, 0x44322144, 0xDB10123, 0x33321234, 0xEC01012, 0x23211023, 0xEEB0112, + 0x22122102, 0xEED1121, 0x21222332, 0xEED2122, 0x10121100, 0xEEC0122, 0x10123433, 0xEEA0021, + 0x12222100, 0xEC00021, 0x1221111, 0xEA00102, 0x22222333, 0xB000301, 0, 0 + } +}; + +extern MenuWork gMenuWork_800BD360; + +extern const int dword_800120B4[]; +extern const int dword_800120CC[]; +extern const int dword_800120E4[]; + +extern char memoryCardFileName[]; +char memoryCardFileName[] = "BISLPM-99999 "; + +STATIC int dword_800AB6EC = 0; +STATIC int dword_800AB6F0 = -1; +STATIC int dword_800AB6F4 = 0; + +static inline KCB* GET_KCB( MenuWork *work ) +{ + return work->field_214_font; +} + +STATIC int saveFile_8004983C(struct MEM_CARD *pMemcard) +{ + int size; + int hours, minutes; + int c99; + int file; + char *buffer, *buffer_copy; + int success; + int retries; + int difficulty; + int flags1, flags2; + int i, idx; + + GM_PadResetDisable = TRUE; + + size = data_info->blocks_count * MC_BLOCK_SIZE; + buffer = GV_AllocMemory(GV_PACKET_MEMORY0, size); + if (!buffer) + { + printf("NO MEMORY FOR FILE BODY\n"); + } + + GV_ZeroMemory(buffer, size); + + buffer[0] = 'S'; + buffer[1] = 'C'; + buffer[2] = 0x11; // static icon + buffer[3] = data_info->blocks_count; + + idx = data_info->field_0[1]; + buffer_copy = buffer; + c99 = 99; + + if (idx == 0) + { + idx = GM_BonusItemsFlag; + flags1 = GM_BonusItemsFlag; + + if (flags1 >= 4) + { + idx = 3; + if (flags1 == 6) + { + idx = 0; + } + } + + flags2 = flags1 | ((GM_CurrentDiskFlag - 1) * 8); + } + else + { + flags2 = idx; + } + + // 96: start position (offset) of CLUT. + // 160 = 32 + 128 = CLUT + icon + // The save data title, which comes before the CLUT, is set few lines below. + memcpy(buffer_copy + 96, clutsAndIcons_8009E774[idx], 160); + + hours = GM_TotalHours; + minutes = GM_TotalSeconds / 60; + if (hours > 99) + { + minutes = hours = c99; + } + + // 4: start position (offset) of save data title. + data_info->make_title(buffer_copy + 4, pMemcard, hours, minutes); // Calls makeTitle_8004D008() + strcpy(memoryCardFileName, MGS_MemoryCardName); + + // 12: start position (offset) of the public part of the memory card file name. + memoryCardFileName[12] = data_info->field_0[0]; + memoryCardFileName[13] = (hours / 10) + '0'; + memoryCardFileName[14] = (hours % 10) + '0'; + + if (data_info->field_0[0] == 71) + { + difficulty = GM_DifficultyFlag; + } + else + { + difficulty = 0; + } + + if (difficulty < 0) + { + difficulty = 0; + memoryCardFileName[13] |= 0x40; + } + + memoryCardFileName[15] = ((minutes / 10) + '0') + ((difficulty & 2) << 5); + memoryCardFileName[16] = ((minutes % 10) + '0') + ((difficulty & 1) << 6); + memoryCardFileName[17] = flags2 + '@'; + memoryCardFileName[18] = data_info->field_2 + '@'; + + if (data_info->field_0[0] == 71 && data_info->field_2 == 1) + { + memoryCardFileName[13] &= 0x40; + memoryCardFileName[13] |= 0x3A; + memoryCardFileName[14] = GM_LastResultFlag + '@'; + } + + for (i = 0; i < 16; i++) + { + flags1 = 0; + memoryCardFileName[19] = 'A' + i; + for (file = 0; file < pMemcard->file_count; file++) + { + if (!strcmp(pMemcard->files[file].name, memoryCardFileName)) + { + flags1 = 1; + } + } + + if (!flags1) + { + break; + } + } + + // 256: start position (offset) of game data (as there is only one icon). + data_info->make_game_data(buffer_copy + 256); // Calls writeGameData() + + // Now try to physically write to the memory card. + success = 0; + for (retries = 4; retries > 0; retries--) + { + memcard_write(pMemcard->card_idx, memoryCardFileName, 0, buffer, size); + while ((flags2 = memcard_get_status()) > 0) + { + mts_wait_vbl(2); + } + + if (memcard_get_status() == 0) + { + // Mark successful write and stop retrying. + success = 1; + break; + } + } + + GV_FreeMemory(GV_PACKET_MEMORY0, buffer); + GM_PadResetDisable = FALSE; + return success; +} + +const char *saveCaptions_8009EB4C[] = { + "\x00", // Different from the other empty strings used below for some reason + // セーブが完了しました。 (Save completed.) + "\x82\x1b\xd0\x06\x82\x36\x81\x0c\x91\x07\x91\x08\x81\x17\x81\x3e\x81\x17\x81\x1f\xd0\x03", + // セーブできませんでした。 (Could not save.) + "\x82\x1b\xd0\x06\x82\x36\x81\x27\x81\x0d\x81\x3e\x81\x1b\x81\x53\x81\x27\x81\x17\x81\x1f\xd0\x03", + // エラーが発生しました。 (An error occurred.) + "\x82\x08\x82\x49\xd0\x06\x81\x0c\x90\x7e\x90\xca\x81\x17\x81\x3e\x81\x17\x81\x1f\xd0\x03", + // 空きブロックがたりません。 (Not enough empty blocks.) + "\x90\x92\x81\x0d\x82\x36\x82\x4d\xc2\x23\x82\x0f\x81\x0c\x81\x1f\x81\x4a\x81\x3e\x81\x1b\x81\x53\xd0\x03", + // メモリーカードが初期化されていません。 (Memory card is not initialized.) + "\x82\x41\x82\x42\x82\x4a\xd0\x06\x82\x0b\xd0\x06\x82\x29\x81\x0c\x91\x10\x91\x11\x90\x64\x81\x15\x81\x4c\x81\x26\x81\x04\x81\x3e\x81\x1b\x81\x53\xd0\x03", + // セーブしました。 (Saved.) + "\x82\x1b\xd0\x06\x82\x36\x81\x17\x81\x3e\x81\x17\x81\x1f\xd0\x03", + // フォーマットに失敗しました。 (Failed to format.) + "\x82\x35\xc2\x09\xd0\x06\x82\x3e\xc2\x23\x82\x28\x81\x2b\x91\x09\x91\x0a\x81\x17\x81\x3e\x81\x17\x81\x1f\xd0\x03", + // メモリーカードがさされていません。 (Memory card is not inserted.) + "\x82\x41\x82\x42\x82\x4a\xd0\x06\x82\x0b\xd0\x06\x82\x29\x81\x0c\x81\x15\x81\x15\x81\x4c\x81\x26\x81\x04\x81\x3e\x81\x1b\x81\x53\xd0\x03", + // セーブ中です。 (Saving.) + "\x82\x1b\xd0\x06\x82\x36\x90\xcd\x81\x27\x81\x19\xd0\x03", + // メモリーカードをチェックしています。 (Checking memory card.) + "\x82\x41\x82\x42\x82\x4a\xd0\x06\x82\x0b\xd0\x06\x82\x29\x81\x52\x82\x21\xc2\x07\xc2\x23\x82\x0f\x81\x17\x81\x26\x81\x04\x81\x3e\x81\x19\xd0\x03", + // フォーマットしています。 (Formatting.) + "\x82\x35\xc2\x09\xd0\x06\x82\x3e\xc2\x23\x82\x28\x81\x17\x81\x26\x81\x04\x81\x3e\x81\x19\xd0\x03", +}; + +const char *loadCaptions_8009EB7C[] = { + "\x00", // Different from the other empty strings used below for some reason + // ロードが完了しました。 (Load completed.) + "\x82\x4d\xd0\x06\x82\x29\x81\x0c\x91\x07\x91\x08\x81\x17\x81\x3e\x81\x17\x81\x1f\xd0\x03", + // ロードできませんでした。 (Could not load.) + "\x82\x4d\xd0\x06\x82\x29\x81\x27\x81\x0d\x81\x3e\x81\x1b\x81\x53\x81\x27\x81\x17\x81\x1f\xd0\x03", + // エラーが発生しました。 (An error occurred.) + "\x82\x08\x82\x49\xd0\x06\x81\x0c\x90\x7e\x90\xca\x81\x17\x81\x3e\x81\x17\x81\x1f\xd0\x03", + // セーブファイルがありません。 (There is no save file.) + "\x82\x1b\xd0\x06\x82\x36\x82\x35\xc2\x01\x82\x04\x82\x4b\x81\x0c\x81\x02\x81\x4a\x81\x3e\x81\x1b\x81\x53\xd0\x03", + // メモリーカードが初期化されていません。 (Memory card is not initialized.) + "\x82\x41\x82\x42\x82\x4a\xd0\x06\x82\x0b\xd0\x06\x82\x29\x81\x0c\x91\x10\x91\x11\x90\x64\x81\x15\x81\x4c\x81\x26\x81\x04\x81\x3e\x81\x1b\x81\x53\xd0\x03", + // ロードしました。 (Loaded.) + "\x82\x4d\xd0\x06\x82\x29\x81\x17\x81\x3e\x81\x17\x81\x1f\xd0\x03", + // フォーマットに失敗しました。 (Failed to format.) + "\x82\x35\xc2\x09\xd0\x06\x82\x3e\xc2\x23\x82\x28\x81\x2b\x91\x09\x91\x0a\x81\x17\x81\x3e\x81\x17\x81\x1f\xd0\x03", + // メモリーカードがさされていません。 (Memory card is not inserted.) + "\x82\x41\x82\x42\x82\x4a\xd0\x06\x82\x0b\xd0\x06\x82\x29\x81\x0c\x81\x15\x81\x15\x81\x4c\x81\x26\x81\x04\x81\x3e\x81\x1b\x81\x53\xd0\x03", + // ロード中です。 (Loading.) + "\x82\x4d\xd0\x06\x82\x29\x90\xcd\x81\x27\x81\x19\xd0\x03", + // メモリーカードをチェックしています。 (Checking memory card.) + "\x82\x41\x82\x42\x82\x4a\xd0\x06\x82\x0b\xd0\x06\x82\x29\x81\x52\x82\x21\xc2\x07\xc2\x23\x82\x0f\x81\x17\x81\x26\x81\x04\x81\x3e\x81\x19\xd0\x03", + // フォーマットしています。 (Formatting.) + "\x82\x35\xc2\x09\xd0\x06\x82\x3e\xc2\x23\x82\x28\x81\x17\x81\x26\x81\x04\x81\x3e\x81\x19\xd0\x03", +}; + +STATIC int loadFile_80049CE8(MEM_CARD *pMemcard, int idx) +{ + int success; + int optionFlag; + short optionFlagTmp; + int retries; + void *buf; + + GM_PadResetDisable = TRUE; + buf = GV_AllocMemory(GV_PACKET_MEMORY0, MC_BLOCK_SIZE); + if (buf == NULL) + { + printf("NO MEMORY FOR FILE BODY\n"); + } + + success = 0; + for (retries = 4; retries > 0; retries--) + { + memcard_read(pMemcard->card_idx, pMemcard->files[idx].name, 0, buf, MC_BLOCK_SIZE); + + while (memcard_get_status() > 0) + { + mts_wait_vbl(2); + } + + if (memcard_get_status() == 0) + { + optionFlagTmp = GM_OptionFlag & ~OPTION_RADAR_OFF; + optionFlag = optionFlagTmp; + // 256: start position (offset) of game data (as there is only one icon). + if (GCL_SetLoadFile(buf + 256) != 0) + { + success = 1; + if (optionFlag & OPTION_UNKNOWN_0010) + { + // 0xE100 == OPTION_ENGLISH | 0x2000 | OPTION_CAPTION_OFF | OPTION_SOUND_MONO + GM_OptionFlag = (GM_OptionFlag & ~0xE100) | (optionFlag & 0xE100); + } + if (optionFlag & OPTION_UNKNOWN_0008) + { + // 0x1007 == OPTION_SHUKAN_REVERSE | OPTION_BUTTON_MASK + GM_OptionFlag = (GM_OptionFlag & ~0x1007) | (optionFlag & 0x1007); + } + GM_OptionFlag &= ~(OPTION_UNKNOWN_0008 | OPTION_UNKNOWN_0010); + GCL_SaveLinkVar(&GM_OptionFlag); + if (GM_OptionFlag & OPTION_SOUND_MONO) + { + GM_SetSound(0xff000005, SD_ASYNC); + } + else + { + GM_SetSound(0xff000006, SD_ASYNC); + } + } + break; + } + } + + GV_FreeMemory(GV_PACKET_MEMORY0, buf); + GM_PadResetDisable = FALSE; + return success; +} + +const char *save_prompt_msg_jp_8009EBAC[] = { + // 上書きしてよろしいですか? + "\x91\x0b\x90\x27\x81\x0d\x81\x17\x81\x26\x81\x48\x81\x4d\x81\x17\x81\x04\x81\x27\x81\x19\x81\x0b\xc0\x3f", + // フォーマットしますか? + "\x82\x35\xc2\x09\xd0\x06\x82\x3e\xc2\x23\x82\x28\x81\x17\x81\x3e\x81\x19\x81\x0b\xc0\x3f", +}; + +const char *save_prompt_msg_en_8009EBB4[] = {"OVERWRITE OK?", "FORMAT OK?"}; + +STATIC int init_file_mode_helper_helper_helper3_80049E94(int param_1) +{ + dword_800ABB58 = param_1; + printf("REQUEST %X\n", param_1); + mts_slp_tsk(); + printf("RESULT %X\n", dword_800ABB5C); + return dword_800ABB5C; +} + +STATIC int dword_800AB6FC = -1; +STATIC int dword_800AB700 = 0; + +STATIC void init_file_mode_helper_helper_80049EDC(void) +{ + int temp_a0; + int temp_a0_2; + int temp_s0; + int err; + int temp_v0; + int temp_v0_2; + int fidx; + int temp_v1; + int temp_v1_2; + int port; + int var_s2; + MEM_CARD *pMemcard; + int negone; + int one; + + mts_set_vsync_task(); + dword_800ABB54 = 1; + +loop_3: + init_file_mode_helper_helper_helper3_80049E94(0x100000A); + + temp_a0 = dword_800AB6FC; + var_s2 = 0; + + if (temp_a0 == -1) + { + int mask; + dword_800AB700 = 0; + + for (port = 0; port < 2; port++) + { + mask = 0x1000000; + temp_v0 = memcard_check(port); + temp_v1 = temp_v0 & 0xFF; + mcd_last_check_800ABB60[port] = temp_v1; + + if ((temp_v0 & mask) && dword_800AB6F0 == port) + { + dword_800AB6F0 = -1; + } + + if (temp_v1 != 3) + { + var_s2 += 1; + dword_800AB700 |= 1 << port; + } + } + + if (var_s2 == 0) + { + init_file_mode_helper_helper_helper3_80049E94(0x41000008); + + for (port = 0; port < 2; port++) + { + if ((memcard_check(port) & 0xFF) != 3) + { + dword_800ABB54 = 1; + goto loop_3; + } + } + + dword_800ABB54 = 2; + dword_800AB6F0 = -1; + goto block_73; + } + + dword_800ABB5C = -2; + dword_800ABB58 = 0x20000001; + dword_800AB700 &= ~0x80; + one = 1; + negone = -1; + +loop_18: + for (port = 0; port < 2; port++) + { + temp_a0_2 = memcard_check(port) & 0xFF; + if (temp_a0_2 != mcd_last_check_800ABB60[port]) + { + mcd_last_check_800ABB60[port] = temp_a0_2; + if (temp_a0_2 == 3) + { + dword_800AB700 &= ~(one << port); + if (dword_800AB6F0 == port) + { + dword_800AB6F0 = negone; + } + } + else if (temp_a0_2 != 0x80000000) + { + dword_800AB700 |= (one << port); + } + dword_800ABB54 = one; + dword_800AB700 |= 0x80; + } + } + + temp_v1_2 = dword_800ABB5C; + + if (temp_v1_2 >= 0) + { + if ((dword_800AB700 == 0) || !(dword_800AB700 & (one << temp_v1_2))) + { + dword_800AB6FC = negone; + dword_800AB6F0 = negone; + dword_800ABB54 = 1; + goto loop_3; + } + dword_800AB6FC = temp_v1_2; + if (dword_800AB6F0 != temp_v1_2) + { + dword_800AB6EC = negone; + } + dword_800AB6F0 = temp_v1_2; + goto block_34; + } + + if (temp_v1_2 != negone) + { +block_34: + port = 0; + if (dword_800ABB5C == -2) + { + goto loop_18; + } + + goto block_40; + } + init_file_mode_helper_helper_helper3_80049E94(0x80000000); + return; + } + else + { + temp_v0_2 = memcard_check(temp_a0); + if (temp_v0_2 & 0x1000000) + { + dword_800AB6EC = -1; + } + temp_v0_2 &= 0xFF; + + mcd_last_check_800ABB60[dword_800AB6FC] = temp_v0_2; + if (mcd_last_check_800ABB60[dword_800AB6FC] == 3) + { + dword_800AB6FC = -1; + dword_800AB6EC = -1; + goto loop_3; + } + +block_40: + temp_s0 = mcd_last_check_800ABB60[dword_800AB6FC]; + if (temp_s0 != 0x80000000) + { + if (temp_s0 == 1) + { + init_file_mode_helper_helper_helper3_80049E94(0x43000005); + if (dword_800ABB48 == 0) + { + if (init_file_mode_helper_helper_helper3_80049E94(0x10000001) == 0) + { + err = memcard_check(dword_800AB6FC) & 0xFF; + if (err != temp_s0) + { + init_file_mode_helper_helper_helper3_80049E94(0x45000003); + goto loop_3; + } + + init_file_mode_helper_helper_helper3_80049E94(0x0100000B); + + if (!memcard_format(dword_800AB6FC)) + { + dword_800AB6FC = -1; + init_file_mode_helper_helper_helper3_80049E94(0x45000007); + goto block_72; + } + + mcd_last_check_800ABB60[dword_800AB6FC] = memcard_check(dword_800AB6FC) & 0xFF; + + if (mcd_last_check_800ABB60[dword_800AB6FC] == 0) + { + dword_800AB6F4 = err; + goto block_51; + } + else + { + init_file_mode_helper_helper_helper3_80049E94(0x45000003); + goto block_72; + } + } + + dword_800ABB54 = 4; + dword_800AB6FC = -1; + goto loop_3; + } + else + { + dword_800ABB54 = 4; + dword_800AB6FC = -1; + goto loop_3; + } + } +block_51: + pMemcard = memcard_get_files(dword_800AB6FC); + mcd_last_file_800ABB68[dword_800AB6FC] = pMemcard; + + if (pMemcard == NULL) + { + init_file_mode_helper_helper_helper3_80049E94(0x45000003); + goto loop_3; + } + +loop_52: + fidx = init_file_mode_helper_helper_helper3_80049E94(0x20000000); + + if (fidx < 0) + { + dword_800AB6FC = -1; + goto loop_3; + } + + if (memcard_check(dword_800AB6FC) != 0) + { + init_file_mode_helper_helper_helper3_80049E94(0x45000003); + goto loop_3; + } + + if (dword_800ABB48 == 0) + { + if (fidx < 16) + { + if (init_file_mode_helper_helper_helper3_80049E94(0x10000000) != 0) + { + goto loop_52; + } + + if (memcard_check(dword_800AB6FC) != 0) + { + init_file_mode_helper_helper_helper3_80049E94(0x45000003); + goto loop_3; + } + + if (memcard_delete(dword_800AB6FC, pMemcard->files[fidx].name) == 0) + { + init_file_mode_helper_helper_helper3_80049E94(0x45000003); + goto block_72; + } + + pMemcard = memcard_get_files(dword_800AB6FC); + mcd_last_file_800ABB68[dword_800AB6FC] = pMemcard; + } + + if (pMemcard->free_blocks == 0) + { + init_file_mode_helper_helper_helper3_80049E94(0x43200004); + goto block_72; + } + + init_file_mode_helper_helper_helper3_80049E94(0x01000009); + + if (saveFile_8004983C(pMemcard) != 0) + { + goto block_75; + } + } + else + { + init_file_mode_helper_helper_helper3_80049E94(0x01000009); + + if (loadFile_80049CE8(pMemcard, fidx) != 0) + { + goto block_69; + } + } + } + + init_file_mode_helper_helper_helper3_80049E94(0x45000003); + goto block_72; + +block_69: + dword_800ABB48 = 2; +block_75: + dword_800ABB54 = 0; + init_file_mode_helper_helper_helper3_80049E94(0xC1000001); + return; + +block_72: + dword_800ABB54 = 5; + +block_73: + init_file_mode_helper_helper_helper3_80049E94(0xC5000002); + } +} + +STATIC void init_file_mode_helper_8004A424(int param_1) +{ + int size = 2048; + + stack_800ABB50 = GV_AllocMemory(GV_PACKET_MEMORY0, size); + + if (!stack_800ABB50) + { + printf("NO MEMORY FOR STACK\n"); + } + + dword_800ABB48 = param_1; + mts_start_task(MTSID_MEMORY_CARD, init_file_mode_helper_helper_80049EDC, stack_800ABB50 + size, size); +} + +const char *dword_8009EBBC[] = { + NULL, + NULL, + "COMPLETE", + NULL, + "ERROR" +}; + +// The small triangle above the files list suggesting that there are more entries. +Menu_Triangle upperTriangle_8009EBD0 = {155, 79, 160, 74, 165, 79, 0x80808080}; +// The small triangle below the files list suggesting that there are more entries. +Menu_Triangle lowerTriangle_8009EBE0 = {156, 184, 160, 188, 164, 184, 0x80808080}; + +void move_coord(int *arr, int len) +{ + int i; + + for (i = 0; i < len; i++, arr += 2) { + arr[0] += arr[1]; + } +} + +STATIC void menu_radio_do_file_mode_helper2_helper_8004A4C4(MenuPrim *pGlue, RadioFileModeStruElem *pElem) +{ + RadioFileModeUnk1 *pUnk; + TextConfig textConfig; + + pUnk = pElem->field_C_unk1; + if (pElem->field_0 == 1) + { + move_coord(&pUnk->field_8, 2); + pUnk->field_18 = 0x3d482e; + } + textConfig.xpos = pUnk->field_8 >> 16; + textConfig.ypos = pUnk->field_10 >> 16; // pUnk->field_10 / 65536 wouldn't match + textConfig.flags = 0x12; + textConfig.color = pUnk->field_18 | 0x66000000; + + _menu_number_draw_string2(pGlue, &textConfig, (char *)pUnk->field_4); // TODO: Fix cast +} + +STATIC void menu_radio_do_file_mode_helper4_helper_8004A54C(MenuPrim *pGlue, RadioFileModeStruElem *pElem) +{ + LINE_F2 *pPrim; + RadioFileModeUnk1 *pUnk; + + pUnk = pElem->field_C_unk1; + if (pElem->field_0 == 1) + { + move_coord(&pUnk->field_4, 4); + } + + if (pUnk->field_4 != pUnk->field_14 || pUnk->field_C != pUnk->field_1C) + { + _NEW_PRIM(pPrim, pGlue); + + LSTORE(pUnk->field_24, &pPrim->r0); + pPrim->x0 = pUnk->field_4 >> 16; + pPrim->y0 = pUnk->field_C >> 16; + pPrim->x1 = pUnk->field_14 >> 16; + pPrim->y1 = pUnk->field_1C >> 16; // pUnk->field_1C / 65536 wouldn't match + setLineF2(pPrim); + addPrim(pGlue->ot, pPrim); + } +} + +STATIC void sub_8004A648(MenuPrim *pGlue, RadioFileModeStruElem *pElem) +{ + RadioFileModeUnk1 *pUnk; + int x, y, w, h; + TILE *pTile; + + pUnk = pElem->field_C_unk1; + if (pElem->field_0 == 1) + { + move_coord(&pUnk->field_4, 4); + } + x = (pUnk->field_4 >> 16); + y = (pUnk->field_C >> 16); + w = pUnk->field_14 >> 16; + h = pUnk->field_1C >> 16; + x -= w / 2; + y -= h / 2; + + _NEW_PRIM(pTile, pGlue); + + LSTORE(0x72A452, &pTile->r0); + setTile(pTile); + pTile->x0 = x; + pTile->y0 = y; + pTile->w = w; + pTile->h = h; + setSemiTrans(pTile, 0); + addPrim(pGlue->ot, pTile); + + radio_draw_face_frame(pGlue, x, y, w, h); + radio_draw_face_frame(pGlue, x, y, w, h); +} + +STATIC void menu_radio_do_file_mode_helper16_8004C164(MenuPrim *pGlue, SELECT_INFO *info); + +STATIC void menu_radio_do_file_mode_helper3_helper_8004A790(MenuPrim *pGlue, RadioFileModeStruElem *pElem) +{ + RadioFileModeUnk1 *pUnk; + + pUnk = pElem->field_C_unk1; + if (pElem->field_0 == 1) + { + move_coord(&pUnk->field_4, 2); + } + + *(short *)pUnk->field_14 = pUnk->field_4 >> 16; // TODO: Fix type of field_14 + *(short *)(pUnk->field_14 + 2) = pUnk->field_C >> 16; // pUnk->field_C / 65536 wouldn't match + menu_radio_do_file_mode_helper16_8004C164(pGlue, (SELECT_INFO *)pUnk->field_14); +} + +//file may be split here + +STATIC void init_file_mode_helper2_8004A800(void) +{ + int i; + + stru_800ABB7C = (RadioFileModeStru_800ABB7C *)GV_AllocMemory(GV_PACKET_MEMORY0, sizeof(RadioFileModeStru_800ABB7C)); + if (stru_800ABB7C == NULL) + { + printf("NO MEMORY FOR OBJ\n"); + } + + for (i = 0; i < 12; i++) + { + stru_800ABB7C->field_0_array[i].field_0 = 0; + } +} + +STATIC void menu_radio_do_file_mode_helper_8004A858(void) +{ + GV_FreeMemory(GV_PACKET_MEMORY0, stru_800ABB7C); +} + +STATIC void menu_radio_do_file_mode_helper2_8004A87C(int idx, int param_2, int param_3, int divisor, int idx2) +{ + RadioFileModeStruElem *pElem; + RadioFileModeUnk2 *pUnk; + + pUnk = &stru_800ABB7C->field_c0_array[idx]; + pElem = &stru_800ABB7C->field_0_array[idx]; + + // Callers call this function with: idx2 = -1 ... 4 + pUnk->field_4 = dword_8009EBBC[idx2]; + + pElem->field_8_pFn = menu_radio_do_file_mode_helper2_helper_8004A4C4; + pElem->field_C_unk1 = (RadioFileModeUnk1 *)pUnk; // TODO: Fix cast + + if (idx2 < 0) + { + pElem->field_0 = 0; + return; + } + + if (divisor <= 0) + { + pUnk->field_8 = param_2 * 65536; + pUnk->field_10 = param_3 * 65536; + pElem->field_0 = 2; + } + else + { + pUnk->field_C = (param_2 * 65536 - pUnk->field_8) / divisor; + pUnk->field_14 = (param_3 * 65536 - pUnk->field_10) / divisor; + pElem->field_0 = 1; + } + + pUnk->field_18 = 0x748956; + pElem->field_4 = divisor; +} + +STATIC void menu_radio_do_file_mode_helper3_8004A994(int idx, int param_2, int param_3, int divisor, SELECT_INFO *field_14) +{ + RadioFileModeUnk1 *pUnk; + RadioFileModeStruElem *pElem; + + pElem = &stru_800ABB7C->field_0_array[idx]; + pUnk = &stru_800ABB7C->field_220_unk1; + + pElem->field_8_pFn = menu_radio_do_file_mode_helper3_helper_8004A790; + pElem->field_C_unk1 = pUnk; + + if (divisor <= 0) + { + pUnk->field_4 = param_2 * 65536; + pUnk->field_C = param_3 * 65536; + pElem->field_0 = 2; + } + else + { + pUnk->field_8 = (param_2 * 65536 - pUnk->field_4) / divisor; + pUnk->field_10 = (param_3 * 65536 - pUnk->field_C) / divisor; + pElem->field_0 = 1; + } + + pUnk->field_14 = (int)field_14; + pElem->field_4 = divisor; +} + +STATIC void menu_radio_do_file_mode_helper4_8004AA68(int idx, int param_2, int param_3, int param_4, int param_5, int divisor) +{ + RadioFileModeUnk1 *pUnk; + RadioFileModeStruElem *pElem; + + pElem = &stru_800ABB7C->field_0_array[idx]; + + // callers to this function use idx in range 5..8 + pUnk = &stru_800ABB7C->field_130_array[idx - 5]; // TODO: really -5? + + pElem->field_8_pFn = menu_radio_do_file_mode_helper4_helper_8004A54C; + pElem->field_C_unk1 = pUnk; + if (divisor <= 0) + { + pUnk->field_4 = param_2 * 65536; + pUnk->field_C = param_3 * 65536; + pUnk->field_14 = param_4 * 65536; + pUnk->field_1C = param_5 * 65536; + pElem->field_0 = 2; + } + else + { + pUnk->field_8 = (param_2 * 65536 - pUnk->field_4) / divisor; + pUnk->field_10 = (param_3 * 65536 - pUnk->field_C) / divisor; + pUnk->field_18 = (param_4 * 65536 - pUnk->field_14) / divisor; + pUnk->field_20 = (param_5 * 65536 - pUnk->field_1C) / divisor; + pElem->field_0 = 1; + } + + pUnk->field_24 = 0x3d482e; + pElem->field_4 = divisor; +} + +STATIC int *menu_radio_do_file_mode_helper5_8004ABDC(int idx) +{ + int *result; + + result = &stru_800ABB7C->field_0_array[idx].field_0; + *result = 0; + return result; +} + +STATIC void sub_8004ABF0(int param_1, int param_2, int param_3, int param_4, int divisor) +{ + RadioFileModeStruElem *pElem = &stru_800ABB7C->field_0_array[11]; + + // TODO: WTF??? Why is it reinterpreting a middle of RadioFileModeUnk1 as another RadioFileModeUnk1, + // going past the memory of original RadioFileModeUnk1? + RadioFileModeUnk1 *pUnk = (RadioFileModeUnk1 *)&(stru_800ABB7C->field_220_unk1).field_18; + + pElem->field_8_pFn = sub_8004A648; + pElem->field_C_unk1 = pUnk; + + if (divisor <= 0) + { + pUnk->field_4 = param_1 * 65536; + pUnk->field_C = param_2 * 65536; + pUnk->field_14 = param_3 * 65536; + pUnk->field_1C = param_4 * 65536; + pElem->field_0 = 2; + } + else + { + pUnk->field_8 = (param_1 * 65536 - pUnk->field_4) / divisor; + pUnk->field_10 = (param_2 * 65536 - pUnk->field_C) / divisor; + pUnk->field_18 = (param_3 * 65536 - pUnk->field_14) / divisor; + pUnk->field_20 = (param_4 * 65536 - pUnk->field_1C) / divisor; + pElem->field_0 = 1; + } + + pElem->field_4 = divisor; +} + +STATIC void menu_radio_do_file_mode_helper6_8004AD40(MenuPrim *pGlue) +{ + int i; + RadioFileModeStruElem *pElem; + + pElem = stru_800ABB7C->field_0_array; + for (i = 0; i < 12; i++, pElem++) + { + if (pElem->field_0 != 0) + { + if (pElem->field_4 < 0) + { + pElem->field_4++; + } + else + { + pElem->field_8_pFn(pGlue, pElem); + if (pElem->field_0 == 1 && --pElem->field_4 == 0) + { + pElem->field_0 = 2; + } + } + } + } +} + +#define setRGB0_Fast(prim, rgbExtra) *(unsigned int *)&prim->r0 = rgbExtra + +static void set_sprt_default(SPRT *sprt) +{ + setRGB0_Fast(sprt, 0x80808080); + setSprt(sprt); + setClut(sprt, 960, 510); +} + +// See also drawCaption_800C5EB4() in camera.c +STATIC void drawCaption_8004AE3C(MenuWork *menuWork, const char *caption) +{ + int height; + KCB *kcb; + + kcb = menuWork->field_214_font; + + height = kcb->height; + kcb->height = 14; + font_clear(kcb); + kcb->height = height; + + font_draw_string(kcb, 0, 0, caption, 0); + font_update(kcb); +} + +#define FONT_SIZE 14 + +STATIC void sub_8004AEA8(SELECT_INFO *info) +{ + int top; + int i, y; + int count; + int x, val2; + KCB *kcb; + char mes[32]; // Uses MGS custom encoding. + char *base; + + kcb = info->field_1C_kcb; + x = 0; + font_clear(kcb); + + val2 = 14; + y = val2; + count = info->max_num - info->top; + if (count > 6) + { + count = 6; + } + + top = info->top; + + for (i = 0; i < count; i++, y += FONT_SIZE) + { + if (i == 4) + { + x = 128; + y = val2; + } + +// clang-format off + base = info->menu[ i + top ].mes; + if( base[ 0 ] != '\0' ){ + //long c; + data_info->make_menu( mes, base ); + font_draw_string( kcb, x, y, mes, 2 ); + } + //y += FONT_SIZE; + } + + font_draw_string( kcb, 0, 0, info->message, 0 ); + + font_update( kcb ); +} + +#define SAVE_MES_X 160 +#define SAVE_MES_Y 200 + +static void show_message( MenuWork *work, OT_TYPE *ot, SELECT_INFO *info ) +{ + SPRT *sprt; + KCB *kcb; + + kcb = GET_KCB( work ); + + /* メッセージ本体の描画 */ + + NEW_PRIM( sprt, work ); + + set_sprt_default( sprt ); + setXY0( sprt, SAVE_MES_X - kcb->max_width / 2, SAVE_MES_Y ); + setUV0( sprt, 0, 4 ); + setWH( sprt, 252, 14 ); + addPrim( ot, sprt ); +} +// clang-format on + +STATIC void menu_radio_do_file_mode_save_memcard_8004B0A0(MenuWork *work, OT_TYPE *ot, SELECT_INFO *info) +{ + TextConfig config; + + char saveid[16]; + char discnum[16]; + char newfile[32]; + char freeBlocksText[32]; + + LINE_F2 *pLine; + SPRT *pSprt; + TILE *pTile; + POLY_FT4 *pPoly; + DR_TPAGE *pTpage; + + int sp88; + int freeBlocksCount; + int difficulty; + int sp8C; + int sp90; + int sp94; + int sp98; + int sp9C; + int spA0; + MENU_CURPOS *pos; + MenuPrim *prim; + + int var_a2; + int var_s5; + int i; + int var_s0; + int var_t0; + + int mult; + int mult2; + + unsigned int s8; + unsigned int s6; + + int new_var; + int new_var2, new_var3; + + do + { + } while (0); + + prim = work->prim; + s8 = 0; + + if (info->max_num == 0) + { + return; + } + + sp90 = info->top; + sp8C = info->max_num - info->top; + + s6 = 200; + if (sp8C > 6) + { + sp8C = 6; + } + sp94 = 0; + sp98 = 274; + + var_s5 = 80; + + mult = 52; + mult2 = 18; + + new_var2 = 18; + if (info->open_count > 0) + { + if (--info->open_count >= 5) + { + return; + } + + sp9C = (4 - info->open_count) * mult; + } + else + { + sp9C = s6 + 10; + } + + if (info->field_18 > 0) + { + spA0 = --info->field_18 * (mult2 / 4); + } + else if (info->field_18 == 0) + { + spA0 = 0; + } + else + { + spA0 = 18; + } + + new_var3 = 210; + for (sp88 = 0; sp88 < sp8C; sp88++) + { + new_var = 'A' - 1; + + _NEW_PRIM(pTile, prim); + LSTORE(0, &pTile->r0); + + setXY0(pTile, s8, s6); + setWH(pTile, new_var3, new_var2); + + setTile(pTile); + addPrim(prim->ot, pTile); + + var_a2 = 293; + if (GV_Clock == 0) + { + var_a2 = 288; + } + + _NEW_PRIM(pPoly, prim); + LSTORE(0x80808080, &pPoly->r0); + + if ((sp90 + sp88) == info->current_index) + { + var_s0 = 18; + } + else + { + var_s0 = spA0; + } + + pPoly->x0 = 160 - (sp9C / 2); + pPoly->y0 = var_s5; + + pPoly->x1 = 160 - (sp9C / 2) + sp9C; + pPoly->y1 = var_s5; + + pPoly->x2 = 160 - (sp9C / 2); + pPoly->y2 = var_s5 + var_s0; + + pPoly->x3 = 160 - (sp9C / 2) + sp9C; + pPoly->y3 = var_s5 + var_s0; + + setUVWH(pPoly, s8, s6, 210, 18); + + pPoly->tpage = var_a2; + + setPolyFT4(pPoly); + setSemiTrans(pPoly, 1); + + addPrim(prim->ot, pPoly); + + if (((sp90 + sp88) == info->current_index) && (info->field_14 != 0)) + { + do + { + sub_8004ABF0(160, var_s5 + 9, info->field_12, 16, 2); + } while (0); + } + + config.color = 0x66748956; + config.flags = 0; + var_s5 += var_s0; + + if ((info->menu[sp90 + sp88].field_20 >= 0) && (info->menu[sp90 + sp88].field_20 < 16)) + { + pos = &info->menu[sp90 + sp88]; + + if (pos->mes[0] == 'G') + { + config.xpos = s8 + 178; + config.ypos = s6 + 3; + sprintf(discnum, "DISC %d", ((pos->mes[5] - new_var) >> 3) + 1); + _menu_number_draw_string(prim, &config, discnum); + } + else + { + config.xpos = s8 + 178; + config.ypos = s6 + 3; + _menu_number_draw_string(prim, &config, "TIME"); + } + + if (pos->mes[0] == 'G') + { + static const char *difficulty_str_8009EBF0[] = {"EZ", "NM", "HD", "EX"}; + difficulty = ((pos->mes[3] & 0x40) >> 5) | ((pos->mes[4] & 0x40) >> 6); + + config.xpos = s8 + 164; + config.ypos = s6 + 10; + + if (pos->mes[1] & 0x40) + { + _menu_number_draw_string(prim, &config, "VE"); + } + else + { + _menu_number_draw_string(prim, &config, difficulty_str_8009EBF0[difficulty]); + } + } + + config.xpos = s8 + 180; + config.ypos = s6 + 10; + + saveid[0] = pos->mes[1]; + saveid[1] = pos->mes[2]; + saveid[2] = ':'; + saveid[3] = pos->mes[3] & 0x3F; + saveid[4] = pos->mes[4] & 0x3F; + + saveid[0] = saveid[0] & 0x3F; + + if (saveid[0] == ':') + { + memcpy(saveid, "CLEAR", 6); + } + + saveid[5] = 0; + _menu_number_draw_string(prim, &config, saveid); + } + else + { + char *msg; + char *dst = newfile; + char *plural; + unsigned char blocksCount; + long blocksCount_long; // Why? + config.ypos = s6 + 8; + config.flags = 0x2; + config.xpos = s8 + (sp9C / 2); + msg = "NEW FILE [ NEED %d BLOCK%s ]"; + blocksCount = data_info->blocks_count; + blocksCount_long = blocksCount; + if (blocksCount_long >= 2) + plural = "S"; + else + plural = ""; + + sprintf(dst, msg, blocksCount, plural); + + _menu_number_draw_string2(prim, &config, newfile); + } + + if (sp88 == 4) + { + sp94 = 128; + sp98 = 274; + } + + _NEW_PRIM(pSprt, prim); + set_sprt_default(pSprt); + + setUV0(pSprt, sp94, sp98); + setWH(pSprt, 128, 14); + setXY0(pSprt, s8, s6 + 2); + + addPrim(ot, pSprt); + + _NEW_PRIM(pTpage, prim); + setDrawTPage(pTpage, 0, 1, getTPage(0, 1, 960, 256)); + addPrim(prim->ot, pTpage); + + sp98 += 14; + } + + var_t0 = -(sp9C + 10) / 2; + + for (i = 0; i < 2; i++) + { + _NEW_PRIM(pLine, prim); + + pLine->x0 = pLine->x1 = var_t0 + 160; + pLine->y0 = 78; + pLine->y1 = var_s5 + 4; + + LSTORE(0x748956, &pLine->r0); + + setLineF2(pLine); + setSemiTrans(pLine, 1); + addPrim(prim->ot, pLine); + + var_t0 += 10 + sp9C; + } + + if (info->field_18 >= 0) + { + return; + } + + freeBlocksCount = mcd_last_file_800ABB68[dword_800AB6FC]->free_blocks; + + if (dword_800ABB74->max_num == 1) + { + config.xpos = 160; + } + else if (dword_800AB6FC == 0) + { + config.xpos = 96; + } + else + { + config.xpos = 224; + } + + config.ypos = 68; + config.flags = 0x2; + config.color = 0x66748956; + + sprintf(freeBlocksText, "FREE: %d BLOCK%s", freeBlocksCount, (freeBlocksCount > 1) ? "S" : ""); + _menu_number_draw_string2(prim, &config, freeBlocksText); + + // Blinking effect. + if ((GV_Time % 32) > 10) + { + // If the list scrolled up at least once, it means there are more + // entries above the visible ones, so show the upper triangle. + if (sp90 > 0) + { + menu_draw_triangle(work->prim, &upperTriangle_8009EBD0); + } + + // If there are more entries below the visible ones + // (which are always six), show the lower triangle. + if ((sp90 + 6) < info->max_num) + { + menu_draw_triangle(work->prim, &lowerTriangle_8009EBE0); + } + } +} + +STATIC void menu_radio_do_file_mode_helper12_helper_8004B8FC(char *param_1, char *param_2) +{ + strcpy(param_1, param_2 + 0xc); +} + +STATIC void freeMemoryForSelectInfo(SELECT_INFO *selectInfo) +{ + printf("close info\n"); + if (selectInfo) + { + GV_FreeMemory(GV_PACKET_MEMORY0, selectInfo); + } +} + +STATIC void allocMemoryForSelectInfo(SELECT_INFO **selectInfo, int num) +{ + if (!*selectInfo) + { + *selectInfo = GV_AllocMemory(GV_PACKET_MEMORY0, (sizeof(MENU_CURPOS) * num) + sizeof(SELECT_INFO)); + + if (!*selectInfo) + { + printf("NO MEMORY FOR INFO\n"); + } + + printf("alloc info %X\n", (unsigned int)*selectInfo); + } +} + +// See also updateCurrentEntry_800C6984() in camera.c +STATIC void updateCurrentEntry_8004B9C4(SELECT_INFO *info, int dir) +{ + short top; + short newIndex; + int previousIndex; + + previousIndex = info->current_index; + newIndex = info->current_index + dir; + info->current_index = newIndex; + if (newIndex < 0) + { + info->current_index = 0; + } + else if (newIndex >= info->max_num) + { + info->current_index = info->max_num - 1; + } + else + { + top = info->top; + if (newIndex < top) + { + info->top = newIndex; + } + else if (newIndex >= (top + 6)) + { + info->top = newIndex - 5; + } + } + if (info->current_index != previousIndex) + { + GM_SeSet2(0, 0x3F, SE_MENU_CURSOR); + } + sub_8004AEA8(info); +} + +STATIC int menu_radio_do_file_mode_helper12_8004BA80(MenuWork *work, MEM_CARD *pMemcard, const char *param_3, + SELECT_INFO *info) +{ + MENU_CURPOS *pIter; + MEM_CARD_FILE *pMcFile; + int i; + + pIter = info->menu; + + strcpy(memoryCardFileName, MGS_MemoryCardName); + memoryCardFileName[12] = data_info->field_0[0]; + + for (i = 0; i < pMemcard->file_count; i++) + { + pMcFile = &pMemcard->files[i]; + printf("FILE %s\n", pMcFile->name); + + if (strncmp(pMcFile->name, memoryCardFileName, 13) == 0) + { + menu_radio_do_file_mode_helper12_helper_8004B8FC(pIter->mes, pMcFile->name); + pIter->field_20 = i; + pIter++; + } + } + + if (dword_800ABB48 == 0 && pMemcard->free_blocks >= data_info->blocks_count) + { + memcpy(pIter->mes, "", 1); + pIter->field_20 = 16; + pIter++; + } + + info->field_1C_kcb = work->field_214_font; + info->max_num = pIter - info->menu; + + if (data_info->field_0[0] != 71) + { + if (info->max_num && pIter[-1].field_20 == 16) + { + info->current_index = info->max_num - 1; + } + else + { + info->current_index = 0; + } + } + else if (dword_800AB6EC == -1 || dword_800AB6EC >= info->max_num) + { + if (dword_800ABB48 == 0 && info->max_num && pIter[-1].field_20 == 16) + { + info->current_index = info->max_num - 1; + } + else + { + info->current_index = 0; + } + } + else + { + info->current_index = dword_800AB6EC; + } + + info->top = 0; + info->message = param_3; + info->field_E = -1; + info->field_0_xpos = 40; + info->field_2_ypos = 40; + info->open_count = 8; + info->current_dir = 0; + info->field_18 = -1; + info->field_12 = 240; + info->field_14 = 1; + updateCurrentEntry_8004B9C4(info, 0); + return info->max_num != 0; +} + +// See also SelectAct_800C32D8() in select.c +STATIC int menu_radio_do_file_mode_helper13_8004BCF8(GV_PAD *pPad, int *pOut, SELECT_INFO *info) +{ + int newDir; + int field_20; + int press; + int status; + + if (dword_800AB6F4 != 0) + { + dword_800AB6F4 = 0; + pPad->press |= PAD_CIRCLE; + } + else + { + status = pPad->status; + if (info->max_num != 0) + { + if (status & (PAD_DOWN | PAD_UP)) + { + newDir = 1; + if (status & PAD_UP) + { + newDir = -1; + } + if (info->current_dir == newDir) + { + if (--info->scroll_delay < 0) + { + updateCurrentEntry_8004B9C4(info, newDir); + info->scroll_delay = 2; + } + } + else + { + updateCurrentEntry_8004B9C4(info, newDir); + info->scroll_delay = 10; + info->current_dir = newDir; + } + } + else + { + info->current_dir = 0; + } + } + } + press = pPad->press; + if (press & PAD_CIRCLE) + { + GM_SeSet2(0, 0x3F, SE_MENU_SELECT); + if (info->max_num == 0) + { + *pOut = -1; + return 1; + } + field_20 = info->menu[info->current_index].field_20; + *pOut = field_20; + if (data_info->field_0[0] == 71) + { + if (field_20 < 16) + { + dword_800AB6EC = info->current_index; + } + else + { + dword_800AB6EC = -1; + } + } + return 1; + } + if (press & PAD_CROSS) + { + GM_SeSet2(0, 0x3F, SE_MENU_EXIT); + *pOut = info->field_E; + return 1; + } + return 0; +} + +STATIC const char *gMemoryCardNames_8009EC00[] = { + "MEMORY CARD 1", // 0 + "MEMORY CARD 2" // 1 +}; + +STATIC void menu_radio_do_file_mode_helper14_8004BE98(MenuWork *work, char *param_2, SELECT_INFO *info) +{ + MENU_CURPOS *infoChild; + int idx, idx_copy; + int memoryCardNo; + int bit; + int minusOne; + + infoChild = info->menu; + idx = -1; + for (memoryCardNo = 0; memoryCardNo < 2; memoryCardNo++) + { + bit = 1; + bit <<= memoryCardNo; + + if (dword_800AB700 & bit) + { + strcpy(infoChild->mes, gMemoryCardNames_8009EC00[memoryCardNo]); + infoChild->field_20 = memoryCardNo; + if (memoryCardNo == dword_800AB6F0) + { + idx = infoChild - info->menu; + } + infoChild++; + } + } + + idx_copy = idx; + if (infoChild == info->menu) + { + memcpy(&info->menu[0].mes, "NO CARD", 8); + infoChild->field_20 = 2; + infoChild = &info->menu[1]; + } + + info->field_1C_kcb = work->field_214_font; + info->max_num = infoChild - info->menu; + + if (idx_copy < 0) + { + idx_copy = 0; + } + + minusOne = -1; + do {} while (0); + + info->field_0_xpos = 160; + info->field_2_ypos = 100; + info->current_index = idx_copy; + info->top = 0; + info->message = param_2; + info->field_E = minusOne; + info->field_10 = 128; + info->field_18 = minusOne; + info->open_count = 4; + info->field_12 = 128; + info->field_14 = 1; + info->current_dir = 0; +} + +STATIC void menu_radio_do_file_mode_helper15_8004C04C(MenuWork *work, const char **srcs, int cnt, int index, const char *field_20, + SELECT_INFO *info) +{ + KCB *kcb; + const char *src; + int i; + MENU_CURPOS *dest; + + dest = info->menu; + for (i = 0; i < cnt; i++, dest++) + { + src = srcs[i]; + strcpy(dest->mes, src); + dest->field_20 = i; + } + + kcb = work->field_214_font; + + info->max_num = dest - info->menu; + info->current_index = index; + info->top = 0; + info->message = field_20; + info->field_E = 1; + info->field_0_xpos = 160; + info->current_dir = 0; + info->field_14 = 1; + info->field_2_ypos = 128; + info->field_10 = 64; + info->field_12 = 32; + info->open_count = 4; + info->field_1C_kcb = kcb; +} + +STATIC void menu_radio_do_file_mode_helper16_8004C164(MenuPrim *pGlue, SELECT_INFO *info) +{ + int i; + int xpos, ypos; + TextConfig textConfig; + + textConfig.flags = 0x12; + if (info->field_14 != 0) + { + textConfig.color = 0x66748956; + } + else + { + textConfig.color = 0x663d482e; + } + textConfig.xpos = info->field_0_xpos; + textConfig.ypos = info->field_2_ypos; + _menu_number_draw_string2(pGlue, &textConfig, info->message); + if (info->max_num == 1) + { + xpos = info->field_0_xpos; + } + else + { + xpos = info->field_0_xpos - info->field_10 / 2; + } + for (i = 0; i < info->max_num; i++, xpos += info->field_10) + { + textConfig.xpos = xpos; + ypos = info->field_2_ypos; + textConfig.ypos = ypos + 12; + + if (i == info->current_index) + { + textConfig.color = 0x66748956; + if (info->field_14 != 0) + { + ypos += 16; + sub_8004ABF0(textConfig.xpos, ypos, info->field_12, 12, 2); + } + } + else + { + textConfig.color = 0x663d482e; + } + _menu_number_draw_string2(pGlue, &textConfig, info->menu[i].mes); + } +} + +STATIC int menu_radio_do_file_mode_helper17_8004C2E4(GV_PAD *pPad, int *outParam, SELECT_INFO *info) +{ + short status; + + if (info->open_count > 0) + { + info->open_count--; + return 0; + } + + status = pPad->status; + if (info->max_num >= 2) + { + if (status & PAD_LEFT) + { + if (info->current_index != 0) + { + GM_SeSet2(0, 0x3F, SE_MENU_CURSOR); + info->current_index = 0; + } + } + else if ((status & PAD_RIGHT) && info->current_index == 0) + { + GM_SeSet2(0, 0x3F, SE_MENU_CURSOR); + info->current_index = 1; + } + } + if (pPad->press & PAD_CIRCLE) + { + *outParam = info->menu[info->current_index].field_20; + GM_SeSet2(0, 0x3F, SE_MENU_SELECT); + return 1; + } + if (pPad->press & PAD_CROSS) + { + GM_SeSet2(0, 0x3F, SE_MENU_EXIT); + *outParam = info->field_E; + return 1; + } + return 0; +} + +int menu_radio_do_file_mode(MenuWork *work, GV_PAD *pPad) +{ + TextConfig textConfig1, textConfig2; + int res1, res2, res3; + char **captions; + SELECT_INFO *info; + int xpos; + int divisor; + int var_v0_2; // ypos? + unsigned int flags; + DR_TPAGE *tpage; + u_long *ot; + int flagsExtracted; + + ot = work->prim->ot; + if (dword_800ABB48 == 0) + { + captions = (char **)saveCaptions_8009EB4C; + dword_8009EBBC[0] = data_info->field_4_name; + dword_8009EBBC[1] = "SAVING..."; + dword_8009EBBC[3] = "NO SPACE"; + } + else + { + captions = (char **)loadCaptions_8009EB7C; + dword_8009EBBC[0] = "LOAD DATA"; + dword_8009EBBC[1] = "LOADING..."; + dword_8009EBBC[3] = "NO FILE"; + } + switch (dword_800ABB80) + { + case 0: + sub_80048124(); + dword_800ABB84 = 0; + font_set_color(work->field_214_font, 1, 0x19F2, 0); + font_clut_update(work->field_214_font); + dword_800ABB80 = 1; + dword_800ABB88 = NULL; + dword_800ABB78 = NULL; + dword_800ABB74 = NULL; + dword_800ABB70 = NULL; + menu_radio_do_file_mode_helper2_8004A87C(0, 160, 100, 0, 0); + sub_8004ABF0(160, 0x67, 0x60, 0xC, 0); + GM_SeSet2(0, 0x3F, SE_MENU_SELECT); + dword_800AB6F4 = 0; + dword_800AB6FC = -1; + break; + case 1: + menu_radio_do_file_mode_helper2_8004A87C(3, 160, 0x80, 0, -1); + flags = dword_800ABB58; + if (flags == 0) + { + break; + } + if (flags == 0xC1000001) + { + if (dword_800ABB48 == 0) + { + strcpy(dword_800ABB70->menu[dword_800ABB70->current_index].mes, memoryCardFileName + 0xc); + dword_800ABB70->menu[dword_800ABB70->current_index].field_20 = 0; + sub_8004AEA8(dword_800ABB70); + } + menu_radio_do_file_mode_helper2_8004A87C(2, 160, 0x80, 0, 2); + } + if (flags == 0x1000009) + { + menu_radio_do_file_mode_helper2_8004A87C(2, 160, 0x80, 0, 1); + menu_radio_do_file_mode_helper4_8004AA68(8, 160, 0x6E, 160, 0x7A, 0); + sub_8004ABF0(160, 0x83, 0x60, 0xC, 4); + } + if (flags & 0x04000000) + { + menu_radio_do_file_mode_helper2_8004A87C(2, 160, 0x80, 0, 4); + sub_8004ABF0(160, 0x83, 0x60, 0xC, 4); + } + if (flags & 0x01000000) + { + drawCaption_8004AE3C(work, captions[(unsigned char)dword_800ABB58]); + dword_800ABB84 = 0xF; + } + if (flags & 0x40000000) + { + dword_800ABB80 = 2; + dword_800ABB84 = 1; + } + else if (flags & 0x20000000) + { + dword_800ABB84 = 0; + switch ((unsigned char)dword_800ABB58) + { + case 0: + dword_800ABB80 = 4; + allocMemoryForSelectInfo(&dword_800ABB70, 0x11); + dword_800ABB88 = dword_800ABB70; + if (menu_radio_do_file_mode_helper12_8004BA80(work, mcd_last_file_800ABB68[dword_800AB6FC], "", dword_800ABB70) == 0) + { + drawCaption_8004AE3C(work, captions[4]); + dword_800ABB84 = 1; + menu_radio_do_file_mode_helper2_8004A87C(3, 160, 0x80, 0, 3); + sub_8004ABF0(160, 0x83, 0x60, 0xC, 4); + dword_800ABB54 = 3; + } + if (dword_800ABB74 == NULL) + { + allocMemoryForSelectInfo(&dword_800ABB74, 3); + menu_radio_do_file_mode_helper14_8004BE98(work, "SELECT MEMORY CARD", dword_800ABB74); + divisor = -4; + dword_800ABB74->field_14 = 0; + } + else + { + divisor = 4; + } + menu_radio_do_file_mode_helper2_8004A87C(0, 0x40, 0x18, 4, 0); + menu_radio_do_file_mode_helper3_8004A994(4, 160, 0x28, divisor, dword_800ABB74); + menu_radio_do_file_mode_helper4_8004AA68(5, 0x6C, 0x1A, 160, 0x1A, divisor); + menu_radio_do_file_mode_helper4_8004AA68(6, 160, 0x1A, 160, 0x24, divisor); + info = dword_800ABB74; + if (dword_800ABB74->max_num == 1) + { + xpos = info->field_0_xpos; + } + else + { + xpos = info->field_0_xpos - info->field_10 / 2; + if (info->current_index == 1) + { + xpos += info->field_10; + } + } + + if (dword_800ABB88->max_num == 0) + { + xpos = info->field_0_xpos; + var_v0_2 = 0x7A; + } + else + { + var_v0_2 = 0x40; + } + menu_radio_do_file_mode_helper4_8004AA68(7, xpos, 0x3C, xpos, var_v0_2, -4); + menu_radio_do_file_mode_helper5_8004ABDC(8); + break; + case 1: + if (dword_800ABB74 != NULL) + { + divisor = 4; + } + else + { + divisor = -4; + } + if (dword_800ABB70 != NULL) + { + freeMemoryForSelectInfo(dword_800ABB70); + dword_800ABB70 = NULL; + } + allocMemoryForSelectInfo(&dword_800ABB74, 3); + dword_800ABB88 = dword_800ABB74; + menu_radio_do_file_mode_helper14_8004BE98(work, "SELECT MEMORY CARD", dword_800ABB74); + dword_800ABB80 = 3; + menu_radio_do_file_mode_helper2_8004A87C(0, 160, 0x18, 4, 0); + menu_radio_do_file_mode_helper3_8004A994(4, 160, 100, divisor, dword_800ABB74); + menu_radio_do_file_mode_helper4_8004AA68(5, 160, 0x1A, 160, 0x1A, divisor); + menu_radio_do_file_mode_helper4_8004AA68(6, 160, 0x20, 160, 0x60, divisor); + menu_radio_do_file_mode_helper5_8004ABDC(7); + menu_radio_do_file_mode_helper5_8004ABDC(8); + break; + } + } + else if (flags & 0x10000000) + { + static const char *off_8009EC08[] = {"YES", "NO"}; + + flagsExtracted = ((flags >> 20) ^ 1); + flagsExtracted &= 1; + dword_800ABB80 = 5; + allocMemoryForSelectInfo(&dword_800ABB78, 2); + dword_800ABB88 = dword_800ABB78; + menu_radio_do_file_mode_helper15_8004C04C(work, off_8009EC08, 2, flagsExtracted, + save_prompt_msg_en_8009EBB4[(unsigned char)dword_800ABB58], dword_800ABB78); + drawCaption_8004AE3C(work, save_prompt_msg_jp_8009EBAC[(unsigned char)dword_800ABB58]); + dword_800ABB84 = 1; + } + else if ((int)flags < 0) + { + dword_800ABB80 = 6; + dword_800ABB58 = 0; + mts_wup_tsk(MTSID_MEMORY_CARD); + } + else + { + dword_800ABB80 = 1; + dword_800ABB58 = 0; + mts_wup_tsk(MTSID_MEMORY_CARD); + } + break; + case 2: + if (pPad->press & (PAD_TRIANGLE | PAD_CROSS | PAD_SQUARE | PAD_CIRCLE)) + { + dword_800ABB84 = 0; + if (dword_800ABB58 < 0) + { + dword_800ABB80 = 6; + dword_800ABB58 = 0; + } + else + { + dword_800ABB80 = 1; + dword_800ABB58 = 0; + } + mts_wup_tsk(MTSID_MEMORY_CARD); + menu_radio_do_file_mode_helper2_8004A87C(3, 160, 0x80, 0, -1); + menu_radio_do_file_mode_helper2_8004A87C(2, 160, 0x80, 0, -1); + } + else + { + draw_radio_wait_mark(work, ot); + } + break; + case 3: + if (dword_800AB700 & 0x80) + { + menu_radio_do_file_mode_helper14_8004BE98(work, "SELECT MEMORY CARD", dword_800ABB74); + dword_800AB700 &= ~0x80; + } + textConfig1.xpos = 160; + textConfig1.ypos = 0xC8; + textConfig1.flags = 0x12; + textConfig1.color = 0x66748956; + _menu_number_draw_string2(work->prim, &textConfig1, "PRESS * TO EXIT"); + if (menu_radio_do_file_mode_helper17_8004C2E4(pPad, &res1, dword_800ABB74) != 0) + { + printf("Res %d\n", res1); + dword_800ABB58 = 0; + dword_800ABB80 = 1; + dword_800ABB84 = 0; + dword_800ABB74->field_14 = 0; + dword_800ABB5C = res1; + } + break; + case 5: + if (menu_radio_do_file_mode_helper17_8004C2E4(pPad, &res2, dword_800ABB78) != 0) + { + printf("Res %d\n", res2); + dword_800ABB78->field_14 = 0; + if (res2 == -1) + { + menu_radio_do_file_mode_helper5_8004ABDC(8); + } + dword_800ABB58 = 0; + dword_800ABB5C = res2; + mts_wup_tsk(MTSID_MEMORY_CARD); + dword_800ABB80 = 1; + dword_800ABB84 = 0; + } + else + { + menu_radio_do_file_mode_helper16_8004C164(work->prim, dword_800ABB88); + if (dword_800ABB70 != NULL) + { + menu_radio_do_file_mode_helper4_8004AA68(8, 160, 0x6E, 160, 0x7A, 0); + } + } + break; + case 4: + if (dword_800ABB70->max_num != 0) + { + textConfig2.xpos = 160; + textConfig2.ypos = 0xC8; + textConfig2.flags = 0x12; + textConfig2.color = 0x66748956; + _menu_number_draw_string2(work->prim, &textConfig2, "PRESS * TO SELECT MEMORY CARD"); + } + if (menu_radio_do_file_mode_helper13_8004BCF8(pPad, &res3, dword_800ABB70) != 0) + { + printf("Res %d\n", res3); + dword_800ABB70->field_14 = 0; + if (res3 == -1) + { + freeMemoryForSelectInfo(dword_800ABB70); + dword_800ABB70 = NULL; + } + else + { + dword_800ABB70->field_18 = 4; + } + dword_800ABB5C = res3; + if (dword_800ABB80 == 4) + { + dword_800ABB58 = 0; + mts_wup_tsk(MTSID_MEMORY_CARD); + } + dword_800ABB80 = 1; + dword_800ABB84 = 0; + } + break; + case 6: + if (mts_get_task_status(MTSID_MEMORY_CARD) == 0) + { + printf("END SAVE MODE\n"); + freeMemoryForSelectInfo(dword_800ABB70); + freeMemoryForSelectInfo(dword_800ABB74); + freeMemoryForSelectInfo(dword_800ABB78); + menu_radio_do_file_mode_helper_8004A858(); + GV_FreeMemory(GV_PACKET_MEMORY0, stack_800ABB50); + GM_LastResultFlag = dword_800ABB54; + printf("END STATE %d\n", GM_LastResultFlag); + if (dword_800ABB48 != 2) + { + font_set_color(work->field_214_font, 1, 0x3BEF, 0); + return 1; + } + return 2; + } + break; + } + if (dword_800ABB84 >= 2) + { + dword_800ABB84--; + } + else if (dword_800ABB84 > 0) + { + show_message(work, ot, dword_800ABB88); + } + NEW_PRIM(tpage, work); + setDrawTPage(tpage, 0, 1, getTPage(0, 1, 960, 256)); + addPrim(ot, tpage); + if (dword_800ABB70 != NULL) + { + menu_radio_do_file_mode_save_memcard_8004B0A0(work, ot, dword_800ABB70); + } + menu_radio_do_file_mode_helper6_8004AD40(work->prim); + return 0; +} + +/* +pAreaNameForMenu uses MGS custom encoding. +pAreaNameForSaveData uses Shift-JIS encoding. + +Example for "Heliport": +MGS : 80 48 80 65 80 6C 80 69 80 70 80 6F 80 72 80 74 +Shift-JIS: 82 67 82 85 82 8C 82 89 82 90 82 8F 82 92 82 94 + H e l i p o r t +*/ +STATIC void getAreaName_8004CF20(int code, char **pAreaNameForMenu, char **pAreaNameForSaveData) +{ + int i; + + if (code == 0) + { + *pAreaNameForMenu = "NO PLACE"; + *pAreaNameForSaveData = "\x81\x40"; + } + GCL_SetArgTop((char *)dword_800ABB8C); + printf("code %d\n", code); + for (i = 0; i < code; i++) { + if (GCL_NextStr() == 0) + { + return; + } + *pAreaNameForMenu = GCL_GetString(GCL_NextStr()); + if (dword_800ABB90 > 0 && dword_800ABB48 == 0) + { + *pAreaNameForSaveData = GCL_GetString(GCL_NextStr()); + } + } +} + +extern char dword_800122F4[]; + +char dockAreaName_8009EC10[] = { "\x82\x63\x82\x8F\x82\x83\x82\x8B" }; // Dock + +const char *diff_names_8009EC1C[] = { + "\x81\x6D\x82\x75\x82\x64\x81\x6E", /* [VE] */ + "\x81\x6D\x82\x64\x82\x79\x81\x6E", /* [EZ] */ + "\x81\x6D\x82\x6D\x82\x6C\x81\x6E", /* [NM] */ + "\x81\x6D\x82\x67\x82\x63\x81\x6E", /* [HD] */ + "\x81\x6D\x82\x64\x82\x77\x81\x6E" /* [EX] */ +}; + +// Called by data_info->make_title +STATIC void makeTitle_8004D008(char *title, MEM_CARD *pUnused, int hours, int minutes) +{ + char playTime[11]; + char *discard; + char *areaName; + + // 0x824F is the '0' Shift-JIS character. + playTime[0] = 0x82; + playTime[1] = (hours / 10) + 0x4f; + playTime[2] = 0x82; + playTime[3] = (hours % 10) + 0x4f; + playTime[4] = 0x81; + playTime[5] = 0x46; + playTime[6] = 0x82; + playTime[7] = (minutes / 10) + 0x4f; + playTime[8] = 0x82; + playTime[9] = (minutes % 10) + 0x4f; + playTime[10] = '\0'; + if (dword_800ABB90 == 0) + { + getAreaName_8004CF20(1, &discard, &areaName); + areaName = dockAreaName_8009EC10; + } + else + { + getAreaName_8004CF20(dword_800ABB90, &discard, &areaName); + } + + /* MGS∫ */ + sprintf(title, "%s%s%s%s%s%s", "\x82\x6C\x82\x66\x82\x72\x81\xE7", diff_names_8009EC1C[GM_DifficultyFlag + 1], "\x81\x40", playTime, "\x81\x40", areaName); +} + +// Called by data_info->make_menu +STATIC void getAreaNameForMenu_8004D14C(char *areaNameForMenu, char *param_2) +{ + char *areaName; + char *discard; + int val; + + getAreaName_8004CF20(param_2[6] - 0x40, &areaName, &discard); + + val = (param_2[5] - 0x40) & 7; + if (val == 0) + { + val = 2; + } + else if (val < 3) + { + val = 1; + } + else + { + val = 3; + } + sprintf(areaNameForMenu, "\f%c%s", val | 0x30, areaName); +} + +// Called by data_info->make_game_data +STATIC void writeGameData(char *saveBuf) +{ + int currentOffset; + int size; + char *saveBufIter; + + currentOffset = 0x100; + saveBufIter = saveBuf; + // Note: the counter is incremented here, but the memory card + // has not been written yet. If a write error occurs and + // then you save again (this time successfully), the + // counter is wrong because it is never decremented. + GM_TotalSaves++; + for (;;) + { + size = GCL_MakeSaveFile(saveBufIter); + currentOffset += size; + + if (currentOffset + size > MC_BLOCK_SIZE) + { + break; + } + + saveBufIter += size; + printf("*"); + } +} + +void init_file_mode(DATA_INFO *pSaveMode, int param_2) +{ + dword_800ABB80 = 0; + data_info = pSaveMode; + init_file_mode_helper2_8004A800(); + init_file_mode_helper_8004A424(param_2); +} + +DATA_INFO dataInfo_8009EC30 = { + {0x47, 0}, // field_0 + 0, // field_2 + 1, // blocks_count + "SAVE DATA", // field_4_name + (void *)makeTitle_8004D008, // make_title + (void *)getAreaNameForMenu_8004D14C, // make_menu + (void *)writeGameData // make_game_data +}; + +void menu_radio_init_save_mode(int param_1, int param_2) +{ + dataInfo_8009EC30.field_0[1] = 0; + if (param_2 == 0) + { + dataInfo_8009EC30.field_2 = 1; + } + else + { + dataInfo_8009EC30.field_2 = param_2; + } + dword_800ABB8C = param_1; + dword_800ABB90 = param_2; + init_file_mode(&dataInfo_8009EC30, 0); +} + +void menu_radio_update_helper4_8004D2D0(int param_1) +{ + dword_800ABB8C = param_1; + init_file_mode(&dataInfo_8009EC30, 1); +} + +void menu_radio_8004D2FC(DATA_INFO *pSaveMode) +{ + init_radio_message_board_80040F74(&gMenuWork_800BD360); + init_file_mode(pSaveMode, 0); +} + +int menu_radio_8004D334(GV_PAD *pPad) +{ + return menu_radio_do_file_mode(&gMenuWork_800BD360, pPad); +} + +void menu_radio_8004D35C(void) +{ + sub_8004124C(&gMenuWork_800BD360); +} diff --git a/components/mgs/mgs/source/menu/debug.c b/components/mgs/mgs/source/menu/debug.c new file mode 100644 index 00000000..7cdafe7f --- /dev/null +++ b/components/mgs/mgs/source/menu/debug.c @@ -0,0 +1,647 @@ +#include "menuman.h" + +#include "common.h" +#include "radio.h" +#include "game/game.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "mts/mts.h" +#include "mts/mts_pad.h" + +extern GV_HEAP MemorySystems_800AD2F0[ MAX_MEMSYS ]; +extern DG_TEX TexSets[DG_MAX_TEXTURES]; +extern unsigned short gOldRootCnt_800B1DC8[32]; + +unsigned char SECTION(".sbss") menu_current_debug_screen_800ABB20; +unsigned char SECTION(".sbss") dword_800ABB21; +short SECTION(".sbss") word_800ABB22; +DG_TEX *SECTION(".sbss") dword_800ABB24; + +STATIC int menu_draw_mem_debug(MenuWork *work, OT_TYPE *ot) +{ + GV_HEAP *pHeap; + LINE_F2 *pLine; + int i; + GV_ALLOC *pAlloc; + int heap_size; + char *alloc_start; + LINE_G4 *pLine2; + int alloc_len; + int alloc_len2; + int used; + int units; + int color; + int allocated; + int size; + int x1; + + pHeap = MemorySystems_800AD2F0; + + NEW_PRIM(pLine, work); + setXY2(pLine, 272, 120, 272, 168); + + LSTORE(0xFF0000, &pLine->r0); + + setLineF2(pLine); + addPrim(ot, pLine); + + used = 1; + for (i = 0; i < 3; i++, pHeap++) + { + size = pHeap->end - pHeap->start; + pAlloc = pHeap->units; + heap_size = size; + + if (heap_size == 0) + { + continue; + } + + allocated = 0; + + for (units = pHeap->used; units > 0; units--, pAlloc++) + { + alloc_start = pAlloc->start; + + if (pAlloc->state == 0) + { + continue; + } + + if (pAlloc->state == 1) + { + color = 0xFFFFFF; + + while (pAlloc[1].state == 1 && units > 2) + { + pAlloc++; + units--; + } + } + else + { + color = 0xFF00; + + while (pAlloc[1].state >= 2 && units > 2) + { + pAlloc++; + units--; + } + } + + alloc_len = (alloc_start - (char *)pHeap->start) * 240; + alloc_len = (alloc_len / heap_size) + 32; + + x1 = 120 + i * 16; + + size = (char *)pAlloc[1].start - alloc_start; + + alloc_len2 = ((char *)pAlloc[1].start - (char *)pHeap->start) * 240; + alloc_len2 = (alloc_len2 / heap_size) + 32; + + if (used < 256) + { + NEW_PRIM(pLine2, work); + setXY4(pLine2, alloc_len, x1, alloc_len2, x1, alloc_len2, 134 + i * 16, alloc_len, 134 + i * 16); + LSTORE(0, &pLine2->r0); + LSTORE(color, &pLine2->r1); + LSTORE(color, &pLine2->r2); + LSTORE(0, &pLine2->r3); + + setLineG4(pLine2); + addPrim(ot, pLine2); + } + + allocated += size; + used++; + } + + menu_number_draw(work, ot, 300, 168 - ((3 - i) * 12), (allocated * 100) / heap_size, 1); + } + + return used; +} + +STATIC short word_800AB660 = 0; +STATIC short word_800AB662 = 0; +STATIC int dword_800AB664 = 0; +STATIC int dword_800AB668 = 0; +STATIC short word_800AB66C = 0; +STATIC short word_800AB66E = 0; +STATIC int dword_800AB670 = 0; + +STATIC int menu_draw_pow_debug(MenuWork *work, OT_TYPE *ot) +{ + int prims_used, left, right, bottom, idx, i; + unsigned short *pCount; + unsigned first_count; + LINE_G4 *pLine; + unsigned short delta; + LINE_G2 *pLine2; + + prims_used = 0; + + pCount = gOldRootCnt_800B1DC8; + first_count = *pCount++; + + left = 0; + bottom = 136; + + for (idx = N_ChanlPerfMax - 1; idx > 0; idx--) + { + right = (*pCount++ - first_count) & 0xFFFF; + + if (right > 511) + { + right = 511; + } + right = right * 240 / 512; + + // Draw top blue bar + NEW_PRIM(pLine, work); + + setXY4(pLine, left + 32, bottom, right + 32, 136, right + 32, 150, left + 32, bottom + 14); + + LSTORE(0, &pLine->r0); + LSTORE(0xFF0000, &pLine->r1); + LSTORE(0xFF0000, &pLine->r2); + LSTORE(0, &pLine->r3); + + setLineG4(pLine); + addPrim(ot, pLine); + prims_used++; + + bottom = 136; // to get a match + left = right; + } + + if (GV_PadData[1].press & PAD_L1) + { + dword_800AB668 = (dword_800AB668 + 1) % N_ChanlPerfMax; + dword_800AB664 = 0; + word_800AB660 = 0; + } + + if (dword_800AB668 == 0) + { + delta = gOldRootCnt_800B1DC8[N_ChanlPerfMax - 1] - gOldRootCnt_800B1DC8[1]; + } + else + { + delta = gOldRootCnt_800B1DC8[dword_800AB668] - gOldRootCnt_800B1DC8[dword_800AB668 - 1]; + menu_number_draw(work, ot, 270, 168, dword_800AB668, 1); + } + + dword_800AB664 += delta; + if (++word_800AB660 >= 128) + { + word_800AB662 = dword_800AB664 / word_800AB660; + word_800AB660 = 0; + dword_800AB664 = 0; + } + + menu_number_draw(work, ot, 300, 168, word_800AB662, 1); + dword_800AB670 += (unsigned short)(gOldRootCnt_800B1DC8[N_ChanlPerfMax - 1] - first_count); + + if (++word_800AB66C >= 128) + { + word_800AB66E = dword_800AB670 / word_800AB66C; + word_800AB66C = 0; + dword_800AB670 = 0; + } + + menu_number_draw(work, ot, 240, 168, word_800AB66E, 1); + menu_number_draw(work, ot, 300, 144, + (unsigned short)(gOldRootCnt_800B1DC8[N_ChanlPerfMax - 1] - first_count), 1); + + right = (unsigned short)(word_800AB982 - first_count); + if (right > 511) + { + right = 511; + } + right = right * 240 / 512; + + bottom = 152; // to get a match + i = 32; // to get a match + right += 32; + + // Draw bottom red bar + NEW_PRIM(pLine, work); + + setXY4(pLine, 32, 152, right, 152, right, 166, 32, 166); + + LSTORE(0, &pLine->r0); + LSTORE(0xFF, &pLine->r1); + LSTORE(0xFF, &pLine->r2); + LSTORE(0, &pLine->r3); + + setLineG4(pLine); + addPrim(ot, pLine); + prims_used++; + + menu_number_draw(work, ot, 300, 156, (unsigned short)(word_800AB982 - first_count), 1); + + // Draw vertical bars + for (i = 0; i <= 240; i += 24) + { + NEW_PRIM(pLine2, work); + bottom = 132; // to get a match + + setXY2(pLine2, i + 32, 132, i + 32, 164); + + // Red for centre bar otherwise green + if (i == 120) + { + LSTORE(0xFF, &pLine2->r0); + } + else + { + LSTORE(0xFF00, &pLine2->r0); + } + LSTORE(0, &pLine2->r1); + + setLineG2(pLine2); + addPrim(ot, pLine2); + prims_used++; + } + + return prims_used; +} + +STATIC int menu_draw_ply_debug(MenuWork *work, OT_TYPE *ot) +{ + OT_TYPE *chnlOt; + int numOTEntries; + OT_TYPE *otMin; + u_long *curPrim; + int primCount; + int totalprimCount; + + LINE_F2 *lineF2; + LINE_G4 *lineG4; + int x_0_1; + int y_0_1; + int x_2_3; + int y_2_3; + + int i; + + int returnVal; + + totalprimCount = 0; + returnVal = 0; + y_0_1 = 0xa8; + y_2_3 = 0xa8; + + chnlOt = DG_Chanl(0)->ot[1 - GV_Clock]; + numOTEntries = DG_Chanl(0)->ot_size - 4; + + NEW_PRIM(lineF2, work); + + setXY2(lineF2, 0x20, 0x76, 0x110, 0x76); + LSTORE(0x800000, &lineF2->r0); + setLineF2(lineF2); + addPrim(ot, lineF2); + + for (i = 0; i < 16; i++) + { + otMin = chnlOt + (i << numOTEntries); + for (curPrim = chnlOt + ((i + 1) << numOTEntries), primCount = 0; + (otMin < curPrim) || (curPrim < chnlOt); curPrim = nextPrim(curPrim)) + { + if (getlen(curPrim) != 0) + { + primCount++; + } + } + + totalprimCount += primCount; + x_0_1 = i * 15; + x_2_3 = x_0_1 + 13; + x_0_1 += 32; + x_2_3 += 32; + + if (primCount > 0) + { + primCount = primCount / 2; + + if (primCount > 176) + { + primCount = 176; + } + + NEW_PRIM(lineG4, work); + setXY4(lineG4, x_0_1, y_0_1, x_0_1, y_0_1 - primCount, x_2_3, y_2_3 - primCount, x_2_3, y_2_3); + LSTORE(0, &lineG4->r0); + LSTORE(0xff00, &lineG4->r1); + returnVal++; + LSTORE(0xff00, &lineG4->r2); + LSTORE(0, &lineG4->r3); + setLineG4(lineG4); + addPrim(ot, lineG4); + } + else + { + NEW_PRIM(lineG4, work); + setXY4(lineG4, x_0_1, y_0_1, x_0_1, y_0_1, x_2_3, y_2_3, x_2_3, y_2_3); + LSTORE(0, &lineG4->r0); + LSTORE(0xff0000, &lineG4->r1); + LSTORE(0xff0000, &lineG4->r2); + LSTORE(0, &lineG4->r3); + setLineG4(lineG4); + addPrim(ot, lineG4); + } + } + + menu_number_draw(work, ot, 0x110, 0x9c, totalprimCount, 1); + + return returnVal; +} + +STATIC int menu_draw_obj_debug(MenuWork *work, OT_TYPE *ot) +{ + DG_OBJS **ppQueue; + DG_OBJS *pObjs; + DG_OBJ *pObj; + LINE_G2 *pLine; + LINE_G4 *pLine2; + int object_count; + int num_models; + int total; + int clip; + + unsigned int color; + int lhs; + int rhs; + int returnVal; + int last_color; + + int bottom, top; + + rhs = 0; + lhs = 0; + + top = 152; + last_color = 0; + returnVal = 0; + + clip = 0; + total = 0; + + object_count = DG_Chanl(0)->objs_index; + ppQueue = DG_Chanl(0)->queue; + + for (; object_count > 0; object_count--) + { + pObjs = *ppQueue++; + + NEW_PRIM(pLine, work); + setXY2(pLine, rhs + 32, top - 6, rhs + 32, top + 19); + + LSTORE(0xFF0000, &pLine->r0); + LSTORE(0, &pLine->r1); + + setLineG2(pLine); + addPrim(ot, pLine); + + pObj = pObjs->objs; + for (num_models = pObjs->n_models; num_models > 0; num_models--) + { + rhs++; + + color = 0xFF00; + + if (pObj->bound_mode == 0) + { + color = 0xFF; + clip++; + } + + if (last_color == 0) + { + last_color = color; + } + + bottom = 152; + + if (color != last_color) + { + NEW_PRIM(pLine2, work); + + setXY4(pLine2, lhs + 32, top, rhs + 32, bottom, rhs + 32, 165, lhs + 32, top + 13); + top = bottom; + + LSTORE(0, &pLine2->r0); + LSTORE(last_color, &pLine2->r1); + LSTORE(last_color, &pLine2->r2); + LSTORE(0, &pLine2->r3); + + setLineG4(pLine2); + addPrim(ot, pLine2); + + returnVal++; + lhs = rhs; + last_color = color; + } + + pObj++; + total++; + } + } + + bottom = 152; + + if (lhs != rhs) + { + NEW_PRIM(pLine2, work); + returnVal++; + + setXY4(pLine2, lhs + 32, top, rhs + 32, bottom, rhs + 32, 165, (bottom = lhs + 32) /* to get a match */, + top + 13); + + LSTORE(0, &pLine2->r0); + LSTORE(last_color, &pLine2->r1); + LSTORE(last_color, &pLine2->r2); + LSTORE(0, &pLine2->r3); + + setLineG4(pLine2); + addPrim(ot, pLine2); + } + + MENU_Locate(300, 128, 0x1); + MENU_Printf("TOTAL %d\n", total); + MENU_Printf("CLIP %d\n", clip); + + return returnVal; +} + +STATIC int menu_draw_tex_debug(MenuWork *work, OT_TYPE *ot) +{ + const int textureRecsCount = DG_MAX_TEXTURES; + short x0, y0; + int i; + DG_TEX *iterTex; + POLY_FT4 *pPoly; + int width, height; + int offx, offy; + int direction; + + iterTex = dword_800ABB24; + direction = 0; + if (GV_PadData[0].status & PAD_RIGHT) + { + direction = 1; + } + else if (GV_PadData[0].status & PAD_LEFT) + { + direction = -1; + } + else + { + word_800ABB22 = -1; + } + if (direction != 0) + { + if (word_800ABB22 <= 0) + { + for (i = textureRecsCount; i > 0; i--) + { + iterTex += direction; + if (iterTex == &TexSets[textureRecsCount]) + { + iterTex -= textureRecsCount; + } + if (iterTex < &TexSets[0]) + { + iterTex = &TexSets[textureRecsCount - 1]; + } + if (iterTex->id != 0) + { + break; + } + } + if (word_800ABB22 < 0) + { + word_800ABB22 = 10; + } + else if (word_800ABB22 == 0) + { + word_800ABB22 = 2; + } + } + word_800ABB22--; + } + + if (iterTex->id == 0) + { + return 0; + } + + dword_800ABB24 = iterTex; + + MENU_Locate(300, 128, 1); + MENU_Printf("No %d\n", iterTex - TexSets); + MENU_Printf("ID %d\n", iterTex->id); + MENU_Printf("COL %d\n", iterTex->col); + MENU_Printf("x %d y %d\n", iterTex->off_x, iterTex->off_y); + MENU_Printf("w %d h %d\n", iterTex->w + 1, iterTex->h + 1); + + NEW_PRIM(pPoly, work); + + LSTORE(0x808080, &pPoly->r0); + + setPolyFT4(pPoly); + + pPoly->clut = iterTex->clut; + pPoly->tpage = iterTex->tpage; + + width = iterTex->w; + height = iterTex->h; + offx = iterTex->off_x; + offy = iterTex->off_y; + + x0 = (319 - width) / 2; + y0 = (239 - height) / 2; + + pPoly->x0 = x0; + pPoly->y0 = y0; + pPoly->y1 = y0; + pPoly->x1 = x0 + width; + pPoly->x2 = x0; + pPoly->y2 = y0 + height; + pPoly->y3 = y0 + height; + pPoly->x3 = x0 + width; + setUVWH(pPoly, offx, offy, width, height); + + addPrim(ot, pPoly); + return 1; +} + +typedef int (*TMenuDebugFn)(MenuWork *, u_long *); + +TMenuDebugFn menu_debug_screens_8009E730[] = { + menu_draw_mem_debug, + menu_draw_pow_debug, + menu_draw_ply_debug, + menu_draw_obj_debug, + menu_draw_tex_debug +}; + +int MENU_PrimUse = 0; + +char *menu_debug_screen_labels_8009E744[] = { + "", + "mem", + "pow", + "ply", + "obj", + "tex", +}; + +void menu_viewer_act(MenuWork *work, OT_TYPE *ot) +{ + mts_read_pad(2); + if (GM_GameStatus & STATE_DEMO_VERBOSE) + { + menu_draw_pow_debug(work, ot); + return; + } + if (!(GM_PlayerStatus & PLAYER_MENU_DISABLE) && GV_PauseLevel != 0 && + (GV_PadData[0].press & PAD_L1)) + { + if (menu_current_debug_screen_800ABB20 == 5) + { + GV_PauseLevel &= ~GV_PAUSE_MENU; + } + menu_current_debug_screen_800ABB20++; + if (menu_current_debug_screen_800ABB20 == 6) + { + menu_current_debug_screen_800ABB20 = 0; + } + if (menu_current_debug_screen_800ABB20 == 5) + { + GV_PauseLevel |= GV_PAUSE_MENU; + } + } + else if (menu_current_debug_screen_800ABB20 != 0) + { + MENU_Locate(300, 8, 1); + menu_draw_num((MENU_PrimUse * 100) / 8192); + MENU_Locate(300, 112, 1); + MENU_Printf(menu_debug_screen_labels_8009E744[menu_current_debug_screen_800ABB20]); + menu_debug_screens_8009E730[menu_current_debug_screen_800ABB20 - 1](work, ot); + } +} + +void menu_viewer_init(MenuWork *work) +{ + menu_current_debug_screen_800ABB20 = 0; + dword_800ABB24 = TexSets; + word_800ABB22 = -1; +} + +void menu_viewer_kill(MenuWork *work) +{ + menu_current_debug_screen_800ABB20 = 0; + return; +} diff --git a/components/mgs/mgs/source/menu/face.h b/components/mgs/mgs/source/menu/face.h new file mode 100644 index 00000000..3e276621 --- /dev/null +++ b/components/mgs/mgs/source/menu/face.h @@ -0,0 +1,77 @@ +#ifndef __MGS_MENU_FACE_H__ +#define __MGS_MENU_FACE_H__ + +// Structures representing data stored in FACE.DAT + +#define FACE_ANIM_SIMPLE ((unsigned short)0) +#define FACE_ANIM_FULL ((unsigned short)1) + +struct face_simple_anim; +struct face_full_anim; + +typedef union face_anim { + struct face_simple_anim *simple_anim; + struct face_full_anim *full_anim; + void *raw_ptr; + int intptr; +} face_anim; + +// Equivalent of: +// https://github.com/Joy-Division/tools-mgs/blob/4e9394baa8ac6872e66202ff0ce26352cd45bef9/face-extract.c#L167-L169 +typedef struct face_header +{ + unsigned short field_0_anim_type; + unsigned short field_2_code; + int field_4; + face_anim field_8_anim_data; +} face_header; + +// Equivalent of: +// https://github.com/Joy-Division/tools-mgs/blob/4e9394baa8ac6872e66202ff0ce26352cd45bef9/face-extract.c#L155-L161 +typedef struct faces_group +{ + int field_0_face_count; + face_header field_4_faces[0]; + + // This is where the actual data is stored, as pointed + // by pointers in field_4_faces. Added for + // informative purposes, it is not referenced directly. + char actual_data[0]; +} faces_group; + +typedef struct face_anim_image +{ + char field_0_xpos; + char field_1_ypos; + char field_2_width; + char field_3_height; + unsigned int field_4_image[0]; +} face_anim_image; + +// "Simple" animation, equivalent of: +// https://github.com/Joy-Division/tools-mgs/blob/4e9394baa8ac6872e66202ff0ce26352cd45bef9/face-extract.c#L174-L189 +typedef struct face_simple_anim +{ + unsigned char *field_0_palette; + face_anim_image *field_4_face; + face_anim_image *field_8_eyes[3]; // last element might not be an eye + face_anim_image *field_14_mouth[3]; // last element might not be a mouth +} face_simple_anim; + +// "Full" animation, equivalent of: +// https://github.com/Joy-Division/tools-mgs/blob/4e9394baa8ac6872e66202ff0ce26352cd45bef9/face-extract.c#L191-L203 +typedef struct face_full_anim_frame +{ + unsigned char *field_0_palette; + face_anim_image *field_4_image; + short field_8; + short field_10; +} face_full_anim_frame; + +typedef struct face_full_anim +{ + int field_0_frame_count; + face_full_anim_frame field_4_frames[0]; +} face_full_anim; + +#endif // __MGS_MENU_FACE_H__ diff --git a/components/mgs/mgs/source/menu/item.c b/components/mgs/mgs/source/menu/item.c new file mode 100644 index 00000000..cb539269 --- /dev/null +++ b/components/mgs/mgs/source/menu/item.c @@ -0,0 +1,1234 @@ +#include "menuman.h" + +#include "common.h" +#include "libdg/libdg.h" +#include "bullet/blast.h" +#include "game/game.h" +#include "linkvar.h" +#include "sound/g_sound.h" +#include "radio.h" + +extern PANEL_TEXTURE gMenuLeftItems_800BD5A0[]; +extern short GM_WeaponTypes[]; +extern short GM_ItemTypes[]; +extern int dword_8009F46C; + +int SECTION(".sbss") dword_800ABAD0; +int SECTION(".sbss") dword_800ABAD4; + +// Also see dword_8009E5CC. +// Those strings are passed to font_draw_string(). +char *SECTION(".data") itm_descriptions_8009E3E4[] = { + /* IT_Cigs */ + (char[]){0xB0,0x14,0x90,0xB0,0x90,0xB1,0xD0,0x15,0x80,0x7C,0x82,0x1D,0x82,0x4A,0xC2,0x23, + 0x82,0x29,0xC0,0x7F,0x82,0x19,0x82,0x2D,0xD0,0x06,0x82,0x0F,0x90,0x1F,0x90,0x49, + 0x81,0x2E,0x90,0xB0,0x90,0xB1,0xD0,0x03,0x90,0xB2,0x81,0x04,0x90,0xB3,0x81,0x0E, + 0x81,0x2B,0x81,0x2F,0x90,0xB4,0x90,0xB5,0x81,0x17,0x81,0x3E,0x81,0x17,0xC1,0x47, + 0x81,0x06,0xD0,0x03,0x00,0x00,0x00,0x00}, + /* IT_Scope */ + (char[]){0xB0,0x14,0x90,0x21,0x90,0x22,0x90,0x23,0xD0,0x15,0x80,0x7C,0x90,0xB6,0x90,0x63, + 0x90,0xB7,0x90,0xB8,0x90,0x21,0x90,0x22,0x90,0x23,0xD0,0x03,0x90,0x24,0x81,0x27, + 0x82,0x1A,0xD0,0x06,0x82,0x40,0x82,0x04,0x82,0x53,0xD0,0x02,0x90,0x18,0x81,0x27, + 0x82,0x1A,0xD0,0x06,0x82,0x40,0x82,0x02,0x82,0x06,0x82,0x28,0xD0,0x03,0x00,0x00}, + /* IT_Box1 */ + (char[]){0xB0,0x14,0x82,0x20,0x82,0x53,0x82,0x3C,0xD0,0x06,0x82,0x4B,0x90,0xB9,0x41,0xD0, + 0x15,0x80,0x7C,0xB0,0x0B,0x82,0x38,0x82,0x4A,0x82,0x3D,0xD0,0x06,0x82,0x28,0x90, + 0x26,0xD0,0x0C,0x81,0x28,0x90,0x27,0x81,0x0B,0x81,0x4C,0x81,0x26,0x81,0x04,0x81, + 0x4B,0xD0,0x03,0x00}, + /* IT_Box2 */ + (char[]){0xB0,0x14,0x82,0x20,0x82,0x53,0x82,0x3C,0xD0,0x06,0x82,0x4B,0x90,0xB9,0x42,0xD0, + 0x15,0x80,0x7C,0xB0,0x0B,0x90,0x2A,0x90,0x85,0x90,0xBA,0x90,0x2B,0x90,0x2C,0x90, + 0xBB,0x90,0x26,0xD0,0x0C,0x81,0x28,0x90,0x27,0x81,0x04,0x81,0x26,0x81,0x02,0x81, + 0x4B,0xD0,0x03,0x00}, + /* IT_Box3 */ + (char[]){0xB0,0x14,0x82,0x20,0x82,0x53,0x82,0x3C,0xD0,0x06,0x82,0x4B,0x90,0xB9,0x43,0xD0, + 0x15,0x80,0x7C,0xB0,0x0B,0x90,0x2F,0x90,0x30,0x90,0x26,0xD0,0x0C,0x81,0x28,0x90, + 0x27,0x81,0x04,0x81,0x26,0x81,0x02,0x81,0x4B,0xD0,0x03,0x00}, + /* IT_NVG */ + (char[]){0xB0,0x14,0x90,0x31,0x90,0x32,0x82,0x14,0xD0,0x06,0x82,0x10,0x82,0x4B,0xD0,0x15, + 0x80,0x7C,0x90,0x4A,0x81,0x52,0x90,0xA6,0x90,0xA7,0x90,0xBC,0x81,0x2B,0x90,0xBD, + 0x90,0xBE,0x81,0x17,0x81,0x26,0x90,0xBF,0x90,0xC0,0x90,0x64,0x81,0x19,0x81,0x4B, + 0x90,0x31,0x90,0x32,0x90,0xC1,0x90,0xC2,0xD0,0x03,0x90,0x31,0x81,0x04,0x90,0xC3, + 0x90,0xC4,0x81,0x27,0x81,0x42,0x90,0x32,0x90,0xA4,0x81,0x52,0x90,0xC5,0x90,0x2B, + 0x81,0x19,0x81,0x4B,0x90,0xC6,0x81,0x0C,0x81,0x27,0x81,0x0D,0x81,0x4B,0xD0,0x03, + 0x00,0x00,0x00,0x00}, + /* IT_ThermG */ + (char[]){0xB0,0x14,0x82,0x15,0xD0,0x06,0x82,0x3E,0x82,0x4B,0xC0,0x7F,0x82,0x14,0xD0,0x06, + 0x82,0x10,0x82,0x4B,0xD0,0x15,0x80,0x7C,0x90,0xC7,0x90,0xC8,0x81,0x2E,0x90,0x38, + 0x90,0xC9,0x81,0x52,0x90,0xBF,0x90,0xC0,0x90,0x64,0x81,0x19,0x81,0x4B,0x90,0x31, + 0x90,0x32,0x90,0xC1,0x90,0xC2,0xD0,0x03,0x90,0x31,0x81,0x04,0x90,0xC3,0x90,0xC4, + 0x81,0x27,0x81,0x42,0x90,0x32,0x90,0xA4,0x81,0x52,0x90,0xC5,0x90,0x2B,0x81,0x19, + 0x81,0x4B,0x90,0xC6,0x81,0x0C,0x81,0x27,0x81,0x0D,0x81,0x4B,0xD0,0x03,0x00,0x00}, + /* IT_GasMask */ + (char[]){0xB0,0x14,0x82,0x0C,0x82,0x19,0x82,0x3E,0x82,0x19,0x82,0x0F,0xD0,0x15,0x80,0x7C, + 0x90,0x91,0x90,0xCA,0x90,0x3A,0x90,0x64,0x90,0x4B,0x90,0xCB,0x90,0xC1,0x90,0xCC, + 0x90,0x49,0x82,0x0C,0x82,0x19,0x82,0x3E,0x82,0x19,0x82,0x0F,0xD0,0x03,0x90,0x3F, + 0x82,0x0C,0x82,0x19,0x81,0x2E,0x90,0xCD,0x81,0x27,0x80,0x4F,0x80,0x32,0x82,0x12, + 0xD0,0x06,0x82,0x18,0x81,0x0C,0x90,0x43,0x90,0xCE,0x81,0x19,0x81,0x4B,0x82,0x19, + 0x82,0x34,0xD0,0x06,0x82,0x29,0x81,0x0C,0x90,0xCF,0x81,0x0F,0x81,0x2A,0x81,0x4B, + 0xD0,0x03,0x00,0x00}, + /* IT_BodyArmor */ + (char[]){0xB0,0x14,0x82,0x3C,0x82,0x27,0xC2,0x03,0xD0,0x06,0x82,0x02,0xD0,0x06,0x82,0x3E, + 0xD0,0x06,0xD0,0x15,0x80,0x7C,0x90,0xFE,0x90,0xFF,0x90,0xD0,0x90,0xD1,0x81,0x52, + 0x90,0x48,0x90,0x49,0x81,0x17,0x81,0x1F,0x90,0xD2,0x90,0x85,0x82,0x39,0x82,0x19, + 0x82,0x28,0xD0,0x03,0x82,0x20,0x82,0x41,0xD0,0x06,0x82,0x18,0x81,0x52,0x90,0x42, + 0x90,0x43,0x81,0x15,0x81,0x1B,0x81,0x4B,0x90,0xC6,0x81,0x0C,0x81,0x27,0x81,0x0D, + 0x81,0x4B,0xD0,0x03,0x00,0x00,0x00,0x00}, + /* IT_Ketchup */ + (char[]){0xB0,0x14,0x82,0x11,0x82,0x21,0xC2,0x43,0xC2,0x23,0x82,0x37,0xD0,0x15,0x80,0x7C, + 0x90,0x44,0x90,0x45,0x90,0x46,0xD0,0x03,0x82,0x04,0x82,0x1F,0x82,0x4A,0x82,0x02, + 0x90,0x47,0x82,0x28,0x82,0x3E,0x82,0x28,0x90,0x48,0x90,0x49,0xD0,0x03,0x80,0x23, + 0x90,0x24,0x82,0x3C,0x82,0x1F,0x82,0x53,0x81,0x27,0x90,0x48,0x90,0x49,0x90,0xB6, + 0x91,0x0F,0xD0,0x03,0x00,0x00,0x00,0x00}, + /* IT_Stealth */ + (char[]){0xB0,0x14,0x82,0x19,0x82,0x26,0x82,0x4B,0x82,0x19,0x90,0x4C,0x90,0x4D,0xD0,0x15, + 0x80,0x7C,0x82,0x0A,0x82,0x1F,0x82,0x13,0x82,0x53,0x81,0x0C,0x90,0x54,0x90,0x7E, + 0x81,0x17,0x81,0x1F,0xD0,0x02,0x90,0x4A,0x81,0x52,0x90,0xD3,0x90,0xD4,0x81,0x15, + 0x81,0x1B,0x81,0x26,0x90,0x93,0x90,0x94,0x81,0x17,0x81,0x1F,0x90,0xD5,0x81,0x52, + 0x90,0xD6,0x90,0x68,0x90,0x64,0x81,0x19,0x81,0x4B,0x90,0x4C,0x90,0x4D,0x82,0x17, + 0x82,0x19,0x82,0x26,0x82,0x40,0xD0,0x03,0x00,0x00,0x00,0x00}, + /* IT_Bandana */ + (char[]){0xB0,0x14,0x82,0x40,0x82,0x12,0x82,0x53,0x82,0x30,0x82,0x53,0x82,0x20,0x82,0x2A, + 0xD0,0x15,0x80,0x7C,0x82,0x41,0x82,0x4A,0x82,0x4B,0x81,0x0C,0x90,0x34,0x81,0x24, + 0x81,0x11,0x81,0x1F,0x82,0x30,0x82,0x53,0x82,0x20,0x82,0x2A,0xD0,0x03,0x80,0x7C, + 0xB0,0x0B,0x82,0x40,0x82,0x12,0x82,0x53,0xD0,0x0C,0x81,0x28,0x81,0x04,0x81,0x06, + 0x90,0x8E,0x81,0x0C,0x81,0x2C,0x81,0x04,0x81,0x13,0x81,0x3E,0x81,0x4C,0x81,0x26, + 0x81,0x04,0x81,0x4B,0xD0,0x03,0x00,0x00}, + /* IT_Camera */ + (char[]){0xB0,0x14,0x82,0x0B,0x82,0x41,0x82,0x49,0xD0,0x15,0x80,0x7C,0x81,0x04,0x81,0x4D, + 0x81,0x04,0x81,0x4D,0x81,0x2A,0x81,0x28,0x81,0x13,0x81,0x4D,0x81,0x52,0x90,0x4F, + 0x90,0x50,0x81,0x27,0x81,0x0D,0x81,0x4B,0xD0,0x03,0x90,0x24,0x81,0x27,0x82,0x1A, + 0xD0,0x06,0x82,0x40,0x82,0x04,0x82,0x53,0xD0,0x02,0x90,0x18,0x81,0x27,0x82,0x1A, + 0xD0,0x06,0x82,0x40,0x82,0x02,0x82,0x06,0x82,0x28,0xD0,0x03,0x90,0x1D,0x81,0x27, + 0x90,0x4F,0x90,0x50,0xD0,0x03,0x00,0x00}, + /* IT_Ration */ + (char[]){0xB0,0x14,0x82,0x4C,0xD0,0x06,0x82,0x17,0xC2,0x47,0x82,0x53,0xD0,0x15,0x80,0x7C, + 0x90,0xD7,0x90,0x49,0x90,0xD8,0x90,0xD9,0x90,0xDA,0x90,0xDB,0xD0,0x03,0x80,0x4C, + 0x80,0x49,0x80,0x46,0x80,0x45,0x81,0x52,0x90,0x52,0x90,0x53,0x81,0x19,0x81,0x4B, + 0xD0,0x03,0x82,0x41,0x82,0x2B,0xC2,0x45,0xD0,0x06,0x81,0x52,0x90,0x54,0x81,0x04, + 0x81,0x26,0x81,0x04,0x81,0x4B,0x90,0xDC,0x81,0x2B,0x90,0x1B,0x82,0x3C,0x82,0x1F, + 0x82,0x53,0x81,0x52,0x90,0x7B,0x81,0x17,0x81,0x26,0x90,0x48,0x90,0x49,0xD0,0x03, + 0x90,0x93,0x90,0x94,0x81,0x27,0x91,0x0C,0x90,0x98,0x90,0x48,0x90,0x49,0xD0,0x03, + 0x00,0x00,0x00,0x00}, + /* IT_ColdMedicine */ + (char[]){0xB0,0x14,0x90,0x55,0x90,0x56,0x90,0x57,0xD0,0x15,0x80,0x7C,0x90,0xDD,0x90,0xDE, + 0x90,0xDF,0x90,0xE0,0x90,0x57,0xD0,0x03,0x90,0x55,0x90,0x56,0x81,0x52,0x90,0xE1, + 0x81,0x19,0x90,0xC6,0x81,0x0C,0x81,0x27,0x81,0x0D,0x81,0x4B,0xD0,0x03,0x82,0x41, + 0x82,0x2B,0xC2,0x45,0xD0,0x06,0x81,0x52,0x90,0x54,0x81,0x04,0x81,0x26,0x81,0x04, + 0x81,0x4B,0x90,0xDC,0x81,0x2B,0x90,0x1B,0x82,0x3C,0x82,0x1F,0x82,0x53,0x81,0x52, + 0x90,0x7B,0x81,0x17,0x81,0x26,0x90,0xE2,0x90,0x49,0xD0,0x03,0x00,0x00,0x00,0x00}, + /* IT_Diazepam */ + (char[]){0xB0,0x14,0x82,0x18,0x82,0x02,0x82,0x1C,0x82,0x31,0x82,0x40,0xD0,0x15,0x80,0x7C, + 0x90,0x59,0x90,0x5A,0x90,0x5B,0x90,0x5C,0x90,0x5D,0xD0,0x03,0x90,0x5E,0x81,0x2E, + 0x81,0x35,0x81,0x4B,0x81,0x08,0x81,0x52,0x90,0xE3,0x90,0x5C,0x90,0xDC,0x90,0xE4, + 0x90,0x58,0x81,0x41,0x81,0x4B,0xD0,0x03,0x82,0x41,0x82,0x2B,0xC2,0x45,0xD0,0x06, + 0x81,0x52,0x90,0x54,0x81,0x04,0x81,0x26,0x81,0x04,0x81,0x4B,0x90,0xDC,0x81,0x2B, + 0x90,0x1B,0x82,0x3C,0x82,0x1F,0x82,0x53,0x81,0x52,0x90,0x7B,0x81,0x17,0x81,0x26, + 0x90,0xE2,0x90,0x49,0xD0,0x03,0x00,0x00}, + /* IT_PalKey */ + (char[]){0xB0,0x14,0x50,0x41,0x4C,0x82,0x0B,0xD0,0x06,0x82,0x29,0xD0,0x15,0x80,0x7C,0x90, + 0xE5,0x90,0xA2,0x82,0x13,0xD0,0x06,0x82,0x29,0x90,0xE6,0x90,0xE7,0x90,0xAC,0x90, + 0x51,0x90,0x16,0x90,0xE8,0x90,0xE9,0x90,0x49,0x81,0x2E,0x82,0x0B,0xD0,0x06,0x82, + 0x29,0xC0,0x7F,0x82,0x0D,0xD0,0x06,0xD0,0x03,0x00,0x00,0x00}, + /* IT_Card */ + (char[]){0xB0,0x14,0x49,0x44,0x82,0x0B,0xD0,0x06,0x82,0x29,0xD0,0x15,0x80,0x7C,0x90,0x93, + 0x90,0x94,0x81,0x19,0x81,0x4B,0x81,0x28,0x82,0x1B,0x82,0x0D,0xC2,0x45,0x82,0x4A, + 0x82,0x26,0xC2,0x03,0xC0,0x7F,0x82,0x4C,0x82,0x39,0x82,0x4B,0x81,0x0C,0x31,0x81, + 0x3E,0x81,0x27,0x81,0x2E,0x90,0xEA,0x81,0x52,0x90,0x54,0x81,0x11,0x81,0x4B,0x90, + 0xC6,0x81,0x0C,0x81,0x27,0x81,0x0D,0x81,0x4B,0xD0,0x03,0x00}, + /* IT_TimerBomb */ + (char[]){0xB0,0x14,0xC0,0x3F,0xD0,0x15,0x80,0x7C,0x90,0x01,0x90,0x67,0x90,0x68,0xD0,0x03, + 0x82,0x41,0x82,0x2B,0xC2,0x45,0xD0,0x06,0x81,0x52,0x90,0x54,0x81,0x04,0x81,0x26, + 0x81,0x04,0x81,0x4B,0x81,0x28,0x81,0x0D,0x81,0x2B,0x90,0x24,0x81,0x27,0x90,0x69, + 0x81,0x26,0x81,0x4B,0x81,0x13,0x81,0x28,0x81,0x0C,0x81,0x27,0x81,0x0D,0x81,0x4B, + 0xD0,0x03,0x00,0x00}, + /* IT_MineDetector */ + (char[]){0xB0,0x14,0x90,0x6A,0x90,0x6B,0x90,0x6C,0x90,0x6D,0x90,0x6E,0xD0,0x15,0x80,0x7C, + 0x90,0x93,0x90,0x94,0x81,0x19,0x81,0x4B,0x81,0x28,0xD0,0x02,0x90,0xEB,0x90,0x9D, + 0x81,0x15,0x81,0x4C,0x81,0x1F,0x90,0x6A,0x90,0x6B,0x81,0x2E,0x90,0xEC,0x90,0x9E, + 0x81,0x0C,0x82,0x4C,0xD0,0x06,0x82,0x20,0xD0,0x06,0x81,0x2B,0x90,0xED,0x90,0xEE, + 0x81,0x15,0x81,0x4C,0x81,0x4B,0xD0,0x03,0x00,0x00,0x00,0x00}, + /* IT_Disk */ + (char[]){0xB0,0x14,0x90,0x4A,0x82,0x27,0xC2,0x03,0x82,0x19,0x82,0x0F,0xD0,0x15,0x80,0x7C, + 0x82,0x41,0x82,0x1F,0x82,0x4B,0x82,0x0E,0x82,0x02,0x81,0x2E,0x90,0x70,0x90,0x71, + 0x82,0x27,0xD0,0x06,0x82,0x1F,0x81,0x0C,0x90,0x72,0x81,0x41,0x81,0x49,0x81,0x4C, + 0x81,0x26,0x81,0x04,0x81,0x4B,0x81,0x28,0x81,0x04,0x81,0x06,0x90,0x4A,0x82,0x27, + 0xC2,0x03,0x82,0x19,0x82,0x0F,0xD0,0x03,0x00,0x00,0x00,0x00}, + /* IT_Rope */ + (char[]){0xB0,0x14,0x82,0x4D,0xD0,0x06,0x82,0x37,0xD0,0x15,0x80,0x7C,0x90,0x73,0x81,0x0F, + 0x81,0x26,0x90,0x74,0x90,0x75,0x81,0x2A,0x82,0x4D,0xD0,0x06,0x82,0x37,0xD0,0x03, + 0x82,0x2A,0x82,0x04,0x82,0x4D,0x82,0x53,0x90,0xD0,0x90,0xD1,0x90,0xEF,0xD0,0x03, + 0x00,0x00,0x00,0x00}, + /* IT_Handkerchief */ + (char[]){0xB0,0x14,0x82,0x2F,0x82,0x53,0x82,0x0B,0x82,0x21,0xD0,0x15,0x80,0x7C,0x82, + 0x19,0x82,0x2A,0x82,0x04,0x82,0x31,0xD0,0x06,0xC0,0x7F,0x82,0x06,0x82,0x4B, + 0x82,0x35,0x81,0x2E,0x82,0x2F,0x82,0x53,0x82,0x0B,0x82,0x21,0xD0,0x03,0x81, + 0x0B,0x81,0x19,0x81,0x0B,0x81,0x2B,0x90,0xF0,0x90,0xF1,0x81,0x2E,0x90,0x78, + 0x81,0x04,0x81,0x0C,0x81,0x19,0x81,0x4B,0xD0,0x03,0x00,0x00}, + /* IT_Suppressor */ + (char[]){0xB0,0x14,0x82,0x15,0x82,0x37,0x82,0x4C,0xC2,0x23,0x82,0x15,0xD0,0x06,0xD0,0x15, + 0x80,0x7C,0x82,0x1D,0xD0,0x06,0x82,0x13,0x82,0x40,0x82,0x34,0x82,0x19,0x82,0x28, + 0x82,0x4B,0x90,0xF2,0x90,0x49,0x81,0x2E,0x82,0x15,0x82,0x37,0x82,0x4C,0xC2,0x23, + 0x82,0x15,0xD0,0x06,0xD0,0x03,0x82,0x1D,0xD0,0x06,0x82,0x13,0x82,0x40,0x82,0x34, + 0x82,0x19,0x82,0x28,0x82,0x4B,0x81,0x2E,0x90,0x7E,0x90,0x8C,0x90,0xF3,0x81,0x52, + 0x90,0xF4,0x81,0x08,0x81,0x4B,0xD0,0x03,0x00,0x00,0x00,0x00}, +}; + +char *SECTION(".data") frozenItemsDescriptions_8009E444[] = { + /* Frozen Ration */ + (char[]){0xB0,0x14,0x82,0x4C,0xD0,0x06,0x82,0x17,0xC2,0x47,0x82,0x53,0xD0,0x15,0x80,0x7C, + 0x90,0x79,0x81,0x4A,0x81,0x24,0x81,0x04,0x81,0x26,0x81,0x04,0x81,0x26,0xD0,0x02, + 0x90,0x48,0x90,0x49,0x81,0x27,0x81,0x0D,0x81,0x2A,0x81,0x04,0xD0,0x03,0x00,0x00}, + /* Frozen Ketchup */ + (char[]){0xB0,0x14,0x82,0x11,0x82,0x21,0xC2,0x43,0xC2,0x23,0x82,0x37,0xD0,0x15,0x80,0x7C, + 0x90,0x79,0x81,0x4A,0x81,0x24,0x81,0x04,0x81,0x26,0x81,0x04,0x81,0x26,0xD0,0x02, + 0x90,0x48,0x90,0x49,0x81,0x27,0x81,0x0D,0x81,0x2A,0x81,0x04,0xD0,0x03,0x00,0x00}, +}; + +// Mine detector cannot be used in Hard and Extreme mode. +char SECTION(".data") mineDetectorUnusable_8009E44C[] = { + 0xB0,0x14,0x90,0x6A,0x90,0x6B,0x90,0x6C,0x90,0x6D,0x90,0x6E,0xD0,0x15,0x80,0x7C, + 0x48,0x41,0x52,0x44,0x2C,0x20,0x45,0x58,0x54,0x52,0x45,0x4D,0x20,0x82,0x42,0xD0, + 0x06,0x82,0x29,0x81,0x27,0x81,0x2F,0x90,0x48,0x90,0x49,0x81,0x27,0x81,0x0D,0x81, + 0x2A,0x81,0x04,0xD0,0x03,0x00,0x00,0x00}; + +menu_weapon_rpk_info SECTION(".data") gMenuItemRpkInfo_8009E484[] = { + { "CIGS", 14 }, + { "SCOPE", 30 }, + { "C.BOX A", 17 }, + { "C.BOX B", 17 }, + { "C.BOX C", 17 }, + { "N.V.G", 15 }, + { "THERM.G", 16 }, + { "GASMASK", 19 }, + { "B.ARMOR", 18 }, + { "KETCHUP", 25 }, + { "STEALTH", 32 }, + { "BANDANA", 31 }, + { "CAMERA", 12 }, + { "RATION", 22 }, + { "MEDICINE", 21 }, + { "DIAZEPAM", 21 }, + { "PAL KEY", 23 }, + { "CARD", 27 }, + { "TIMER.B", 26 }, + { "MINE.D", 20 }, + { "DISC", 28 }, + { "ROPE", 24 }, + { "SCARF", 29 }, + { "SUPPR.", 13 } +}; + +void menu_sub_8003B568(void) +{ + int imgIdx; + u_long palIdx; + int i; + + for (i = 0; i < MENU_ITEM_COUNT; i++) + { + imgIdx = 0xc + i; + palIdx = 0x2e; + if (imgIdx != 0x16) + { + palIdx = 0; + if (imgIdx == 0x1b) + { + palIdx = 0x2f; + } + } + menu_init_rpk_item_8003DDCC(&gMenuLeftItems_800BD5A0[i], imgIdx, palIdx); + } +} + +PANEL_TEXTURE *menu_rpk_8003B5E0(int index) +{ + return &gMenuLeftItems_800BD5A0[gMenuItemRpkInfo_8009E484[index].field_4_rpk_idx - 12]; +} + +void menu_item_printDescription_8003B614(int itemIndex) +{ + char *itemDescription = itm_descriptions_8009E3E4[itemIndex]; + + if (GM_FrozenItemsState == 1) + { + if (itemIndex != IT_Ketchup) + { + if (itemIndex == IT_Ration) + { + itemDescription = frozenItemsDescriptions_8009E444[0]; + } + } + else + { + itemDescription = frozenItemsDescriptions_8009E444[1]; + } + } + + if (itemIndex == IT_Card) + { + itemDescription[46] = GM_CardFlag + 48; + } + + if (itemIndex == IT_MineDetector && GM_DifficultyFlag >= DIFFICULTY_HARD) + { + itemDescription = (char *)mineDetectorUnusable_8009E44C; + } + + menu_printDescription_8003F97C(itemDescription); +} + +/** + * @brief Checks if a given item is disabled based on various game conditions. + * + * This function determines whether a specified item is currently disabled due + * to the player's status, the current weapon, or other game-specific conditions. + * + * @param item The item ID to check. + * @return 1 if the item is disabled, 0 otherwise. + */ +int menu_item_IsItemDisabled_8003B6D0(int item) +{ + int bit; + + // If both the weapon and item use the first person view + if ((GM_WeaponTypes[GM_CurrentWeaponId + 1] & 0x200) && (GM_ItemTypes[item + 1] & 1)) + { + return TRUE; + } + + // Crawling + if (GM_PlayerStatus & (PLAYER_GROUND | PLAYER_INTRUDE)) + { + if ((item == IT_Box1) || (item == IT_Box2) || (item == IT_Box3)) + { + return TRUE; + } + } + // If a Nikita missile is in flight + if (dword_8009F46C != 0) + { + if ((item == IT_Scope) || (item == IT_Box1) || (item == IT_Box2) || (item == IT_Box3) || + (item == IT_Camera)) + { + return TRUE; + } + } + + bit = 1 << item; + // If the items are explicitly disabled (e.g from gcl command MENU) + return (GM_DisableItem & bit) != 0; +} + +void menu_drawPalKey_8003B794(MenuWork *work, OT_TYPE *ot, int id) +{ + RECT pal_rect; + RECT img_rect; + RPK_ITEM *pPalItem; + RPK_ITEM *pImgItem; + SPRT *pSprt; + + pPalItem = menu_rpk_get_pal_8003DD9C(id * 2 + 33); + pImgItem = menu_rpk_get_img_8003DDB4(id * 2 + 34); + + pal_rect.x = 960; + pal_rect.y = 336; + pal_rect.w = 16; + pal_rect.h = 1; + LoadImage(&pal_rect, pPalItem->data); + + img_rect.x = 960; + img_rect.y = 337; + img_rect.w = pImgItem->w; + img_rect.h = pImgItem->h; + LoadImage(&img_rect, pImgItem->data); + + NEW_PRIM(pSprt, work); + + pSprt->v0 = 81; + pSprt->x0 = 230; + pSprt->u0 = 0; + pSprt->y0 = 116; + pSprt->w = pImgItem->w * 4; + pSprt->h = pImgItem->h; + LSTORE(0x80808080, &pSprt->r0); + setClut(pSprt, pal_rect.x, pal_rect.y); + setSprt(pSprt); + addPrim(ot, pSprt); +} + +/** + * @brief Draw the current item in the menu. + * + * This function handles the drawing of the current item in the menu, + * including its icon, state (disabled or frozen), quantity (for consumables), + * and name. It also draws the frame around the item. + * + * @param work Pointer to the MenuWork actor. + * @param ot Pointer to the ordering table. + * @param xpos The x-coordinate where the item should be drawn. + * @param ypos The y-coordinate where the item should be drawn. + * @param pMenuSub Pointer to the current item in the inventory. + */ +void menu_item_helper_8003B8F0(MenuWork *work, OT_TYPE *ot, int xpos, int ypos, Menu_Inventory *pMenuSub) +{ + PANEL_TEXTURE *pMenuSprt; // $s6 + SPRT *pIconSprt; // $s0 + int rgb; // $s1 + int bBlueBackground; // $a3 + TextConfig textConfig; // [sp+18h] [-10h] BYREF + + // If the current item is valid + if (pMenuSub->field_0_current.field_0_id >= 0) + { + pMenuSprt = menu_rpk_8003B5E0(pMenuSub->field_0_current.field_0_id); + menu_draw_texture_8003CEF8(pMenuSprt); + // Draw "DISABLED" and "FROZEN" depending on the item state + if (menu_item_IsItemDisabled_8003B6D0(pMenuSub->field_0_current.field_0_id)) + { + menu_draw_nouse(work->prim, xpos, ypos); + } + if (GM_FrozenItemsState == 1) + { + if (pMenuSub->field_0_current.field_0_id == IT_Ration || + pMenuSub->field_0_current.field_0_id == IT_Ketchup) + { + menu_draw_frozen(work->prim, xpos, ypos); + } + } + // If the item is a consumable, draw the current and max values + if (GM_ItemTypes[pMenuSub->field_0_current.field_0_id + 1] & ITEMTYPE_CONSUMABLE) + { + menu_number_draw_number2(work, xpos, ypos + 11, pMenuSub->field_0_current.field_2_num, + GM_ItemsMax[pMenuSub->field_0_current.field_0_id]); + } + else if (pMenuSub->field_0_current.field_0_id == IT_Card) + { + // Draw the card level text + textConfig.xpos = xpos; + textConfig.ypos = ypos + 14; + textConfig.flags = 0; + textConfig.color = 0x64808080; + _menu_number_draw_string(work->prim, &textConfig, "LV."); + textConfig.ypos -= 2; + _menu_number_draw(work->prim, &textConfig, GM_Items[IT_Card]); + } + else if (pMenuSub->field_0_current.field_0_id == IT_TimerBomb) + { + menu_number_draw(work, ot, xpos + 10, ypos + 10, GM_Items[IT_TimerBomb], 0); + } + + // Draw the item icon + if (pMenuSprt->field_C_uvclut) + { + NEW_PRIM(pIconSprt, work); + + if (!pMenuSub->field_0_current.field_4_pos) + { + rgb = 0x808080; + } + else + { + rgb = 0x404040; + } + menu_init_sprt_8003D0D0(pIconSprt, pMenuSprt, xpos, ypos); + LSTORE(rgb, &pIconSprt->r0); + setSprt(pIconSprt); + addPrim(ot, pIconSprt); + } + // Draw the name of the item/weapon + menu_number_draw_string(work, ot, xpos + 46, ypos + 22, + gMenuItemRpkInfo_8009E484[pMenuSub->field_0_current.field_0_id].field_0_weapon_name, 1); + } + else + { + menu_number_draw_string(work, ot, xpos + 46, ypos + 22, "NO ITEM", 1); + } + // Draw the frame around the item. + // Use a blue background for the item in the selection slot + // while the menu is open. + if (!pMenuSub->field_0_current.field_4_pos) + { + bBlueBackground = pMenuSub->field_0_current.field_6_current == 0; + } + else + { + bBlueBackground = 0; + } + Menu_item_render_frame_rects_8003DBAC(work->prim, xpos, ypos, bBlueBackground); +} + +void menu_8003BBEC(MenuWork *work) +{ + Menu_Item_Unknown *temp_v0 = work->field_1DC_menu_item.field_C_alloc; + int index; + short *pLinkVar; + + work->field_1DC_menu_item.field_10_state = 0; + + AssignXYFromVec_8003D1B8(&work->field_1DC_menu_item.field_0_current, + &temp_v0->field_20_array[temp_v0->field_0_main.field_4_selected_idx]); + + if (work->field_1DC_menu_item.field_0_current.field_2_num <= 0) + { + work->field_1DC_menu_item.field_0_current.field_0_id = IT_None; + } + + index = work->field_1DC_menu_item.field_0_current.field_0_id; + pLinkVar = linkvarbuf; + + if ((index >= 0) && !menu_item_IsItemDisabled_8003B6D0(index)) + { + pLinkVar[15] = index; + sub_8003CFE0(menu_rpk_8003B5E0(index), 0); + work->field_1DC_menu_item.field_11 = pLinkVar[15]; + } + else + { + if (index != IT_None) + { + dword_800ABAD0 = index; + } + + GM_CurrentItemId = IT_None; + work->field_1DC_menu_item.field_0_current.field_0_id = IT_None; + } + + work->field_1DC_menu_item.field_12_flashingAnimationFrame = 10; + + menu_panel_free_8003D184(work->field_1DC_menu_item.field_C_alloc); + menu_font_kill_8003FC0C(); + + GM_SeSet2(0, 63, SE_ITEM_EQUIP); +} + +STATIC int dword_800AB574 = 0; +STATIC int dword_800AB578 = 0; + +int menu_item_update_helper_8003BCD4(MenuWork *work) +{ + int activeItems; + int i; + Menu_Item_Unknown *pPanels; + int cardVal; + int panelIndex; + + if (!(GM_GameStatus & GAME_FLAG_BIT_19)) + { + activeItems = 0; + + for (i = 0; i < IT_Max; i++) + { + if (GM_Items[i] > 0) + { + activeItems++; + } + } + + pPanels = menu_alloc_panel_8003D124(activeItems + 1); + work->field_1DC_menu_item.field_C_alloc = pPanels; + + if (!pPanels) + { + return 0; + } + + if ((GM_CurrentItemId != IT_None) && (GM_CurrentItemId != IT_Card)) + { + dword_800ABAD0 = GM_CurrentItemId; + } + else if (dword_800ABAD0 < 0) + { + dword_800ABAD0 = 0; + } + + cardVal = -1; + + if (GM_CardFlag > 0) + { + cardVal = 0; + } + + panelIndex = 0; + + for (i = 0; i < IT_Max; i++) + { + if (i == dword_800ABAD0) + { + AssignXY_8003D1A8(&pPanels->field_20_array[panelIndex], IT_None, 1); + + panelIndex++; + + if (cardVal == 0) + { + if (GM_CurrentItemId == IT_None) + { + AssignXY_8003D1A8(&pPanels->field_20_array[panelIndex], IT_Card, GM_Items[IT_Card]); + panelIndex++; + } + else + { + cardVal = 1; + } + } + } + + if ((GM_Items[i] > 0) && (i != IT_Card)) + { + AssignXY_8003D1A8(&pPanels->field_20_array[panelIndex], i, GM_Items[i]); + panelIndex++; + } + + if (cardVal > 0) + { + AssignXY_8003D1A8(&pPanels->field_20_array[panelIndex], IT_Card, GM_Items[IT_Card]); + panelIndex++; + cardVal = 0; + } + } + } + else + { + pPanels = menu_alloc_panel_8003D124(1); + work->field_1DC_menu_item.field_C_alloc = pPanels; + + if (!pPanels) + { + return 0; + } + + AssignXY_8003D1A8(&pPanels->field_20_array[0], IT_None, 1); + } + + if (!sub_8003F84C(0)) + { + menu_panel_free_8003D184(pPanels); + return 0; + } + + dword_800AB578 = 0; + dword_800AB574 = 0; + work->field_1DC_menu_item.field_10_state = 2; + + sub_8003D520(); + sub_8003CE40(gMenuLeftItems_800BD5A0, MENU_ITEM_COUNT); + menu_panel_8003D2BC(work->field_1DC_menu_item.field_C_alloc, work->field_1DC_menu_item.field_0_current.field_0_id); + GM_SeSet2(0, 63, SE_ITEM_OPENWINDOW); + return 1; +} + +void menu_item_update_helper2_8003BF1C(MenuWork *work, OT_TYPE *ot) +{ + unsigned short anim_frame; + int anim_frame2; + int switched_weapon; + int last_id; + Menu_Item_Unknown *pAlloc; + PANEL *pPanel; + + switch (work->field_1DC_menu_item.field_10_state) + { + case 0: + anim_frame = work->field_1DC_menu_item.field_12_flashingAnimationFrame & 0xffff; + anim_frame2 = anim_frame & 0xffff; + + if (anim_frame2 != 0) + { + work->field_1DC_menu_item.field_12_flashingAnimationFrame--; + + if ((anim_frame2 & 3) > 1) + { + menu_sub_menu_update_8003DA0C(work, ot, &work->field_1DC_menu_item); + + if (((anim_frame2 & 3) == 3) && + (work->field_1DC_menu_item.field_0_current.field_0_id != GM_CurrentItemId) && + menu_item_IsItemDisabled_8003B6D0(work->field_1DC_menu_item.field_0_current.field_0_id) && + (DG_UnDrawFrameCount == 0)) + { + GM_SeSet2(0, 63, SE_ITEM_CURSOR); + break; + } + } + } + else + { + switched_weapon = 0; + if (menu_item_IsItemDisabled_8003B6D0(GM_CurrentItemId)) + { + last_id = GM_CurrentItemId; + GM_CurrentItemId = IT_None; + work->field_1DC_menu_item.field_12_flashingAnimationFrame = 19; + dword_800ABAD0 = last_id; + break; + } + + if (work->field_1DC_menu_item.field_0_current.field_0_id != GM_CurrentItemId) + { + switched_weapon = 1; + + if (work->field_1DC_menu_item.field_0_current.field_0_id != IT_None && + work->field_1DC_menu_item.field_0_current.field_0_id != IT_Card) + { + dword_800ABAD0 = work->field_1DC_menu_item.field_0_current.field_0_id; + } + + work->field_1DC_menu_item.field_0_current.field_0_id = GM_CurrentItemId; + } + + if (GM_CurrentItemId >= 0) + { + if (switched_weapon != 0) + { + sub_8003CFE0(menu_rpk_8003B5E0(GM_CurrentItemId), 0); + work->field_1DC_menu_item.field_11 = GM_CurrentItemId; + } + + work->field_1DC_menu_item.field_0_current.field_2_num = GM_Items[GM_CurrentItemId]; + menu_sub_menu_update_8003DA0C(work, ot, &work->field_1DC_menu_item); + } + } + break; + + case 2: + if (menu_8003D538()) + { + work->field_1DC_menu_item.field_10_state = 1; + } + + dword_800AB574 = 0; + + case 1: + pAlloc = work->field_1DC_menu_item.field_C_alloc; + pPanel = &pAlloc->field_20_array[pAlloc->field_0_main.field_4_selected_idx]; + + if (GM_GameStatus & GAME_FLAG_BIT_13) + { + dword_800AB578 = 0; + dword_800AB574 = 0; + } + + if ((pPanel->field_4_pos == 0) && (pPanel->field_0_id >= 0)) + { + if (dword_800AB578 == 0) + { + if (++dword_800AB574 == 4) + { + menu_item_printDescription_8003B614(pPanel->field_0_id); + dword_800AB578 = 1; + } + } + } + else + { + dword_800AB578 = 0; + dword_800AB574 = 0; + } + + if (dword_800AB578 != 0) + { + if (pPanel->field_0_id == IT_PalKey) + { + menu_drawPalKey_8003B794(work, ot, GM_ShapeKeyState); + } + + menu_drawDescriptionPanel_8003F9B4(work, ot, "EQUIP"); + } + + menu_8003D7DC(work, ot, &work->field_1DC_menu_item); + break; + + case 3: + if (sub_8003D568() != 0) + { + work->field_2A_state = MENU_CLOSED; + // Unpause the gameplay + GV_PauseLevel &= ~GV_PAUSE_MENU; + menu_8003BBEC(work); + } + else + { + menu_8003D7DC(work, ot, &work->field_1DC_menu_item); + } + break; + } +} + +/** + * @brief Use the selected consumable item (if possible) when pressing circle + * while the menu is open. + * + * @param work Pointer to the MenuWork actor. + * @param ot Pointer to the ordering table. + */ +void UseConsumableItem_8003C24C(Menu_Item_Unknown *pPanels, unsigned short press) +{ + PANEL *pPanel; + short heal_amount; + short item_amount; + + if (!(press & PAD_CIRCLE)) + { + return; + } + + pPanel = &pPanels->field_20_array[pPanels->field_0_main.field_4_selected_idx]; + + if (pPanel->field_2_num <= 0) + { + return; + } + + switch (pPanel->field_0_id) + { + case IT_Ketchup: + case IT_Ration: + if (GM_FrozenItemsState != 0) + { + GM_SeSet2(0, 63, SE_RATION_FROZEN); + return; + } + + if (GM_SnakeCurrentHealth == GM_SnakeMaxHealth) + { + GM_SeSet2(0, 63, SE_BUZZER); + return; + } + + if (pPanel->field_0_id == IT_Ration) + { + if (GM_DifficultyFlag == DIFFICULTY_VERY_EASY) + { + heal_amount = 1024; + } + else if (GM_DifficultyFlag == DIFFICULTY_EASY) + { + heal_amount = 384; + } + else + { + heal_amount = 256; + } + } + else + { + heal_amount = 64; + pPanel->field_0_id = IT_None; + GM_KetchupFlag = 0; + } + + GM_SnakeCurrentHealth += heal_amount; + GM_TotalRationsUsed++; + + if (GM_SnakeCurrentHealth > GM_SnakeMaxHealth) + { + GM_SnakeCurrentHealth = GM_SnakeMaxHealth; + } + + GM_SeSet2(0, 63, SE_RECOVER_LIFE); + break; + + case IT_ColdMedicine: + if (GM_StatusEvent & EV_CommonCold) // Snake has a cold :( + { + GM_StatusEvent &= ~EV_CommonCold; + GM_SnakeColdTimer = 0; + GM_SnakeColdUnk9A = 0; + } + + GM_SeSet2(0, 63, SE_ITEM_MEDICINE); + break; + + case IT_Diazepam: + GM_StatusEvent |= EV_Tranquilizer; + + if (GM_TranquilizerTimer < 0) + { + GM_TranquilizerTimer = 0; + } + + GM_TranquilizerTimer += 1200; + + GM_SeSet2(0, 63, SE_ITEM_MEDICINE); + break; + + case IT_TimerBomb: + if ((GM_PlayerStatus & (PLAYER_INTRUDE | PLAYER_SQUAT | PLAYER_GROUND | PLAYER_DAMAGED | PLAYER_DOWNED)) || + dword_8009F46C || menu_item_IsItemDisabled_8003B6D0(IT_TimerBomb)) + { + GM_SeSet2(0, 63, SE_BUZZER); + } + else + { + pPanel->field_0_id = IT_None; + GM_TimerBombFlag = IT_None; + GM_PlayerStatus |= PLAYER_TIMERBOMB_THROWN; + GM_SeSet2(0, 63, SE_MENU_EXIT); + } + return; + + default: + return; + } + + if (pPanel->field_0_id >= 0) + { + item_amount = GM_Items[pPanel->field_0_id]; + pPanel->field_2_num = item_amount - 1; + GM_Items[pPanel->field_0_id] = item_amount - 1; + } +} + +/** + * @brief Manages frozen items, PAL key temperature, and timer bomb. + * + * This function updates the state of as frozen items, the PAL key temperature, + * and the timer bomb based on the current game time and environment conditions. + */ +void UpdateEnvironmentalEffects_8003C4EC(void) +{ + BLAST_DATA blastData; + MATRIX mtx; + int time; + int speed; + int speed2; + + // If the game is paused, do nothing + if (GV_PauseLevel) + { + return; + } + // Use the time to avoid updating every frame and distribute the updates + time = GV_Time % 30; + switch (time) + { + case 0: // Rations temperature update + // Ignore if no rations are available + if (GM_RationFlag <= 0) + { + break; + } + + speed = GM_EnvironTemp; + // If unspecified, assume 10 as environment temperature + if (speed == 0) + { + speed = 10; + } + // If the player is holding a ration, assumes that the body temperature + // influences the speed of the freezing process + if (GM_CurrentItemId == IT_Ration) + { + if (speed > 0) + { + speed += GM_BodyTemp; + } + else + { + speed = GM_BodyTemp; + } + } + // calculate the current item's temperature + GM_FrozenItemsTemp += speed; + + // set the frozen state if the temperature is below the minimum + if (GM_FrozenItemsTemp < GM_FrozenItemsTempMin) + { + if (GM_FrozenItemsState == 0) + { + GM_FrozenItemsState = 1; + } + + GM_FrozenItemsTemp = GM_FrozenItemsTempMin; + } + + // if still below 0 keep it frozen + if (GM_FrozenItemsTemp < 0) + { + break; + } + // if the temperature is above frezeing point, unfreeze the item + if (GM_FrozenItemsState == 1) + { + GM_FrozenItemsState = 0; + + if (GM_CurrentItemId == IT_Ration || GM_CurrentItemId == IT_Ketchup) + { + GM_SeSet2(0, 63, SE_SIGNAL04); // Unfreeze sound (also used by Nikita) + } + } + // consumable items cannot heat up above 0 + GM_FrozenItemsTemp = 0; + break; + + case 1: + // Ignore if the PAL key is not in the inventory + if (GM_ShapeKeyFlag <= 0) + { + break; + } + + if (GM_EnvironTemp == 0) + { + if (GM_ShapeKeyState == SHAPE_STATE_NEUTRAL) + { + GM_ShapeKeyTemp = 0; + break; + } + // The temperature converges to 0 + if (GM_ShapeKeyTemp < 0) + { + speed2 = 10; + } + else + { + speed2 = -10; + } + } + else + { + speed2 = GM_EnvironTemp; + } + + GM_ShapeKeyTemp += speed2; + + // Update the state of the key based on its temperature + if (GM_ShapeKeyTemp > GM_ShapeKeyTempMax) + { + GM_ShapeKeyState = SHAPE_STATE_HOT; + GM_ShapeKeyTemp = GM_ShapeKeyTempMax; + break; + } + + if (GM_ShapeKeyTemp < GM_ShapeKeyTempMin) + { + GM_ShapeKeyState = SHAPE_STATE_COLD; + GM_ShapeKeyTemp = GM_ShapeKeyTempMin; + break; + } + + if (GM_ShapeKeyTemp >= 0 && GM_ShapeKeyState == SHAPE_STATE_COLD) + { + GM_ShapeKeyState = SHAPE_STATE_NEUTRAL; + break; + } + + if (GM_ShapeKeyTemp <= 0 && GM_ShapeKeyState == SHAPE_STATE_HOT) + { + GM_ShapeKeyState = SHAPE_STATE_NEUTRAL; + } + break; + + case 2: // Timer bomb update + // Ignore if the timer bomb is not in the inventory + if (GM_TimerBombFlag <= 0) + { + break; + } + + if (GM_GameStatus & + (STATE_GAME_OVER | STATE_PADMASK | STATE_PADRELEASE | STATE_PADDEMO | STATE_DEMO)) + { + break; + } + + if (GM_GameOverTimer != 0) + { + break; + } + + if (menu_item_IsItemDisabled_8003B6D0(IT_TimerBomb)) + { + break; + } + // Dectement the timer bomb and check if it has reached 0 + if (--GM_TimerBombFlag <= 0) + { + if (GM_PlayerStatus & PLAYER_INVINCIBLE) + { + GM_TimerBombFlag = 1; + } + else + { + // Explode by creating a blast actor at the player's position + blastData.field_0 = 1024; + blastData.field_4 = 5; + blastData.field_8_z = 1024; + blastData.field_C = 2000; + blastData.weapon = WP_Max; + + GM_CurrentMap = GM_PlayerMap; + + mtx = DG_ZeroMatrix; + mtx.t[0] = GM_PlayerPosition.vx; + mtx.t[1] = GM_PlayerPosition.vy; + mtx.t[2] = GM_PlayerPosition.vz; + NewBlast(&mtx, &blastData); + + GM_CurrentItemId = IT_None; + GM_StatusEvent |= EV_BlownUp; + } + } + else if (GM_CurrentItemId == IT_TimerBomb) + { + GM_SeSet2(0, 63, SE_TIMEBOMB_TICK); + } + break; + + case 3: // Snake's cold status update + if (GM_SnakeColdUnk9A < 0) + { + if (--GM_SnakeColdTimer < GM_SnakeColdUnk98) + { + GM_SnakeColdTimer = GM_SnakeColdUnk98; + GM_StatusEvent |= EV_CommonCold; + } + } + else + { + GM_SnakeColdTimer = 0; + } + + break; + + case 4: + GM_GameTimeSeconds++; + break; + } +} + +void menu_item_update_8003C95C(MenuWork *work, OT_TYPE *ot) +{ + GV_PAD *pPad = work->field_24_pInput; + Menu_Inventory *pLeftRight; + + // If not browsing items/weapons menu and not using Codec... + if (work->field_2A_state == MENU_CLOSED) + { + if (!(GM_GameStatus & (STATE_TAKING_PHOTO | STATE_MENU_OFF))) + { + // If the player is allowed to use items + if (!(GM_PlayerStatus & + (PLAYER_PAD_OFF | PLAYER_ITEM_DISABLE | PLAYER_MENU_DISABLE))) + { + // Menu button is pressed (L2) + if (menu_8003DA9C(&work->field_1DC_menu_item, pPad)) + { + if (menu_item_update_helper_8003BCD4(work)) + { + work->field_2A_state = MENU_LEFT_OPEN; + // Pause the gameplay while the menu is open + GV_PauseLevel |= GV_PAUSE_MENU; + } + } + // Quick item equip (L1) + else if (!(GM_GameStatus & GAME_FLAG_BIT_19) && (pPad->press & PAD_L1)) + { + int itemid = GM_CurrentItemId; + + // Unequip the current item if it is equipped + if (itemid >= 0) + { + GM_CurrentItemId = IT_None; + } + else if (!menu_item_IsItemDisabled_8003B6D0(work->field_1DC_menu_item.field_11)) + { + if (GM_Items[work->field_1DC_menu_item.field_11] > 0) + { + GM_CurrentItemId = work->field_1DC_menu_item.field_11; + } + } + + if (itemid != GM_CurrentItemId) + { + GM_SeSet2(0, 63, SE_ITEM_EQUIP); + } + } + } + } + else + { + return; + } + } + else if (work->field_2A_state == MENU_LEFT_OPEN) // ... else if browsing items menu... + { + pLeftRight = &work->field_1DC_menu_item; + + if (sub_8003DAFC(pLeftRight, pPad)) + { + work->field_1DC_menu_item.field_10_state = 3; + } + else if (sub_8003D52C() > 255) + { + menu_navigation_8003D6CC(pLeftRight, pPad); + UseConsumableItem_8003C24C(work->field_1DC_menu_item.field_C_alloc, pPad->press); + } + } + else if (work->field_2A_state != MENU_CODEC_OPEN) // ... else if not using Codec (i.e. browsing weapons menu)... + { + if (GM_CurrentItemId >= 0) + { + int ret = sub_8003D52C(); + if (ret < 255) + { + sub_8003DA60(work, ot, &work->field_1DC_menu_item, -ret / 4, 0); + work->field_1DC_menu_item.field_12_flashingAnimationFrame = 0; + } + } + else + { + work->field_1DC_menu_item.field_12_flashingAnimationFrame = 0; + } + return; + } + else // ... else using Codec. + { + return; + } + + menu_item_update_helper2_8003BF1C(work, ot); + UpdateEnvironmentalEffects_8003C4EC(); +} + +void sub_8003CB98(MenuWork *work) +{ + int field_0_item_id_idx; // $a0 + PANEL_TEXTURE *pItem; // $v0 + + menu_restore_nouse(); + field_0_item_id_idx = work->field_1DC_menu_item.field_0_current.field_0_id; + if (field_0_item_id_idx != IT_None || + (field_0_item_id_idx = work->field_1DC_menu_item.field_11, field_0_item_id_idx != IT_None)) + { + pItem = menu_rpk_8003B5E0(field_0_item_id_idx); + sub_8003CFE0(pItem, 0); + } +} + +void menu_item_init_8003CBF0(MenuWork *work) +{ + short val = -1; + + work->field_2C_modules[MENU_ITEM] = (TMenuUpdateFn)menu_item_update_8003C95C; + work->field_1DC_menu_item.field_0_current.field_0_id = val; + work->field_1DC_menu_item.field_10_state = 0; + work->field_1DC_menu_item.field_0_current.field_4_pos = 0; + work->field_1DC_menu_item.field_0_current.field_6_current = 1; + work->field_1DC_menu_item.field_11 = val; + work->field_28_flags |= 4; + dword_800ABAD0 = 0; + menu_set_panel_config_8003D6A8(&work->field_1DC_menu_item, 0, menu_item_helper_8003B8F0); + menu_sub_8003B568(); + sub_8003CB98(work); + menu_init_nouse(); +} + +void menu_item_kill_8003CC74(MenuWork *work) +{ + work->field_28_flags &= ~4u; +} + +void MENU_ResetItemPos(void) +{ + dword_800ABAD0 = 0; +} diff --git a/components/mgs/mgs/source/menu/jimaku.c b/components/mgs/mgs/source/menu/jimaku.c new file mode 100644 index 00000000..28a33c76 --- /dev/null +++ b/components/mgs/mgs/source/menu/jimaku.c @@ -0,0 +1,197 @@ +#include "menuman.h" + +#include +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "linkvar.h" + +void *SECTION(".sbss") MENU_JimakuTextBody; + +extern UnkJimakuStruct gUnkJimakuStruct_800BDA70; + +signed char dword_8009E76C[] = {-1, 0, 1, 0, 0, 1, 0, -1}; + +void menu_jimaku_act( MenuWork *work, OT_TYPE *ot ) +{ + TextConfig config; + int i; + SPRT *pSprt; + SPRT *pSprt2; + TILE *pTile; + DR_TPAGE *pTpage; + int y; + KCB *pFont; + + if ( work->field_2B & 0xFE ) + { + gUnkJimakuStruct_800BDA70.field_38_str = NULL; + gUnkJimakuStruct_800BDA70.field_0_active = 0; + return; + } + + if ( gUnkJimakuStruct_800BDA70.field_0_active == 1 ) + { + if ( (gUnkJimakuStruct_800BDA70.field_1_type != 0) || + !(GM_OptionFlag & OPTION_CAPTION_OFF) ) + { + NEW_PRIM(pSprt, work); + LSTORE(0x808080, &pSprt->r0); + LCOPY(&gUnkJimakuStruct_800BDA70.field_4_x, &pSprt->x0); + LCOPY(&gUnkJimakuStruct_800BDA70.field_8_w, &pSprt->w); + setClut(pSprt, 960, 510); + setUV0(pSprt, 0, 0); + setSprt(pSprt); + addPrim(ot, pSprt); + + for (i = 0; i < 8; i += 2) + { + NEW_PRIM(pSprt2, work); + *pSprt2 = *pSprt; + LSTORE(0x64000000, &pSprt2->r0); + pSprt2->x0 += dword_8009E76C[i]; + pSprt2->y0 += dword_8009E76C[i + 1]; + addPrim(ot, pSprt2); + } + } + + if ( gUnkJimakuStruct_800BDA70.field_1_type == 1 ) + { + config.color = 0x64C8C8C8; + config.flags = 0x12; + config.xpos = 160; + config.ypos = 80; + + _menu_number_draw_string2( work->prim, &config, "PAUSE" ); + pTile = menu_render_rect_8003DB2C( work->prim, 0, 0, FRAME_WIDTH, FRAME_HEIGHT, 0 ); + setSemiTrans(pTile, 1); + } + + NEW_PRIM(pTpage, work); + setDrawTPage(pTpage, 1, 0, getTPage(0, 0, 960, 256)); + addPrim(ot, pTpage); + + if ( gUnkJimakuStruct_800BDA70.field_2_timer == -1 ) + { + if ( GV_PadData[0].press & PAD_CIRCLE ) + { + gUnkJimakuStruct_800BDA70.field_0_active = 0; + + if ( gUnkJimakuStruct_800BDA70.field_40 != 0 ) + { + gUnkJimakuStruct_800BDA70.field_38_str = GCL_GetString( gUnkJimakuStruct_800BDA70.field_40 ); + gUnkJimakuStruct_800BDA70.field_40 = GCL_NextStr(); + return; + } + + GV_PauseLevel &= ~GV_PAUSE_STOP; + DG_RestartMainChanlSystem(); + gUnkJimakuStruct_800BDA70.field_2_timer = 1; + + if ( gUnkJimakuStruct_800BDA70.field_3C != -1 ) + { + GCL_ExecProc( gUnkJimakuStruct_800BDA70.field_3C, 0 ); + } + } + } + else if ((gUnkJimakuStruct_800BDA70.field_2_timer > 0) && (--gUnkJimakuStruct_800BDA70.field_2_timer <= 0)) + { + gUnkJimakuStruct_800BDA70.field_0_active = 0; + } + } + + if ( gUnkJimakuStruct_800BDA70.field_38_str != NULL ) + { + y = (gUnkJimakuStruct_800BDA70.field_1_type == 1 ) ? 112 : 204; + pFont = &gUnkJimakuStruct_800BDA70.field_C_font; + + font_print_string( pFont, gUnkJimakuStruct_800BDA70.field_38_str ); + font_update( pFont ); + font_clut_update( pFont ); + + gUnkJimakuStruct_800BDA70.field_8_w = 256; + gUnkJimakuStruct_800BDA70.field_38_str = NULL; + gUnkJimakuStruct_800BDA70.field_0_active = 1; + gUnkJimakuStruct_800BDA70.field_4_x = (FRAME_WIDTH - pFont->max_width) / 2; + gUnkJimakuStruct_800BDA70.field_A_h = pFont->max_height; + gUnkJimakuStruct_800BDA70.field_6_y = y - (pFont->max_height / 2); + } +} + +STATIC void menu_jimaku_init_helper(KCB *kcb) +{ + RECT rect; + setRECT(&rect, 960, 256, 64, 38); + + font_init_kcb(kcb, &rect, 960, 510); + font_set_kcb(kcb, -1, -1, 0, 6, 2, 0); + printf("jimaku_font_buffer_size %d\n", font_get_buffer_size(kcb)); + MENU_JimakuTextBody = GV_AllocResidentMemory(font_get_buffer_size(kcb)); + font_set_buffer(kcb, MENU_JimakuTextBody); + font_set_color(kcb, 0, 0x6739, 0); + font_clut_update(kcb); +} + +void menu_jimaku_init(MenuWork *work) +{ + menu_jimaku_init_helper(&gUnkJimakuStruct_800BDA70.field_C_font); +} + +void MENU_JimakuWrite(char *str, int frames) +{ + gUnkJimakuStruct_800BDA70.field_0_active = 0; + gUnkJimakuStruct_800BDA70.field_2_timer = frames; + gUnkJimakuStruct_800BDA70.field_38_str = str; + + if (frames > 10000) + { + gUnkJimakuStruct_800BDA70.field_1_type = 2; + } + else + { + gUnkJimakuStruct_800BDA70.field_1_type = 0; + } +} + +void MENU_JimakuClear(void) +{ + gUnkJimakuStruct_800BDA70.field_0_active = 0; + gUnkJimakuStruct_800BDA70.field_C_font.flag &= ~0x80; +} + +void MENU_AreaNameWrite(char *areaName) +{ + MENU_JimakuWrite(areaName, 0); + gUnkJimakuStruct_800BDA70.field_1_type = 1; +} + +void NewJimakuStr(char *str, int int_1) +{ + MENU_JimakuWrite(str, -1); + GV_PauseLevel |= GV_PAUSE_STOP; + gUnkJimakuStruct_800BDA70.field_3C = int_1; + DG_FreeObjectQueue(); +} + +void NewJimaku(void) +{ + char *str; + + str = GCL_GetString(GCL_NextStr()); + gUnkJimakuStruct_800BDA70.field_40 = GCL_NextStr(); + MENU_JimakuWrite(str, -1); + + if (GCL_GetOption('e')) + { + gUnkJimakuStruct_800BDA70.field_3C = GCL_StrToInt(GCL_NextStr()); + } + + else + { + gUnkJimakuStruct_800BDA70.field_3C = -1; + } + + GV_PauseLevel |= GV_PAUSE_STOP; + DG_FreeObjectQueue(); +} diff --git a/components/mgs/mgs/source/menu/life.c b/components/mgs/mgs/source/menu/life.c new file mode 100644 index 00000000..9a77ae35 --- /dev/null +++ b/components/mgs/mgs/source/menu/life.c @@ -0,0 +1,579 @@ +#include "menuman.h" + +#include "common.h" +#include "libgcl/libgcl.h" +#include "libdg/libdg.h" +#include "game/game.h" +#include "linkvar.h" +#include "font/font.h" +#include "menu/menuman.h" +#include "menu/radio.h" + +MENU_BAR_CONF gSnakeLifeBarConfig_8009E5F4 = {"LIFE", {0x10, 0x8F, 0x7F}, {0x1F, 0xDF, 0x3F}, 0}; +MENU_BAR_CONF gSnakeO2BarConfig_8009E600 = {"O2", {0x1F, 0x3F, 0xC0}, {0x1F, 0x7F, 0xFF}, 1}; + +STATIC int gTakeDamageCounter_800AB5FC = 0; + +STATIC RECT rect_800AB600 = {960, 256, 50, 80}; + +STATIC short coords_800AB600[2][2] = {{86, 72}, {44, 72}}; + +int SECTION(".sbss") dword_800ABAEC; +int SECTION(".sbss") gSnakeLifeYPos_800ABAF0; +int SECTION(".sbss") dword_800ABAF4; + +/** + * @brief Updates the displayed health bar value to match the actual health of Snake. + * + * This function gradually updates the displayed health bar value (`displayedHp`) to match + * the actual health (`GM_SnakeCurrentHealth`). It uses a delay counter to control the speed + * of the update, making the health bar decrease more slowly when Snake takes damage. + * + * @param pBars Pointer to the MenuMan_MenuBars structure containing the health bar state. + * @return 1 if the displayed health was updated, 0 otherwise. + */ +int menu_life_update_helper_8003ECCC(MenuMan_MenuBars *pBars) +{ + short displayedHp; + + displayedHp = pBars->field_6_snake_hp; + + if (displayedHp == GM_SnakeCurrentHealth) + { + pBars->health_delay_counter = 10; + return 0; + } + else + { + if (GM_SnakeCurrentHealth < displayedHp) + { + if (pBars->health_delay_counter == 0) + { + // try to decrease the displayed health faster + pBars->field_6_snake_hp = displayedHp - 64; + } + else + { + --pBars->health_delay_counter; + } + } + // if we overshhoot the displayed health, just match real health + if (pBars->field_6_snake_hp < GM_SnakeCurrentHealth) + { + pBars->field_6_snake_hp = GM_SnakeCurrentHealth; + } + return 1; + } +} + +/** + * @brief Draws a status bar (such as a health or oxygen bar) on the screen. + * + * This function renders a status bar with a gradient fill, displaying the + * current value (`now`) relative to the maximum value (`max`). + * It also handles the visualization of the portion of the bar that will be + * removed (`rest`) in red if the character is taking damage. + * The function takes into account various game states and configurations to + * determine the appearance and behavior of the bar. + * + * @param prim Pointer to the structure containing the primitive. + * @param x The x-coordinate of the bar's position. + * @param y The y-coordinate of the bar's position. + * @param rest Value for portion of the bar that will be removed. + * @param now The current value to be displayed on the bar. + * @param max The maximum value of the bar. + * @param bconf Pointer to the bar's configuration (such as text and colors). + */ +void menu_draw_bar(MenuPrim *prim, long x, long y, long rest, long now, long max, MENU_BAR_CONF *bconf) +{ + TextConfig text_config; + int scaled_max_val; + int sp2C; + u_long *ot; + TILE *pTile; + TILE *pTile_2; + TILE *pTile_3; + POLY_G4 *pPoly; + DR_TPAGE *pTpage; + int y_with_offset; + int x_text; + int diff; + int width; + + if (GM_GameStatus & (STATE_TAKING_PHOTO | STATE_LIFEBAR_OFF | STATE_DEMO)) + { + return; + } + + ot = prim->ot; + + sp2C = 5 - bconf->height; + y_with_offset = y + 1; + scaled_max_val = max / 8; + + // offset text by 4 pixels to the right + text_config.xpos = x + 4; + x_text = text_config.xpos; + + text_config.flags = 0; + // offset text by 4 pixels down + text_config.ypos = y + 4; + + if ( !((int)bconf & 0x80000000) ) + { + // Red flashing text when taking smoking or O2 damage + text_config.color = 0x643030FF; + } + else + { + // White color for everything else + text_config.color = 0x64FFFFFF; + } + + _menu_number_draw_string(prim, &text_config, bconf->name); + + width = text_config.xpos - x_text + 2; + pTile = menu_render_rect_8003DB2C(prim, x_text - 1, text_config.ypos - 1, width, 7, 0); + setSemiTrans(pTile, 1); + + if (rest > max) + { + rest = max; + } + + // if snake is taking damage, draw in red the part of the health that will + // be removed + if (rest > now) + { + _NEW_PRIM(pTile_2, prim); + + setTile(pTile_2); + + diff = (rest - now) / 8; + + pTile_2->x0 = x + now / 8; + pTile_2->y0 = y_with_offset; + + pTile_2->w = diff; + pTile_2->h = sp2C; + + setRGB0(pTile_2, 200, 0, 0); + + addPrim(ot, pTile_2); + } + + _NEW_PRIM(pPoly, prim); + + setXYWH(pPoly, x, y_with_offset, (now + 7) / 8, sp2C); + + // Set the color of the bar as gradient fill. The color is interpolated + // between the left and right color. + setRGB0(pPoly, bconf->left[0], bconf->left[1], bconf->left[2]); + setRGB1(pPoly, + (bconf->left[0] + ((bconf->right[0] - bconf->left[0]) * (pPoly->x1 - pPoly->x0)) / 128), + (bconf->left[1] + ((bconf->right[1] - bconf->left[1]) * (pPoly->x1 - pPoly->x0)) / 128), + (bconf->left[2] + ((bconf->right[2] - bconf->left[2]) * (pPoly->x1 - pPoly->x0)) / 128)); + setRGB2(pPoly, + (bconf->left[0] + ((bconf->right[0] - bconf->left[0]) * (pPoly->x2 - pPoly->x0)) / 128), + (bconf->left[1] + ((bconf->right[1] - bconf->left[1]) * (pPoly->x2 - pPoly->x0)) / 128), + (bconf->left[2] + ((bconf->right[2] - bconf->left[2]) * (pPoly->x2 - pPoly->x0)) / 128)); + setRGB3(pPoly, + (bconf->left[0] + ((bconf->right[0] - bconf->left[0]) * (pPoly->x3 - pPoly->x0)) / 128), + (bconf->left[1] + ((bconf->right[1] - bconf->left[1]) * (pPoly->x3 - pPoly->x0)) / 128), + (bconf->left[2] + ((bconf->right[2] - bconf->left[2]) * (pPoly->x3 - pPoly->x0)) / 128)); + + setPolyG4(pPoly); + addPrim(ot, pPoly); + + pTile_3 = menu_render_rect_8003DB2C(prim, x, y_with_offset, scaled_max_val, sp2C, 0x181800); + setSemiTrans(pTile_3, 1); + + menu_render_rect_8003DB2C(prim, x - 1, y, 1, sp2C + 2, 0); + menu_render_rect_8003DB2C(prim, x, y, scaled_max_val, 1, 0); + menu_render_rect_8003DB2C(prim, x, y + sp2C + 1, scaled_max_val, 1, 0); + menu_render_rect_8003DB2C(prim, x + scaled_max_val, y, 1, sp2C + 2, 0); + + _NEW_PRIM(pTpage, prim); + + setDrawTPage(pTpage, 1, 1, getTPage(0, 0, 960, 256)); + addPrim(prim->ot, pTpage); +} + +/** + * @brief Draws the life and oxygen bars for Snake. + * + * This function updates the position and state of the life bar and oxygen bar + * for Snake. It handles the visualization of damage taken by changing the color + * of the life bar text to red when Snake takes damage. + * + * @param prim Pointer to the MenuPrim structure containing the primitive. + * @param pBars Pointer to the MenuMan_MenuBars structure containing the bar states. + */ +void menu_life_update_helper2_8003F30C(MenuPrim *prim, MenuMan_MenuBars *pBars) +{ + MENU_BAR_CONF *pBar; + + pBar = &gSnakeLifeBarConfig_8009E5F4; + gSnakeLifeYPos_800ABAF0 = pBars->field_4_bar_y; + + // Reset the flag and start the damage counter if we taken damage + if ((GM_GameStatus & STATE_DAMAGED) != 0) + { + gTakeDamageCounter_800AB5FC = 8; + GM_GameStatus &= ~STATE_DAMAGED; + } + + // If the damage counter is active, decrement it and set the bar to render + // in red + if (gTakeDamageCounter_800AB5FC > 0) + { + --gTakeDamageCounter_800AB5FC; + pBar = UNTAG_PTR(MENU_BAR_CONF, pBar); // pointer flag to make it render in red + } + + menu_draw_bar(prim, + pBars->field_2_bar_x, + pBars->field_4_bar_y, + pBars->field_6_snake_hp, + GM_SnakeCurrentHealth, + GM_SnakeMaxHealth, + pBar); + + // If the oxygen bar is not full then draw it + if (pBars->field_1_O2_hp) + { + menu_draw_bar(prim, + pBars->field_2_bar_x, + pBars->field_4_bar_y + 12, + GM_O2, + GM_O2, + 1024, + &gSnakeO2BarConfig_8009E600); + } +} + +void draw_life_defaultX_8003F408(MenuPrim *prim, long y, long rest, long now, long max, MENU_BAR_CONF *bconf) +{ + GM_GameStatus |= STATE_SHOW_LIFEBAR; + menu_draw_bar(prim, + 16, + y + gSnakeLifeYPos_800ABAF0 - 16, + rest, + now, + max, + bconf); +} + +void draw_life_8003F464(MenuPrim *prim, long x, long y, long rest, long now, long max, MENU_BAR_CONF *bconf) +{ + GM_GameStatus |= STATE_SHOW_LIFEBAR; + menu_draw_bar(prim, + x, + y + gSnakeLifeYPos_800ABAF0 - 16, + rest, + now, + max, + bconf); +} + +void draw_player_life_8003F4B8(MenuPrim *prim, long x, long y) +{ + GM_GameStatus |= STATE_SHOW_LIFEBAR; + menu_draw_bar(prim, + x, + y, + GM_SnakeCurrentHealth, + GM_SnakeCurrentHealth, + GM_SnakeMaxHealth, + &gSnakeLifeBarConfig_8009E5F4); +} + +void menu_font_kill_helper_8003F50C(void) +{ + GM_GameStatus &= ~STATE_SHOW_LIFEBAR; +} + + + +/** + * @brief Display the life bar and oxygen bar according to the bar state. + * + * This function updates the position and visibility of the life bar and oxygen + * bar based on the current state of the game and the player's health. + * + * @param work Pointer to the MenuWork structure containing the bar states. + * @param unused + */ +void menu_life_update_8003F530(MenuWork *work, OT_TYPE *ot) +{ + int updated; + MenuMan_MenuBars *pBars; + int state; + + updated = menu_life_update_helper_8003ECCC(&work->field_204_bars); + pBars = &work->field_204_bars; + state = work->field_2A_state; + + // if the oxygen is not full + if (GM_O2 < 1024) + { + updated = 1; + pBars->field_1_O2_hp = -106; + } + if (state) + { + return; + } + + if (GM_GameStatus & STATE_HIDE_LIFEBAR) + { + pBars->field_0_state = BAR_STATE_MOVING_UP; + } + + if ((pBars->field_0_state == BAR_STATE_HIDDEN || pBars->field_0_state == BAR_STATE_MOVING_UP) && + (updated || GM_GameStatus & STATE_SHOW_LIFEBAR || (GM_SnakeMaxHealth / 2) >= GM_SnakeCurrentHealth)) + { + // hide the bar by moving it off screen + if (pBars->field_0_state == BAR_STATE_HIDDEN) + { + pBars->field_4_bar_y = -48; + } + + pBars->field_0_state = BAR_STATE_MOVING_DOWN; + gTakeDamageCounter_800AB5FC = 0; + } + + if (pBars->field_0_state == BAR_STATE_HIDDEN) + { + return; + } + + switch (pBars->field_0_state) + { + case BAR_STATE_MOVING_DOWN: + // move the life bar down + pBars->field_4_bar_y += 8; + + // if the bar is fully visible then move to state 2 + if (pBars->field_4_bar_y >= 16) + { + // saturate the vertical position of the bar + pBars->field_4_bar_y = 16; + pBars->field_0_state = BAR_STATE_VISIBLE; + pBars->field_8_hide_bar_delay_counter = 150; + } + break; + + case BAR_STATE_MOVING_UP: + // we need to slowly hide the bars + // reduce the vertical position of the bars by 8 + pBars->field_4_bar_y -= 8; + + // if the bars are off finally screen + if (pBars->field_4_bar_y < -47) + { + pBars->field_0_state = BAR_STATE_HIDDEN; + pBars->field_4_bar_y = -48; + + if (GM_GameStatus & STATE_HIDE_LIFEBAR) + { + GM_GameStatus = (GM_GameStatus & ~STATE_HIDE_LIFEBAR) | STATE_LIFEBAR_OFF; + } + // if oxygen is full then hide the oxygen bar + if (GM_O2 == 1024) + { + pBars->field_1_O2_hp = 0; + } + + return; + } + break; + + case BAR_STATE_VISIBLE: + if (updated || (GM_SnakeMaxHealth / 2) >= GM_SnakeCurrentHealth || GM_GameStatus & STATE_SHOW_LIFEBAR) + { + pBars->field_8_hide_bar_delay_counter = 150; + // If the oxigen bar is not hidden then decrease the oxygen bar + if (pBars->field_1_O2_hp) + { + pBars->field_1_O2_hp--; + } + } + // Keep the bars visible until the delay is over + else if (--pBars->field_8_hide_bar_delay_counter <= 0) + { + pBars->field_0_state = BAR_STATE_MOVING_UP; + } + break; + + case BAR_STATE_FORCE_HIDE: + pBars->field_0_state = BAR_STATE_HIDDEN; + pBars->field_4_bar_y = -48; + break; + } + + menu_life_update_helper2_8003F30C(work->prim, pBars); +} + +/** + * @brief Initialize the bars menu module. + * Set the update function for the bars module and initialize the default state. + * + * @param work Pointer to the Menu actor. + */ +void menu_life_init_8003F7E0(MenuWork *work) +{ + MenuMan_MenuBars *pBar; + + work->field_2C_modules[MENU_LIFE] = menu_life_update_8003F530; + // Set initialized flag + work->field_28_flags |= 1; + + pBar = &work->field_204_bars; + pBar->field_6_snake_hp = GM_SnakeCurrentHealth; + pBar->health_delay_counter = 10; + pBar->field_2_bar_x = 16; + pBar->field_0_state = BAR_STATE_HIDDEN; + // start with the bars off screen + pBar->field_4_bar_y = -48; + pBar->field_1_O2_hp = 0; + gSnakeLifeYPos_800ABAF0 = -48; +} + +/** + * @brief Deinitialize the bars menu module by clearing the initialized flag. + * + * @param work Pointer to the Menu actor. + */ +void menu_life_kill_8003F838(MenuWork *work) +{ + // Clear the initialized flag + work->field_28_flags &= ~1u; +} + +//below may be separate to life but draws it in one function +extern SPRT gMenuSprt_800bd998; +extern KCB font_800BD968; + +int sub_8003F84C(int idx) +{ + void *font_buffer; + + setSprt(&gMenuSprt_800bd998); + + gMenuSprt_800bd998.u0 = 0; + gMenuSprt_800bd998.v0 = rect_800AB600.y; + + gMenuSprt_800bd998.r0 = 128; + gMenuSprt_800bd998.g0 = 128; + gMenuSprt_800bd998.b0 = 128; + + gMenuSprt_800bd998.w = 200; + gMenuSprt_800bd998.h = 80; + + // Callers to this function call it with idx = 0 or idx = 1 + gMenuSprt_800bd998.x0 = coords_800AB600[idx][0]; + gMenuSprt_800bd998.y0 = coords_800AB600[idx][1]; + gMenuSprt_800bd998.clut = 0x7fbc; + + ClearImage(&rect_800AB600, 0, 0, 0); + + font_init_kcb(&font_800BD968, &rect_800AB600, 960, 510); + font_set_kcb(&font_800BD968, -1, -1, 0, 6, 2, 0); + + font_buffer = GV_Malloc(font_get_buffer_size(&font_800BD968)); + if (font_buffer == NULL) + { + return 0; + } + + font_set_buffer(&font_800BD968, font_buffer); + font_set_color(&font_800BD968, 0, 0x6739, 0); + font_clut_update(&font_800BD968); + return 1; +} + +void menu_printDescription_8003F97C(char *description) +{ + font_print_string(&font_800BD968, description); + font_update(&font_800BD968); +} + +// When scrolling items/weapons menu, draws the life bar, the text "EQUIP" or +// "WEAPON" and the background rectangle around the item/weapon description. +void menu_drawDescriptionPanel_8003F9B4(MenuWork *work, OT_TYPE *ot, const char *str) +{ + POLY_F4 *polyF4; + TILE *tile; + SPRT *sprt; + int i; + int w; + + // Variables storing temporary X coordinate calculations + // It's required to get a match... + int x0, x1, x2, x3, x4; + + work->field_2B |= 2; + + NEW_PRIM(sprt, work); + + *sprt = gMenuSprt_800bd998; + addPrim(ot, sprt); + + x0 = gMenuSprt_800bd998.x0; + x3 = x0 - 10; + w = gMenuSprt_800bd998.w + 10; + x1 = x0; + + x4 = menu_number_draw_string(work, ot, x1 - 8, gMenuSprt_800bd998.y0 - 7, str, 0); + draw_player_life_8003F4B8(work->prim, x3, 24); + + i = 0; + x0 = 12; + x0 = x1 - x0; + x2 = x3 + (x4 - x0); + + for (; i < 2; i++) + { + NEW_PRIM(polyF4, work); + + LSTORE(0, &polyF4->r0); + polyF4->x0 = x3; + polyF4->y0 = gMenuSprt_800bd998.y0 - 9; + polyF4->x1 = x2; + polyF4->y1 = gMenuSprt_800bd998.y0 - 9; + polyF4->x2 = x3; + polyF4->y2 = gMenuSprt_800bd998.y0; + polyF4->x3 = x2; + polyF4->y3 = gMenuSprt_800bd998.y0; + polyF4->x3 += 10; + + setPolyF4(polyF4); + setSemiTrans(polyF4, 1); + addPrim(ot, polyF4); + + NEW_PRIM(tile, work); + + LSTORE(0, &tile->r0); + tile->x0 = x3; + tile->y0 = gMenuSprt_800bd998.y0; + tile->w = w; + tile->h = gMenuSprt_800bd998.h; + + setTile(tile); + setSemiTrans(tile, 1); + addPrim(ot, tile); + } +} + +extern MenuWork gMenuWork_800BD360; + +void menu_font_kill_8003FC0C(void) +{ + void *ptr; + + gMenuWork_800BD360.field_2B &= ~2; + menu_font_kill_helper_8003F50C(); + ptr = font_get_buffer_ptr(&font_800BD968); + GV_Free(ptr); +} diff --git a/components/mgs/mgs/source/menu/menuman.c b/components/mgs/mgs/source/menu/menuman.c new file mode 100644 index 00000000..c85b721f --- /dev/null +++ b/components/mgs/mgs/source/menu/menuman.c @@ -0,0 +1,360 @@ +#define __MENU_MENUMAN_C__ +/* Draw the codec conversations (the Colonel calls) rather than answering them + * invisibly. + * + * Held at 0. Turning it on is a one-flag experiment and it has been run: the + * face group asks for 417792 bytes -- 408 KB out of GV_PACKET_MEMORY0, not the + * 150 KB the code's own comment suggests -- and the allocation fails on this + * board. With the real module in charge and the headless drain off, the call + * is then never answered at all, so the story stops: s00a stays on its first + * pass and the dock is never built (4 objects queued instead of 3000). + * + * The memory half is now solved -- the group loads into a PSRAM reserve (see + * port/psyz_port.c) and 417792 bytes arrive without a crash. What is still + * missing is the INDEX: the start sector handed to FS_LoadFileRequest comes + * back as 15658670, and FACE.DAT is 3.5 MB, about 1700 sectors. That value is + * uninitialised memory, so the table mapping a call to its position in the + * file has not been read yet. Faces parsed: 0. + * + * Held at 0 until that index is loaded, because with the module in charge and + * the headless drain off, a call that cannot draw is a call that is never + * answered -- and then s00a never gets past its first pass. */ +#ifndef MGS_CODEC_SCREEN +#define MGS_CODEC_SCREEN 0 +#endif + +#include "menuman.h" + +#include +#include "common.h" +#include "radar.h" +#include "mts/mts.h" // for fprintf +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" + +extern MenuWork gMenuWork_800BD360; +extern unsigned char menu_primbuffers[2][8192]; + +extern int MENU_PrimUse; + +extern GV_PAD *GM_CurrentPadData; +extern GV_PAD *GM_CurrentPadData; + +void menu_texture_init_8003CC94(MenuWork *work); +void menu_radar_init_8003B474(MenuWork *work); +void menu_radio_init(MenuWork *work); +void menu_item_init_8003CBF0(MenuWork *work); +void menu_weapon_init_8003EC2C(MenuWork *work); +void menu_life_init_8003F7E0(MenuWork *work); +void menu_number_init(MenuWork *work); +void menu_jimaku_init(MenuWork *work); + +TInitKillFn gMenuInitFns_8009E290[] = { + menu_texture_init_8003CC94, + menu_radar_init_8003B474, + menu_radio_init, + menu_item_init_8003CBF0, + menu_weapon_init_8003EC2C, + menu_life_init_8003F7E0, + menu_number_init, + menu_jimaku_init, + NULL}; + +void menu_radar_kill_8003B554(MenuWork *work); +void menu_radio_kill(MenuWork *work); +void menu_item_kill_8003CC74(MenuWork *work); +void menu_weapon_kill_8003ECAC(MenuWork *work); +void menu_life_kill_8003F838(MenuWork *work); +void menu_number_kill(MenuWork *work); + +TInitKillFn gMenuKillFns_8009E2B4[] = { + menu_radar_kill_8003B554, + menu_radio_kill, + menu_item_kill_8003CC74, + menu_weapon_kill_8003ECAC, + menu_life_kill_8003F838, + menu_number_kill, + NULL}; + +MenuPrim menu_prim = {0, 0, 0, {0, 0}}; +TextConfig gMenuTextConfig_8009E2E4 = {0, 0, 0, 0x64808080}; + +void menuman_act_800386A4(MenuWork *work) +{ + OT_TYPE *pOtStart; + /* This used to return immediately under __psyz, which switched off the + * whole HUD -- the soliton radar included. The reason given was that some + * drawer still emitted hand-packed PSX tags that desynced the GPU stream, + * but nothing in source/menu/ builds a tag that way under __psyz any more: + * the only two such idioms left are in an #else arm. The stub outlived the + * problem it was written for. */ + int idx_as_flag; + int field_28_flags; + int i; + + pOtStart = (&menu_prim)->ot; + work->field_24_pInput = &GM_CurrentPadData[2]; + menu_jimaku_act(work, pOtStart); + if ( ( !(GV_PauseLevel & GV_PAUSE_PAUSE) && (GM_LoadComplete > 0) ) && + ( !GM_LoadRequest ) ) + { + idx_as_flag = 1; + if (GM_GameStatus >= 0) + { +#ifdef __psyz + /* MGS_CODEC_SCREEN picks which of the two answers a call. + * + * 0: the headless drain -- marks the call answered and dispatches + * its procedure, so the story advances, but nothing is drawn. + * That is what shipped while the face path was known to crash. + * 1: the real radio module, enabled in the mask below. It walks + * FACE.DAT and draws the conversation. Both at once would run + * each call's procedure twice, so it is one or the other. */ + if (!MGS_CODEC_SCREEN) + { + void MENU_RadioDrainHeadless(void); + MENU_RadioDrainHeadless(); + } +#endif + field_28_flags = work->field_28_flags; +#ifdef __psyz + /* Bring the HUD back one drawer at a time rather than all eight. + * + * Restoring the lot at once crashed immediately, but not in a + * drawer: MENU_RADIO's codec task walks FACE.DAT and took a null + * pointer in radioanim.c:145. That is a whole separate subsystem + * (the codec faces) and nothing the player needs to move around, + * so it stays off while the radar -- which is what tells you where + * you are -- comes back. Widen this mask as each one is proven. */ + field_28_flags &= (1 << MENU_RADAR) | + (MGS_CODEC_SCREEN ? (1 << MENU_RADIO) : 0); +#endif + for (i = 0; i < MENU_MODULE_MAX; i++) + { + if ((field_28_flags & idx_as_flag) != 0) + { + work->field_2C_modules[i](work, pOtStart); + } + idx_as_flag *= 2; + } + } + } + + addPrim(pOtStart, &work->field_4C_drawEnv[GV_Clock]); +} + +void menuman_kill_800387E8(MenuWork *work) +{ + TInitKillFn *pIter; + + pIter = gMenuKillFns_8009E2B4; + while (*pIter) + { + (*pIter)(work); + pIter++; + } + + menu_viewer_kill(work); +} + +void menu_init_subsystems_8003884C(MenuWork *work) +{ + TInitKillFn *pIter; + DRAWENV drawEnv; + + work->field_2A_state = MENU_CLOSED; + work->field_29 = 0; + work->field_28_flags = 0; + + work->prim = &menu_prim; + + menu_prim.buf[0] = menu_primbuffers[0]; + menu_prim.buf[1] = menu_primbuffers[1]; + + DG_InitDrawEnv(&drawEnv, 0, 0, FRAME_WIDTH, FRAME_HEIGHT); + drawEnv.isbg = 0; + drawEnv.tpage = 31; + SetDrawEnv(&work->field_4C_drawEnv[0], &drawEnv); + + DG_InitDrawEnv(&drawEnv, 320, 0, FRAME_WIDTH, FRAME_HEIGHT); + drawEnv.isbg = 0; + drawEnv.tpage = 31; + SetDrawEnv(&work->field_4C_drawEnv[1], &drawEnv); + + menu_rpk_init_8003DD1C("item"); + + pIter = &gMenuInitFns_8009E290[0]; + while (*pIter) + { + (*pIter)(work); + pIter++; + } + + menu_viewer_init(work); +} + +void menuman_init_80038954(void) +{ + GV_SetNamedActor(&gMenuWork_800BD360.actor, menuman_act_800386A4, + menuman_kill_800387E8, "menuman.c"); + menu_init_subsystems_8003884C(&gMenuWork_800BD360); + MENU_InitRadioTable(); +} + +void menuman_Reset(void) +{ + MENU_ResetCall(); + MENU_ClearRadioTable(); + MENU_SetRadarScale(4096); + MENU_SetRadarFunc(NULL); + gMenuWork_800BD360.field_CC_radar_data.prev_mode = 0; + gMenuWork_800BD360.field_CC_radar_data.counter = 0; + gMenuWork_800BD360.field_2B = 0; + gMenuWork_800BD360.field_1DC_menu_item.field_12_flashingAnimationFrame = 0; + gMenuWork_800BD360.field_1F0_menu_weapon.field_12_flashingAnimationFrame = 0; + menu_life_init_8003F7E0(&gMenuWork_800BD360); +} + +void MENU_ResetTexture(void) +{ + menu_weapon_unknown_8003DEB0(); +} + +void MENU_StartDeamon(void) +{ + GV_InitActor(GV_ACTOR_MANAGER, &gMenuWork_800BD360.actor, NULL); + GV_SetNamedActor(&gMenuWork_800BD360.actor, NULL, NULL, "menuman.c"); +} + +void menu_radio_update_helper_80038A6C(void) +{ + gMenuWork_800BD360.field_CC_radar_data.display_flag = 1; +} + +void menu_radio_update_helper2_80038A7C(void) +{ + gMenuWork_800BD360.field_CC_radar_data.display_flag = 0; +} + +void MENU_ResetSystem(void) +{ + if (menu_prim.next > menu_prim.end) + { + fprintf(-1, "!!!! MENU PRIM OVER !!!!\n"); + } + + MENU_PrimUse = menu_prim.next - menu_prim.buf[1 - GV_Clock]; + + menu_prim.next = menu_prim.buf[GV_Clock]; + menu_prim.end = menu_prim.next + 0x2000; + menu_prim.ot = DG_ChanlOTag(1); + MENU_ResetText(); +} + +void MENU_Locate(int xpos, int ypos, int flags) +{ + TextConfig *config = &gMenuTextConfig_8009E2E4; + + config->xpos = xpos; + config->ypos = ypos; + config->flags = flags; +} + +void MENU_Color(int r, int g, int b) +{ + unsigned int color; + unsigned int code; + TextConfig *config = &gMenuTextConfig_8009E2E4; + + if ((config->flags & TextConfig_Flags_eSemiTransparent_20) != 0) + { + color = MAKE_RGB(r, g, b); + code = ((GPU_CODE_SPRT | GPU_CODE_SEMITRANS) << RGBA_A_SHIFT); + } + else + { + color = MAKE_RGB(r, g, b); + code = ((GPU_CODE_SPRT & ~GPU_CODE_SEMITRANS) << RGBA_A_SHIFT); + } + + config->color = color | code; +} + +void MENU_ResetText(void) +{ + TextConfig *config = &gMenuTextConfig_8009E2E4; + config->color = MAKE_RGBA(128,128,128, GPU_CODE_SPRT); + config->flags = 0; +} + +void menu_Text_PrimUnknown_80038BB4(void) +{ + DR_TPAGE *pPrim; // $a0 + + pPrim = (DR_TPAGE*)menu_prim.next; + menu_prim.next += sizeof(DR_TPAGE); + setDrawTPage(pPrim, 1, 1, getTPage(0, gMenuTextConfig_8009E2E4.flags >> 8, 960, 256)); + + addPrim(menu_prim.ot, pPrim); +} + +int MENU_Printf(const char *fmt, const char *str, int param_3, int param_4, int param_5) +{ + int string_length; + unsigned int free_space; + char string_buffer[64]; + + if (menu_prim.next) + { + sprintf(string_buffer, (char *)fmt, str, param_3, param_4, param_5); + free_space = menu_prim.end - menu_prim.next; + string_length = strlen(string_buffer); + if (string_length * 0x14 + 0x28U <= free_space) + { + if (gMenuTextConfig_8009E2E4.flags & TextConfig_Flags_eLargeFont_10) + { + _menu_number_draw_string2(&menu_prim, &gMenuTextConfig_8009E2E4, + string_buffer); + } + else + { + _menu_number_draw_string(&menu_prim, &gMenuTextConfig_8009E2E4, + string_buffer); + } + menu_Text_PrimUnknown_80038BB4(); + } + } + return gMenuTextConfig_8009E2E4.xpos; +} + +int menu_draw_num(int number) +{ + if (!menu_prim.next) + { + + return gMenuTextConfig_8009E2E4.xpos; + } + _menu_number_draw(&menu_prim, &gMenuTextConfig_8009E2E4, number); + menu_Text_PrimUnknown_80038BB4(); + return gMenuTextConfig_8009E2E4.xpos; +} + +MenuPrim *MENU_GetPrimInfo(void) +{ + return &menu_prim; +} + +// specifically an enemy life bar +void MENU_DrawBar(int xpos, int ypos, int rest, int now, MENU_BAR_CONF *bconf) +{ + GM_GameStatus |= STATE_SHOW_LIFEBAR; + draw_life_8003F464(&menu_prim, xpos, ypos, rest, now, 1024, bconf); + menu_Text_PrimUnknown_80038BB4(); +} + +void MENU_DrawBar2(int ypos, int rest, int now, int max, MENU_BAR_CONF *bconf) +{ + draw_life_defaultX_8003F408(&menu_prim, ypos, rest, now, max, bconf); +} diff --git a/components/mgs/mgs/source/menu/menuman.h b/components/mgs/mgs/source/menu/menuman.h new file mode 100644 index 00000000..81bd112d --- /dev/null +++ b/components/mgs/mgs/source/menu/menuman.h @@ -0,0 +1,649 @@ +#ifndef __MGS_MENU_MENUMAN_H__ +#define __MGS_MENU_MENUMAN_H__ + +#include +#include +#include +#include "libgv/libgv.h" +#include "font/font.h" +#include "face.h" +#include "memcard/memcard.h" + +#define UNTAG_PTR(Type, Ptr) (Type *)((unsigned int)Ptr & 0x7FFFFFFF) + +typedef struct +{ + const char *field_0_weapon_name; + int field_4_rpk_idx; +} menu_weapon_rpk_info; + +typedef struct RadioMemory +{ + short frequency; + char name[18]; +} RadioMemory; + +typedef struct radio_table_entry +{ + int field_0_contactFrequency; + int field_4_radioTableCode; +} radio_table_entry; + +typedef struct radio_table +{ + radio_table_entry field_0_entries[8]; +} radio_table; + +#define RADIO_MEMORY_COUNT 16 + +#define MENU_ITEM_COUNT 21 +#define MENU_WEAPON_COUNT 11 + +enum ShapePalKeyState{ + SHAPE_STATE_NEUTRAL = 0, + SHAPE_STATE_HOT = 1, + SHAPE_STATE_COLD = 2 +}; + +typedef struct menu_chara_struct_sub +{ + // 1 = field_14_face_anim is FACE_ANIM_SIMPLE + // 2 = field_14_face_anim is FACE_ANIM_FULL + short field_0_animState; + + unsigned short field_2_chara; + short field_4; + short field_6; + short field_8_animFrameNum; + short field_A; + short field_C; + short field_E_eyesAnimFrame; // values 0-6. + short field_4C_mouthAnimFrame; // values 0-3. + short field_12; + face_anim field_14_face_anim; +} menu_chara_struct_sub; + +typedef struct menu_chara_struct +{ + int field_0_state; + int field_4; + char *field_8_radioDatFragment; + unsigned char *field_C_pScript; + void *field_10_subtitles; // Points to subtitle data. + int field_14_bInExecBlock; // to prevent re-entering? + unsigned short field_18; + short field_1A_index; + char *field_1C_radioDatFragment; + faces_group *field_20_pFacesGroup; + void *field_24_pImgData256; + char *stack; + short *field_2C_pSaveText; + int field_30_face_count; + face_header *field_34_faces; + int field_38; + menu_chara_struct_sub field_3C[2]; +} menu_chara_struct; + +typedef struct PANEL_TEXTURE +{ + u_long *field_0_pixels; + u_long *field_4_word_ptr_pixels; + signed char field_8_bufid; + signed char field_9_xofs; + signed char field_A_yofs; + char field_B_pad; + unsigned long field_C_uvclut; + short field_10_w; + short field_12_h; +} PANEL_TEXTURE; + +typedef struct menu_0x14 +{ + short field_0_bUnknown; + short field_2_bTaskWup; + int field_4_chara; + int field_8_code; + int field_C; + menu_chara_struct *field_10_pCharaStru; +} menu_0x14; + +typedef struct array_800BD748_child +{ + char field_0_u; + char field_1_v; + short field_2_clut; + PANEL_TEXTURE *field_4_panelTexture; + RECT field_8_rect1; + RECT field_10_rect2; +} array_800BD748_child; + +struct _MenuWork; +struct Menu_Inventory; +struct PANEL; +struct PANEL_CONF; + +typedef void (*PANEL_CONF_update)(struct _MenuWork *, u_long *, int, int, struct PANEL *); +typedef void (*PANEL_CONF_fn2)(struct PANEL_CONF *, int, int*, int*); + +typedef struct PANEL_CONF +{ + // X offset from left of screen. + short field_0_xOffset; + // Y offset from top of screen. + short field_2_yOffset; + // 8003D6CC() reads this in the following instructions and, if the field is set to 0: + // - @ 0x8003d700, it will not allow the menu to open; + // - @ 0x8003d768, it will not allow the menu to remain open. + int field_4_input; + int field_8; + int field_C; + PANEL_CONF_fn2 field_10_pFn2; + PANEL_CONF_fn2 field_14_pFn2; + PANEL_CONF_update field_18_pFnUpdate; +} PANEL_CONF; + +typedef void (*TMenuSaveModeDataUnknownFn1)(char *, struct MEM_CARD *, int, int); +typedef void (*TMenuSaveModeDataUnknownFn2)(char *, char *); +typedef void (*TMenuSaveModeDataUnknownFn3)(char *); + +typedef struct DATA_INFO +{ + char field_0[2]; + char field_2; + char blocks_count; + const char *field_4_name; // "SAVE DATA", "SAVE PHOTO" + TMenuSaveModeDataUnknownFn1 make_title; + TMenuSaveModeDataUnknownFn2 make_menu; + TMenuSaveModeDataUnknownFn3 make_game_data; +} DATA_INFO; + +struct Menu_Item_Unknown; +typedef void (*TMenuItemUnknownFn)(struct Menu_Item_Unknown *, int); + +typedef struct Menu_Item_Unknown_Main +{ + int field_0_array_count; + int field_4_selected_idx; + int field_8; + int field_C; + int field_10; + int field_14_fn_ctx; + int field_18; + TMenuItemUnknownFn field_1C_fn; +} Menu_Item_Unknown_Main; + +typedef struct PANEL +{ + short field_0_id; + short field_2_num; + short field_4_pos; + short field_6_current; +} PANEL; + +typedef struct Menu_Item_Unknown +{ + Menu_Item_Unknown_Main field_0_main; + PANEL field_20_array[0]; +} Menu_Item_Unknown; + +typedef struct Menu_Inventory +{ + PANEL field_0_current; + PANEL_CONF *field_8_panel_conf; + Menu_Item_Unknown *field_C_alloc; + char field_10_state; + signed char field_11; // item_idx ? + // Current frame of the "flashing" animation played when the menu is closed, counts down from 0xa to 0x0. + unsigned short field_12_flashingAnimationFrame; +} Menu_Inventory; + +typedef struct +{ + DR_ENV dr_env[ 2 ]; // 0x00 + RECT clip_rect; // 0x80 + DR_ENV org_env[ 2 ]; // 0x88 + // Radar X offset from default X position (not from top of screen). + short pos_x; // 0x108 + // Radar Y offset from default Y position (not from left of screen). + short pos_y; // 0x10A + // Health bar display countdown from 0x96 to 0x0, handled by 8003F530(): + // - 0x8003f784: resets the countdown to 0x96; + // - 0x8003f7a0: decrements the value. + char display_flag; // 0x10C + char prev_mode; // 0x10D + short counter; // 0x10E +} RADAR_T; + +typedef void (*TMenuUpdateFn)(struct _MenuWork *, u_long *); // todo +typedef void (*ButtonStates)(void); // todo + +typedef struct MenuPrim +{ + u_char *next; + u_long *ot; + u_char *end; + u_char *buf[2]; +} MenuPrim; + +typedef struct RPK_ITEM +{ + char x, y, w, h; + u_long data[0]; +} RPK_ITEM; + +// This struct describes the structure of +// ".rpk" files (".rpk", 'r', 0x72). Note +// that 'r' (0x72) can be either a ".rar", +// ".res" or ".rpk" file. +typedef struct RPK // resource pack? +{ + u_char palettes; + u_char images; + short pad; + RPK_ITEM *items[0]; +} RPK; + +typedef struct _MENU_BAR_CONF +{ + const char *name; + unsigned char left[3]; + unsigned char right[3]; + short height; +} MENU_BAR_CONF; + +enum MenuStatBarsState { + BAR_STATE_HIDDEN = 0, + BAR_STATE_MOVING_DOWN = 1, + BAR_STATE_VISIBLE = 2, + BAR_STATE_MOVING_UP = 3, + BAR_STATE_FORCE_HIDE = 4 +}; + +typedef struct MenuMan_MenuBars +{ + u_char field_0_state; + u_char field_1_O2_hp; + short int field_2_bar_x; + short int field_4_bar_y; + short int field_6_snake_hp; + short int field_8_hide_bar_delay_counter; + short int health_delay_counter; +} MenuMan_MenuBars; + +typedef void (*TInitKillFn)(struct _MenuWork *); + +enum +{ + MENUFLAGS_CAN_OPEN_RIGHT_MENU = 0x2, + MENUFLAGS_CAN_OPEN_LEFT_MENU = 0x4, + MENUFLAGS_CAN_SEE_RADAR = 0x8, + MENUFLAGS_CAN_OPEN_CODEC = 0x10 +}; + +enum // MenuWork->field_2C_modules +{ + MENU_LIFE = 0, // Life bars + MENU_WEAPON = 1, // Weapons inventory + MENU_ITEM = 2, // Items inventory + MENU_RADAR = 3, // Radar + MENU_RADIO = 4, // Codec + MENU_CARD = 5, // Memcard ? + MENU_JIMAKU = 6, // Subtitle + MENU_VIEWER = 7, // Photo viewer +}; +#define MENU_MODULE_MAX 8 + +typedef unsigned char MenuFlags; + +enum MenuState +{ + MENU_CLOSED = 0, + MENU_RIGHT_OPEN = 1, + MENU_LEFT_OPEN = 2, + MENU_CODEC_OPEN = 4 +}; + +typedef struct _MenuWork +{ + GV_ACT actor; + MenuPrim *prim; + GV_PAD *field_24_pInput; // Points to 0x800b05e0, ie gPad1_800B05C0[2]. + MenuFlags field_28_flags; + char field_29; + // Bit 0: browsing right menu. + // Bit 1: browsing left menu. + // Bit 2: using Codec. + unsigned char field_2A_state; + char field_2B; + TMenuUpdateFn field_2C_modules[ MENU_MODULE_MAX ]; +// int field_48; + DR_ENV field_4C_drawEnv[ 2 ]; + RADAR_T field_CC_radar_data; + Menu_Inventory field_1DC_menu_item; + Menu_Inventory field_1F0_menu_weapon; + MenuMan_MenuBars field_204_bars; + short field_210_codec_state; + short field_212; + KCB *field_214_font; + menu_chara_struct *field_218; + int field_21C; +} MenuWork; + +// here or jimctl.h? +typedef struct UnkJimakuStruct // @ 800BDA70 +{ + char field_0_active; // if true, display on screen and count down timer + char field_1_type; // 0, 1, 2? - 1: pause + short field_2_timer; // how many frames to be active + short field_4_x; // location on screen + short field_6_y; + short field_8_w; + short field_A_h; + KCB field_C_font; + char *field_38_str; // the string to display + int field_3C; // hashed proc name used as first arg to GCL_ExecProc + char *field_40; // char * gcl string? + // int field_44 // padding? +} UnkJimakuStruct; + +typedef struct MenuCallbackProc_800ABB08 +{ + int procNameHashed; + short type; + short param2; +} MenuCallbackProc_800ABB08; + +enum TextConfig_Flags +{ + TextConfig_Flags_eLeftAlign_00 = 0x00, + TextConfig_Flags_eRightAlign_01 = 0x01, + TextConfig_Flags_eCentreAlign_02 = 0x02, + TextConfig_Flags_eLargeFont_10 = 0x10, + TextConfig_Flags_eSemiTransparent_20 = 0x20, + TextConfig_Flags_ePadding_40 = 0x40, +}; + +typedef struct TextConfig +{ + int xpos; + int ypos; + int flags; // 0xF = alignement + // the first byte is a flag or an offset (0x64 opaque, 0x65 semi-transparent) + // the rest is BGR + int color; +} TextConfig; + +typedef struct Menu_Triangle +{ + short field_0_x0; + short field_2_y0; + short field_4_x1; + short field_6_y1; + short field_8_x2; + short field_A_y2; + unsigned int field_C_rgb; +} Menu_Triangle; + +/*---------------------------------------------------------------------------*/ + +/* menuman.c */ +void menuman_act_800386A4(MenuWork *); +void menuman_kill_800387E8(MenuWork *); +void menu_init_subsystems_8003884C(MenuWork *work); +void menuman_init_80038954(void); +void menuman_Reset(void); +void MENU_ResetTexture(void); +void MENU_StartDeamon(void); +void menu_radio_update_helper_80038A6C(void); +void menu_radio_update_helper2_80038A7C(void); +void MENU_ResetSystem(void); +void MENU_Locate(int xpos, int ypos, int flags); +void MENU_Color(int r, int g, int b); +void MENU_ResetText(void); +void menu_Text_PrimUnknown_80038BB4(void); +#ifndef __MENU_MENUMAN_C__ +int MENU_Printf(const char *fmt, ...); +#endif +int menu_draw_num(int number); +MenuPrim *MENU_GetPrimInfo(void); +void MENU_DrawBar(int xpos, int ypos, int rest, int now, MENU_BAR_CONF *bconf); +void MENU_DrawBar2(int ypos, int rest, int now, int max, MENU_BAR_CONF *bconf); + +/* radar.c */ +void MENU_SetRadarScale(int); +// void MENU_SetRadarFunc(TRadarFn_800AB48C func); +// void draw_radar_vision_cone_80038F3C(MenuWork *work, char *ot, RADAR_SIGHT_PARAM *r_param, int x, int y, int color, int fadeColor, int scale); +// void drawBorder_800390FC(MenuWork *menuMan, unsigned char *ot); +// void drawMap_800391D0(MenuWork *work, unsigned char *ot, int arg2); +// void initSprt_80039D5C(SPRT *pSprt, int x, int y, radar_uv *pRadarUV, int rgb); +// void drawHeader_helper_helper_80039DB4(MenuPrim *prim, SPRT *pSprt, radar_uv *pRadarUV); +// void drawHeader_80039EC4(MenuPrim *pGlue, int y, int idx); +// void drawConsole_alertEvasion_8003A0BC(MenuPrim *prim, int code); +// void drawConsole_jamming_8003A2D0(MenuPrim *pGlue, int idx); +// void drawCounter_8003A664(MenuPrim *pGlue, int alertLevel, int code); +// void drawSymbols_8003A978(MenuPrim *prim, int x, int code); +// void drawAlertEvasionJammingPanel_8003AA2C(MenuWork *work, char *ot, int radarMode, int alertLevel); +// void menu_radar_load_rpk_8003AD64(void); +// void menu_init_radar_helper_8003ADAC(void); +// void menu_radar_helper_8003ADD8(MenuWork *work, int index); +// void draw_radar(MenuWork *work, unsigned char * ot); +// void menu_radar_update_8003B350(MenuWork *work, unsigned char *ot); +// void menu_radar_init_8003B474(MenuWork *work); +// void menu_radar_kill_8003B554(MenuWork *work); + +/* item.c */ +void menu_sub_8003B568(void); +PANEL_TEXTURE *menu_rpk_8003B5E0(int idx); +void menu_item_printDescription_8003B614(int itemIndex); +int menu_item_IsItemDisabled_8003B6D0(int item_idx); +void menu_drawPalKey_8003B794(MenuWork *work, OT_TYPE *ot, int id); +void menu_item_helper_8003B8F0(MenuWork *work, OT_TYPE *ot, int xpos, int ypos, Menu_Inventory *pMenuSub); +void menu_8003BBEC(MenuWork *work); +int menu_item_update_helper_8003BCD4(MenuWork *work); +void menu_item_update_helper2_8003BF1C(MenuWork *work, OT_TYPE *ot); +void UseConsumableItem_8003C24C(Menu_Item_Unknown *pPanels, unsigned short press); +void UpdateEnvironmentalEffects_8003C4EC(void); +void menu_item_update_8003C95C(MenuWork *menuMan, OT_TYPE *ot); +void sub_8003CB98(MenuWork *work); +void menu_item_init_8003CBF0(MenuWork *work); +void menu_item_kill_8003CC74(MenuWork *work); +void MENU_ResetItemPos(void); + +/* weapon.c */ +void menu_texture_init_8003CC94(MenuWork *work); +void sub_8003CE40(PANEL_TEXTURE *, int); +void sub_8003CE78(void); +void sub_8003CE84(void); +void menu_draw_texture_8003CEF8(PANEL_TEXTURE *); +void sub_8003CFE0(PANEL_TEXTURE *images, int index); +// void sub_8003D070(PANEL_TEXTURE *pPanelTex, TIM *tim); +void menu_init_sprt_8003D0D0(SPRT *pPrim, PANEL_TEXTURE *pPanelTex, int offset_x, int offset_y); +Menu_Item_Unknown *menu_alloc_panel_8003D124(int count); +void menu_panel_free_8003D184(Menu_Item_Unknown *pPanel); +void AssignXY_8003D1A8(PANEL *pArray, int idx, short amount); +void AssignXYFromVec_8003D1B8(PANEL *pArray, PANEL *pOther); +void sub_8003D1DC(Menu_Item_Unknown *pMenuItem); +int menu_panel_8003D2BC(Menu_Item_Unknown *, int); +void sub_8003D34C(Menu_Item_Unknown *pItem, int a2); +void sub_8003D3A4(Menu_Item_Unknown *pItem, int a2); +void sub_8003D3FC(Menu_Item_Unknown *pMenu, int a2); +void sub_8003D44C(Menu_Item_Unknown *pMenu, int a2, int a3); +int sub_8003D4CC(Menu_Item_Unknown *pMenuItem); +void sub_8003D520(void); +int sub_8003D52C(void); +int menu_8003D538(void); +int sub_8003D568(void); +void sub_8003D594(PANEL_CONF *pPanelConf, int pos, int *xoff, int *yoff); +void sub_8003D5F0(PANEL_CONF *pPanelConf, int pos, int *xoff, int *yoff); +void sub_8003D64C(PANEL_CONF *pPanelConf, int pos, int *xoff, int *yoff); +void menu_set_panel_config_8003D6A8(struct Menu_Inventory *pMenuLeft, int bIsRight, void *pUpdateFn); +void menu_navigation_8003D6CC(Menu_Inventory *pLeftRight, GV_PAD *pPad); +void menu_8003D7DC(struct _MenuWork *work, OT_TYPE *ot, Menu_Inventory *pSubMenu); +void menu_sub_menu_update_8003DA0C(MenuWork *work, OT_TYPE *ot, Menu_Inventory *pSubMenu); +void sub_8003DA60(struct _MenuWork *work, OT_TYPE *ot, Menu_Inventory *pLeftRight, int off1, int off2); +int menu_8003DA9C(struct Menu_Inventory *pMenu, GV_PAD *pPad); +int sub_8003DAFC(Menu_Inventory *pLeftRight, GV_PAD *pPad); +TILE *menu_render_rect_8003DB2C(MenuPrim *ot, int x, int y, int w, int h, int rgb); +void Menu_item_render_frame_rects_8003DBAC(MenuPrim *pGlue, int x, int y, int param_4); +RPK_ITEM **menu_rpk_init_8003DD1C(const char *pFileName); +RPK_ITEM *menu_rpk_get_pal_8003DD9C(int id); +RPK_ITEM *menu_rpk_get_img_8003DDB4(int id); +void menu_init_rpk_item_8003DDCC(PANEL_TEXTURE *pUnk, int imgIdx, int palIdx); +void menu_inventory_right_init_items_8003DE50(void); +void menu_weapon_unknown_8003DEB0(void); +PANEL_TEXTURE *menu_weapon_get_weapon_rpk_info_8003DED8(int weaponIdx); +int menu_weapon_isWeaponDisabled_8003DF30(int weaponId); +void menu_weapon_printDescription_8003E030(int wpn_id); +void menu_weapon_init_helper_8003E0E8(MenuWork *param_1, OT_TYPE *ot, int param_3, int param_4, PANEL *param_5); +void menu_weapon_update_helper2_helper2_8003E3B0(MenuWork *work); +int menu_weapon_update_helper_8003E4B8(MenuWork *work); +void menu_weapon_update_helper2_8003E674(MenuWork *work, OT_TYPE *ot); +void menu_weapon_update_8003E990(struct _MenuWork *menuMan, OT_TYPE *ot); +void sub_8003EBDC(struct _MenuWork *a1); +void menu_weapon_init_8003EC2C(MenuWork *work); +void menu_weapon_kill_8003ECAC(MenuWork *work); +void MENU_ResetWeaponPos(void); + +/* life.c */ +int menu_life_update_helper_8003ECCC(MenuMan_MenuBars *pBars); +void menu_draw_bar(MenuPrim *prim, long x, long y, long rest, long now, long max, MENU_BAR_CONF *bconf); +void menu_life_update_helper2_8003F30C(MenuPrim *prim, MenuMan_MenuBars *pBars); +void draw_life_defaultX_8003F408(MenuPrim *prim, long y, long rest, long now, long max, MENU_BAR_CONF *bconf); +void draw_life_8003F464(MenuPrim *prim, long x, long y, long rest, long now, long max, MENU_BAR_CONF *bconf); +void draw_player_life_8003F4B8(MenuPrim *prim, long x, long y); +void menu_font_kill_helper_8003F50C(void); +void menu_life_update_8003F530(MenuWork *work, OT_TYPE *ot); +void menu_life_init_8003F7E0(MenuWork *work); +void menu_life_kill_8003F838(MenuWork *work); +int sub_8003F84C(int); +void menu_printDescription_8003F97C(char *description); +void menu_drawDescriptionPanel_8003F9B4(MenuWork *work, OT_TYPE *ot, const char *str); +void menu_font_kill_8003FC0C(void); + +/* radio.c */ +// ...in radio.h +void MENU_RadioCall(int param_1, int param_2, int param_3); +void MENU_ResetCall(void); +void MENU_SetRadioCallbackProc(int proc_id); +void _menu_number_draw(MenuPrim *ot, TextConfig *pSettings, int number); +void _menu_number_draw_string(MenuPrim *pGlue, TextConfig *pTextConfig, const char *str); +void _menu_number_draw_string2(MenuPrim *pGlue, TextConfig *pTextConfig, const char *str); +void menu_restore_nouse(void); +void menu_init_nouse(void); + +/* debug.c */ +void menu_viewer_init(MenuWork *work); +void menu_viewer_kill(MenuWork *work); + +/* radiotex.c */ +void sub_800469F0(menu_chara_struct *pStru); +void menu_radio_codec_helper_helper7_helper_80046A98(menu_chara_struct *pStru); +void LoadFaceAnimImage_80046B10(face_anim_image *image, int idx); +void menu_radio_load_palette_80046B74(void *image, int idx); +void sub_80046BD8(int idx); + +/* radioanim.c */ +int sub_80046C90(menu_chara_struct_sub *pSub, int idx, face_full_anim *pFullAnim, int pFrameNum); +void menu_radio_draw_face_helper_helper_80046DF4(int idx, menu_chara_struct *pChara, int chara, int code, int a5); +void menu_radio_codec_task_proc_helper_80046F3C(menu_chara_struct *pStru, faces_group *pFacesGroup); +unsigned char *menu_gcl_read_word_80047098(int *pOut, unsigned char *pScript); +void menu_800470B4(int idx, menu_chara_struct *pStru, int chara, int code, int faceUnk, int taskWup); +void menu_radio_draw_face_helper_800470F4(int idx); + +/* radiomes.c */ +void radio_anim_with_subtitles_800471AC(menu_chara_struct *unk, unsigned char *pScript); +void radio_anim_face_80047280(menu_chara_struct *unk, unsigned char *pScript); +void radio_voice_80047330(menu_chara_struct *unk, unsigned char *pScript); +void radio_sound_80047414(menu_chara_struct *unk, unsigned char *pScript); +unsigned char *radio_getNextValue_800474EC(int *retValue, unsigned char *pScript); +void radio_if_80047514(menu_chara_struct *unk, unsigned char *pScript); +void radio_switch_800475B8(menu_chara_struct *unk, unsigned char *pScript); +void radio_randSwitch_80047660(menu_chara_struct *unk, unsigned char *pScript); +void radio_eval_80047748(menu_chara_struct *unk, unsigned char *pScript); +void radio_add_contact_80047768(menu_chara_struct *unk, unsigned char *pScript); +void radio_memsave_800477B0(menu_chara_struct *unk, unsigned char *pScript); +void radio_prompt_8004780C(menu_chara_struct *unk, unsigned char *pScript); +void radio_varsave_80047838(menu_chara_struct *unk, unsigned char *pScript); +unsigned char *radio_moveToNext_80047880(menu_chara_struct *unk, unsigned char *pScript); +unsigned char *menu_gcl_exec_block_800478B4(menu_chara_struct *unknown, unsigned char *pScript); +void menu_radio_codec_task_proc_80047AA0(void); +void menu_radio_codec_start_task_80047C3C(void); +void ResetCharacterCodecStruct(menu_chara_struct *unknown); +void menu_radio_codec_create_state_80047CE4(MenuWork *menuMan); +void menu_radio_update_helper6_80047D40(MenuWork *work); +void sub_80047D70(MenuWork *work, int param_2, int pRadioCode); +void menu_radio_codec_helper_helper10_80047EFC(MenuWork *work, int param_2); +void menu_radio_codec_helper_helper3_80047F44(MenuWork *work, int param_2); +int menu_radio_codec_helper_helper9_80047FF4(); +void menu_radio_codec_state_2_helper_80048024(); +void menu_radio_codec_helper_helper8_80048044(); +void menu_radio_codec_helper_helper7_80048080(); +void ResetCodecState(); + +/* radiofacedraw.c */ +void sub_80048124(void); +void radio_draw_face_frame(MenuPrim *pGlue, int x, int y, int w, int h); +void menu_radio_draw_face_helper6_800486A0(menu_chara_struct_sub *a1, int idx); +void menu_radio_draw_face_helper2_800486F4(menu_chara_struct_sub *pSub, int idx); +void menu_radio_draw_face_helper3_800487DC(menu_chara_struct *pStru, int idx); +void menu_radio_draw_face_helper4_80048868(MenuPrim *prim, menu_chara_struct_sub *a2, int idx); +void menu_radio_draw_face_helper5_8004896C(MenuPrim *prim, menu_chara_struct_sub *a2, int idx); +void menu_radio_draw_face(MenuWork *work, menu_chara_struct *chara_struct); +int menu_radio_end_check(void); + +/* jimaku.c */ +void menu_jimaku_act(MenuWork *work, OT_TYPE *ot); +void menu_jimaku_init(MenuWork *work); +void MENU_JimakuWrite(char *str, int frames); +void MENU_JimakuClear(void); +void MENU_AreaNameWrite(char *areaName); +void NewJimakuStr(char *str, int int_1); +void NewJimaku(void); + +/* radiotable.c */ +void MENU_InitRadioTable(void); +void MENU_ClearRadioTable(void); +radio_table_entry *sub_8004969C(radio_table *pRadioTable, int contactFrequency); +void MENU_SetRadioBaseCall(int contactFrequency, int radioTableCode); +void MENU_SetRadioOverCall(int contactFrequency, int radioTableCode); +int MENU_GetRadioCode(int param_1); + +/* datasave.c */ +void move_coord(int *arr, int len); +int menu_radio_do_file_mode(MenuWork *work, GV_PAD *pPad); +void init_file_mode(DATA_INFO *pSaveMode, int param_2); +void menu_radio_init_save_mode(int param_1, int param_2); +void menu_radio_update_helper4_8004D2D0(int param_1); +void menu_radio_8004D2FC(DATA_INFO *pSaveMode); +int menu_radio_8004D334(GV_PAD *pPad); +void menu_radio_8004D35C(void); + +/* radiomem.c */ +RadioMemory *menu_radio_table_find_8004D380(int toFind); +RadioMemory *menu_radio_table_next_free_8004D3B8(void); +void menu_radio_compact_free_vars_8004D3D8(void); +// void sub_8004D4A0(RadioCodecStru_800ABB98 *pStru); +void sub_8004D580(int pressed); +void menu_radio_draw_mem(MenuWork *work, OT_TYPE *ot); +void menu_radio_codec_helper_helper4_8004DE20(MenuWork *work); +void menu_radio_codec_helper__helper3_sub_8004DF44(); +int menu_radio_codec_helper_helper2_8004DF68(MenuWork *work, GV_PAD *pPad); +void MENU_InitRadioMemory(void); +void MENU_SetRadioMemory(int varId, const char *pVarName); +void menu_radio_codec_helper_helper_8004E198(int toFind); + +/*---------------------------------------------------------------------------*/ + +// For MenuPrim +#define _NEW_PRIM(_prim, _buf) \ + { \ + __typeof__(_prim) p; \ + p = (__typeof__(_prim))_buf->next; \ + _buf->next += sizeof(*_prim); \ + _prim = p; \ + } + +// For MenuWork +#define NEW_PRIM(_prim, _work) _NEW_PRIM(_prim, _work->prim) + +#endif // __MGS_MENU_MENUMAN_H__ diff --git a/components/mgs/mgs/source/menu/mload.c b/components/mgs/mgs/source/menu/mload.c new file mode 100644 index 00000000..94e5e256 --- /dev/null +++ b/components/mgs/mgs/source/menu/mload.c @@ -0,0 +1,42 @@ +#include "game/game.h" +#include "libgcl/libgcl.h" +#include "menu/radio.h" + +void *NewLoadData(int name, int where) +{ + int proc_id; + char *code; + + proc_id = GCL_GetNextInt(); + code = GCL_NextStr(); + MENU_SetLoad(proc_id, code, 1); + return code; +} + +static void Act(GV_ACT *work) +{ + gTotalFrameTime = 0; + + if (GM_LoadRequest != 0) + { + GV_DestroyActor(work); + } +} + +void *NewSaveData(int name, int where) +{ + int proc_id; + char *code; + GV_ACT *work; + + proc_id = GCL_GetNextInt(); + code = GCL_NextStr(); + MENU_SetLoad(proc_id, code, 2); + + work = GV_NewActor(GV_ACTOR_MANAGER, sizeof(GV_ACT)); + if (work != NULL) + { + GV_SetNamedActor(work, Act, NULL, "mload.c"); + } + return (void *)work; +} diff --git a/components/mgs/mgs/source/menu/radar.c b/components/mgs/mgs/source/menu/radar.c new file mode 100644 index 00000000..3f347ace --- /dev/null +++ b/components/mgs/mgs/source/menu/radar.c @@ -0,0 +1,1284 @@ +#include "radar.h" +#include "menuman.h" + +#include +#include "common.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "game/game.h" +#include "linkvar.h" +#include "sound/g_sound.h" +#include "psxdefs.h" // for getScratchAddr2 + +int MENU_RadarScale = 13; +int MENU_RadarRangeH = 21845; +int MENU_RadarRangeV = 16383; + +STATIC TRadarFn_800AB48C gFn_radar_800AB48C = NULL; +STATIC RECT rect_800AB490 = {992, 382, 32, 2}; +STATIC short gRadarClut_800AB498[4] = {0x5FBE, 0x5FBF, 0x5FFE, 0x5FFF}; + +// The following two arrays are indexed by 3 - radarMode: +// 0 -> alert, 1 -> evasion, 2 and 3 -> never used? See comment on their usage. +STATIC short active7segmentColors_800AB4A0[4] = {0x8C91, 0x8D11, 0x8C91, 0x9A23}; +STATIC short inactive7segmentColors_800AB4A8[4] = {0x8023, 0x8023, 0x8023, 0x0000}; +STATIC int cons_current_y_800AB4B0 = 0; +STATIC int cons_current_x_800AB4B4 = 0; + +RPK_ITEM *SECTION(".sbss") gRadar_rpk_800ABAC8; +int SECTION(".sbss") dword_800ABACC; + +extern short image_8009E338[]; +extern char gDigit7Segment_8009E60C[]; + +extern MATRIX gRadarScaleMatrix_800BD580; + +// Used for colors of vision cones of soldiers and surveillance cameras in the radar. +typedef struct visionConeColors +{ + int mainColor, fadeColor; +} visionConeColors; + +// 0x808000 (light blue): color of vision cone of soldiers in normal condition. +// 0xA0 (red): color of vision cone of soldiers when suspicious (heard a noise, etc.) or alerted (just saw Snake). +// 0xA0A0 (yellow): color of vision cone of surveillance cameras. +visionConeColors visionConeColors_8009E2F4[] = {{0x808000, 0x100000}, {0xA0, 0x10}, {0xA0A0, 0x808}}; + +radar_uv gRadarUV_8009E30C[] = { + {128, 80, 28, 12}, // Symbols in alert mode. + {128, 92, 44, 7}, // "ALERT" text. + {156, 80, 28, 12}, // Symbols in evasion mode. + {128, 99, 58, 7}, // "EVASION" text. + {184, 80, 28, 12}, // ? + {180, 92, 44, 7}, // ? + {138, 106, 58, 12}, // Symbols in jamming mode. + {138, 118, 58, 7}, // "JAMMING" text. + {188, 99, 36, 3}, // Dashed line under "ALERT", "EVASION" and "JAMMING". + {128, 106, 10, 16}, // Four digits of the counter (99.99) in alert and evasion mode. + {196, 102, 28, 22} // The "console" in alert, evasion and jamming mode. +}; + +// 32x2 image: +short image_8009E338[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC210, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC210, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xC210}; + +int gRadarRGBTable_8009E3B8[] = {0x182589, 0x184789, 0x182589, 0x338918}; + +// Used to draw the static elements around the counter (99.99) +// when in alert or evasion mode. +radar_uv gStaticCounterElsUVs_8009E3C8[] = { + { 7, 29, 55, 1}, // Horizontal line above the counter. + { 7, 50, 55, 1}, // Horizontal line under the counter. + {33, 46, 3, 2} // Dot of the counter. +}; + +// Indexed by radarMode - 1, it is the background color of the whole radar: +// 0 -> jamming (green), 1 -> evasion (yellow), 2 -> alert (red), 3 -> normal? +int gRadarRGBTable2_8009E3D4[] = {0x48A000, 0x6E6E, 0xDE, 0x181800}; + +#define CONSOLE_WIDTH 24 +//#define CONSOLE_HEIGHT + +//#define CENTER_X +//#define CENTER_Y + +#define CONSOLE_LONG_LINES 5 // 7 in the leaks + +//#define CONSOLE_LONG_X0 ( 6 - CENTER_X ) +//#define CONSOLE_LONG_Y0 ( 30 - CENTER_Y ) +#define CONSOLE_X0 4 +#define CONSOLE_Y0 -12 + +//#define CONSOLE_LONG_WIDTH 56 +//#define CONSOLE_TEX_WIDTH 28 + +void MENU_SetRadarScale(int scale) +{ + int scale2; + VECTOR scale_vec; + + MENU_RadarScale = scale * 13 / 4096; + + scale2 = 65536 * 13 / MENU_RadarScale; + + MENU_RadarRangeH = scale2 / 3; + MENU_RadarRangeV = scale2 / 4; + + gRadarScaleMatrix_800BD580 = DG_ZeroMatrix; + scale_vec.vz = MENU_RadarScale; + scale_vec.vy = MENU_RadarScale; + scale_vec.vx = MENU_RadarScale; + ScaleMatrix(&gRadarScaleMatrix_800BD580, &scale_vec); +} + +void MENU_SetRadarFunc(TRadarFn_800AB48C func) +{ + gFn_radar_800AB48C = func; +} + +void draw_radar_vision_cone_80038F3C(MenuWork *work, OT_TYPE *ot, RADAR_SIGHT_PARAM *r_param, int x, int y, int color, + int fadeColor, int scale) +{ + SVECTOR right; + SVECTOR left; + SVECTOR front; + POLY_G4 *poly; // Four vertices: origin, main direction and two lateral directions. + int len; + int dir; + + len = ((r_param->dis * scale / 4096) * 3) / 2; + GV_DirVec2(r_param->dir, len, &front); + + dir = GV_DiffDirU(r_param->dir, r_param->range / 2); + GV_DirVec2(dir, len, &right); + + dir = GV_DiffDirU(r_param->dir, -r_param->range / 2); + GV_DirVec2(dir, len, &left); + + NEW_PRIM(poly, work); + + poly->x0 = x - left.vx; + poly->y0 = left.vz + y; + poly->x1 = x; + poly->y1 = y; + poly->x2 = front.vx + x; + poly->y2 = front.vz + y; + poly->x3 = x - right.vx; + poly->y3 = right.vz + y; + + LSTORE(color, &poly->r1); + LSTORE(fadeColor, &poly->r0); + LSTORE(fadeColor, &poly->r2); + LSTORE(fadeColor, &poly->r3); + + setPolyG4(poly); + setSemiTrans(poly, 1); + + addPrim(ot, poly); +} + +// Draws the black border around the radar. +void drawBorder_800390FC(MenuWork *menuMan, OT_TYPE *ot) +{ + int x1, y1, x2, y2; + + x1 = menuMan->field_CC_radar_data.pos_x; + y1 = menuMan->field_CC_radar_data.pos_y; + x2 = x1 + 234; + y2 = y1 + 15; + menu_render_rect_8003DB2C(menuMan->prim, x2, y2, 1, 53, 0); // Left border. + menu_render_rect_8003DB2C(menuMan->prim, x2, y2, 70, 1, 0); // Top border. + menu_render_rect_8003DB2C(menuMan->prim, x1 + 304, y2, 1, 54, 0); // Right border. + menu_render_rect_8003DB2C(menuMan->prim, x2, y1 + 68, 70, 1, 0); // Bottom border. +} + +// clang-format off +// gte_stbv but with sh instead of sb +#ifndef __psyz +#define gte_stbh( r0 ) __asm__ volatile ( \ + "mfc2 $12, $9;" \ + "mfc2 $13, $10;" \ + "sh $12, 0( %0 );" \ + "sh $13, 2( %0 )" \ + : \ + : "r"( r0 ) \ + : "$12", "$13", "memory" ) +#endif /* __psyz: psyz provides a portable form */ + + +// gte_ldv0 but without the second load +#ifndef __psyz +#define gte_ldv0h( r0 ) __asm__ volatile ( \ + "lwc2 $0, 0( %0 )" \ + : \ + : "r"( r0 ) ) +#endif /* __psyz: psyz provides a portable form */ + +// clang-format on + +extern CONTROL *GM_WhereList[96]; +extern int GM_N_WhereList; + +// Couldn't test it, but it should be the appropriate function name. +void drawMap_800391D0(MenuWork *work, OT_TYPE *ot, int arg2) +{ + RADAR_SIGHT_PARAM cone; + + CONTROL **entities; + MAP *pMap; + + int xoff; + int zoff; + + HZD_SEG *pWalls; + HZD_SEG **ppWalls; + void *pLimit; + int area_bits; + + CONTROL *control; + + TILE *pTile; + TILE_1 *pTile1; + TILE_1 *pTile1_2; + DR_TPAGE *pTpage; + DR_TPAGE *pTpage_2; + + u_long *prim; + + int x; + int z; + + int areas; + int count; + int count2; + int count3; + + int scale; + + int cmp1, cmp2; + + int vy; + int cond1; + int i; + int area_mask; + + int cond2; + int rgb; + + u_long *ot2; + LINE_F2 *pLine; + + int radar_atr; + + char *pWallFlags; + char *pWallFlags2; + + HZD_GRP *pArea; + HZD_SEG *pWall; + HZD_HDL *pHzdMap; + + short *scratchShort; + + DG_PVECTOR *pvec; + SVECTOR *svec; + + int *pWallDst; + int *pWallDst2; + int j; + + if (GM_GameStatus < 0) + { + return; + } + + scale = MENU_RadarScale; + + entities = GM_WhereList; + control = entities[0]; // entities[0] is Snake + + *getScratchAddr2(SVECTOR, 0) = entities[0]->mov; + getScratchAddr2(SVECTOR, 0)->vy = control->hzd_height; + + entities++; + + pMap = control->map; + + if (!pMap) + { + return; + } + + xoff = (getScratchAddr2(SVECTOR, 0)->vx * scale) / 4096; + zoff = (getScratchAddr2(SVECTOR, 0)->vz * scale) / 4096; + + if ((GV_Time % 8) >= 2) + { + // Draw white dot for Snake in middle of radar + NEW_PRIM(pTile1, work); + LSTORE(0xC8C8C8, &pTile1->r0); + setTile1(pTile1); + setXY0(pTile1, 0, 0); + addPrim(ot, pTile1); + + if (GM_PlayerStatus & PLAYER_WATCH) + { + // Draw Snake's vision cone in first person + cone.dir = control->rot.vy; + cone.dis = (rcos(control->rot.vx) * 6144) / 4096; + cone.range = 600; + + draw_radar_vision_cone_80038F3C(work, ot, &cone, 0, 0, MAKE_RGB(0, 160, 72), MAKE_RGB(0, 0, 0), scale); + } + + for (count = GM_N_WhereList - 1; count > 0; count--) + { + control = *entities++; + radar_atr = control->radar_atr; + + x = ((control->mov.vx * scale) / 4096) - xoff; + z = ((control->mov.vz * scale) / 4096) - zoff; + + if ((radar_atr & RADAR_VISIBLE) && ((radar_atr & RADAR_ALL_MAP) || (control->map->index & GM_PlayerMap))) + { + NEW_PRIM(pTile1_2, work); + + setXY0(pTile1_2, x, z); + + if (radar_atr & RADAR_NOISE) + { + LSTORE(0x80FF00, &pTile1_2->r0); + setTile1(pTile1_2); + addPrim(pTile1, pTile1_2); + + for (j = 0; j < 16; j++) + { + int new_var; + NEW_PRIM(pTile1_2, work); + + new_var = 2; + pTile1_2->x0 = x - new_var + rand() % 4; + pTile1_2->y0 = z - new_var + rand() % 4; + + LSTORE(0x64C800, &pTile1_2->r0); + setTile1(pTile1_2); + addPrim(pTile1, pTile1_2); + } + } + else + { + vy = control->mov.vy - getScratchAddr2(SVECTOR, 0)->vy; + + // bool inline? + cond1 = 0; + + if (radar_atr & RADAR_UNK2) + { + short vy_s = vy; + cond1 = (vy_s >= 0) && (vy_s < 6000); + radar_atr |= RADAR_SIGHT; + } + else + { + short vy_s = vy; + if (radar_atr & RADAR_UNK1) + { + cond2 = (vy > -2750) && (vy < 2000); // why??? + } + else + { + cond2 = (vy_s >= -2000) && (vy_s < 2000); + } + + if (cond2 != 0) + { + cond1 = 1; + } + } + + if (cond1) + { + LSTORE(0x64FF, &pTile1_2->r0); + setTile1(pTile1_2); + addPrim(ot, pTile1_2); + + if (radar_atr & RADAR_SIGHT) + { + int idx = radar_atr >> 12; + draw_radar_vision_cone_80038F3C(work, ot, &control->radar_param, x, z, + visionConeColors_8009E2F4[idx].mainColor, visionConeColors_8009E2F4[idx].fadeColor, + scale); + } + } + else + { + LSTORE(0xC8, &pTile1_2->r0); + setTile1(pTile1_2); + addPrim(pTile1, pTile1_2); + } + } + } + } + + NEW_PRIM(pTpage, work); + setDrawTPage(pTpage, 1, 0, getTPage(0, 1, 960, 256)); + addPrim(ot, pTpage); + } + +#ifdef __psyz + /* This bare word is the chain anchor the wall lines below insert behind. + * With the 8-byte tag+len header a 4-byte allocation makes the anchor's + * len field OVERLAP the next prim's tag: the first wall line written + * there turned the anchor's length into a pointer value and the walker + * dropped the whole radar chain. Allocate a real two-word header. */ + prim = (u_long *)work->prim->next; + work->prim->next += 8; + setaddr(prim, 0); + setlen(prim, 0); + addPrim(ot, prim); +#else + NEW_PRIM(prim, work); + *prim = 0; + addPrim(ot, prim); +#endif + + pvec = getScratchAddr2(DG_PVECTOR, 0); + svec = getScratchAddr2(SVECTOR, 0); + + pvec[1].vxy = svec[0].vx - MENU_RadarRangeH / 2; + pvec[1].vz = svec[0].vx + MENU_RadarRangeH / 2; + + pvec[2].vxy = svec[0].vz - MENU_RadarRangeV / 2; + pvec[2].vz = svec[0].vz + MENU_RadarRangeV / 2; + + pvec[3].vxy = svec[0].vy + 1000; + pvec[3].vz = svec[0].vy - 800; + + pLine = (LINE_F2 *)work->prim->next; + pLimit = work->prim->end - 1024; + + gRadarScaleMatrix_800BD580.t[0] = -xoff; + gRadarScaleMatrix_800BD580.t[1] = -zoff; + + gte_SetRotMatrix(&gRadarScaleMatrix_800BD580); + gte_SetTransMatrix(&gRadarScaleMatrix_800BD580); + + for (i = 0; i < 2; i++) + { + pHzdMap = NULL; + pWallDst = getScratchAddr2(int, 0x20); + pWallDst2 = getScratchAddr2(int, 0x24); + scratchShort = (short *)svec; + area_bits = HZD_CurrentGroup; + + area_mask = 1 << pMap->hzd->def->n_groups; + areas = pMap->hzd->def->n_groups * 24; + + while (1) + { + if (i == 0) + { + area_mask >>= 1; + areas -= 24; + + if (area_mask == 0) + { + break; + } + + if (!(area_bits & area_mask)) + { + continue; + } + + pArea = (HZD_GRP *)((char *)pMap->hzd->def->groups + areas); + pWallFlags = pArea->wallsFlags; + pWallFlags2 = (char *)pArea->wallsFlags + pArea->n_walls; + pWalls = pArea->walls; + count2 = pArea->n_walls; + } + else + { + pHzdMap = GM_IterHazard(pHzdMap); + + if (!pHzdMap) + { + break; + } + + ppWalls = pHzdMap->dynamic_segments; + pWallFlags = pHzdMap->dynamic_flags; + pWallFlags2 = pWallFlags + pHzdMap->max_dynamic_segments; + count2 = pHzdMap->dynamic_queue_index; + } + + for (count3 = count2; count3 > 0; count3--, pWallFlags++, pWallFlags2++) + { + if (i == 0) + { + pWall = pWalls++; + } + else + { + pWall = *ppWalls++; + } + + if (*pWallFlags & 0x80) + { + continue; + } + + *pWallDst = *(int *)&pWall->p1.x; + *pWallDst2 = *(int *)&pWall->p2.x; + + if (((short *)scratchShort)[0x20 / 2] > ((int *)scratchShort)[0xC / 4]) + { + continue; + } + + if (((int *)scratchShort)[0x8 / 4] > ((short *)scratchShort)[0x24 / 2]) + { + continue; + } + + // The scratchpad and its consequences have been a disaster for the human race. + cmp1 = scratchShort[0x22 / 2]; + cmp2 = scratchShort[0x26 / 2]; + + if (cmp1 > cmp2) + { + if (((int *)scratchShort)[0x14 / 4] < cmp2 || cmp1 < ((int *)scratchShort)[0x10 / 4]) + { + continue; + } + } + else + { + if (((int *)scratchShort)[0x14 / 4] < cmp1 || cmp2 < ((int *)scratchShort)[0x10 / 4]) + { + continue; + } + } + + gte_ldv0h((SCRPAD_ADDR + 0x020)); + gte_rt(); + + if (((((int *)scratchShort)[0x18 / 4] < pWall->p1.y) || + ((pWall->p1.y + pWall->p1.h) < ((int *)scratchShort)[0x1C / 4])) && + ((((int *)scratchShort)[0x18 / 4] < pWall->p2.y) || + ((pWall->p2.y + pWall->p2.h) < ((int *)scratchShort)[0x1C / 4]))) + { + rgb = 0x40004000; + ot2 = ot; + } + else + { + if (*pWallFlags2 & 0x80) + { + rgb = 0x404020B4; + } + else + { + rgb = 0x4048A000; + } + + ot2 = prim; + } + + gte_stbh(&pLine->x0); + + gte_ldv0h((SCRPAD_ADDR + 0x024)); + gte_rt(); + + LSTORE(rgb, &pLine->r0); +#ifdef __psyz + /* the packed idiom split into the two-word header: length in + * its own field, the link a full pointer (the 24-bit mask is + * what sent the walker to 0x00cacb24) */ + setaddr(pLine, *ot2); + setlen(pLine, 3); + *ot2 = (u_long)pLine; +#else + pLine->tag = *ot2 | 0x03000000; + *ot2 = (int)(pLine)&0xffffff; +#endif + gte_stbh(&pLine->x1); + + pLine++; + + if ((void *)pLine > pLimit) + { + goto end; + } + } + } + } + +end: + work->prim->next = (unsigned char *)pLine; + + NEW_PRIM(pTile, work); + + pTile->x0 = -34; + pTile->w = 69; + pTile->y0 = -26; + pTile->h = 52; + + // arg2 is always 0. + if (arg2 == 0) + { + LSTORE(0x181800, &pTile->r0); + } + else + { + LSTORE(0x00FFFF, &pTile->r0); + } + + setTile(pTile); + setSemiTrans(pTile, 1); + addPrim(ot, pTile); + + NEW_PRIM(pTpage_2, work); + setDrawTPage(pTpage_2, 1, 0, getTPage(0, 0, 960, 256)); + addPrim(ot, pTpage_2); +} + +void initSprt_80039D5C(SPRT *pSprt, int x, int y, radar_uv *pRadarUV, int rgb) +{ + short clut; + + setXY0(pSprt, x, y); + setUV0(pSprt, pRadarUV->field_0_x, pRadarUV->field_1_y); + setWH(pSprt, pRadarUV->field_2_w, pRadarUV->field_3_h); + + do {} while (0); // Force a match + + clut = gRadarClut_800AB498[0]; + LSTORE(rgb, &pSprt->r0); + + setSprt(pSprt); + setSemiTrans(pSprt, 1); + + pSprt->clut = clut; +} + +void drawHeader_helper_helper_80039DB4(MenuPrim *prim, SPRT *pSprt, radar_uv *pRadarUV) +{ + int x0; + TILE *tile1; + TILE *tile2; + + x0 = pSprt->x0; + + _NEW_PRIM( tile1, prim ); + + tile1->x0 = -34; + tile1->y0 = pSprt->y0; + tile1->w = x0 - tile1->x0; + tile1->h = pRadarUV->field_3_h; + LCOPY(&pSprt->r0, &tile1->r0); + + setTile(tile1); + setSemiTrans(tile1, 1); + addPrim(prim->ot, tile1); + + _NEW_PRIM( tile2, prim ); + + *tile2 = *tile1; + tile2->x0 = pRadarUV->field_2_w + x0; + tile2->w = 69 - tile2->x0; + + addPrim(prim->ot, tile2); +} + +static inline void drawHeader_helper(MenuPrim *prim, int y, radar_uv *pRadarUV, int *rgbs) +{ + SPRT *sprt; + + _NEW_PRIM( sprt, prim ); + + initSprt_80039D5C(sprt, -pRadarUV->field_2_w / 2, y, pRadarUV, rgbs[0]); + addPrim(prim->ot, sprt); + drawHeader_helper_helper_80039DB4(prim, sprt, pRadarUV); +} + +// Draws the radar "header", i.e. "ALERT" / "EVASION" / "JAMMING" text and the dashed line under it. +void drawHeader_80039EC4(MenuPrim *pGlue, int y, int idx) +{ + int time, time2; + int rgbs[2]; + radar_uv *pRadarUV; + + rgbs[0] = gRadarRGBTable_8009E3B8[idx]; + + time = GV_Time % 16; + + if (time < 8) + { + time2 = time * 512; + gte_ldfcdir3(0, 0, 0); + gte_lddp(time2); + gte_ldrgb(rgbs); + gte_dpcs(); + gte_strgb(&rgbs[1]); + LCOPY(&rgbs[1], rgbs); + } + + // Draw "ALERT" / "EVASION" / "JAMMING" text. + pRadarUV = &gRadarUV_8009E30C[idx * 2 + 1]; + drawHeader_helper(pGlue, y, pRadarUV, rgbs); + + // Draw the dashed line under the text. + y += pRadarUV->field_3_h; + pRadarUV = &gRadarUV_8009E30C[8]; + drawHeader_helper(pGlue, y, pRadarUV, rgbs); +} + +void drawConsole_alertEvasion_8003A0BC(MenuPrim *prim, int code) +{ + SPRT *spb; + radar_uv *uv; // CHARA_TABLE *tp; + int i; + int h; + int color; + + uv = &gRadarUV_8009E30C[10]; // tp = &( radar_chara[ CHARA_RANDOM ] ); + color = gRadarRGBTable_8009E3B8[code]; + h = cons_current_y_800AB4B0 > CONSOLE_LONG_LINES ? CONSOLE_LONG_LINES : cons_current_y_800AB4B0; + + _NEW_PRIM( spb, prim ); + + spb->clut = gRadarClut_800AB498[0]; + + spb->x0 = CONSOLE_X0; + spb->y0 = CONSOLE_Y0 + h * 2; + + spb->u0 = uv->field_0_x; + spb->v0 = cons_current_y_800AB4B0 % 16 + uv->field_1_y; + + spb->w = cons_current_x_800AB4B4; + spb->h = 1; + + LSTORE(color, &spb->r0); + + setSprt(spb); + setSemiTrans(spb, 1); + addPrim(prim->ot, spb); + + for( i = 1; i <= h; i++ ) + { + SPRT *sprt; + _NEW_PRIM( sprt, prim ); + *sprt = *spb; + sprt->y0 = spb->y0 - i * 2; + sprt->w = CONSOLE_WIDTH; + sprt->v0 = uv->field_1_y + ( cons_current_y_800AB4B0 - i + 16 ) % 16; + addPrim( prim->ot, sprt ); + } + + if (++cons_current_x_800AB4B4 > CONSOLE_WIDTH) + { + cons_current_x_800AB4B4 = 0; + cons_current_y_800AB4B0++; + } +} + +void drawConsole_jamming_8003A2D0(MenuPrim *pGlue, int idx) +{ + int i; + int count; + SPRT *sprt1, *sprt2, *sprt3, *sprt4; + radar_uv *uv; + int rgb; + short clut; + short y0; + + uv = &gRadarUV_8009E30C[10]; + rgb = gRadarRGBTable_8009E3B8[idx]; + + count = 7; + if (cons_current_y_800AB4B0 < 8) + { + count = cons_current_y_800AB4B0; + } + + _NEW_PRIM(sprt1, pGlue); + clut = gRadarClut_800AB498[0]; + + sprt1->x0 = -28; + sprt1->y0 = count * 2 + 4; + sprt1->clut = clut; + sprt1->u0 = uv->field_0_x; + sprt1->v0 = uv->field_1_y + cons_current_y_800AB4B0 % 16; + sprt1->h = 1; + LSTORE(rgb, &sprt1->r0); + + setSprt(sprt1); + setSemiTrans(sprt1, 1); + addPrim(pGlue->ot, sprt1); + + if (cons_current_x_800AB4B4 > 28) + { + sprt1->w = 28; + + _NEW_PRIM(sprt2, pGlue); + *sprt2 = *sprt1; + + sprt2->v0 = uv->field_1_y + (cons_current_y_800AB4B0 + 3) % 16; + sprt2->x0 += 28; + sprt2->w = cons_current_x_800AB4B4 - 28; + + addPrim(pGlue->ot, sprt2); + } + else + { + sprt1->w = cons_current_x_800AB4B4; + } + + for (i = 1; i <= count; i++) + { + _NEW_PRIM(sprt3, pGlue); + *sprt3 = *sprt1; + + y0 = sprt1->y0; + + sprt3->w = 28; + sprt3->y0 = y0 - i * 2; + sprt3->v0 = uv->field_1_y + (cons_current_y_800AB4B0 - i + 16) % 16; + + addPrim(pGlue->ot, sprt3); + + _NEW_PRIM(sprt4, pGlue); + *sprt4 = *sprt3; + + sprt4->w = 28; + sprt4->v0 = uv->field_1_y + (cons_current_y_800AB4B0 - i + 19) % 16; + sprt4->x0 += 28; + + addPrim(pGlue->ot, sprt4); + } + + cons_current_x_800AB4B4++; + if (cons_current_x_800AB4B4 >= 57) + { + cons_current_x_800AB4B4 = 0; + cons_current_y_800AB4B0++; + } +} + +void drawCounter_8003A664(MenuPrim *pGlue, int alertLevel, int code) +{ + int digit; + int i, j; + short *pImagePixel; + int var_v1; + TILE *pCounterOrnamentTile; + radar_uv *pCounterOrnamentUV; + radar_uv *pCounterDigitsUV; + SPRT *pCounterDigitSprt; + int counterDigitX, counterDigitY; + + if (alertLevel == 255) + { + alertLevel = 256; + } + + alertLevel = (alertLevel * 9999) / 256; + + // For each digit of the counter (99.99), from the rightmost (i = 0) to the leftmost (i = 3). + for (i = 0; i < 4; i++) + { + digit = alertLevel % 10; + alertLevel /= 10; + + pImagePixel = &image_8009E338[i * 16 + 1]; + var_v1 = gDigit7Segment_8009E60C[digit]; + // For each segment of the digit, determine whether it + // is active or not (not really sure about this). + for (j = 0; j < 7; j++, pImagePixel++, var_v1 >>= 1) + { + // According to the call stack, code can be 0 or 1, so the last + // two elements of the arrays below are never addressed. + if (var_v1 & 1) + { + *pImagePixel = active7segmentColors_800AB4A0[code]; + } + else + { + *pImagePixel = inactive7segmentColors_800AB4A8[code]; + } + } + } + + LoadImage(&rect_800AB490, (u_long *)image_8009E338); + + // Draw the static elements around the counter (two horizontal lines and the dot). + pCounterOrnamentUV = gStaticCounterElsUVs_8009E3C8; + for (i = 3; i > 0; i--) + { + pCounterOrnamentTile = (TILE *)pGlue->next; + pGlue->next += sizeof(TILE); + + pCounterOrnamentTile->x0 = pCounterOrnamentUV->field_0_x - 34; + pCounterOrnamentTile->y0 = pCounterOrnamentUV->field_1_y - 26; + LSTORE(gRadarRGBTable_8009E3B8[code], &pCounterOrnamentTile->r0); + pCounterOrnamentTile->w = pCounterOrnamentUV->field_2_w; + pCounterOrnamentTile->h = pCounterOrnamentUV->field_3_h; + + setTile(pCounterOrnamentTile); + setSemiTrans(pCounterOrnamentTile, 1); + + addPrim(pGlue->ot, pCounterOrnamentTile); + + pCounterOrnamentUV++; + } + + // Draw the four digits of the counter. + pCounterDigitsUV = &gRadarUV_8009E30C[9]; + counterDigitX = -25; + counterDigitY = 6; + for (i = 0; i < 4; i++) + { + pCounterDigitSprt = (SPRT *)pGlue->next; + pGlue->next += sizeof(SPRT); + + initSprt_80039D5C(pCounterDigitSprt, counterDigitX, counterDigitY, pCounterDigitsUV, 0x80808080); + pCounterDigitSprt->clut = gRadarClut_800AB498[3 - i]; + + addPrim(pGlue->ot, pCounterDigitSprt); + + counterDigitX += 12; + if (i == 1) + { + counterDigitX += 5; + } + } + + drawConsole_alertEvasion_8003A0BC(pGlue, code); +} + +void drawSymbols_8003A978(MenuPrim *prim, int x, int code) +{ + SPRT *sprt; + + _NEW_PRIM(sprt, prim); + + // code can be 0 (alert), 1 (evasion) and 3 (jamming). + initSprt_80039D5C(sprt, x - 34, -12, &gRadarUV_8009E30C[code * 2], gRadarRGBTable_8009E3B8[code]); + addPrim(prim->ot, sprt); +} + +// Slightly misleading name as it also handles the radar in normal mode. +void drawAlertEvasionJammingPanel_8003AA2C(MenuWork *work, OT_TYPE *ot, int radarMode, int alertLevel) +{ + unsigned int randValue; + DR_TPAGE *tpage1; + TILE *tile; + DR_TPAGE *tpage2; + LINE_F2 *line; + int i; + + switch (radarMode) + { + case ALERT_JAMMING: + LoadImage(&rect_800AB490, (u_long *)image_8009E338); + drawSymbols_8003A978(work->prim, 6, 3); + drawConsole_jamming_8003A2D0(work->prim, 3); + drawHeader_80039EC4(work->prim, -25, 3); + break; + + case ALERT_OFF: + radarMode = 4; + break; + + default: + drawCounter_8003A664(work->prim, alertLevel, 3 - radarMode); + drawSymbols_8003A978(work->prim, 9, 3 - radarMode); + drawHeader_80039EC4(work->prim, -25, 3 - radarMode); + break; + } + + NEW_PRIM(tpage1, work); + + setDrawTPage(tpage1, 1, 0, getTPage(0, 2, 960, 256)); + addPrim(ot, tpage1); + + // Draw some horizontal lines with random width (never noticed them...). + randValue = (rand() << 16) | (rand()); + for (i = 0; i < 52;) + { + NEW_PRIM(line, work); + + line->y0 = i - 26; + line->y1 = i - 26; + line->x0 = randValue % 138 - 69; + line->x1 = line->x0 + (randValue / 256) % 69 + 8; + LSTORE(gRadarRGBTable2_8009E3D4[radarMode - 1], &line->r0); + + setLineF2(line); + setSemiTrans(line, 1); + addPrim(ot, line); + + i += 4 + (randValue >> 24 & 7); + randValue = randValue << 25 | randValue >> 7; + } + + // tile is used for the background color of the entire radar when in alert, evasion and jamming mode. + NEW_PRIM(tile, work); + tile->x0 = -34; + tile->w = 69; + tile->y0 = -26; + tile->h = 52; + LSTORE(gRadarRGBTable2_8009E3D4[radarMode - 1], &tile->r0); + setTile(tile); + setSemiTrans(tile, 1); + addPrim(ot, tile); + + NEW_PRIM(tpage2, work); + setDrawTPage(tpage2, 1, 0, getTPage(0, 0, 960, 256)); + addPrim(ot, tpage2); +} + +void menu_radar_load_rpk_8003AD64(void) +{ + RECT rect; + + rect.x = 992; + rect.y = 336; + rect.w = gRadar_rpk_800ABAC8->w; + rect.h = gRadar_rpk_800ABAC8->h; + LoadImage(&rect, (u_long *)&gRadar_rpk_800ABAC8[1]); +} + +void menu_init_radar_helper_8003ADAC(void) +{ + gRadar_rpk_800ABAC8 = menu_rpk_get_img_8003DDB4(41); + menu_radar_load_rpk_8003AD64(); +} + +void menu_radar_helper_8003ADD8(MenuWork *work, int index) +{ + DRAWENV drawEnv; + RADAR_T *radar; + + radar = &work->field_CC_radar_data; + if (index == 0) + { + DG_InitDrawEnv(&drawEnv, + radar->pos_x + 235, + radar->pos_y + 16, + 69, 52); + drawEnv.ofs[0] = radar->pos_x + 269; + drawEnv.ofs[1] = radar->pos_y + 42; + } + else + { + DG_InitDrawEnv(&drawEnv, + radar->pos_x + 555, + radar->pos_y + 16, + 69, 52); + drawEnv.ofs[0] = radar->pos_x + 589; + drawEnv.ofs[1] = radar->pos_y + 42; + } + + work->field_CC_radar_data.clip_rect = drawEnv.clip; + drawEnv.isbg = 0; + SetDrawEnv(&work->field_CC_radar_data.dr_env[index], &drawEnv); +} + +void draw_radar(MenuWork *work, OT_TYPE *ot) +{ + int alertLevel, alertMode; + DR_AREA *twin, *twin2, *twin3; + POLY_G4 *verticalSweep; + DR_TPAGE *tpage; + RECT clip; + + alertLevel = GM_AlertLevel; + alertMode = GM_AlertMode; + + if (alertMode > ALERT_ACTIVE) + { + alertMode = ALERT_OFF; + } + + if (alertMode == ALERT_OFF && gFn_radar_800AB48C == NULL) + { + if (GM_PlayerStatus & PLAYER_INTRUDE) + { + return; + } + + if (GM_OptionFlag & OPTION_RADAR_OFF) + { + return; + } + } + + drawBorder_800390FC(work, ot); + addPrim(ot, &work->field_CC_radar_data.org_env[GV_Clock]); + + if (gFn_radar_800AB48C) + { + gFn_radar_800AB48C(work, ot); + } + else + { + if (alertMode == ALERT_OFF) + { + if (GM_GameStatus & (STATE_CHAFF | STATE_JAMMING)) + { + alertMode = ALERT_JAMMING; + } + } + + switch (alertMode) + { + case ALERT_OFF: + alertLevel = work->field_CC_radar_data.counter; + + if (alertLevel > 0) + { + alertLevel -= 24; + + if (alertLevel == 69) + { + GM_SeSet2(0, 0x3F, SE_RADAR_CHIME); // Used when evasion or jamming mode ends. + } + + clip = work->field_CC_radar_data.clip_rect; + + if (alertLevel >= 0) + { + drawAlertEvasionJammingPanel_8003AA2C(work, ot, 0, 0); + clip.w = alertLevel; + clip.x += 69; + clip.x -= alertLevel; + NEW_PRIM(twin2, work); + twin = twin2; + SetDrawArea(twin, &clip); + addPrim(ot, twin); + } + + // Draw the green, vertical sweeping rectangle that appears + // when evasion or jamming mode ends. + NEW_PRIM(verticalSweep, work); + verticalSweep->x0 = 11 - alertLevel; + verticalSweep->y0 = -26; + verticalSweep->x1 = 35 - alertLevel; + verticalSweep->y1 = -26; + verticalSweep->x2 = 11 - alertLevel; + verticalSweep->y2 = clip.h - 26; + verticalSweep->x3 = 35 - alertLevel; + verticalSweep->y3 = clip.h - 26; + LSTORE(0x000000, &verticalSweep->r0); + LSTORE(0x000000, &verticalSweep->r2); + LSTORE(0x48A000, &verticalSweep->r1); + LSTORE(0x48A000, &verticalSweep->r3); + setPolyG4(verticalSweep); + setSemiTrans(verticalSweep, 1); + addPrim(ot, verticalSweep); + + NEW_PRIM(tpage, work); + setDrawTPage(tpage, 1, 0, getTPage(0, 1, 960, 256)); + + addPrim(ot, tpage); + + drawMap_800391D0(work, ot, 0); + clip = work->field_CC_radar_data.clip_rect; + + if (alertLevel >= 0) + { + clip.w = (0x45 - alertLevel); + } + + NEW_PRIM(twin3, work); + twin = twin3; + SetDrawArea(twin, &clip); + addPrim(ot, twin); + work->field_CC_radar_data.counter -= 2; + } + else + { + drawMap_800391D0(work, ot, 0); + cons_current_y_800AB4B0 = 0; + cons_current_x_800AB4B4 = 0; + } + break; + + case ALERT_JAMMING: + case ALERT_EVASION: + case ALERT_ACTIVE: + work->field_CC_radar_data.counter = 93; + + // RadarMode enum better clarifies what's going on here. + // AlertMode and RadarMode kind of interlace. + if (alertMode == ALERT_JAMMING && work->field_CC_radar_data.prev_mode == ALERT_OFF) + { + GM_SeSet2(0, 0x3F, SE_RADAR_JAMMED); + } + drawAlertEvasionJammingPanel_8003AA2C(work, ot, alertMode, alertLevel); + break; + } + + work->field_CC_radar_data.prev_mode = alertMode; + } + + addPrim(ot, &work->field_CC_radar_data.dr_env[GV_Clock]); +} + +void menu_radar_update_8003B350(MenuWork *work, OT_TYPE *ot) +{ + /* Also used to return here under __psyz, for the same reason menuman.c + * did, and it is stale for the same reason: the hand-packed tag idioms it + * warns about are all in the #else arm. The radar is what tells the player + * where they are, so its absence is not the small loss the note claimed. */ + int clipY; + + if (work->field_CC_radar_data.display_flag) + { + if (work->field_2A_state == MENU_CLOSED) + { + if ((GM_GameStatus & STATE_HIDE_RADAR) != 0) + { + clipY = work->field_CC_radar_data.pos_y - 16; + if (clipY < (-63)) + { + GM_GameStatus |= STATE_RADAR_OFF; + GM_GameStatus &= ~STATE_HIDE_RADAR; + clipY = -64; + } + } + else if ((GM_GameStatus & STATE_SHOW_RADAR) != 0) + { + GM_GameStatus &= ~STATE_RADAR_OFF; + clipY = work->field_CC_radar_data.pos_y + 16; + if (clipY >= 0) + { + clipY = 0; + GM_GameStatus &= ~STATE_SHOW_RADAR; + } + } + else + { + clipY = 0; + } + if ((GM_GameStatus & (STATE_JPEGCAM | STATE_RADAR_OFF)) != 0) + { + work->field_CC_radar_data.pos_y = -64; + } + else + { + work->field_CC_radar_data.pos_y = clipY; + menu_radar_helper_8003ADD8(work, GV_Clock); + draw_radar(work, ot); + } + } + } +} + +void menu_radar_init_8003B474(MenuWork *work) +{ + unsigned char field_28_flags; // $v1 + + field_28_flags = work->field_28_flags; + work->field_2C_modules[MENU_RADAR] = menu_radar_update_8003B350; + work->field_CC_radar_data.display_flag = 1; + work->field_CC_radar_data.pos_x = 0; + work->field_CC_radar_data.pos_y = 0; + work->field_28_flags = field_28_flags | 8; + menu_radar_helper_8003ADD8(work, 0); + menu_radar_helper_8003ADD8(work, 1); + + work->field_CC_radar_data.org_env[0] = work->field_4C_drawEnv[0]; + work->field_CC_radar_data.org_env[1] = work->field_4C_drawEnv[1]; + + menu_init_radar_helper_8003ADAC(); + gFn_radar_800AB48C = 0; + MENU_SetRadarScale(4096); +} + +void menu_radar_kill_8003B554(MenuWork *work) +{ + work->field_28_flags &= ~8u; +} diff --git a/components/mgs/mgs/source/menu/radar.h b/components/mgs/mgs/source/menu/radar.h new file mode 100644 index 00000000..1797b1e1 --- /dev/null +++ b/components/mgs/mgs/source/menu/radar.h @@ -0,0 +1,24 @@ +#ifndef __MGS_MENU_RADAR_H__ +#define __MGS_MENU_RADAR_H__ + +#include "menuman.h" + +typedef struct radar_uv // CHARA_TABLE +{ + char field_0_x; + char field_1_y; + unsigned char field_2_w; + unsigned char field_3_h; +} radar_uv; + +typedef void (*TRadarFn_800AB48C)(MenuWork *, u_long *); + +void menu_radar_update_8003B350(MenuWork *work, OT_TYPE *ot); +void menu_init_radar_helper_8003ADAC(void); +void menu_radar_helper_8003ADD8(MenuWork *work, int index); +void MENU_SetRadarFunc(TRadarFn_800AB48C func); + +void draw_radar(MenuWork *work, OT_TYPE *ot); +void menu_radar_load_rpk_8003AD64(void); + +#endif // __MGS_MENU_RADAR_H__ diff --git a/components/mgs/mgs/source/menu/radio.c b/components/mgs/mgs/source/menu/radio.c new file mode 100644 index 00000000..30d9a266 --- /dev/null +++ b/components/mgs/mgs/source/menu/radio.c @@ -0,0 +1,2399 @@ +#include "radio.h" +#include "menuman.h" + +#include +#include +#include +#include +#include + +#include "common.h" +#include "radar.h" +#include "libgv/libgv.h" +#include "libdg/libdg.h" +#include "libgcl/libgcl.h" +#include "game/game.h" +#include "linkvar.h" +#include "sound/g_sound.h" + +int SECTION(".sbss") dword_800ABAF8; +int SECTION(".sbss") gRadioClut_800ABAFC; +int SECTION(".sbss") gCodecAction; +char *SECTION(".sbss") dword_800ABB04; +MenuCallbackProc_800ABB08 SECTION(".sbss") gMenuCallbackProc_800ABB08; +int SECTION(".sbss") dword_800ABB10; +int SECTION(".sbss") dword_800ABB14; +short SECTION(".sbss") gCodecFadingCount; +short SECTION(".sbss") word_800ABB1A; +int SECTION(".sbss") dword_800ABB1C; + +extern PANEL_TEXTURE dword_800BDA10; +extern PANEL_TEXTURE dword_800BDA30; +extern RadioIncomingCall gRadioIncomingCall_8009E708; + +STATIC char dword_800AB610[8] = {2, 4, 3, 1, 4, 3, 1, 0}; + +extern int FS_DiskNum; + +extern char menu_string_format_8009E714[]; + +// 7 segment digit bits +// --0-- +// | | +// 1 2 +// --3-- +// 4 5 +// | | +// --6-- +// example 4 = 0b101110 = 0x2E +// 8, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 +char gDigit7Segment_8009E60C[] = {0x77, 0x24, 0x5D, 0x6D, 0x2E, 0x6B, 0x7B, 0x25, 0x7F, 0x6F, 0x00, 0x00}; + +RadioUnknown dword_8009E618 = +{ + {{1, 0, 7, 2}, {0, 1, 2, 7}, {7, 1, 2, 7}, {1, 7, 7, 2}, {0, 9, 2, 7}, {7, 9, 2, 7}, {1, 15, 7, 2}}, + 0x3D472E, + 0x3D472E +}; + +RadioUnknown dword_8009E63C = +{ + {{1, 0, 5, 2}, {0, 1, 2, 6}, {5, 1, 2, 6}, {1, 6, 5, 2}, {0, 8, 2, 6}, {5, 8, 2, 6}, {1, 13, 5, 2}}, + 0x3D472E, + 0x3D472E +}; + +char dword_8009E660[] = {10, 10, 15, 12}; + +Radio_8009E664 dword_8009E664[] = { +// x0 y0 w h r0 g0 b0 code + {120, 58, 80, 48, 0x13, 0x1F, 0x1A, GPU_CODE_TILE}, + {116, 55, 5, 2, 0x20, 0x30, 0x29, GPU_CODE_TILE}, + {199, 55, 5, 2, 0x20, 0x30, 0x29, GPU_CODE_TILE}, + {116, 107, 5, 2, 0x20, 0x30, 0x29, GPU_CODE_TILE}, + {199, 107, 5, 2, 0x20, 0x30, 0x29, GPU_CODE_TILE}, + {121, 55, 78, 1, 0x20, 0x30, 0x29, GPU_CODE_TILE}, + {121, 108, 78, 1, 0x20, 0x30, 0x29, GPU_CODE_TILE}, + {129, 38, 62, 10, 0x13, 0x1F, 0x1A, GPU_CODE_TILE}, + {129, 117, 62, 10, 0x13, 0x1F, 0x1A, GPU_CODE_TILE}, + {90, 76, 25, 11, 0x13, 0x1F, 0x1A, GPU_CODE_TILE}, + {205, 76, 25, 11, 0x13, 0x1F, 0x1A, GPU_CODE_TILE}, + {84, 38, 43, 1, 0x13, 0x1F, 0x1A, GPU_CODE_TILE}, + {193, 38, 43, 1, 0x13, 0x1F, 0x1A, GPU_CODE_TILE}, + {84, 126, 43, 1, 0x13, 0x1F, 0x1A, GPU_CODE_TILE}, + {193, 126, 43, 1, 0x13, 0x1F, 0x1A, GPU_CODE_TILE}, + {91, 46, 36, 1, 0x13, 0x1F, 0x1A, GPU_CODE_TILE}, + {193, 46, 36, 1, 0x13, 0x1F, 0x1A, GPU_CODE_TILE}, + {91, 118, 36, 1, 0x13, 0x1F, 0x1A, GPU_CODE_TILE}, + {193, 118, 36, 1, 0x13, 0x1F, 0x1A, GPU_CODE_TILE} +}; + +RadioCoordsStru_8009E6FC gRadioCoords_8009E6FC[] = { + {222, 81, 214, 77, 214, 85}, + {98, 81, 106, 77, 106, 85} +}; + +RadioIncomingCall gRadioIncomingCall_8009E708 = {0, 0, -1, 0}; + +// width map for the special characters +char menu_string_format_8009E714[] = { + '.', 2, + '@', 2, + ':', 2, + '_', 4, + '!', 2, + '?', 6, + '+', 6, + '-', 6, + '/', 6, + '*', 6, + '{', 3, + '}', 3, + '\0' +}; + +enum CodecAction { + CODEC_ACTION_NONE = 0x00, + CODEC_ACTION_PTT = 0x01, + CODEC_ACTION_MEMORY = 0x02, + CODEC_ACTION_RIGHT = 0x04, + CODEC_ACTION_LEFT = 0x08, +}; + +/** + * @brief Fade in/out the codec screen. + * + * This function creates a semi-transparent tile to fade in or out the codec + * screen using the intensity specified. + * + * @param work Pointer to the MenuWork structure. + * @param ot Pointer to the ordering table. + * @param opacity The intensity for the fade effect (0-255). + */ +void FadeCodecScreen(MenuWork *work, OT_TYPE *ot, int opacity) +{ + TILE *tile; + DR_TPAGE *tpage; + + NEW_PRIM(tile, work); + + if (opacity > 255) + { + opacity = 255; + } + if (opacity < 0) + { + opacity = 0; + } + LSTORE(opacity << 16 | opacity << 8 | opacity, &tile->r0); + + setTile(tile); + setSemiTrans(tile, 1); + + tile->x0 = 0; + tile->y0 = 0; + tile->w = FRAME_WIDTH; + tile->h = FRAME_HEIGHT; + addPrim(ot, tile); + + NEW_PRIM(tpage, work); + + setDrawTPage(tpage, 1, 0, getTPage(0, 2, 960, 256)); + addPrim(ot, tpage); +} + +// draw the 7 segment display on the codec screen +void sub_8003FD50(MenuPrim *pMenuPrim, int xoff, int yoff, int param_4, RadioUnknown *pRadioUnknown, int abe) +{ + int bit; + int iVar1; + int i; + unsigned int color; + LINE_F4 *pLine; + char *pCoords; + int iVar2; + + bit = 1; + iVar1 = gDigit7Segment_8009E60C[param_4]; + + for (i = 0; i < 7; i++, bit <<= 1) + { + if (iVar1 & bit) + { + color = pRadioUnknown->color1; + } + else + { + color = pRadioUnknown->color2; + + if (color == 0) + { + continue; + } + } + + _NEW_PRIM(pLine, pMenuPrim); + LSTORE(color, &pLine->r0); + + pCoords = pRadioUnknown->coords[i]; + pLine->x0 = pCoords[0] + xoff; + pLine->y0 = pCoords[1] + yoff; + + iVar2 = dword_800AB610[i]; + + if (iVar2 < 3) + { + pLine->x1 = pLine->x0 + pCoords[2] - 1; + pLine->y1 = pLine->y0; + pLine->x2 = pLine->x1; + pLine->y2 = pLine->y0 + pCoords[3] - 1; + pLine->x3 = pLine->x0; + pLine->y3 = pLine->y2; + + if (iVar2 == 1) + { + pLine->x0++; + pLine->x1--; + } + else + { + pLine->x2--; + pLine->x3++; + } + } + else + { + pLine->x1 = pLine->x0; + pLine->y1 = pLine->y0 + pCoords[3] - 1; + pLine->x2 = pLine->x1 + pCoords[2] - 1; + pLine->y2 = pLine->y1; + pLine->x3 = pLine->x2; + pLine->y3 = pLine->y0; + + if (iVar2 == 3) + { + pLine->y0++; + pLine->y1--; + } + else + { + pLine->y2--; + pLine->y3++; + } + } + + setLineF4(pLine); + setSemiTrans(pLine, abe); + addPrim(pMenuPrim->ot, pLine); + } +} + +void menu_init_sprt_8003D0D0(SPRT *pPrim, PANEL_TEXTURE *pUnk, int offset_x, int offset_y); + +void sub_8003FFB0(MenuPrim *pGlue, int x0, int y0, int rgb) +{ + TILE *pPrim; + + _NEW_PRIM(pPrim, pGlue); + + pPrim->w = 2; + pPrim->h = 2; + LSTORE(rgb, &pPrim->r0); + pPrim->x0 = x0 + 11; + pPrim->y0 = y0 + 15; + + setTile(pPrim); + addPrim(pGlue->ot, pPrim); +} + +extern RadioUnknown dword_8009E618; +extern RadioUnknown dword_8009E63C; +extern char dword_8009E660[]; // This should have at least 5 elements (loop in menu_radio_codec_helper_helper14_helper_80040034 has 5 iterations) + +void menu_radio_codec_helper_helper14_helper_80040034(MenuPrim *pGlue, int x, int y, int param_4) +{ + int color; + int idx; + int count; + RadioUnknown *pRadioUnknown; + + color = 0x3D472E; + + pRadioUnknown = &dword_8009E63C; + pRadioUnknown->color1 = color; + pRadioUnknown->color2 = 0; + + idx = 0; + count = 10000; + + while (count > 0) + { + sub_8003FD50(pGlue, x, y, (param_4 / count) % 10, pRadioUnknown, 0); + + if (count == 100) + { + sub_8003FFB0(pGlue, x, y, color); + } + else if (count == 1000) + { + pRadioUnknown = &dword_8009E618; + pRadioUnknown->color1 = color; + pRadioUnknown->color2 = 0; + y -= 2; + } + + count /= 10; + x += dword_8009E660[idx++]; + } +} + +void menu_radio_codec_helper_helper14_helper2_800401AC(MenuPrim *pGlue, int xoff, int yoff) +{ + RadioUnknown *pUnk; + int i; + int color; + + color = 0x3D472E; + + pUnk = &dword_8009E63C; + pUnk->color1 = color; + + for (i = 0; i < 5; xoff += dword_8009E660[i], i++) // TODO: does this read one past the end of dword_8009E660? + { + sub_8003FD50(pGlue, xoff, yoff, 8, pUnk, 0); + if (i == 1) + { + pUnk = &dword_8009E618; + pUnk->color1 = color; + + yoff -= 2; + } + else if (i == 2) + { + sub_8003FFB0(pGlue, xoff, yoff, color); + } + } +} + +void menu_radio_codec_helper_helper14_helper5_800402A0(MenuPrim *pGlue, int xoff, int yoff, int param_4) +{ + int color, color2; + int i; + TILE *pTile; + int width; + + color2 = (param_4 < 0) ? 0x3D472E : 0; + param_4 *= 5; + + for (i = 1; i < 44; i++) + { + if ((i % 5) == 0) + { + continue; + } + + if (param_4 > (44 - i)) + { + color = 0x3D472E; + } + else + { + color = color2; + + if (color == 0) + { + continue; + } + } + + _NEW_PRIM(pTile, pGlue); + + pTile->x0 = xoff; + pTile->y0 = yoff + i; + + width = 300; + width /= i; + width += 3; + + if (width > 76) + { + width = 76; + } + + pTile->w = width; + pTile->h = 1; + LSTORE(color, &pTile->r0); + + setTile(pTile); + addPrim(pGlue->ot, pTile); + } +} + +void menu_RadioCall_helper_800403E4(void) +{ + TIM *tim; + RECT clut_rect; + char *pixel_data; + RECT *pixel_rect; + + tim = GV_GetCache(GV_CacheID2("call", 'r')); + + clut_rect.x = 960; + clut_rect.y = 371; + clut_rect.w = 16; + clut_rect.h = 1; + + LoadImage(&clut_rect, (u_long *)tim->clut_data); + + pixel_data = (char *)&tim->clut_data[tim->clut.bnum >> 1]; + pixel_rect = (RECT *)(pixel_data - 8); + + gRadioClut_800ABAFC = getClut(clut_rect.x, clut_rect.y); + + pixel_rect->x = 960; + pixel_rect->y = 372; + + LoadImage(pixel_rect, (u_long *)pixel_data); +} + +void menu_radio_update_helper3_80040498(MenuPrim *pGlue) +{ + SPRT *pPrim; + DR_TPAGE *tpage; + + _NEW_PRIM(pPrim, pGlue); + + LSTORE(0x80808080, &pPrim->r0); + setUV0(pPrim, 0, 116); + setXY0(pPrim, 130, 49); + setWH(pPrim, 60, 12); + pPrim->clut = gRadioClut_800ABAFC; + + setSprt(pPrim); + setSemiTrans(pPrim, 1); + addPrim(pGlue->ot, pPrim); + + _NEW_PRIM(tpage, pGlue); + + setDrawTPage(tpage, 1, 0, getTPage(0, 1, 960, 256)); + + addPrim(pGlue->ot, tpage); +} + +void menu_radio_codec_helper_helper14_helper3_80040590(MenuPrim *pGlue, Radio_8009E664 *param_2, int cnt, int xoff, int yoff) +{ + Radio_8009E664 *tileInfo; + TILE *tile; + + tileInfo = param_2; + while (cnt > 0) + { + _NEW_PRIM(tile, pGlue); + + setlen(tile, 3); // part of setTile() + LCOPY(&tileInfo->r0, &tile->r0); + + tile->x0 = tileInfo->x0 + xoff; + tile->y0 = tileInfo->y0 + yoff; + tile->w = tileInfo->w; + tile->h = tileInfo->h; + + addPrim(pGlue->ot, tile); + cnt--; + tileInfo++; + } +} + +extern RadioCoordsStru_8009E6FC gRadioCoords_8009E6FC[]; + +static inline RadioCoordsStru_8009E6FC *get_gRadioCoords(int idx) +{ + return &gRadioCoords_8009E6FC[idx]; +} + +// Highlight the codec arrow buttons when changing frequency +// (idx 0 = right, idx 1 = left) +void menu_radio_codec_helper_helper14_helper6_helper_8004064C(MenuPrim *pGlue, int xpos, int ypos, int color, int idx) +{ + short coord; + POLY_F3 *pPoly; + LINE_F4 *pLine; + RadioCoordsStru_8009E6FC *radioCoords; + + _NEW_PRIM(pPoly, pGlue); + _NEW_PRIM(pLine, pGlue); + + LSTORE(color, &pPoly->r0); + LSTORE(color, &pLine->r0); + + setPolyF3(pPoly); + setLineF4(pLine); + + radioCoords = get_gRadioCoords(idx); + coord = radioCoords->field_0 + xpos; + pLine->x0 = coord; + pPoly->x0 = coord; + pLine->x3 = coord; + coord = radioCoords->field_1 + ypos; + pLine->y0 = coord; + pPoly->y0 = coord; + pLine->y3 = coord; + coord = radioCoords->field_2 + xpos; + pLine->x1 = coord; + pPoly->x1 = coord; + coord = radioCoords->field_3 + ypos; + pLine->y1 = coord; + pPoly->y1 = coord; + coord = radioCoords->field_4 + xpos; + pLine->x2 = coord; + pPoly->x2 = coord; + coord = radioCoords->field_5 + ypos; + pLine->y2 = coord; + pPoly->y2 = coord; + + addPrim(pGlue->ot, pPoly); + addPrim(pGlue->ot, pLine); +} + +// Highlight codec ui element depending on the action flags +void menu_radio_codec_helper_helper14_helper6_800407A4(MenuPrim *pGlue, int xpos, int ypos, int flags) +{ + unsigned color; + + if (flags < 0) + { + flags = 0xf; // Go into all branches below + color = 0x3d472e; + } + else + { + color = 0x3d472e; + } + + // Highlight PTT text when calling + if (flags & CODEC_ACTION_PTT) + { + TextConfig conf; + conf.xpos = xpos + 0x90; + conf.ypos = ypos + 0x28; + conf.color = 0x64000000 | color; + conf.flags = 0; + _menu_number_draw_string2(pGlue, &conf, "P#3T#3T"); + } + // Highlight MEMORY text when browsing the codec memory + if (flags & CODEC_ACTION_MEMORY) + { + TextConfig conf; + conf.xpos = xpos + 0x86; + conf.ypos = ypos + 0x77; + conf.color = 0x64000000 | color; + conf.flags = 0; + _menu_number_draw_string2(pGlue, &conf, "MEMORY"); + } + if (flags & CODEC_ACTION_RIGHT) + { + menu_radio_codec_helper_helper14_helper6_helper_8004064C(pGlue, xpos, ypos, color, 0); + } + if (flags & CODEC_ACTION_LEFT) + { + menu_radio_codec_helper_helper14_helper6_helper_8004064C(pGlue, xpos, ypos, color, 1); + } + return; +} + +void menu_radio_codec_helper_helper14_helper4_800408BC(MenuPrim *pGlue, int x, int y, int w, int h, int x2, int y2) +{ + TILE *pTile; + DR_TPAGE *pTpage; + SPRT *pSprt; + SPRT *pSprt2; + int i, j; + int ia, ja; + int color; + int color2; + + _NEW_PRIM(pTile, pGlue); + + LSTORE(0, &pTile->r0); + pTile->x0 = x; + pTile->y0 = y; + pTile->w = w; + pTile->h = h; + setTile(pTile); + addPrim(pGlue->ot, pTile); + + _NEW_PRIM(pTpage, pGlue); + _NEW_PRIM(pSprt, pGlue); + + color2 = COLOR_GRAY; + if (GV_Clock == 0) + { + setDrawTPage(pTpage, 0, 1, 0x120); + pSprt->u0 = x; + } + else + { + setDrawTPage(pTpage, 0, 1, 0x124); + pSprt->u0 = x + 64; + } + + pSprt->v0 = y; + setWH(pSprt, w, h); + setXY0(pSprt, x2, y2); + + setSprt(pSprt); + LSTORE(0x66000000 | color2, &pSprt->r0); + + addPrim(pGlue->ot, pSprt); + color2 >>= 1; + + for (i = -1; i <= 1; i++) + { + for (j = -1; j <= 1; j++) + { + ia = abs(i); + ja = abs(j); + + color = color2; + + if ((ja + ia) == 2) + { + color >>= 1; + } + + if ((j != 0) || (i != 0)) + { + _NEW_PRIM(pSprt2, pGlue); + + *pSprt2 = *pSprt; + LSTORE(0x66000000 | color, &pSprt2->r0); + + pSprt2->x0 += j; + pSprt2->y0 += i; + + addPrim(pGlue->ot, pSprt2); + } + } + } + + addPrim(pGlue->ot, pTpage); +} + +void menu_radio_codec_helper_helper15_80040B8C(MenuPrim *pGlue) +{ + DR_TPAGE *tpage1, *tpage2; + SPRT *sprt1, *sprt2; + int time; + short w, w_2; + short u0, u0_2; + + time = (GV_Time / 2) % 128; + + _NEW_PRIM(tpage1, pGlue); + _NEW_PRIM(tpage2, pGlue); + + if (GV_Clock == 0) + { + setDrawTPage(tpage1, 0, 1, getTPage(2, 1, 0, 0)); + setDrawTPage(tpage2, 0, 1, getTPage(2, 1, 256, 0)); + + u0 = 0; + w = 256; + u0_2 = 0; + w_2 = 64; + } + else + { + setDrawTPage(tpage1, 0, 1, getTPage(2, 1, 256, 0)); + setDrawTPage(tpage2, 0, 1, getTPage(2, 1, 512, 0)); + + u0 = 64; + w = 192; + u0_2 = 0; + w_2 = 128; + } + + _NEW_PRIM(sprt1, pGlue); + + LSTORE(0x181818, &sprt1->r0); + sprt1->y0 = time; + sprt1->x0 = 0; + sprt1->w = w; + sprt1->u0 = u0; + sprt1->h = 8; + sprt1->v0 = time; + + setSprt(sprt1); + setSemiTrans(sprt1, 1); + + addPrim(pGlue->ot, sprt1); + addPrim(pGlue->ot, tpage1); + + _NEW_PRIM(sprt2, pGlue); + + *sprt2 = *sprt1; + sprt2->x0 = w; + sprt2->w = w_2; + sprt2->u0 = u0_2; + + addPrim(pGlue->ot, sprt2); + addPrim(pGlue->ot, tpage2); +} + +extern Radio_8009E664 dword_8009E664[]; + +void menu_radio_codec_helper_helper14_80040DC4(MenuWork *work, int param_2) +{ + DR_TPAGE *tpage; + DR_STP *stp; + MenuPrim *pGlue; + + if (work->field_210_codec_state != 11 && work->field_210_codec_state != 14) + { + pGlue = work->prim; + menu_radio_codec_helper_helper14_helper4_800408BC(pGlue, 0, 128, 140, 89, 90, 30); + menu_radio_codec_helper_helper14_helper6_800407A4(pGlue, -90, 90, gCodecAction); + menu_radio_codec_helper_helper14_helper_80040034(pGlue, 51, 178, param_2); + menu_radio_codec_helper_helper14_helper5_800402A0(pGlue, 32, 149, dword_800ABAF8); + menu_radio_codec_helper_helper14_helper6_800407A4(pGlue, 0, -8, -1); + menu_radio_codec_helper_helper14_helper2_800401AC(pGlue, 141, 80); + menu_radio_codec_helper_helper14_helper5_800402A0(pGlue, 122, 51, -1); + menu_radio_codec_helper_helper14_helper3_80040590(pGlue, dword_8009E664, 19, 0, -8); + + _NEW_PRIM(stp, pGlue); + SetDrawStp(stp, 1); + addPrim(pGlue->ot, stp); + + _NEW_PRIM(tpage, pGlue); + setDrawTPage(tpage, 1, 0, getTPage(0, 0, 960, 256)); + addPrim(pGlue->ot, tpage); + } +} + +STATIC RECT RADIO_MES_VRAM_POS_800AB630 = {960, 260, 63, 76}; + +void init_radio_message_board_80040F74(MenuWork *work) +{ + KCB local_kcb; + KCB *allocated_kcb; + + if (work->field_214_font == NULL) + { + KCB *ptr_local_kcb = &local_kcb; + + GV_ZeroMemory(ptr_local_kcb, sizeof(KCB)); + ClearImage(&RADIO_MES_VRAM_POS_800AB630, 0, 0, 0); + + font_init_kcb(ptr_local_kcb, &RADIO_MES_VRAM_POS_800AB630, 960, 510); + font_set_kcb(ptr_local_kcb, -1, -1, 0, 6, 2, 0); + + allocated_kcb = (KCB *)GV_AllocMemory(GV_PACKET_MEMORY0, font_get_buffer_size(ptr_local_kcb) + sizeof(KCB)); + font_set_buffer(ptr_local_kcb, allocated_kcb + 1); + font_set_color(ptr_local_kcb, 0, 0x6739, 0); + font_set_color(ptr_local_kcb, 1, 0x3bef, 0); + font_set_color(ptr_local_kcb, 2, 0x3a4b, 0); + font_set_color(ptr_local_kcb, 3, 0x1094, 0); + font_clut_update(ptr_local_kcb); + + work->field_214_font = allocated_kcb; + memcpy(allocated_kcb, ptr_local_kcb, sizeof(KCB)); + + dword_800ABB04 = NULL; + } +} + +void menu_radio_codec_helper__helper13_800410E4(MenuWork *work, char *string) +{ + KCB *kcb = work->field_214_font; + dword_800ABB04 = string; + font_print_string(kcb, string); + font_update(kcb); +} + +void sub_80041118(MenuWork *work) +{ + KCB *kcb = work->field_214_font; + dword_800ABB04 = NULL; + font_clear(kcb); + font_update(kcb); +} + +int draw_radio_message(MenuWork *work, OT_TYPE *ot) +{ + KCB *kcb; + SPRT *pPrim; + + if (!dword_800ABB04) + { + return 0; + } + + kcb = work->field_214_font; + + NEW_PRIM(pPrim, work); + + setRGB0(pPrim, 128, 128, 128); + + pPrim->u0 = (RADIO_MES_VRAM_POS_800AB630.x % 64) * 4; + pPrim->v0 = RADIO_MES_VRAM_POS_800AB630.y; + setWH(pPrim, 252, 76); + setClut(pPrim, 960, 510); + pPrim->x0 = (FRAME_WIDTH - kcb->max_width) / 2; + pPrim->y0 = 132; + + setSprt(pPrim); + addPrim(ot, pPrim); + return 1; +} + +void sub_8004124C(MenuWork *work) +{ + GV_FreeMemory(GV_PACKET_MEMORY0, work->field_214_font); + work->field_214_font = NULL; + dword_800ABB04 = NULL; +} + +// nop count was incorrect to this point +// was a new function added in VR? +// or simply I made a counting mistake and no function was added + +int menu_radio_codec_helper_helper12_80041280(MenuWork *work, OT_TYPE *ot, GV_PAD *pPad) +{ + menu_chara_struct *pMenuChara; + KCB *kcb; + int last_index; + int var_s7; + int var_s2; + int index; + int ypos; + int color; + char *string; + + pMenuChara = work->field_218; + kcb = work->field_214_font; + + dword_800ABB04 = pMenuChara->field_C_pScript; + last_index = pMenuChara->field_1A_index; + + if ((pPad->press & PAD_UP) && (pMenuChara->field_1A_index > 0)) + { + pMenuChara->field_1A_index--; + } + + if (pPad->press & PAD_DOWN) + { + pMenuChara->field_1A_index++; + } + + var_s7 = 0; + var_s2 = 0; + index = 0; + ypos = kcb->l_skip; + + font_clear(kcb); + + GCL_SetArgTop(pMenuChara->field_C_pScript); + + while (var_s2 >= 0) + { + color = 0; + string = GCL_GetString(GCL_NextStr()); + + if (index == pMenuChara->field_1A_index) + { + color = 1; + var_s2 = 1; + } + + if (!GCL_NextStr()) + { + if (var_s2 == 0) + { + pMenuChara->field_1A_index = index; + color = 1; + } + + var_s2 = -1; + } + + font_draw_string(kcb, 0, ypos, string, color); + + if (var_s7 < kcb->max_width) + { + var_s7 = kcb->max_width; + } + + ypos += 18; + index++; + } + + if (last_index != pMenuChara->field_1A_index) + { + GM_SeSet2(0, 63, SE_MENU_CURSOR); + } + + font_update(kcb); + + kcb->max_width = var_s7; + + if (pPad->press & PAD_CIRCLE) + { + GM_LastResultFlag = pMenuChara->field_1A_index; + sub_80041118(work); + return 1; + } + + return 0; +} + +void draw_radio_wait_mark(MenuWork *work, OT_TYPE *ot) +{ + MenuPrim *pOtBuffer; // $v1 + POLY_F3 *pPrim; // $a0 + + if ( GV_Time % 16 >= 4 ) + { + pOtBuffer = work->prim; + _NEW_PRIM(pPrim, pOtBuffer); + pPrim->x1 = 288; + pPrim->y1 = 210; + + pPrim->x0 = pPrim->x1 - 4; + pPrim->x2 = pPrim->x1 + 4; + + pPrim->y0 = pPrim->y2 = pPrim->y1 - 4; + + *(int *)&pPrim->r0 = 0x80808080; + setPolyF3(pPrim); + addPrim(ot, pPrim); + } +} + +STATIC int codec_freq_800AB638 = 14000; + +// call a frequency +STATIC void menu_radio_codec_helper_helper11_8004150C(MenuWork *work) +{ + int pRadioCode; + short codec_freq_800AB638_copy; + + init_radio_message_board_80040F74(work); + work->field_212 = 0x1e; + GM_SeSet2(0, 0x3f, SE_RADIO_SEND); + pRadioCode = MENU_GetRadioCode(codec_freq_800AB638); + if (pRadioCode >= 0) + { + sub_80047D70(work, codec_freq_800AB638, pRadioCode); + codec_freq_800AB638_copy = codec_freq_800AB638; + work->field_210_codec_state = 2; + gMenuCallbackProc_800ABB08.type = 0; + gMenuCallbackProc_800ABB08.param2 = codec_freq_800AB638_copy; + return; + } + + work->field_210_codec_state = 9; +} + +STATIC int dword_800AB63C = 0; +STATIC short gCodecFadingStep = 0; +STATIC int dword_800AB644 = -1; + +STATIC void menu_radio_codec_helper_8004158C(MenuWork *work, OT_TYPE *ot, GV_PAD *pPad) +{ + menu_chara_struct *pCharaStruct; + menu_chara_struct *pCharaStruct2; + menu_chara_struct *pCharaStruct3; + int ret1; + int ret2; + int direction; + DR_TPAGE *tpage1; + DR_TPAGE *tpage2; + void *subtitles; + + if (gCodecFadingStep == 0) + { + if (gCodecFadingCount == 0) + { + goto skip_fading; + } + } + else + { + gCodecFadingCount += gCodecFadingStep; + if (gCodecFadingCount >= 0xFF) + { + gCodecFadingCount = 0xFF; + gCodecFadingStep = 0; + } + else if (gCodecFadingCount <= 0) + { + gCodecFadingCount = 0; + gCodecFadingStep = 0; + } + } + + FadeCodecScreen(work, ot, gCodecFadingCount); +skip_fading: + + switch (work->field_210_codec_state) + { + case 0: // codec opening + if (gCodecFadingStep == 0) + { + work->field_210_codec_state = 1; + } + break; + case 1: // codec open + + // if we have an incoming call + if (gRadioIncomingCall_8009E708.field_0 > 0) + { + if (gRadioIncomingCall_8009E708.field_2_timer < 0) + { + gMenuCallbackProc_800ABB08.type = (~gRadioIncomingCall_8009E708.field_2_timer << 4) | 2; + } + else + { + gMenuCallbackProc_800ABB08.type = 1; + } + gMenuCallbackProc_800ABB08.param2 = gRadioIncomingCall_8009E708.field_0; + codec_freq_800AB638 = gRadioIncomingCall_8009E708.field_0; + sub_80047D70(work, gRadioIncomingCall_8009E708.field_0, gRadioIncomingCall_8009E708.field_4); + work->field_210_codec_state = 2; + init_radio_message_board_80040F74(work); + gRadioIncomingCall_8009E708.field_0 = 0; + } + else + { + gCodecAction = CODEC_ACTION_NONE; + if (pPad->press & (PAD_SELECT | PAD_CROSS)) + { + work->field_212 = 0; + work->field_210_codec_state = 0x12; + } + else if (pPad->press & PAD_DOWN) + { + gCodecAction = CODEC_ACTION_MEMORY; + menu_radio_codec_helper_helper4_8004DE20(work); + work->field_210_codec_state = 0xA; + GM_SeSet2(0, 0x3F, SE_RADIO_SELECT); + } + else if (pPad->press & (PAD_UP | PAD_CIRCLE)) + { + gCodecAction = CODEC_ACTION_PTT; + menu_radio_codec_helper_helper11_8004150C(work); + } + else if (pPad->status & (PAD_LEFT | PAD_RIGHT)) + { + if (pPad->status & PAD_LEFT) + { + direction = -1; + gCodecAction = CODEC_ACTION_LEFT; + } + else + { + direction = 1; + gCodecAction = CODEC_ACTION_RIGHT; + } + if (dword_800ABB10 == direction) + { + if (dword_800AB63C < 10) + { + dword_800AB63C++; + } + else if (direction != 0) + { + codec_freq_800AB638 += direction; + GM_SeSet2(0, 0x3F, SE_RADIO_TUNING); + dword_800AB63C++; + } + } + else + { + dword_800AB63C = 0; + dword_800ABB10 = direction; + codec_freq_800AB638 += direction; + GM_SeSet2(0, 0x3F, SE_RADIO_TUNING); + } + if (codec_freq_800AB638 >= 14200) + { + codec_freq_800AB638 = 14000; + } + else if (codec_freq_800AB638 < 14000) + { + codec_freq_800AB638 = 14199; + } + } + else + { + dword_800ABB10 = 0; + dword_800AB63C = 0; + } + + // Blink the "PRESS SELECT TO EXIT" text periodically + if (GV_Time % 64 < 0x34) + { + MENU_Locate(0xA0, 0x82, 2); + MENU_Color(0x2E, 0x47, 0x3D); + MENU_Printf("PRESS SELECT TO EXIT"); + MENU_ResetText(); + } + } + break; + case 10: // Codec Memory open + menu_radio_draw_mem(work, ot); + ret1 = menu_radio_codec_helper_helper2_8004DF68(work, pPad); + if (pPad->press & PAD_SELECT) + { + work->field_212 = 0; + work->field_210_codec_state = 0x12; + menu_radio_codec_helper__helper3_sub_8004DF44(work); + sub_8004124C(work); + } + else if (ret1 != 0) + { + menu_radio_codec_helper__helper3_sub_8004DF44(work); + sub_8004124C(work); + if (ret1 >= 0) + { + codec_freq_800AB638 = ret1; + menu_radio_codec_helper_helper11_8004150C(work); + } + else + { + work->field_210_codec_state = 1; + } + } + break; + case 2: // codec call ringing + work->field_212--; + if (work->field_212 > 0) + { + menu_radio_codec_helper_helper9_80047FF4(); + } + else if (menu_radio_codec_helper_helper9_80047FF4() <= 0) + { + dword_800ABAF8 = 0; + work->field_210_codec_state = 3; + menu_radio_codec_state_2_helper_80048024(work); + work->field_212 = 0x10; + if (!(gMenuCallbackProc_800ABB08.type & 0x10)) + { + GM_SeSet2(0, 0x3F, SE_RADIO_STATIC); + } + } + break; + case 3: // codec call started? + pCharaStruct = work->field_218; + if (pCharaStruct->field_0_state != 5) + { + if (work->field_212 > 0) + { + if (work->field_212 == 8) + { + if (gMenuCallbackProc_800ABB08.type & 0x10) + { + menu_radio_codec_helper_helper10_80047EFC(work, 0); + gCodecFadingStep = -0x20; + } + else + { + menu_radio_codec_helper_helper10_80047EFC(work, 1); + } + } + work->field_212--; + dword_800ABAF8 = (0x10 - work->field_212) / 2; + } + else + { + switch (pCharaStruct->field_0_state) + { + case 0: + break; + case 1: + if (pCharaStruct->field_14_bInExecBlock == 0) + { + menu_radio_codec_helper__helper13_800410E4(work, pCharaStruct->field_C_pScript); + work->field_210_codec_state = 4; + work->field_212 = pPad->status; + } + else + { + work->field_210_codec_state = 5; + pCharaStruct->field_10_subtitles = NULL; + work->field_212 = 0; + } + break; + case 2: + printf("set call freq %d\n", codec_freq_800AB638); + menu_radio_codec_helper_helper_8004E198(codec_freq_800AB638); + work->field_210_codec_state = 6; + break; + case 3: + menu_radio_init_save_mode((int)pCharaStruct->field_C_pScript, + pCharaStruct->field_1A_index); + work->field_210_codec_state = 0xB; + gCodecFadingStep = 0x20; + break; + case 4: + work->field_210_codec_state = 16; + break; + } + } + } + break; + case 4: // codec call interrupted + draw_radio_wait_mark(work, ot); + if ((pPad->release & + (PAD_UP | PAD_DOWN | PAD_LEFT | PAD_RIGHT | PAD_TRIANGLE | PAD_CROSS | PAD_SQUARE | PAD_CIRCLE)) && + work->field_212 == 0) + { + menu_radio_codec_helper_helper8_80048044(); + sub_80041118(work); + work->field_210_codec_state = 3; + } + if (!(pPad->status & work->field_212)) + { + work->field_212 = 0; + } + else + { + work->field_212 = pPad->status; + } + break; + case 5: // codec call in progress + pCharaStruct2 = work->field_218; + if (!(GV_Time & 3)) + { + if (rand() % 4 <= 0) + { + if (dword_800ABAF8 < 7) + { + dword_800AB644 = 1; + } + } + else + { + if (dword_800ABAF8 >= 7) + { + dword_800AB644 = -1; + } + } + if (!(rand() & 7)) + { + dword_800AB644 = -dword_800AB644; + } + dword_800ABAF8 += dword_800AB644; + if (dword_800ABAF8 >= 0xA) + { + dword_800ABAF8 = 9; + } + else if (dword_800ABAF8 < 6) + { + dword_800ABAF8 = 6; + } + } + if (pCharaStruct2->field_14_bInExecBlock == 0) + { + work->field_210_codec_state = 3; + } + else + { + if (!(pCharaStruct2->field_18 & 1) && (pPad->release & PAD_CROSS)) + { + menu_radio_codec_helper__helper13_800410E4(work, pCharaStruct2->field_C_pScript); + dword_800ABAF8 = 8; + pCharaStruct2->field_14_bInExecBlock = 0; + pCharaStruct2->field_18 |= 0x100; + GM_StreamPlayStop(); + if (pCharaStruct2->field_0_state == 1) + { + work->field_210_codec_state = 4; + } + else + { + work->field_210_codec_state = 3; + } + } + else + { + if (pCharaStruct2->field_0_state == 2) + { + work->field_210_codec_state = 6; + } + else if (pCharaStruct2->field_0_state != 1) + { + break; + } + + subtitles = menu_radio_codec_helper_helper17_80038678(); + if (pCharaStruct2->field_10_subtitles != subtitles) + { + if (subtitles == NULL) + { + sub_80041118(work); + menu_radio_codec_helper_helper8_80048044(work); + } + else + { + menu_radio_codec_helper__helper13_800410E4(work, pCharaStruct2->field_C_pScript); + } + pCharaStruct2->field_10_subtitles = subtitles; + } + } + } + break; + case 9: + if (work->field_212 > 0) + { + work->field_212--; + if (work->field_212 == 0) + { + if (GM_OptionFlag & OPTION_ENGLISH) + { + menu_radio_codec_helper__helper13_800410E4(work, "NO RESPONSE"); + } + else + { + // 応答ありません。 + menu_radio_codec_helper__helper13_800410E4(work, + "\x90\xAD\x90\xAE\x81\x02\x81\x4A\x81\x3E\x81\x1B\x81\x53\xD0\x03"); + } + } + } + else + { + draw_radio_wait_mark(work, ot); + if (pPad->press & (PAD_UDLR | PAD_ABXY)) + { + work->field_210_codec_state = 7; + } + } + break; + case 6: + if (!(gMenuCallbackProc_800ABB08.type & 0x20)) + { + menu_radio_codec_helper_helper3_80047F44(work, 1); + } + work->field_210_codec_state = 7; + case 7: // codec call terminating + pCharaStruct3 = work->field_218; + dword_800ABAF8 = 0; + if (gMenuCallbackProc_800ABB08.type & 0x20) + { + sub_8004124C(work); + gCodecFadingStep = 0x10; + work->field_212 = 0; + work->field_210_codec_state = 0x13; + } + else if (pCharaStruct3->field_1C_radioDatFragment != NULL) + { + if (menu_radio_end_check() != 0) + { + menu_radio_codec_helper_helper7_80048080(); + ResetCodecState(); + } + } + else + { + sub_8004124C(work); + if ((gMenuCallbackProc_800ABB08.type & 0xF) == 2) + { + work->field_212 = 0; + work->field_210_codec_state = 18; + } + else + { + work->field_210_codec_state = 1; + } + } + break; + case 18: + if (dword_800ABB14 >= 0) + { + GM_VoxStream(dword_800ABB14, 0x40000000); + work->field_210_codec_state = 0x11; + dword_800ABB14 = -1; + } + GM_SetSound(0x01ffff21, SD_ASYNC); + gCodecFadingStep = 0x10; + work->field_212 = 0; + work->field_210_codec_state = 0x13; + break; + case 19: // codec screen closing + if (gCodecFadingStep == 0) + { + if (work->field_212 == 0 && (gMenuCallbackProc_800ABB08.type & 0x20)) + { + menu_radio_codec_helper_helper7_80048080(); + } + work->field_212++; + if (work->field_212 >= 3) + { + work->field_210_codec_state = 20; + } + } + break; + case 11: // opening save screen + if (gCodecFadingCount >= 0xFF) + { + gCodecFadingCount = 0; + gCodecFadingStep = 0; + work->field_210_codec_state = 12; + } + break; + case 12: // save screen open + if (menu_radio_do_file_mode(work, pPad) != 0) + { + work->field_210_codec_state = 13; + gCodecFadingCount = 0xFF; + gCodecFadingStep = -32; + return; + } + return; + case 13: // closing save screen + if ((gCodecFadingCount <= 0) && (pPad->status == 0)) + { + work->field_210_codec_state = 3; + menu_radio_codec_helper_helper8_80048044(); + } + break; + case 14: + ret2 = menu_radio_do_file_mode(work, pPad); + if (ret2 != 0) + { + sub_8004124C(work); + work->field_210_codec_state = 15; + GM_SetSound(0x01ffff21, SD_ASYNC); + gMenuCallbackProc_800ABB08.param2 = 0; + if (ret2 == 2) + { + GCL_RestoreVar(); + codec_freq_800AB638 = 0x36B0; + gMenuCallbackProc_800ABB08.param2 = FS_DiskNum + 1; + return; + } + } + return; + case 15: + work->field_210_codec_state = 20; + return; + case 16: // save dialog + if (menu_radio_codec_helper_helper12_80041280(work, ot, pPad) != 0) + { + work->field_210_codec_state = 3; + menu_radio_codec_helper_helper8_80048044(); + } + break; + } + + draw_radio_message(work, ot); + + NEW_PRIM(tpage1, work); + setDrawTPage(tpage1, 1, 0, getTPage(0, 0, 960, 256)); + addPrim(ot, tpage1); + + menu_radio_codec_helper_helper15_80040B8C(work->prim); + menu_radio_draw_face(work, work->field_218); + menu_radio_codec_helper_helper14_80040DC4(work, codec_freq_800AB638); + + NEW_PRIM(tpage2, work); + setDrawTPage(tpage2, 0, 1, getTPage(0, 0, 960, 256)); + addPrim(ot, tpage2); +} + +STATIC void menu_radio_update_helper5_80042160(MenuWork *work) +{ + dword_800AB63C = 0; + dword_800ABB10 = 0; + work->field_212 = 8; + work->field_210_codec_state = 0; + menu_radio_codec_create_state_80047CE4(work); +} + +STATIC void menu_radio_init_nullsub_80042190(MenuWork *work) +{ +} + +STATIC int dword_800AB648 = 0; + +STATIC void menu_radio_update_80042198(MenuWork *work, OT_TYPE *ot) +{ + GCL_ARGS args; + long argv[2]; + + GV_PAD *pPad; + int timer; + int streamStatus; + int lastCode; + unsigned int state; + + state = work->field_2A_state; + pPad = work->field_24_pInput; + if (GM_PlayerStatus & PLAYER_MENU_DISABLE) + { + return; + } + if (state == MENU_CLOSED) + { + if (!(GM_GameStatus & (STATE_VOX_STREAM | STATE_TAKING_PHOTO | STATE_RADIO_OFF)) && GV_PauseLevel == 0) + { + if ((gRadioIncomingCall_8009E708.field_0 > 0 && gRadioIncomingCall_8009E708.field_2_timer < 0) || + pPad->press & PAD_SELECT) + { + dword_800ABB1C = DG_FrameRate; + DG_FrameRate = 2; + gMenuCallbackProc_800ABB08.type = 0xF; + GM_GameStatus &= ~(STATE_PADMASK | STATE_PADRELEASE); + work->field_2A_state = MENU_CODEC_OPEN; + menu_radio_update_helper2_80038A7C(); + MENU_JimakuClear(); + GV_PauseLevel |= GV_PAUSE_STOP; + DG_FreeObjectQueue(); + DG_BackGroundBlack(); + GV_SetPacketTempMemory(); + menu_radio_update_helper5_80042160(work); + gCodecFadingStep = -32; + gCodecFadingCount = 0xFF; + if (gRadioIncomingCall_8009E708.field_0 > 0) + { + codec_freq_800AB638 = gRadioIncomingCall_8009E708.field_0; + if (gRadioIncomingCall_8009E708.field_2_timer == -2 || + gRadioIncomingCall_8009E708.field_2_timer == -4) + { + gCodecFadingStep = 0; + work->field_210_codec_state = 1; + } + } + if (gRadioIncomingCall_8009E708.field_8 != 0) + { + gRadioIncomingCall_8009E708.field_2_timer = -1; + } + work->field_214_font = NULL; + streamStatus = GM_StreamStatus(); + if (streamStatus != -1) + { + lastCode = GM_StreamGetLastCode(); + dword_800ABB14 = lastCode; + printf("GetPotion %d\n", lastCode); + } + else + { + dword_800ABB14 = streamStatus; + } + GM_StreamPlayStop(); + DG_UnDrawFrameCount = 2; + dword_800AB648 = 3; + GM_SetSound(0x01ffff20, SD_ASYNC); + if (gRadioIncomingCall_8009E708.field_0 >= 1 && gRadioIncomingCall_8009E708.field_0 <= 2) + { + init_radio_message_board_80040F74(work); + if (gRadioIncomingCall_8009E708.field_0 == 1) + { + menu_radio_update_helper4_8004D2D0(gRadioIncomingCall_8009E708.field_4); + } + else + { + menu_radio_init_save_mode((int)gRadioIncomingCall_8009E708.field_4, 0); + } + work->field_210_codec_state = 14; + gMenuCallbackProc_800ABB08.type = 1; + } + } + if (gRadioIncomingCall_8009E708.field_0 > 0 && gRadioIncomingCall_8009E708.field_2_timer > 0) + { + if (--gRadioIncomingCall_8009E708.field_2_timer == 0) + { + if (gRadioIncomingCall_8009E708.field_8 != 0) + { + gRadioIncomingCall_8009E708.field_2_timer = -1; + return; + } + gRadioIncomingCall_8009E708.field_0 = 0; + return; + } + if (gRadioIncomingCall_8009E708.field_8 != 0 || gRadioIncomingCall_8009E708.field_2_timer > 80) + { + timer = (399 - gRadioIncomingCall_8009E708.field_2_timer) % 40; + if (timer < 26 && timer % 13 < 8) + { + menu_radio_update_helper3_80040498(work->prim); + MENU_Color(0xFF, 0xFF, 0xFF); + MENU_Locate(0xA0, 0x3F, 2); + MENU_Printf("PUSH SELECT"); + MENU_ResetText(); + } + if (timer == 0 && + (gRadioIncomingCall_8009E708.field_2_timer > 240 || gRadioIncomingCall_8009E708.field_8 != 0)) + { + GM_SeSet2(0, 0x3F, SE_RADIO_INCOMING); + return; + } + } + } + } + } + else if (state == MENU_CODEC_OPEN) + { + if (work->field_210_codec_state == 20 && GM_StreamStatus() != 0) + { + work->field_2A_state = MENU_CLOSED; + menu_radio_update_helper_80038A6C(); + menu_radio_update_helper6_80047D40(work); + GV_ResetPacketMemory(); + GV_PauseLevel &= ~GV_PAUSE_STOP; + DG_RestartMainChanlSystem(); + menu_radio_init_nullsub_80042190(work); + DrawSync(0); + sub_8003CB98(work); + sub_8003EBDC(work); + menu_radar_load_rpk_8003AD64(); + gRadioIncomingCall_8009E708.field_0 = 0; + GM_GameStatus &= ~STATE_MENU_OFF; + printf("callback type %d proc %X\n", gMenuCallbackProc_800ABB08.type, + gMenuCallbackProc_800ABB08.procNameHashed); + if (gMenuCallbackProc_800ABB08.type != 0xF && gMenuCallbackProc_800ABB08.procNameHashed > 0) + { + args.argc = 2; + args.argv = argv; + argv[0] = gMenuCallbackProc_800ABB08.type & 0xF; + argv[1] = gMenuCallbackProc_800ABB08.param2; + printf("ExecProc\n"); + GCL_ExecProc(gMenuCallbackProc_800ABB08.procNameHashed, &args); + } + DG_ChangeReso(0); + DG_UnDrawFrameCount = 3; + DG_BackGroundNormal(); + DG_FrameRate = dword_800ABB1C; + printf("EXIT MUSENKI\n"); + return; + } + if (--dword_800AB648 == -1) + { + DG_ChangeReso(1); + } + menu_radio_codec_helper_8004158C(work, ot, pPad); + } +} + +void menu_radio_init(MenuWork *work) +{ + work->field_2C_modules[MENU_RADIO] = menu_radio_update_80042198; + work->field_28_flags |= 0x10u; +} + +void menu_radio_kill(MenuWork *work) +{ + work->field_28_flags &= ~0x10u; +} + +void MENU_RadioCall(int param_1, int param_2, int time) +{ + gRadioIncomingCall_8009E708.field_0 = param_1; + gRadioIncomingCall_8009E708.field_4 = param_2; + + if (time == 0) + { + gRadioIncomingCall_8009E708.field_2_timer = 360; + gRadioIncomingCall_8009E708.field_8 = 0; + menu_RadioCall_helper_800403E4(); + } + else + { + if (time >= 5) + { + menu_RadioCall_helper_800403E4(); + gRadioIncomingCall_8009E708.field_8 = 1; + gRadioIncomingCall_8009E708.field_2_timer = 40; + GV_PadMask = 0x100; + GM_GameStatus |= STATE_PADMASK; + } + else + { + gRadioIncomingCall_8009E708.field_2_timer = -time; + gRadioIncomingCall_8009E708.field_8 = 0; + } + + GM_GameStatus |= STATE_MENU_OFF; + } +} + +void MENU_SetLoad(int procNameHashed, char *param_2, short param_3) +{ + gRadioIncomingCall_8009E708.field_0 = param_3; + gRadioIncomingCall_8009E708.field_2_timer = -1; + gRadioIncomingCall_8009E708.field_4 = (int)param_2; + gMenuCallbackProc_800ABB08.procNameHashed = procNameHashed; + gMenuCallbackProc_800ABB08.type = 1; +} + +void MENU_ResetCall(void) +{ + gRadioIncomingCall_8009E708.field_0 = 0; + gRadioIncomingCall_8009E708.field_4 = 0; + gRadioIncomingCall_8009E708.field_2_timer = 0; + gRadioIncomingCall_8009E708.field_8 = 0; + + gMenuCallbackProc_800ABB08.procNameHashed = -1; +} + +void MENU_SetRadioCallbackProc(int proc_id) +{ + gMenuCallbackProc_800ABB08.procNameHashed = proc_id; +} + +// TODO: Functions from this point forward don't belong to radio.c! + +// rect_800AB64C is declared as a 1-element array +// in order for menu_number_init +// to declare extern to an array with unspecified size. +// That way the "small data" optimization of -G8 doesn't kick +// in (the compiler then doesn't know that rect_800AB64C is 8 bytes large, +// under the "8 bytes" threshold in G8). Without it, it would force +// us to compile this function (and the entire file) with -G0. +STATIC RECT SECTION(".sdata") rect_800AB64C[] = {{960, 488, 64, 10}}; + +extern SPRT gRadioNumberSprt_800bd9b0; +extern SPRT gRadioNumberSprt2_800bd9d0; + +void menu_number_init(MenuWork *work) +{ + RECT texture_rect; + RECT clut_rect; + TIM *tim; + SPRT *sprt; + + texture_rect = rect_800AB64C[0]; + + // Loads "num.res" (c70e.r) file: + tim = GV_GetCache(GV_CacheID2("num", 'r')); + + // The first CLUT entry must be transparent + tim->clut_data[0] = 0; + + clut_rect.x = 960; + clut_rect.y = 511; + clut_rect.w = 16; + clut_rect.h = 1; + + LoadImage(&clut_rect, (u_long *)tim->clut_data); + LoadImage(&texture_rect, (u_long *)&tim->clut_data[tim->clut.bnum >> 1]); + + sprt = &gRadioNumberSprt_800bd9b0; + setSprt(sprt); + setRGB0(sprt, 128, 128, 128); + setUV0(sprt, 0x9c, 0xe8); + setWH(sprt, 6, 7); + setClut(sprt, 960, 511); + + sprt = &gRadioNumberSprt2_800bd9d0; + setSprt(sprt); + setRGB0(sprt, 128, 128, 128); + setUV0(sprt, 0, 0xed); + setWH(sprt, 6, 5); + setClut(sprt, 960, 511); + + menu_set_string2(); +} + +void menu_number_kill(MenuWork *work) +{ + /* do nothing */ +} + +void _menu_number_draw(MenuPrim *prim, TextConfig *pSettings, int number) +{ + int digit; + SPRT *sprtIter; + int x0; + int minimumDigits; // Pad the displayed number with leading 0s + SPRT *firstSprt; + int xpos; + SPRT *sprt; + u_long *ot; + int color; + + x0 = 0; + firstSprt = 0; + ot = prim->ot; + color = pSettings->color; + minimumDigits = 0; + + if (pSettings->flags & TextConfig_Flags_ePadding_40) + { + minimumDigits = pSettings->flags >> 8; + } + + do + { + _NEW_PRIM(sprt, prim); + + x0 -= 6; + + digit = number % 10; + number /= 10; + + if (!firstSprt) + { + firstSprt = sprt; + } + + *sprt = gRadioNumberSprt_800bd9b0; + LSTORE(color, &sprt->r0); + sprt->x0 = x0; + sprt->y0 = pSettings->ypos; + sprt->u0 = digit * 6 - 100; + addPrim(ot, sprt); + + minimumDigits--; + } while (number > 0 || minimumDigits > 0); + + if (firstSprt) + { + switch (pSettings->flags & 0xF) + { + case 0: + default: + xpos = pSettings->xpos - x0; + pSettings->xpos = xpos; + break; + case 1: + xpos = pSettings->xpos; + pSettings->xpos = xpos + x0; + break; + case 2: + xpos = pSettings->xpos - x0 / 2; + break; + } + for (sprtIter = firstSprt; sprtIter < (SPRT *)prim->next; sprtIter++) + { + sprtIter->x0 += xpos; + } + } +} + +/** + * @brief Adjusts the horizontal position of a series of sprites based on + * an alignment value and the alignment flags. + * + * @param pPrim Pointer to the first SPRT primitive to be adjusted. + * @param pFreeLocation Pointer to the end of the primitive buffer. + * @param x The initial x-coordinate. + * @param align The alignment value + * @param flags TextConfig_Flags value that determines the alignment. + * @return The final x-coordinate after adjustment. + */ +STATIC int _menu_draw_number_draw_helper(SPRT *pPrim, char *pFreeLocation, int x, int align, int flags) +{ + int offset_x0; + + // check if the PRIM is valid and if it's within the buffer + if (pPrim != 0 && (char*)pPrim < pFreeLocation) + { + switch (flags & 0xF) + { + case TextConfig_Flags_eLeftAlign_00: + default: + offset_x0 = x; + x = align + offset_x0; + break; + + case TextConfig_Flags_eRightAlign_01: + x -= align; + offset_x0 = x; + break; + + case TextConfig_Flags_eCentreAlign_02: + offset_x0 = x - align / 2; + x = align; + break; + + } + + while ((char*)pPrim < pFreeLocation) + { + pPrim->x0 += offset_x0; + pPrim++; + } + } + + return x; +} + +void _menu_number_draw_string(MenuPrim *pGlue, TextConfig *pTextConfig, const char *str) +{ + SPRT *pSprt; + int width; + u_long *ot; + int color; + const char *str2; + unsigned int c; + unsigned int lc; + int tpx, tpy; + int skip; + int xoffset; + char *pFormat; + SPRT *pSprt2; + + pSprt = NULL; + width = 0; + + ot = pGlue->ot; + color = pTextConfig->color; + + str2 = str; + + for (c = *str2; c != 0; c = *++str2) + { + if (c == '\n') + { + _menu_draw_number_draw_helper(pSprt, pGlue->next, pTextConfig->xpos, width, pTextConfig->flags); + // move to the next line + pTextConfig->ypos += 8; + pSprt = (SPRT *)pGlue->next; + width = 0; + continue; + } + // convert to lowercase + lc = c | 0x20; + + // if is a number + if ((lc - '0') < 10) + { + tpx = (lc - '0') * 6; + tpy = 488; + skip = 6; + } + // if is a letter + else if ((lc - 'a') < 26) + { + tpx = (lc - 'a') * 6; + tpy = 493; + + if (lc == 'i') + { + skip = 3; + width += 1; + } + else + { + skip = 6; + } + } + else if (lc == ' ') + { + width += 4; + continue; + } + else if (lc == '#') + { + width += 6; + continue; + } + else // if special character + { + xoffset = 0; + pFormat = menu_string_format_8009E714; + // iterate over the menu_string_format_8009E714 map + // to find the "skip" value + while (1) + { + if (pFormat[0] == '\0') + { + break; + } + + tpx = 60 + xoffset; + + if (pFormat[0] == lc) + { + break; + } + + pFormat += 2; + xoffset += 6; + } + + if (pFormat[0] == '\0') + { + // if not found ignore the character + continue; + } + + skip = pFormat[1]; + tpy = 488; + + if (skip < 3) + { + width += 1; + skip += 1; + } + } + + _NEW_PRIM(pSprt2, pGlue); + + if (!pSprt) + { + pSprt = pSprt2; + } + + *pSprt2 = gRadioNumberSprt2_800bd9d0; + + // draw the character sprite + LSTORE(color, &pSprt2->r0); + + setXY0(pSprt2, width, pTextConfig->ypos); + setUV0(pSprt2, tpx, tpy); + + addPrim(ot, pSprt2); + width += skip; + } + + pTextConfig->xpos = _menu_draw_number_draw_helper(pSprt, pGlue->next, pTextConfig->xpos, width, pTextConfig->flags); +} + +void menu_number_draw_magazine(MenuWork *work, OT_TYPE *ot, int xoff, int yoff, + int pMagSize, int pAmmo, int pSubCnt2) +{ + SPRT *sprt; + int i; + + for (i = 0; i < pAmmo; i++) + { + NEW_PRIM(sprt, work); + + xoff = xoff - 3; + *sprt = gRadioNumberSprt2_800bd9d0; + sprt->x0 = xoff; + sprt->y0 = yoff; + + if (i < pMagSize) + { + if (i < pSubCnt2) + { + setRGB0(sprt, 128, 32, 32); + } + setUV0(sprt, 0xD8, 0xE8); + } + else + { + setUV0(sprt, 0xDC, 0xE8); + } + + sprt->w = 4; + sprt->h = 9; + addPrim(ot, sprt); + } +} + +int menu_number_draw(MenuWork *work, OT_TYPE *ot, int xpos, int ypos, int number, int flags) +{ + TextConfig textConfig; // [sp+10h] [-10h] BYREF + + textConfig.xpos = xpos; + textConfig.ypos = ypos; + textConfig.color = 0x64808080; + textConfig.flags = flags; + _menu_number_draw(work->prim, &textConfig, number); + return textConfig.xpos; +} + +extern SPRT gRadioNumberSprt_800bd9b0; + +int menu_number_draw_number2(MenuWork *work, int xpos, int ypos, int current, int total) +{ + SPRT *pPrim; + TextConfig textConfig; + + // Draw the "current" number + textConfig.xpos = xpos; + textConfig.ypos = ypos; + textConfig.flags = 0; + // Use red color if "current" is 0, otherwise use grey + textConfig.color = (current == 0 ? 0x64002080 : 0x64575757); + _menu_number_draw(work->prim, &textConfig, current); + + // Draw the "/" separator between the current and total numbers + NEW_PRIM(pPrim, work); + + *pPrim = gRadioNumberSprt_800bd9b0; + LSTORE(textConfig.color, &pPrim->r0); + pPrim->x0 = textConfig.xpos; + pPrim->y0 = textConfig.ypos; + pPrim->u0 = 224; + + addPrim(work->prim->ot, pPrim); + + // Draw the "total" number + textConfig.xpos = textConfig.xpos + 6; + _menu_number_draw(work->prim, &textConfig, total); + return textConfig.xpos; +} + +int menu_number_draw_string(MenuWork *work, OT_TYPE *ot, int xpos, int ypos, const char *str, int flags) +{ + TextConfig textConfig; + + textConfig.xpos = xpos; + textConfig.ypos = ypos; + textConfig.color = 0x64808080; + textConfig.flags = flags; + _menu_number_draw_string(work->prim, &textConfig, str); + return textConfig.xpos; +} + +STATIC RECT gRadioStringRect_800AB658 = {960, 498, 0, 0}; + +extern SPRT gRadioStringSprt_800BD9F0; + +void menu_set_string2(void) +{ + PANEL_TEXTURE pPanelTex; + RECT rect; + + menu_init_rpk_item_8003DDCC(&pPanelTex, 45, 44); + + gRadioStringRect_800AB658.w = pPanelTex.field_10_w / 4; + gRadioStringRect_800AB658.h = pPanelTex.field_12_h; + LoadImage(&gRadioStringRect_800AB658, pPanelTex.field_0_pixels); + + rect.x = 976; + rect.y = 511; + rect.w = 16; + rect.h = 1; + + LoadImage(&rect, pPanelTex.field_4_word_ptr_pixels); + + gRadioStringSprt_800BD9F0.u0 = 0; + LSTORE(0x80808080, &gRadioStringSprt_800BD9F0.r0); + gRadioStringSprt_800BD9F0.v0 = 242; + gRadioStringSprt_800BD9F0.w = 8; + gRadioStringSprt_800BD9F0.h = 6; + setSprt(&gRadioStringSprt_800BD9F0); + setClut(&gRadioStringSprt_800BD9F0, rect.x, rect.y); +} + +// wider spacing because of the larger font +void _menu_number_draw_string2(MenuPrim *pGlue, TextConfig *pTextConfig, const char *str) +{ + SPRT *pSprt; + int width; + u_long *ot; + int color; + const char *str2; + unsigned int c; + unsigned int lc; + int tpx, tpy; + int skip; + int xoffset; + char *pFormat; + SPRT *pSprt2; + short test; + + pSprt = NULL; + width = 0; + + ot = pGlue->ot; + color = pTextConfig->color; + + str2 = str; + + for (c = *str2; c != 0; c = *++str2) + { + // convert to lowercase + lc = c | 0x20; + + if (c == '\n') + { + _menu_draw_number_draw_helper(pSprt, pGlue->next, pTextConfig->xpos, width, pTextConfig->flags); + // move to the next line + pTextConfig->ypos += 8; + pSprt = (SPRT *)pGlue->next; + width = 0; + continue; + } + + // if is a number + if ((lc - '0') < 10) + { + tpx = (lc - '0') * 8; + tpy = 248; + skip = 9; + } + // if is a letter + else if ((lc - 'a') < 26) + { + tpx = (lc - 'a') * 8; + tpy = 242; + + if (lc == 'i') + { + skip = 4; + width += 1; + } + else + { + skip = 9; + } + } + else if (lc == ' ') + { + width += 4; + continue; + } + // Add spacing equal to the value of the next character + else if (lc == '#') + { + width += (str2[1] - '0'); + str2++; + continue; + } + else // if special character + { + xoffset = 0; + pFormat = menu_string_format_8009E714; + // iterate over the menu_string_format_8009E714 map + // to find the "skip" value + while (1) + { + if (pFormat[0] == '\0') + { + // if not found ignore the character + goto loop; + } + + tpx = xoffset + 80; + + if (pFormat[0] == lc) + { + skip = pFormat[1]; + tpy = 248; + + if (skip < 3) + { + width += 1; + skip += 1; + } + else if (skip == 6) + { + skip = 9; + } + break; + } + + pFormat += 2; + xoffset += 8; + } + } + + _NEW_PRIM(pSprt2, pGlue); + + test = !pSprt; + if (test) + { + pSprt = pSprt2; + } + + *pSprt2 = gRadioStringSprt_800BD9F0; + + // draw the character sprite + LSTORE(color, &pSprt2->r0); + + setXY0(pSprt2, width, pTextConfig->ypos); + setUV0(pSprt2, tpx, tpy); + + addPrim(ot, pSprt2); + width += skip; +loop: + } + + pTextConfig->xpos = _menu_draw_number_draw_helper(pSprt, pGlue->next, pTextConfig->xpos, width, pTextConfig->flags); +} + +void menu_restore_nouse(void) +{ + sub_8003CFE0(&dword_800BDA10, 2); + sub_8003CFE0(&dword_800BDA30, 3); +} + +void menu_init_nouse(void) +{ + dword_800BDA10.field_8_bufid = 0; + menu_init_rpk_item_8003DDCC(&dword_800BDA10, 40, 39); + menu_init_rpk_item_8003DDCC(&dword_800BDA30, 48, 39); + menu_restore_nouse(); +} + +STATIC void sub_800434F4(MenuPrim *pGlue, int offset_x, int offset_y, PANEL_TEXTURE *pUnk) +{ + SPRT *pPrim; + + _NEW_PRIM(pPrim, pGlue); + + LSTORE(0x80808080, &pPrim->r0); + menu_init_sprt_8003D0D0(pPrim, pUnk, offset_x - 2, offset_y + 6); + + setSprt(pPrim); + addPrim(pGlue->ot, pPrim); +} + +void menu_draw_nouse(MenuPrim *pGlue, int offset_x, int offset_y) +{ + sub_800434F4(pGlue, offset_x, offset_y, &dword_800BDA10); +} + +void menu_draw_frozen(MenuPrim *pGlue, int offset_x, int offset_y) +{ + sub_800434F4(pGlue, offset_x, offset_y, &dword_800BDA30); +} + +void menu_draw_triangle(MenuPrim *pGlue, Menu_Triangle *pTriangle) +{ + POLY_F3 *pPrim; + + _NEW_PRIM(pPrim, pGlue); + + LCOPY(&pTriangle->field_0_x0, &pPrim->x0); + LCOPY(&pTriangle->field_4_x1, &pPrim->x1); + LCOPY(&pTriangle->field_8_x2, &pPrim->x2); + LCOPY(&pTriangle->field_C_rgb, &pPrim->r0); + + setPolyF3(pPrim); + addPrim(pGlue->ot, pPrim); +} + +#ifdef __psyz +/*---------------------------------------------------------------------------*/ +/* Answer codec calls without opening the codec. + * + * MGS's story runs on the codec. A stage script places a call with + * radio -c