An Android port of Nazi Zombies: Portable
  • C 97.8%
  • Shell 2.2%
Find a file
zqpvr01 2784e3e406
All checks were successful
Auto build APK / build (push) Successful in 6m16s
CI: Title releases "Auto build" and their build number
2026-11-01 17:36:53 -04:00
.forgejo/workflows CI: Title releases "Auto build" and their build number 2026-11-01 17:36:53 -04:00
app BUILD: Rebuild GnuTLS 3.8.13 from a script that runs on Linux 2026-10-31 23:20:35 -03:00
fteqw@8f24466f73 BUILD: Move the engine pointer to the WindowInsetsController fullscreen 2026-10-31 23:24:09 -03:00
gradle/wrapper BUILD: Move to the current Android toolchain 2026-10-31 23:15:07 -03:00
quakec@44038c82d6 BUILD: Build the game code from a quakec fork instead of a patch 2026-10-30 13:13:51 -03:00
tools BUILD: Rebuild GnuTLS 3.8.13 from a script that runs on Linux 2026-10-31 23:20:35 -03:00
.gitattributes BUILD: Ship NZ:P's last FTE-era game data, read in place from the APK 2026-10-30 13:34:43 -03:00
.gitignore BUILD: Ship NZ:P's last FTE-era game data, read in place from the APK 2026-10-30 13:34:43 -03:00
.gitmodules BUILD: Fetch the engine and game code from their sibling repos, and the game data from git.zqpvr.net 2026-10-31 17:21:14 -03:00
build.gradle BUILD: Move to the current Android toolchain 2026-10-31 23:15:07 -03:00
gradle.properties BUILD: Set up the Android project and toolchain 2026-06-15 20:33:30 -03:00
gradlew BUILD: Move to the current Android toolchain 2026-10-31 23:15:07 -03:00
gradlew.bat BUILD: Move to the current Android toolchain 2026-10-31 23:15:07 -03:00
LICENSE DOCS: Add the README and license for publishing 2026-06-23 18:49:14 -03:00
README.md BUILD: Rebuild GnuTLS 3.8.13 from a script that runs on Linux 2026-10-31 23:20:35 -03:00
settings.gradle BUILD: Set up the Android project and toolchain 2026-06-15 20:33:30 -03:00

Nazi Zombies: Portable for Android

Call of Duty Zombies, on a phone, without a controller.

Nazi Zombies: Portable is a de-make of the Zombies mode built on Spike's FTEQW engine. This is a native arm64 build of that engine, not a wrapper or an emulator, so it runs the same game code the desktop builds do.

The game data is inside the APK and is read from there, so there is nothing to unpack, side-load or copy across.

Signed builds are on the releases page.

Features

  • On-screen controls drawn by the game: a stick that anchors wherever your thumb lands, drag-to-look, and eleven action buttons.
  • One shoot button that raises the sights before it fires and adapts to the weapon, rather than a fire button and an aim button.
  • Running is automatic. Push the stick forward and you sprint.
  • Bluetooth and USB controllers, with desktop-matching binds, glyph prompts and stick menu navigation. Picking one up hides the on-screen pad.
  • Cross-platform online play against a Windows host, both hosting and joining.
  • On a server running another version of the game, whose game code has no touch controls, the engine draws its own: buttons and a movement stick.
  • Vulkan by default, OpenGL ES as a fallback, switchable from the video menu without editing a config.
  • Realtime lighting that lights the world from the map's own light entities.
  • The phone vibrates when you fire, knife, take a hit or get caught in a blast. Rumble in the controller menu turns it off.
  • Automated builds, released only once every map loads with the game code and data going into them.

Requirements

An arm64 device on Android 7.0 or newer, and about 125 MB of space: the APK is all there is, since the game data is read from inside it.

Vulkan 1.2 is used when the driver offers it, which is most devices from the last few years. Anything older falls back to OpenGL ES 2.

Controls

The left half of the screen is the stick, the right half is look. Neither has a fixed position; both work from wherever you touch.

Button Does
Aim-fire Raises the sights, then fires. Drag off it to keep aiming while shooting
Reload Reloads, and running waits until it has finished
Knife Melee
Grenade Throws the grenade you have selected
Grenade swap Switches between frag and monkey bomb
Betty Places a Bouncing Betty
Use Doors, wall buys, perk machines, the box
Swap Switches between your two weapons
Jump Jumps
Crouch Changes stance, or dolphin dives if you are sprinting
Pause Opens the menu

Shooting follows the weapon rather than one rule for everything. Full-autos fire while held. Semi-autos, grenades, blades and the ray gun tap at a deliberate rate instead of as fast as the button can be sampled. Scoped snipers and rocket launchers hold the sights and fire when released, and a quick tap fires them from the hip. Weapons with no sights fire the moment you press.

How it works

The engine and the game code are forks of NZ:P's, fteqw and quakec, each on an android branch. Their changes are ordinary commits rather than patch files, so they can be read and reverted individually.

Piece Where it comes from
Engine fteqw submodule, our fork
Game code quakec submodule, our fork, on the last upstream commit FTE can run
Game data NZ:P's assets from their last FTE build, packed into the APK and read in place
TLS GnuTLS 3.8.13, built for arm64 by tools/build-gnutls.sh and vendored

NZ:P's PC build moved from FTE to its own Vril engine in September 2026, and the game code and assets moved with it: upstream's progs.dat now calls builtins FTE doesn't have, and FTE's particles, shaders, menu art and Ogg music are gone from its data. So both are pinned to the last FTE build, quakec 6ed6305 and assets c5a1d8070b.

The data sits in the root of the APK, which the engine already mounts as a pack, so it is read in place rather than unpacked to storage.

Online play goes through FTE's ICE broker, which requires DTLS, and FTE's only crypto backend is GnuTLS, dlopen'd at runtime and absent on Android. So it is cross-compiled here by tools/build-gnutls.sh, with GMP and Nettle static-linked into one 2.9 MB library whose only dependencies are libc and libdl. It has 16 KB page alignment, because devices with 16 KB pages reject a 4 KB-aligned library and that is what made dlopen fail.

Joining a Windows host needed an engine fix as well. FTE's SChannel backend cannot act as a DTLS server, so the Android side always offers a=setup:passive and makes the peer be the client.

The touch controls live in the game code rather than the engine, so they are drawn and driven by the same layer that draws the HUD. Touch reports K_TOUCH, which the menu system does not recognise as a click, so it is translated into the mouse events the menus already understand.

Building

Needs JDK 17 or newer (CI uses 25), NDK 30.0.16248370 and CMake 4.1.2, with Gradle 9.8 and AGP 9.5. compileSdk is 37, targetSdk 34 and minSdk 24.

The game data isn't tracked. tools/build-data.sh assembles it into nzp/, downloading NZ:P's assets and building the game code from quakec/, which needs a python3 with pandas, fastcrc and colorama.

git submodule update --init
tools/build-data.sh
./gradlew :app:assembleDebug

Release builds are signed from the environment, so assembleRelease reads KEYSTORE_FILE, KEYSTORE_PASSWORD, KEY_ALIAS and KEY_PASSWORD, and falls back to debug signing when they are absent.

A workflow builds the signed APK on every push and publishes it in place of the previous release, but only after a headless server built from the same engine has loaded every map with the same game code and data (tools/smoke-test.sh).

The APK ships a stripped library, so a native crash arrives as bare offsets. The unstripped one is kept as a build artifact, stamped with its GNU build ID, so a tombstone can be resolved against the exact binary it came from.

Layout

app/
  build.gradle          SDK levels, signing, CMake and NDK wiring
  src/main/resources/   our files packed next to the game data: config, server
                        list and touch art
  src/main/jniLibs/     libgnutls.so
fteqw/                  the engine, a submodule pointing at our fork
nzp/                    the game data, from tools/build-data.sh (not tracked)
quakec/                 the game code, a submodule pointing at our fork
tools/                  build-data.sh, smoke-test.sh and build-gnutls.sh

Licence

GPL-2.0, inherited from FTEQW and NZ:P, see LICENSE. The on-screen button artwork is from glKarin's Diii4a, which is GPL-3.0. The movement stick and the remaining icons were drawn for this port. NZ:P's own assets stay under the terms the NZ:P team set.

Not affiliated with or endorsed by the NZ:P team. Report problems here rather than to them.