From a27452493c4985bf1afcd1182463b7557ff27af2 Mon Sep 17 00:00:00 2001 From: turbocat Date: Tue, 31 Aug 2021 18:22:39 +0000 Subject: [PATCH] OpenTyrian: Sources uploaded git-svn-id: svn://kolibrios.org@9169 a494cfbc-eb01-0410-851d-a64ba20cac60 --- contrib/games/opentyrian/.editorconfig | 26 + contrib/games/opentyrian/.gitignore | 36 + contrib/games/opentyrian/Makefile | 88 + contrib/games/opentyrian/Makefile.linux | 141 + contrib/games/opentyrian/SDL/SDL_audiocvt.c | 642 + contrib/games/opentyrian/SDL/SDL_mixer.c | 218 + contrib/games/opentyrian/SDL/SDL_sysaudio.h | 150 + contrib/games/opentyrian/SDL/SDL_wave.c | 591 + contrib/games/opentyrian/SDL/SDL_wave.h | 65 + contrib/games/opentyrian/SDL/joystick_stub.c | 153 + contrib/games/opentyrian/SDL/uSDL.c | 53 + contrib/games/opentyrian/src/animlib.c | 431 + contrib/games/opentyrian/src/animlib.h | 27 + contrib/games/opentyrian/src/arg_parse.c | 248 + contrib/games/opentyrian/src/arg_parse.h | 58 + contrib/games/opentyrian/src/backgrnd.c | 527 + contrib/games/opentyrian/src/backgrnd.h | 64 + contrib/games/opentyrian/src/config.c | 1014 ++ contrib/games/opentyrian/src/config.h | 148 + contrib/games/opentyrian/src/config_file.c | 1033 ++ contrib/games/opentyrian/src/config_file.h | 596 + contrib/games/opentyrian/src/destruct.c | 2690 ++++ contrib/games/opentyrian/src/destruct.h | 27 + contrib/games/opentyrian/src/editship.c | 92 + contrib/games/opentyrian/src/editship.h | 36 + contrib/games/opentyrian/src/episodes.c | 267 + contrib/games/opentyrian/src/episodes.h | 173 + contrib/games/opentyrian/src/file.c | 208 + contrib/games/opentyrian/src/file.h | 44 + contrib/games/opentyrian/src/font.c | 275 + contrib/games/opentyrian/src/font.h | 49 + contrib/games/opentyrian/src/fonthand.c | 334 + contrib/games/opentyrian/src/fonthand.h | 58 + contrib/games/opentyrian/src/game_menu.c | 3243 +++++ contrib/games/opentyrian/src/game_menu.h | 58 + contrib/games/opentyrian/src/helptext.c | 381 + contrib/games/opentyrian/src/helptext.h | 80 + contrib/games/opentyrian/src/joystick.c | 653 + contrib/games/opentyrian/src/joystick.h | 98 + contrib/games/opentyrian/src/jukebox.c | 203 + contrib/games/opentyrian/src/jukebox.h | 27 + contrib/games/opentyrian/src/keyboard.c | 255 + contrib/games/opentyrian/src/keyboard.h | 57 + contrib/games/opentyrian/src/lds_play.c | 769 + contrib/games/opentyrian/src/lds_play.h | 74 + contrib/games/opentyrian/src/loudness.c | 294 + contrib/games/opentyrian/src/loudness.h | 59 + contrib/games/opentyrian/src/lvllib.c | 42 + contrib/games/opentyrian/src/lvllib.h | 34 + contrib/games/opentyrian/src/lvlmast.c | 173 + contrib/games/opentyrian/src/lvlmast.h | 47 + contrib/games/opentyrian/src/mainint.c | 4538 ++++++ contrib/games/opentyrian/src/mainint.h | 93 + contrib/games/opentyrian/src/menus.c | 262 + contrib/games/opentyrian/src/menus.h | 32 + contrib/games/opentyrian/src/mouse.c | 115 + contrib/games/opentyrian/src/mouse.h | 43 + contrib/games/opentyrian/src/mtrand.c | 107 + contrib/games/opentyrian/src/mtrand.h | 30 + contrib/games/opentyrian/src/musmast.c | 116 + contrib/games/opentyrian/src/musmast.h | 41 + contrib/games/opentyrian/src/network.c | 791 + contrib/games/opentyrian/src/network.h | 101 + contrib/games/opentyrian/src/nortsong.c | 225 + contrib/games/opentyrian/src/nortsong.h | 64 + contrib/games/opentyrian/src/nortvars.c | 88 + contrib/games/opentyrian/src/nortvars.h | 37 + contrib/games/opentyrian/src/opentyr.c | 392 + contrib/games/opentyrian/src/opentyr.h | 69 + .../games/opentyrian/src/opentyrian_version.h | 8 + contrib/games/opentyrian/src/opl.c | 1613 +++ contrib/games/opentyrian/src/opl.h | 51 + contrib/games/opentyrian/src/palette.c | 225 + contrib/games/opentyrian/src/palette.h | 51 + contrib/games/opentyrian/src/params.c | 267 + contrib/games/opentyrian/src/params.h | 29 + contrib/games/opentyrian/src/pcxload.c | 67 + contrib/games/opentyrian/src/pcxload.h | 27 + contrib/games/opentyrian/src/pcxmast.c | 48 + contrib/games/opentyrian/src/pcxmast.h | 35 + contrib/games/opentyrian/src/picload.c | 87 + contrib/games/opentyrian/src/picload.h | 29 + contrib/games/opentyrian/src/player.c | 55 + contrib/games/opentyrian/src/player.h | 129 + contrib/games/opentyrian/src/scroller.c | 315 + contrib/games/opentyrian/src/scroller.h | 32 + contrib/games/opentyrian/src/setup.c | 95 + contrib/games/opentyrian/src/setup.h | 27 + contrib/games/opentyrian/src/shots.c | 500 + contrib/games/opentyrian/src/shots.h | 58 + contrib/games/opentyrian/src/sizebuf.c | 224 + contrib/games/opentyrian/src/sizebuf.h | 50 + contrib/games/opentyrian/src/sndmast.c | 77 + contrib/games/opentyrian/src/sndmast.h | 74 + contrib/games/opentyrian/src/sprite.c | 734 + contrib/games/opentyrian/src/sprite.h | 116 + contrib/games/opentyrian/src/starlib.c | 431 + contrib/games/opentyrian/src/starlib.h | 32 + contrib/games/opentyrian/src/std_support.c | 26 + contrib/games/opentyrian/src/std_support.h | 37 + contrib/games/opentyrian/src/tyrian2.c | 5112 +++++++ contrib/games/opentyrian/src/tyrian2.h | 70 + contrib/games/opentyrian/src/varz.c | 1185 ++ contrib/games/opentyrian/src/varz.h | 359 + contrib/games/opentyrian/src/vga256d.c | 161 + contrib/games/opentyrian/src/vga256d.h | 44 + contrib/games/opentyrian/src/vga_palette.c | 71 + contrib/games/opentyrian/src/vga_palette.h | 8 + contrib/games/opentyrian/src/video.c | 204 + contrib/games/opentyrian/src/video.h | 49 + contrib/games/opentyrian/src/video_scale.c | 437 + contrib/games/opentyrian/src/video_scale.h | 42 + .../games/opentyrian/src/video_scale_hqNx.c | 11895 ++++++++++++++++ contrib/games/opentyrian/src/xmas.c | 100 + contrib/games/opentyrian/src/xmas.h | 30 + 115 files changed, 49569 insertions(+) create mode 100644 contrib/games/opentyrian/.editorconfig create mode 100644 contrib/games/opentyrian/.gitignore create mode 100755 contrib/games/opentyrian/Makefile create mode 100644 contrib/games/opentyrian/Makefile.linux create mode 100644 contrib/games/opentyrian/SDL/SDL_audiocvt.c create mode 100644 contrib/games/opentyrian/SDL/SDL_mixer.c create mode 100644 contrib/games/opentyrian/SDL/SDL_sysaudio.h create mode 100644 contrib/games/opentyrian/SDL/SDL_wave.c create mode 100644 contrib/games/opentyrian/SDL/SDL_wave.h create mode 100755 contrib/games/opentyrian/SDL/joystick_stub.c create mode 100644 contrib/games/opentyrian/SDL/uSDL.c create mode 100644 contrib/games/opentyrian/src/animlib.c create mode 100644 contrib/games/opentyrian/src/animlib.h create mode 100644 contrib/games/opentyrian/src/arg_parse.c create mode 100644 contrib/games/opentyrian/src/arg_parse.h create mode 100644 contrib/games/opentyrian/src/backgrnd.c create mode 100644 contrib/games/opentyrian/src/backgrnd.h create mode 100644 contrib/games/opentyrian/src/config.c create mode 100644 contrib/games/opentyrian/src/config.h create mode 100644 contrib/games/opentyrian/src/config_file.c create mode 100644 contrib/games/opentyrian/src/config_file.h create mode 100644 contrib/games/opentyrian/src/destruct.c create mode 100644 contrib/games/opentyrian/src/destruct.h create mode 100644 contrib/games/opentyrian/src/editship.c create mode 100644 contrib/games/opentyrian/src/editship.h create mode 100644 contrib/games/opentyrian/src/episodes.c create mode 100644 contrib/games/opentyrian/src/episodes.h create mode 100644 contrib/games/opentyrian/src/file.c create mode 100644 contrib/games/opentyrian/src/file.h create mode 100644 contrib/games/opentyrian/src/font.c create mode 100644 contrib/games/opentyrian/src/font.h create mode 100644 contrib/games/opentyrian/src/fonthand.c create mode 100644 contrib/games/opentyrian/src/fonthand.h create mode 100644 contrib/games/opentyrian/src/game_menu.c create mode 100644 contrib/games/opentyrian/src/game_menu.h create mode 100644 contrib/games/opentyrian/src/helptext.c create mode 100644 contrib/games/opentyrian/src/helptext.h create mode 100644 contrib/games/opentyrian/src/joystick.c create mode 100644 contrib/games/opentyrian/src/joystick.h create mode 100644 contrib/games/opentyrian/src/jukebox.c create mode 100644 contrib/games/opentyrian/src/jukebox.h create mode 100644 contrib/games/opentyrian/src/keyboard.c create mode 100644 contrib/games/opentyrian/src/keyboard.h create mode 100644 contrib/games/opentyrian/src/lds_play.c create mode 100644 contrib/games/opentyrian/src/lds_play.h create mode 100644 contrib/games/opentyrian/src/loudness.c create mode 100644 contrib/games/opentyrian/src/loudness.h create mode 100644 contrib/games/opentyrian/src/lvllib.c create mode 100644 contrib/games/opentyrian/src/lvllib.h create mode 100644 contrib/games/opentyrian/src/lvlmast.c create mode 100644 contrib/games/opentyrian/src/lvlmast.h create mode 100644 contrib/games/opentyrian/src/mainint.c create mode 100644 contrib/games/opentyrian/src/mainint.h create mode 100644 contrib/games/opentyrian/src/menus.c create mode 100644 contrib/games/opentyrian/src/menus.h create mode 100644 contrib/games/opentyrian/src/mouse.c create mode 100644 contrib/games/opentyrian/src/mouse.h create mode 100644 contrib/games/opentyrian/src/mtrand.c create mode 100644 contrib/games/opentyrian/src/mtrand.h create mode 100644 contrib/games/opentyrian/src/musmast.c create mode 100644 contrib/games/opentyrian/src/musmast.h create mode 100644 contrib/games/opentyrian/src/network.c create mode 100644 contrib/games/opentyrian/src/network.h create mode 100644 contrib/games/opentyrian/src/nortsong.c create mode 100644 contrib/games/opentyrian/src/nortsong.h create mode 100644 contrib/games/opentyrian/src/nortvars.c create mode 100644 contrib/games/opentyrian/src/nortvars.h create mode 100644 contrib/games/opentyrian/src/opentyr.c create mode 100644 contrib/games/opentyrian/src/opentyr.h create mode 100644 contrib/games/opentyrian/src/opentyrian_version.h create mode 100644 contrib/games/opentyrian/src/opl.c create mode 100644 contrib/games/opentyrian/src/opl.h create mode 100644 contrib/games/opentyrian/src/palette.c create mode 100644 contrib/games/opentyrian/src/palette.h create mode 100644 contrib/games/opentyrian/src/params.c create mode 100644 contrib/games/opentyrian/src/params.h create mode 100644 contrib/games/opentyrian/src/pcxload.c create mode 100644 contrib/games/opentyrian/src/pcxload.h create mode 100644 contrib/games/opentyrian/src/pcxmast.c create mode 100644 contrib/games/opentyrian/src/pcxmast.h create mode 100644 contrib/games/opentyrian/src/picload.c create mode 100644 contrib/games/opentyrian/src/picload.h create mode 100644 contrib/games/opentyrian/src/player.c create mode 100644 contrib/games/opentyrian/src/player.h create mode 100644 contrib/games/opentyrian/src/scroller.c create mode 100644 contrib/games/opentyrian/src/scroller.h create mode 100644 contrib/games/opentyrian/src/setup.c create mode 100644 contrib/games/opentyrian/src/setup.h create mode 100644 contrib/games/opentyrian/src/shots.c create mode 100644 contrib/games/opentyrian/src/shots.h create mode 100644 contrib/games/opentyrian/src/sizebuf.c create mode 100644 contrib/games/opentyrian/src/sizebuf.h create mode 100644 contrib/games/opentyrian/src/sndmast.c create mode 100644 contrib/games/opentyrian/src/sndmast.h create mode 100644 contrib/games/opentyrian/src/sprite.c create mode 100644 contrib/games/opentyrian/src/sprite.h create mode 100644 contrib/games/opentyrian/src/starlib.c create mode 100644 contrib/games/opentyrian/src/starlib.h create mode 100644 contrib/games/opentyrian/src/std_support.c create mode 100644 contrib/games/opentyrian/src/std_support.h create mode 100644 contrib/games/opentyrian/src/tyrian2.c create mode 100644 contrib/games/opentyrian/src/tyrian2.h create mode 100644 contrib/games/opentyrian/src/varz.c create mode 100644 contrib/games/opentyrian/src/varz.h create mode 100644 contrib/games/opentyrian/src/vga256d.c create mode 100644 contrib/games/opentyrian/src/vga256d.h create mode 100644 contrib/games/opentyrian/src/vga_palette.c create mode 100644 contrib/games/opentyrian/src/vga_palette.h create mode 100644 contrib/games/opentyrian/src/video.c create mode 100644 contrib/games/opentyrian/src/video.h create mode 100644 contrib/games/opentyrian/src/video_scale.c create mode 100644 contrib/games/opentyrian/src/video_scale.h create mode 100644 contrib/games/opentyrian/src/video_scale_hqNx.c create mode 100644 contrib/games/opentyrian/src/xmas.c create mode 100644 contrib/games/opentyrian/src/xmas.h diff --git a/contrib/games/opentyrian/.editorconfig b/contrib/games/opentyrian/.editorconfig new file mode 100644 index 000000000..8d2d98a7e --- /dev/null +++ b/contrib/games/opentyrian/.editorconfig @@ -0,0 +1,26 @@ +root = true + +[*] +charset = utf-8 +trim_trailing_whitespace = true +end_of_line = lf +insert_final_newline = true + +[Makefile] +indent_style = tab + +[*.sln] +charset = utf-8-bom +indent_style = tab +end_of_line = crlf +insert_final_newline = false + +[*.{vcxproj,props.template}] +indent_style = space +indent_size = 2 +end_of_line = crlf +insert_final_newline = false + +[*.rc] +charset = latin1 +end_of_line = crlf diff --git a/contrib/games/opentyrian/.gitignore b/contrib/games/opentyrian/.gitignore new file mode 100644 index 000000000..e1bc4d27f --- /dev/null +++ b/contrib/games/opentyrian/.gitignore @@ -0,0 +1,36 @@ +/data/ + +opentyrian.cfg +tyrian.cfg +tyrian.sav + +# Text editor detritus +*~ +*.swp + +# Make build output +/obj/ +/src/*.gch +/opentyrian + +# Windows build output +/opentyrian.exe +/SDL.dll +/SDL_net.dll + +# Windows runtime output +/stderr.txt +/stdout.txt + +# MSVC build output and project +/opentyrian-*.exe +/opentyrian-*.ilk +/opentyrian-*.pdb +/opentyrian-*.iobj +/opentyrian-*.ipdb +/visualc/.vs/ +/visualc/*.props +/visualc/*.user + +# Doxygen output +/doc/doxygen/ diff --git a/contrib/games/opentyrian/Makefile b/contrib/games/opentyrian/Makefile new file mode 100755 index 000000000..7a76a6986 --- /dev/null +++ b/contrib/games/opentyrian/Makefile @@ -0,0 +1,88 @@ +CC = kos32-gcc +LD = kos32-ld +OBJCOPY = kos32-objcopy +KPACK = kpack + +TYRIAN = opentyrian + +SDK_DIR = $(abspath ../../sdk) + +CFLAGS = -c -fno-ident -O2 -fomit-frame-pointer -fno-ident \ + -U__WIN32__ -U_Win32 -U_WIN32 -U__MINGW32__ -UWIN32 -D_KOLIBRI \ + -DTYRIAN_DIR='"/kolibrios/games/tyrian"' -DSDL_strlcpy=strncpy \ + -D_GNU_SOURCE=1 -D_REENTRANT -DNDEBUG -Wno-missing-field-initializers + +LDFLAGS = -static -S -nostdlib -T $(SDK_DIR)/sources/newlib/app.lds -Map=output.map --image-base 0 --subsystem native + +INCLUDES = -I$(SDK_DIR)/sources/newlib/libc/include -I$(SDK_DIR)/sources/SDL-1.2.2_newlib/include -Isrc +LIBPATH = -L$(SDK_DIR)/lib -L /home/autobuild/tools/win32/mingw32/lib -L $(SDK_DIR)/lib + +OBJS = ./src/scroller.o \ + ./src/config.o \ + ./src/game_menu.o \ + ./src/file.o \ + ./src/opentyr.o \ + ./src/sndmast.o \ + ./src/sizebuf.o \ + ./src/video_scale.o \ + ./src/loudness.o \ + ./src/palette.o \ + ./src/joystick.o \ + ./src/lds_play.o \ + ./src/font.o \ + ./src/config_file.o \ + ./src/network.o \ + ./src/helptext.o \ + ./src/xmas.o \ + ./src/keyboard.o \ + ./src/jukebox.o \ + ./src/picload.o \ + ./src/shots.o \ + ./src/setup.o \ + ./src/mouse.o \ + ./src/musmast.o \ + ./src/nortvars.o \ + ./src/backgrnd.o \ + ./src/destruct.o \ + ./src/lvllib.o \ + ./src/video_scale_hqNx.o \ + ./src/std_support.o \ + ./src/mtrand.o \ + ./src/sprite.o \ + ./src/episodes.o \ + ./src/arg_parse.o \ + ./src/opl.o \ + ./src/video.o \ + ./src/editship.o \ + ./src/vga_palette.o \ + ./src/pcxload.o \ + ./src/fonthand.o \ + ./src/mainint.o \ + ./src/tyrian2.o \ + ./src/lvlmast.o \ + ./src/animlib.o \ + ./src/pcxmast.o \ + ./src/menus.o \ + ./src/starlib.o \ + ./src/player.o \ + ./src/nortsong.o \ + ./src/vga256d.o \ + ./src/varz.o \ + ./src/params.o \ + ./SDL/joystick_stub.o \ + ./SDL/SDL_wave.o \ + ./SDL/SDL_audiocvt.o \ + ./SDL/uSDL.o + +LIBS = -lgcc -lSDLn -lsound -lc.dll + +$(TYRIAN): $(OBJS) + $(LD) $(LDFLAGS) $(LIBPATH) $(OBJS) -o $(TYRIAN) $(LIBS) + $(OBJCOPY) $(TYRIAN) -O binary + $(KPACK) --nologo $(TYRIAN) + +%.o : %.c + $(CC) $(CFLAGS) $(INCLUDES) -o $@ $< + +clean: + rm src/*.o diff --git a/contrib/games/opentyrian/Makefile.linux b/contrib/games/opentyrian/Makefile.linux new file mode 100644 index 000000000..a5440f685 --- /dev/null +++ b/contrib/games/opentyrian/Makefile.linux @@ -0,0 +1,141 @@ +# BUILD SETTINGS ############################################################### + +ifneq ($(filter Msys Cygwin, $(shell uname -o)), ) + PLATFORM := WIN32 + TYRIAN_DIR = C:\\TYRIAN +else + PLATFORM := UNIX + TYRIAN_DIR = $(gamesdir)/tyrian +endif + +WITH_NETWORK := true + +################################################################################ + +# see https://www.gnu.org/prep/standards/html_node/Makefile-Conventions.html + +SHELL = /bin/sh + +CC ?= gcc +INSTALL ?= install +PKG_CONFIG ?= pkg-config + +VCS_IDREV ?= (git describe --tags || git rev-parse --short HEAD) + +INSTALL_PROGRAM ?= $(INSTALL) +INSTALL_DATA ?= $(INSTALL) -m 644 + +prefix ?= /usr/local +exec_prefix ?= $(prefix) + +bindir ?= $(exec_prefix)/bin +datarootdir ?= $(prefix)/share +datadir ?= $(datarootdir) +docdir ?= $(datarootdir)/doc/opentyrian +mandir ?= $(datarootdir)/man +man6dir ?= $(mandir)/man6 +man6ext ?= .6 + +# see http://www.pathname.com/fhs/pub/fhs-2.3.html + +gamesdir ?= $(datadir)/games + +### + +TARGET := opentyrian + +SRCS := $(wildcard src/*.c) +OBJS := $(SRCS:src/%.c=obj/%.o) +DEPS := $(SRCS:src/%.c=obj/%.d) + +### + +ifeq ($(WITH_NETWORK), true) + EXTRA_CPPFLAGS += -DWITH_NETWORK +endif + +OPENTYRIAN_VERSION := $(shell $(VCS_IDREV) 2>/dev/null && \ + touch src/opentyrian_version.h) +ifneq ($(OPENTYRIAN_VERSION), ) + EXTRA_CPPFLAGS += -DOPENTYRIAN_VERSION='"$(OPENTYRIAN_VERSION)"' +endif + +CPPFLAGS := -DNDEBUG +CFLAGS := -pedantic +CFLAGS += -MMD +CFLAGS += -Wall \ + -Wextra \ + -Wno-missing-field-initializers +CFLAGS += -O2 +CFLAGS += -DuSDL_Delay=SDL_Delay + +LDFLAGS := +LDLIBS := + +ifeq ($(WITH_NETWORK), true) + SDL_CPPFLAGS := $(shell $(PKG_CONFIG) sdl SDL_net --cflags) + SDL_LDFLAGS := $(shell $(PKG_CONFIG) sdl SDL_net --libs-only-L --libs-only-other) + SDL_LDLIBS := $(shell $(PKG_CONFIG) sdl SDL_net --libs-only-l) +else + SDL_CPPFLAGS := $(shell $(PKG_CONFIG) sdl --cflags) + SDL_LDFLAGS := $(shell $(PKG_CONFIG) sdl --libs-only-L --libs-only-other) + SDL_LDLIBS := $(shell $(PKG_CONFIG) sdl --libs-only-l) +endif + +ALL_CPPFLAGS = -DTARGET_$(PLATFORM) \ + -DTYRIAN_DIR='"$(TYRIAN_DIR)"' \ + $(EXTRA_CPPFLAGS) \ + $(SDL_CPPFLAGS) \ + $(CPPFLAGS) +ALL_CFLAGS = -std=iso9899:1999 \ + $(CFLAGS) +ALL_LDFLAGS = $(SDL_LDFLAGS) \ + $(LDFLAGS) +ALL_LDLIBS = -lm \ + $(SDL_LDLIBS) \ + $(LDLIBS) + +### + +.PHONY : all +all : $(TARGET) + +.PHONY : debug +debug : CPPFLAGS += -UNDEBUG +debug : CFLAGS += -Werror +debug : CFLAGS += -O0 +debug : CFLAGS += -g3 +debug : all + +.PHONY : installdirs +installdirs : + mkdir -p $(DESTDIR)$(bindir) + mkdir -p $(DESTDIR)$(docdir) + mkdir -p $(DESTDIR)$(man6dir) + +.PHONY : install +install : $(TARGET) installdirs + $(INSTALL_PROGRAM) $(TARGET) $(DESTDIR)$(bindir)/ + $(INSTALL_DATA) CREDITS NEWS README $(DESTDIR)$(docdir)/ + $(INSTALL_DATA) linux/man/opentyrian.6 $(DESTDIR)$(man6dir)/opentyrian$(man6ext) + +.PHONY : uninstall +uninstall : + rm -f $(DESTDIR)$(bindir)/$(TARGET) + rm -f $(DESTDIR)$(docdir)/{CREDITS,NEWS,README} + rm -f $(DESTDIR)$(man6dir)/opentyrian$(man6ext) + +.PHONY : clean +clean : + rm -f $(OBJS) + rm -f $(DEPS) + rm -f $(TARGET) + +$(TARGET) : $(OBJS) + $(CC) $(ALL_CFLAGS) $(ALL_LDFLAGS) -o $@ $^ $(ALL_LDLIBS) + +-include $(DEPS) + +obj/%.o : src/%.c + @mkdir -p "$(dir $@)" + $(CC) $(ALL_CPPFLAGS) $(ALL_CFLAGS) -c -o $@ $< diff --git a/contrib/games/opentyrian/SDL/SDL_audiocvt.c b/contrib/games/opentyrian/SDL/SDL_audiocvt.c new file mode 100644 index 000000000..e9a82ce6c --- /dev/null +++ b/contrib/games/opentyrian/SDL/SDL_audiocvt.c @@ -0,0 +1,642 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@devolution.com +*/ + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id: SDL_audiocvt.c,v 1.2 2001/04/26 16:50:17 hercules Exp $"; +#endif + +/* Functions for audio drivers to perform runtime conversion of audio format */ + +#include + +#include "SDL_error.h" +#include "SDL_audio.h" + + +/* Effectively mix right and left channels into a single channel */ +void SDL_ConvertMono(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Sint32 sample; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting to mono\n"); +#endif + switch (format&0x8018) { + + case AUDIO_U8: { + Uint8 *src, *dst; + + src = cvt->buf; + dst = cvt->buf; + for ( i=cvt->len_cvt/2; i; --i ) { + sample = src[0] + src[1]; + if ( sample > 255 ) { + *dst = 255; + } else { + *dst = sample; + } + src += 2; + dst += 1; + } + } + break; + + case AUDIO_S8: { + Sint8 *src, *dst; + + src = (Sint8 *)cvt->buf; + dst = (Sint8 *)cvt->buf; + for ( i=cvt->len_cvt/2; i; --i ) { + sample = src[0] + src[1]; + if ( sample > 127 ) { + *dst = 127; + } else + if ( sample < -128 ) { + *dst = -128; + } else { + *dst = sample; + } + src += 2; + dst += 1; + } + } + break; + + case AUDIO_U16: { + Uint8 *src, *dst; + + src = cvt->buf; + dst = cvt->buf; + if ( (format & 0x1000) == 0x1000 ) { + for ( i=cvt->len_cvt/4; i; --i ) { + sample = (Uint16)((src[0]<<8)|src[1])+ + (Uint16)((src[2]<<8)|src[3]); + if ( sample > 65535 ) { + dst[0] = 0xFF; + dst[1] = 0xFF; + } else { + dst[1] = (sample&0xFF); + sample >>= 8; + dst[0] = (sample&0xFF); + } + src += 4; + dst += 2; + } + } else { + for ( i=cvt->len_cvt/4; i; --i ) { + sample = (Uint16)((src[1]<<8)|src[0])+ + (Uint16)((src[3]<<8)|src[2]); + if ( sample > 65535 ) { + dst[0] = 0xFF; + dst[1] = 0xFF; + } else { + dst[0] = (sample&0xFF); + sample >>= 8; + dst[1] = (sample&0xFF); + } + src += 4; + dst += 2; + } + } + } + break; + + case AUDIO_S16: { + Uint8 *src, *dst; + + src = cvt->buf; + dst = cvt->buf; + if ( (format & 0x1000) == 0x1000 ) { + for ( i=cvt->len_cvt/4; i; --i ) { + sample = (Sint16)((src[0]<<8)|src[1])+ + (Sint16)((src[2]<<8)|src[3]); + if ( sample > 32767 ) { + dst[0] = 0x7F; + dst[1] = 0xFF; + } else + if ( sample < -32768 ) { + dst[0] = 0x80; + dst[1] = 0x00; + } else { + dst[1] = (sample&0xFF); + sample >>= 8; + dst[0] = (sample&0xFF); + } + src += 4; + dst += 2; + } + } else { + for ( i=cvt->len_cvt/4; i; --i ) { + sample = (Sint16)((src[1]<<8)|src[0])+ + (Sint16)((src[3]<<8)|src[2]); + if ( sample > 32767 ) { + dst[1] = 0x7F; + dst[0] = 0xFF; + } else + if ( sample < -32768 ) { + dst[1] = 0x80; + dst[0] = 0x00; + } else { + dst[0] = (sample&0xFF); + sample >>= 8; + dst[1] = (sample&0xFF); + } + src += 4; + dst += 2; + } + } + } + break; + } + cvt->len_cvt /= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + + +/* Duplicate a mono channel to both stereo channels */ +void SDL_ConvertStereo(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting to stereo\n"); +#endif + if ( (format & 0xFF) == 16 ) { + Uint16 *src, *dst; + + src = (Uint16 *)(cvt->buf+cvt->len_cvt); + dst = (Uint16 *)(cvt->buf+cvt->len_cvt*2); + for ( i=cvt->len_cvt/2; i; --i ) { + dst -= 2; + src -= 1; + dst[0] = src[0]; + dst[1] = src[0]; + } + } else { + Uint8 *src, *dst; + + src = cvt->buf+cvt->len_cvt; + dst = cvt->buf+cvt->len_cvt*2; + for ( i=cvt->len_cvt; i; --i ) { + dst -= 2; + src -= 1; + dst[0] = src[0]; + dst[1] = src[0]; + } + } + cvt->len_cvt *= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Convert 8-bit to 16-bit - LSB */ +void SDL_Convert16LSB(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting to 16-bit LSB\n"); +#endif + src = cvt->buf+cvt->len_cvt; + dst = cvt->buf+cvt->len_cvt*2; + for ( i=cvt->len_cvt; i; --i ) { + src -= 1; + dst -= 2; + dst[1] = *src; + dst[0] = 0; + } + format = ((format & ~0x0008) | AUDIO_U16LSB); + cvt->len_cvt *= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} +/* Convert 8-bit to 16-bit - MSB */ +void SDL_Convert16MSB(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting to 16-bit MSB\n"); +#endif + src = cvt->buf+cvt->len_cvt; + dst = cvt->buf+cvt->len_cvt*2; + for ( i=cvt->len_cvt; i; --i ) { + src -= 1; + dst -= 2; + dst[0] = *src; + dst[1] = 0; + } + format = ((format & ~0x0008) | AUDIO_U16MSB); + cvt->len_cvt *= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Convert 16-bit to 8-bit */ +void SDL_Convert8(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting to 8-bit\n"); +#endif + src = cvt->buf; + dst = cvt->buf; + if ( (format & 0x1000) != 0x1000 ) { /* Little endian */ + ++src; + } + for ( i=cvt->len_cvt/2; i; --i ) { + *dst = *src; + src += 2; + dst += 1; + } + format = ((format & ~0x9010) | AUDIO_U8); + cvt->len_cvt /= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Toggle signed/unsigned */ +void SDL_ConvertSign(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *data; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio signedness\n"); +#endif + data = cvt->buf; + if ( (format & 0xFF) == 16 ) { + if ( (format & 0x1000) != 0x1000 ) { /* Little endian */ + ++data; + } + for ( i=cvt->len_cvt/2; i; --i ) { + *data ^= 0x80; + data += 2; + } + } else { + for ( i=cvt->len_cvt; i; --i ) { + *data++ ^= 0x80; + } + } + format = (format ^ 0x8000); + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Toggle endianness */ +void SDL_ConvertEndian(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *data, tmp; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio endianness\n"); +#endif + data = cvt->buf; + for ( i=cvt->len_cvt/2; i; --i ) { + tmp = data[0]; + data[0] = data[1]; + data[1] = tmp; + data += 2; + } + format = (format ^ 0x1000); + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Convert rate up by multiple of 2 */ +void SDL_RateMUL2(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio rate * 2\n"); +#endif + src = cvt->buf+cvt->len_cvt; + dst = cvt->buf+cvt->len_cvt*2; + switch (format & 0xFF) { + case 8: + for ( i=cvt->len_cvt; i; --i ) { + src -= 1; + dst -= 2; + dst[0] = src[0]; + dst[1] = src[0]; + } + break; + case 16: + for ( i=cvt->len_cvt/2; i; --i ) { + src -= 2; + dst -= 4; + dst[0] = src[0]; + dst[1] = src[1]; + dst[2] = src[0]; + dst[3] = src[1]; + } + break; + } + cvt->len_cvt *= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Convert rate down by multiple of 2 */ +void SDL_RateDIV2(SDL_AudioCVT *cvt, Uint16 format) +{ + int i; + Uint8 *src, *dst; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio rate / 2\n"); +#endif + src = cvt->buf; + dst = cvt->buf; + switch (format & 0xFF) { + case 8: + for ( i=cvt->len_cvt/2; i; --i ) { + dst[0] = src[0]; + src += 2; + dst += 1; + } + break; + case 16: + for ( i=cvt->len_cvt/4; i; --i ) { + dst[0] = src[0]; + dst[1] = src[1]; + src += 4; + dst += 2; + } + break; + } + cvt->len_cvt /= 2; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +/* Very slow rate conversion routine */ +void SDL_RateSLOW(SDL_AudioCVT *cvt, Uint16 format) +{ + double ipos; + int i, clen; + +#ifdef DEBUG_CONVERT + fprintf(stderr, "Converting audio rate * %4.4f\n", 1.0/cvt->rate_incr); +#endif + clen = (int)((double)cvt->len_cvt / cvt->rate_incr); + if ( cvt->rate_incr > 1.0 ) { + switch (format & 0xFF) { + case 8: { + Uint8 *output; + + output = cvt->buf; + ipos = 0.0; + for ( i=clen; i; --i ) { + *output = cvt->buf[(int)ipos]; + ipos += cvt->rate_incr; + output += 1; + } + } + break; + + case 16: { + Uint16 *output; + + clen &= ~1; + output = (Uint16 *)cvt->buf; + ipos = 0.0; + for ( i=clen/2; i; --i ) { + *output=((Uint16 *)cvt->buf)[(int)ipos]; + ipos += cvt->rate_incr; + output += 1; + } + } + break; + } + } else { + switch (format & 0xFF) { + case 8: { + Uint8 *output; + + output = cvt->buf+clen; + ipos = (double)cvt->len_cvt; + for ( i=clen; i; --i ) { + ipos -= cvt->rate_incr; + output -= 1; + *output = cvt->buf[(int)ipos]; + } + } + break; + + case 16: { + Uint16 *output; + + clen &= ~1; + output = (Uint16 *)(cvt->buf+clen); + ipos = (double)cvt->len_cvt/2; + for ( i=clen/2; i; --i ) { + ipos -= cvt->rate_incr; + output -= 1; + *output=((Uint16 *)cvt->buf)[(int)ipos]; + } + } + break; + } + } + cvt->len_cvt = clen; + if ( cvt->filters[++cvt->filter_index] ) { + cvt->filters[cvt->filter_index](cvt, format); + } +} + +int SDL_ConvertAudio(SDL_AudioCVT *cvt) +{ + /* Make sure there's data to convert */ + if ( cvt->buf == NULL ) { + SDL_SetError("No buffer allocated for conversion"); + return(-1); + } + /* Return okay if no conversion is necessary */ + cvt->len_cvt = cvt->len; + if ( cvt->filters[0] == NULL ) { + return(0); + } + + /* Set up the conversion and go! */ + cvt->filter_index = 0; + cvt->filters[0](cvt, cvt->src_format); + return(0); +} + +/* Creates a set of audio filters to convert from one format to another. + Returns -1 if the format conversion is not supported, or 1 if the + audio filter is set up. +*/ + +int SDL_BuildAudioCVT(SDL_AudioCVT *cvt, + Uint16 src_format, Uint8 src_channels, int src_rate, + Uint16 dst_format, Uint8 dst_channels, int dst_rate) +{ + /* Start off with no conversion necessary */ + cvt->needed = 0; + cvt->filter_index = 0; + cvt->filters[0] = NULL; + cvt->len_mult = 1; + cvt->len_ratio = 1.0; + + /* First filter: Endian conversion from src to dst */ + if ( (src_format & 0x1000) != (dst_format & 0x1000) + && ((src_format & 0xff) != 8) ) { + cvt->filters[cvt->filter_index++] = SDL_ConvertEndian; + } + + /* Second filter: Sign conversion -- signed/unsigned */ + if ( (src_format & 0x8000) != (dst_format & 0x8000) ) { + cvt->filters[cvt->filter_index++] = SDL_ConvertSign; + } + + /* Next filter: Convert 16 bit <--> 8 bit PCM */ + if ( (src_format & 0xFF) != (dst_format & 0xFF) ) { + switch (dst_format&0x10FF) { + case AUDIO_U8: + cvt->filters[cvt->filter_index++] = + SDL_Convert8; + cvt->len_ratio /= 2; + break; + case AUDIO_U16LSB: + cvt->filters[cvt->filter_index++] = + SDL_Convert16LSB; + cvt->len_mult *= 2; + cvt->len_ratio *= 2; + break; + case AUDIO_U16MSB: + cvt->filters[cvt->filter_index++] = + SDL_Convert16MSB; + cvt->len_mult *= 2; + cvt->len_ratio *= 2; + break; + } + } + + /* Last filter: Mono/Stereo conversion */ + if ( src_channels != dst_channels ) { + while ( (src_channels*2) <= dst_channels ) { + cvt->filters[cvt->filter_index++] = + SDL_ConvertStereo; + cvt->len_mult *= 2; + src_channels *= 2; + cvt->len_ratio *= 2; + } + /* This assumes that 4 channel audio is in the format: + Left {front/back} + Right {front/back} + so converting to L/R stereo works properly. + */ + while ( ((src_channels%2) == 0) && + ((src_channels/2) >= dst_channels) ) { + cvt->filters[cvt->filter_index++] = + SDL_ConvertMono; + src_channels /= 2; + cvt->len_ratio /= 2; + } + if ( src_channels != dst_channels ) { + /* Uh oh.. */; + } + } + + /* Do rate conversion */ + cvt->rate_incr = 0.0; + if ( (src_rate/100) != (dst_rate/100) ) { + Uint32 hi_rate, lo_rate; + int len_mult; + double len_ratio; + void (*rate_cvt)(SDL_AudioCVT *cvt, Uint16 format); + + if ( src_rate > dst_rate ) { + hi_rate = src_rate; + lo_rate = dst_rate; + rate_cvt = SDL_RateDIV2; + len_mult = 1; + len_ratio = 0.5; + } else { + hi_rate = dst_rate; + lo_rate = src_rate; + rate_cvt = SDL_RateMUL2; + len_mult = 2; + len_ratio = 2.0; + } + /* If hi_rate = lo_rate*2^x then conversion is easy */ + while ( ((lo_rate*2)/100) <= (hi_rate/100) ) { + cvt->filters[cvt->filter_index++] = rate_cvt; + cvt->len_mult *= len_mult; + lo_rate *= 2; + cvt->len_ratio *= len_ratio; + } + /* We may need a slow conversion here to finish up */ + if ( (lo_rate/100) != (hi_rate/100) ) { +#if 1 + /* The problem with this is that if the input buffer is + say 1K, and the conversion rate is say 1.1, then the + output buffer is 1.1K, which may not be an acceptable + buffer size for the audio driver (not a power of 2) + */ + /* For now, punt and hope the rate distortion isn't great. + */ +#else + if ( src_rate < dst_rate ) { + cvt->rate_incr = (double)lo_rate/hi_rate; + cvt->len_mult *= 2; + cvt->len_ratio /= cvt->rate_incr; + } else { + cvt->rate_incr = (double)hi_rate/lo_rate; + cvt->len_ratio *= cvt->rate_incr; + } + cvt->filters[cvt->filter_index++] = SDL_RateSLOW; +#endif + } + } + + /* Set up the filter information */ + if ( cvt->filter_index != 0 ) { + cvt->needed = 1; + cvt->src_format = src_format; + cvt->dst_format = dst_format; + cvt->len = 0; + cvt->buf = NULL; + cvt->filters[cvt->filter_index] = NULL; + } + return(cvt->needed); +} diff --git a/contrib/games/opentyrian/SDL/SDL_mixer.c b/contrib/games/opentyrian/SDL/SDL_mixer.c new file mode 100644 index 000000000..03cf71358 --- /dev/null +++ b/contrib/games/opentyrian/SDL/SDL_mixer.c @@ -0,0 +1,218 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@devolution.com +*/ + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id: SDL_mixer.c,v 1.2 2001/04/26 16:50:17 hercules Exp $"; +#endif + +/* This provides the default mixing callback for the SDL audio routines */ + +#include +#include +#include + +#include "SDL_audio.h" +#include "SDL_mutex.h" +#include "SDL_timer.h" +#include "SDL_sysaudio.h" + +SDL_AudioDevice *current_audio = NULL; + +/* This table is used to add two sound values together and pin + * the value to avoid overflow. (used with permission from ARDI) + * Changed to use 0xFE instead of 0xFF for better sound quality. + */ +static const Uint8 mix8[] = +{ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03, + 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, + 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, + 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, + 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, + 0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, + 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, + 0x5C, 0x5D, 0x5E, 0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, + 0x67, 0x68, 0x69, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, + 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, + 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, + 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, + 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, + 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, + 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, + 0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, + 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, + 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, + 0xE0, 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, + 0xEB, 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, + 0xF6, 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, + 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE +}; + +/* The volume ranges from 0 - 128 */ +#define ADJUST_VOLUME(s, v) (s = (s*v)/SDL_MIX_MAXVOLUME) +#define ADJUST_VOLUME_U8(s, v) (s = (((s-128)*v)/SDL_MIX_MAXVOLUME)+128) + +void SDL_MixAudio (Uint8 *dst, const Uint8 *src, Uint32 len, int volume) +{ + Uint16 format; + + if ( volume == 0 ) { + return; + } + /* Mix the user-level audio format */ + if ( current_audio ) { + if ( current_audio->convert.needed ) { + format = current_audio->convert.src_format; + } else { + format = current_audio->spec.format; + } + } else { + format = AUDIO_S16; + } + format = AUDIO_S16; + switch (format) { + + case AUDIO_U8: { + Uint8 src_sample; + + while ( len-- ) { + src_sample = *src; + ADJUST_VOLUME_U8(src_sample, volume); + *dst = mix8[*dst+src_sample]; + ++dst; + ++src; + } + } + break; + + case AUDIO_S8: { + Sint8 *dst8, *src8; + Sint8 src_sample; + int dst_sample; + const int max_audioval = ((1<<(8-1))-1); + const int min_audioval = -(1<<(8-1)); + + src8 = (Sint8 *)src; + dst8 = (Sint8 *)dst; + while ( len-- ) { + src_sample = *src8; + ADJUST_VOLUME(src_sample, volume); + dst_sample = *dst8 + src_sample; + if ( dst_sample > max_audioval ) { + *dst8 = max_audioval; + } else + if ( dst_sample < min_audioval ) { + *dst8 = min_audioval; + } else { + *dst8 = dst_sample; + } + ++dst8; + ++src8; + } + } + break; + + case AUDIO_S16LSB: { + Sint16 src1, src2; + int dst_sample; + const int max_audioval = ((1<<(16-1))-1); + const int min_audioval = -(1<<(16-1)); + + len /= 2; + while ( len-- ) { + src1 = ((src[1])<<8|src[0]); + ADJUST_VOLUME(src1, volume); + src2 = ((dst[1])<<8|dst[0]); + src += 2; + dst_sample = src1+src2; + if ( dst_sample > max_audioval ) { + dst_sample = max_audioval; + } else + if ( dst_sample < min_audioval ) { + dst_sample = min_audioval; + } + dst[0] = dst_sample&0xFF; + dst_sample >>= 8; + dst[1] = dst_sample&0xFF; + dst += 2; + } + } + break; + + case AUDIO_S16MSB: { + Sint16 src1, src2; + int dst_sample; + const int max_audioval = ((1<<(16-1))-1); + const int min_audioval = -(1<<(16-1)); + + len /= 2; + while ( len-- ) { + src1 = ((src[0])<<8|src[1]); + ADJUST_VOLUME(src1, volume); + src2 = ((dst[0])<<8|dst[1]); + src += 2; + dst_sample = src1+src2; + if ( dst_sample > max_audioval ) { + dst_sample = max_audioval; + } else + if ( dst_sample < min_audioval ) { + dst_sample = min_audioval; + } + dst[1] = dst_sample&0xFF; + dst_sample >>= 8; + dst[0] = dst_sample&0xFF; + dst += 2; + } + } + break; + + default: /* If this happens... FIXME! */ + SDL_SetError("SDL_MixAudio(): unknown audio format"); + return; + } +} diff --git a/contrib/games/opentyrian/SDL/SDL_sysaudio.h b/contrib/games/opentyrian/SDL/SDL_sysaudio.h new file mode 100644 index 000000000..074ba3e5d --- /dev/null +++ b/contrib/games/opentyrian/SDL/SDL_sysaudio.h @@ -0,0 +1,150 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@devolution.com +*/ + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id: SDL_sysaudio.h,v 1.8 2001/07/23 02:58:42 slouken Exp $"; +#endif + +#ifndef _SDL_sysaudio_h +#define _SDL_sysaudio_h + +#include "SDL_mutex.h" +#include "SDL_thread.h" + +/* The SDL audio driver */ +typedef struct SDL_AudioDevice SDL_AudioDevice; + +/* Define the SDL audio driver structure */ +#define _THIS SDL_AudioDevice *_this +#ifndef _STATUS +#define _STATUS SDL_status *status +#endif +struct SDL_AudioDevice { + /* * * */ + /* The name of this audio driver */ + const char *name; + + /* * * */ + /* The description of this audio driver */ + const char *desc; + + /* * * */ + /* Public driver functions */ + int (*OpenAudio)(_THIS, SDL_AudioSpec *spec); + void (*ThreadInit)(_THIS); /* Called by audio thread at start */ + void (*WaitAudio)(_THIS); + void (*PlayAudio)(_THIS); + Uint8 *(*GetAudioBuf)(_THIS); + void (*WaitDone)(_THIS); + void (*CloseAudio)(_THIS); + + /* * * */ + /* Data common to all devices */ + + /* The current audio specification (shared with audio thread) */ + SDL_AudioSpec spec; + + /* An audio conversion block for audio format emulation */ + SDL_AudioCVT convert; + + /* Current state flags */ + int enabled; + int paused; + int opened; + + /* Fake audio buffer for when the audio hardware is busy */ + Uint8 *fake_stream; + + /* A semaphore for locking the mixing buffers */ + SDL_mutex *mixer_lock; + + /* A thread to feed the audio device */ + SDL_Thread *thread; + Uint32 threadid; + + /* * * */ + /* Data private to this driver */ + struct SDL_PrivateAudioData *hidden; + + /* * * */ + /* The function used to dispose of this structure */ + void (*free)(_THIS); +}; +#undef _THIS + +typedef struct AudioBootStrap { + const char *name; + const char *desc; + int (*available)(void); + SDL_AudioDevice *(*create)(int devindex); +} AudioBootStrap; + +#ifdef OPENBSD_AUDIO_SUPPORT +extern AudioBootStrap OPENBSD_AUDIO_bootstrap; +#endif +#ifdef OSS_SUPPORT +extern AudioBootStrap DSP_bootstrap; +extern AudioBootStrap DMA_bootstrap; +#endif +#ifdef ALSA_SUPPORT +extern AudioBootStrap ALSA_bootstrap; +#endif +#if (defined(unix) && !defined(__CYGWIN32__)) && \ + !defined(OSS_SUPPORT) && !defined(ALSA_SUPPORT) +extern AudioBootStrap AUDIO_bootstrap; +#endif +#ifdef ARTSC_SUPPORT +extern AudioBootStrap ARTSC_bootstrap; +#endif +#ifdef ESD_SUPPORT +extern AudioBootStrap ESD_bootstrap; +#endif +#ifdef NAS_SUPPORT +extern AudioBootStrap NAS_bootstrap; +#endif +#ifdef ENABLE_DIRECTX +extern AudioBootStrap DSOUND_bootstrap; +#endif +#ifdef ENABLE_WINDIB +extern AudioBootStrap WAVEOUT_bootstrap; +#endif +#ifdef _AIX +extern AudioBootStrap Paud_bootstrap; +#endif +#ifdef __BEOS__ +extern AudioBootStrap BAUDIO_bootstrap; +#endif +#if defined(macintosh) || TARGET_API_MAC_CARBON +extern AudioBootStrap SNDMGR_bootstrap; +#endif +#ifdef ENABLE_AHI +extern AudioBootStrap AHI_bootstrap; +#endif +#ifdef DISKAUD_SUPPORT +extern AudioBootStrap DISKAUD_bootstrap; +#endif + +/* This is the current audio device */ +extern SDL_AudioDevice *current_audio; + +#endif /* _SDL_sysaudio_h */ diff --git a/contrib/games/opentyrian/SDL/SDL_wave.c b/contrib/games/opentyrian/SDL/SDL_wave.c new file mode 100644 index 000000000..237ec1508 --- /dev/null +++ b/contrib/games/opentyrian/SDL/SDL_wave.c @@ -0,0 +1,591 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@devolution.com +*/ + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id: SDL_wave.c,v 1.2 2001/04/26 16:50:17 hercules Exp $"; +#endif + +#ifndef DISABLE_FILE + +/* Microsoft WAVE file loading routines */ + +#include +#include + +#include "SDL_error.h" +#include "SDL_audio.h" +#include "SDL_wave.h" +#include "SDL_endian.h" + +#ifndef NELEMS +#define NELEMS(array) ((sizeof array)/(sizeof array[0])) +#endif + +static int ReadChunk(SDL_RWops *src, Chunk *chunk); + +struct MS_ADPCM_decodestate { + Uint8 hPredictor; + Uint16 iDelta; + Sint16 iSamp1; + Sint16 iSamp2; +}; +static struct MS_ADPCM_decoder { + WaveFMT wavefmt; + Uint16 wSamplesPerBlock; + Uint16 wNumCoef; + Sint16 aCoeff[7][2]; + /* * * */ + struct MS_ADPCM_decodestate state[2]; +} MS_ADPCM_state; + +static int InitMS_ADPCM(WaveFMT *format) +{ + Uint8 *rogue_feel; + Uint16 extra_info; + int i; + + /* Set the rogue pointer to the MS_ADPCM specific data */ + MS_ADPCM_state.wavefmt.encoding = SDL_SwapLE16(format->encoding); + MS_ADPCM_state.wavefmt.channels = SDL_SwapLE16(format->channels); + MS_ADPCM_state.wavefmt.frequency = SDL_SwapLE32(format->frequency); + MS_ADPCM_state.wavefmt.byterate = SDL_SwapLE32(format->byterate); + MS_ADPCM_state.wavefmt.blockalign = SDL_SwapLE16(format->blockalign); + MS_ADPCM_state.wavefmt.bitspersample = + SDL_SwapLE16(format->bitspersample); + rogue_feel = (Uint8 *)format+sizeof(*format); + if ( sizeof(*format) == 16 ) { + extra_info = ((rogue_feel[1]<<8)|rogue_feel[0]); + rogue_feel += sizeof(Uint16); + } + MS_ADPCM_state.wSamplesPerBlock = ((rogue_feel[1]<<8)|rogue_feel[0]); + rogue_feel += sizeof(Uint16); + MS_ADPCM_state.wNumCoef = ((rogue_feel[1]<<8)|rogue_feel[0]); + rogue_feel += sizeof(Uint16); + if ( MS_ADPCM_state.wNumCoef != 7 ) { + SDL_SetError("Unknown set of MS_ADPCM coefficients"); + return(-1); + } + for ( i=0; iiSamp1 * coeff[0]) + + (state->iSamp2 * coeff[1]))/256; + if ( nybble & 0x08 ) { + new_sample += state->iDelta * (nybble-0x10); + } else { + new_sample += state->iDelta * nybble; + } + if ( new_sample < min_audioval ) { + new_sample = min_audioval; + } else + if ( new_sample > max_audioval ) { + new_sample = max_audioval; + } + delta = ((Sint32)state->iDelta * adaptive[nybble])/256; + if ( delta < 16 ) { + delta = 16; + } + state->iDelta = delta; + state->iSamp2 = state->iSamp1; + state->iSamp1 = new_sample; + return(new_sample); +} + +static int MS_ADPCM_decode(Uint8 **audio_buf, Uint32 *audio_len) +{ + struct MS_ADPCM_decodestate *state[2]; + Uint8 *freeable, *encoded, *decoded; + Sint32 encoded_len, samplesleft; + Sint8 nybble, stereo; + Sint16 *coeff[2]; + Sint32 new_sample; + + /* Allocate the proper sized output buffer */ + encoded_len = *audio_len; + encoded = *audio_buf; + freeable = *audio_buf; + *audio_len = (encoded_len/MS_ADPCM_state.wavefmt.blockalign) * + MS_ADPCM_state.wSamplesPerBlock* + MS_ADPCM_state.wavefmt.channels*sizeof(Sint16); + *audio_buf = (Uint8 *)malloc(*audio_len); + if ( *audio_buf == NULL ) { + SDL_Error(SDL_ENOMEM); + return(-1); + } + decoded = *audio_buf; + + /* Get ready... Go! */ + stereo = (MS_ADPCM_state.wavefmt.channels == 2); + state[0] = &MS_ADPCM_state.state[0]; + state[1] = &MS_ADPCM_state.state[stereo]; + while ( encoded_len >= MS_ADPCM_state.wavefmt.blockalign ) { + /* Grab the initial information for this block */ + state[0]->hPredictor = *encoded++; + if ( stereo ) { + state[1]->hPredictor = *encoded++; + } + state[0]->iDelta = ((encoded[1]<<8)|encoded[0]); + encoded += sizeof(Sint16); + if ( stereo ) { + state[1]->iDelta = ((encoded[1]<<8)|encoded[0]); + encoded += sizeof(Sint16); + } + state[0]->iSamp1 = ((encoded[1]<<8)|encoded[0]); + encoded += sizeof(Sint16); + if ( stereo ) { + state[1]->iSamp1 = ((encoded[1]<<8)|encoded[0]); + encoded += sizeof(Sint16); + } + state[0]->iSamp2 = ((encoded[1]<<8)|encoded[0]); + encoded += sizeof(Sint16); + if ( stereo ) { + state[1]->iSamp2 = ((encoded[1]<<8)|encoded[0]); + encoded += sizeof(Sint16); + } + coeff[0] = MS_ADPCM_state.aCoeff[state[0]->hPredictor]; + coeff[1] = MS_ADPCM_state.aCoeff[state[1]->hPredictor]; + + /* Store the two initial samples we start with */ + decoded[0] = state[0]->iSamp2&0xFF; + decoded[1] = state[0]->iSamp2>>8; + decoded += 2; + if ( stereo ) { + decoded[0] = state[1]->iSamp2&0xFF; + decoded[1] = state[1]->iSamp2>>8; + decoded += 2; + } + decoded[0] = state[0]->iSamp1&0xFF; + decoded[1] = state[0]->iSamp1>>8; + decoded += 2; + if ( stereo ) { + decoded[0] = state[1]->iSamp1&0xFF; + decoded[1] = state[1]->iSamp1>>8; + decoded += 2; + } + + /* Decode and store the other samples in this block */ + samplesleft = (MS_ADPCM_state.wSamplesPerBlock-2)* + MS_ADPCM_state.wavefmt.channels; + while ( samplesleft > 0 ) { + nybble = (*encoded)>>4; + new_sample = MS_ADPCM_nibble(state[0],nybble,coeff[0]); + decoded[0] = new_sample&0xFF; + new_sample >>= 8; + decoded[1] = new_sample&0xFF; + decoded += 2; + + nybble = (*encoded)&0x0F; + new_sample = MS_ADPCM_nibble(state[1],nybble,coeff[1]); + decoded[0] = new_sample&0xFF; + new_sample >>= 8; + decoded[1] = new_sample&0xFF; + decoded += 2; + + ++encoded; + samplesleft -= 2; + } + encoded_len -= MS_ADPCM_state.wavefmt.blockalign; + } + free(freeable); + return(0); +} + +struct IMA_ADPCM_decodestate { + Sint32 sample; + Sint8 index; +}; +static struct IMA_ADPCM_decoder { + WaveFMT wavefmt; + Uint16 wSamplesPerBlock; + /* * * */ + struct IMA_ADPCM_decodestate state[2]; +} IMA_ADPCM_state; + +static int InitIMA_ADPCM(WaveFMT *format) +{ + Uint8 *rogue_feel; + Uint16 extra_info; + + /* Set the rogue pointer to the IMA_ADPCM specific data */ + IMA_ADPCM_state.wavefmt.encoding = SDL_SwapLE16(format->encoding); + IMA_ADPCM_state.wavefmt.channels = SDL_SwapLE16(format->channels); + IMA_ADPCM_state.wavefmt.frequency = SDL_SwapLE32(format->frequency); + IMA_ADPCM_state.wavefmt.byterate = SDL_SwapLE32(format->byterate); + IMA_ADPCM_state.wavefmt.blockalign = SDL_SwapLE16(format->blockalign); + IMA_ADPCM_state.wavefmt.bitspersample = + SDL_SwapLE16(format->bitspersample); + rogue_feel = (Uint8 *)format+sizeof(*format); + if ( sizeof(*format) == 16 ) { + extra_info = ((rogue_feel[1]<<8)|rogue_feel[0]); + rogue_feel += sizeof(Uint16); + } + IMA_ADPCM_state.wSamplesPerBlock = ((rogue_feel[1]<<8)|rogue_feel[0]); + return(0); +} + +static Sint32 IMA_ADPCM_nibble(struct IMA_ADPCM_decodestate *state,Uint8 nybble) +{ + const Sint32 max_audioval = ((1<<(16-1))-1); + const Sint32 min_audioval = -(1<<(16-1)); + const int index_table[16] = { + -1, -1, -1, -1, + 2, 4, 6, 8, + -1, -1, -1, -1, + 2, 4, 6, 8 + }; + const Sint32 step_table[89] = { + 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, + 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, + 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, + 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, + 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, + 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, + 22385, 24623, 27086, 29794, 32767 + }; + Sint32 delta, step; + + /* Compute difference and new sample value */ + step = step_table[state->index]; + delta = step >> 3; + if ( nybble & 0x04 ) delta += step; + if ( nybble & 0x02 ) delta += (step >> 1); + if ( nybble & 0x01 ) delta += (step >> 2); + if ( nybble & 0x08 ) delta = -delta; + state->sample += delta; + + /* Update index value */ + state->index += index_table[nybble]; + if ( state->index > 88 ) { + state->index = 88; + } else + if ( state->index < 0 ) { + state->index = 0; + } + + /* Clamp output sample */ + if ( state->sample > max_audioval ) { + state->sample = max_audioval; + } else + if ( state->sample < min_audioval ) { + state->sample = min_audioval; + } + return(state->sample); +} + +/* Fill the decode buffer with a channel block of data (8 samples) */ +static void Fill_IMA_ADPCM_block(Uint8 *decoded, Uint8 *encoded, + int channel, int numchannels, struct IMA_ADPCM_decodestate *state) +{ + int i; + Sint8 nybble; + Sint32 new_sample; + + decoded += (channel * 2); + for ( i=0; i<4; ++i ) { + nybble = (*encoded)&0x0F; + new_sample = IMA_ADPCM_nibble(state, nybble); + decoded[0] = new_sample&0xFF; + new_sample >>= 8; + decoded[1] = new_sample&0xFF; + decoded += 2 * numchannels; + + nybble = (*encoded)>>4; + new_sample = IMA_ADPCM_nibble(state, nybble); + decoded[0] = new_sample&0xFF; + new_sample >>= 8; + decoded[1] = new_sample&0xFF; + decoded += 2 * numchannels; + + ++encoded; + } +} + +static int IMA_ADPCM_decode(Uint8 **audio_buf, Uint32 *audio_len) +{ + struct IMA_ADPCM_decodestate *state; + Uint8 *freeable, *encoded, *decoded; + Sint32 encoded_len, samplesleft; + int c, channels; + + /* Check to make sure we have enough variables in the state array */ + channels = IMA_ADPCM_state.wavefmt.channels; + if ( channels > NELEMS(IMA_ADPCM_state.state) ) { + SDL_SetError("IMA ADPCM decoder can only handle %d channels", + NELEMS(IMA_ADPCM_state.state)); + return(-1); + } + state = IMA_ADPCM_state.state; + + /* Allocate the proper sized output buffer */ + encoded_len = *audio_len; + encoded = *audio_buf; + freeable = *audio_buf; + *audio_len = (encoded_len/IMA_ADPCM_state.wavefmt.blockalign) * + IMA_ADPCM_state.wSamplesPerBlock* + IMA_ADPCM_state.wavefmt.channels*sizeof(Sint16); + *audio_buf = (Uint8 *)malloc(*audio_len); + if ( *audio_buf == NULL ) { + SDL_Error(SDL_ENOMEM); + return(-1); + } + decoded = *audio_buf; + + /* Get ready... Go! */ + while ( encoded_len >= IMA_ADPCM_state.wavefmt.blockalign ) { + /* Grab the initial information for this block */ + for ( c=0; c>8; + decoded += 2; + } + + /* Decode and store the other samples in this block */ + samplesleft = (IMA_ADPCM_state.wSamplesPerBlock-1)*channels; + while ( samplesleft > 0 ) { + for ( c=0; cencoding)) { + case PCM_CODE: + /* We can understand this */ + break; + case MS_ADPCM_CODE: + /* Try to understand this */ + if ( InitMS_ADPCM(format) < 0 ) { + was_error = 1; + goto done; + } + MS_ADPCM_encoded = 1; + break; + case IMA_ADPCM_CODE: + /* Try to understand this */ + if ( InitIMA_ADPCM(format) < 0 ) { + was_error = 1; + goto done; + } + IMA_ADPCM_encoded = 1; + break; + default: + SDL_SetError("Unknown WAVE data format: 0x%.4x", + SDL_SwapLE16(format->encoding)); + was_error = 1; + goto done; + } + memset(spec, 0, (sizeof *spec)); + spec->freq = SDL_SwapLE32(format->frequency); + switch (SDL_SwapLE16(format->bitspersample)) { + case 4: + if ( MS_ADPCM_encoded || IMA_ADPCM_encoded ) { + spec->format = AUDIO_S16; + } else { + was_error = 1; + } + break; + case 8: + spec->format = AUDIO_U8; + break; + case 16: + spec->format = AUDIO_S16; + break; + default: + was_error = 1; + break; + } + if ( was_error ) { + SDL_SetError("Unknown %d-bit PCM data format", + SDL_SwapLE16(format->bitspersample)); + goto done; + } + spec->channels = (Uint8)SDL_SwapLE16(format->channels); + spec->samples = 4096; /* Good default buffer size */ + + /* Read the audio data chunk */ + *audio_buf = NULL; + do { + if ( *audio_buf != NULL ) { + free(*audio_buf); + } + lenread = ReadChunk(src, &chunk); + if ( lenread < 0 ) { + was_error = 1; + goto done; + } + *audio_len = lenread; + *audio_buf = chunk.data; + } while ( chunk.magic != DATA ); + + if ( MS_ADPCM_encoded ) { + if ( MS_ADPCM_decode(audio_buf, audio_len) < 0 ) { + was_error = 1; + goto done; + } + } + if ( IMA_ADPCM_encoded ) { + if ( IMA_ADPCM_decode(audio_buf, audio_len) < 0 ) { + was_error = 1; + goto done; + } + } + + /* Don't return a buffer that isn't a multiple of samplesize */ + samplesize = ((spec->format & 0xFF)/8)*spec->channels; + *audio_len &= ~(samplesize-1); + +done: + if ( format != NULL ) { + free(format); + } + if ( freesrc && src ) { + SDL_RWclose(src); + } + if ( was_error ) { + spec = NULL; + } + return(spec); +} + +/* Since the WAV memory is allocated in the shared library, it must also + be freed here. (Necessary under Win32, VC++) + */ +void SDL_FreeWAV(Uint8 *audio_buf) +{ + if ( audio_buf != NULL ) { + free(audio_buf); + } +} + +static int ReadChunk(SDL_RWops *src, Chunk *chunk) +{ + chunk->magic = SDL_ReadLE32(src); + chunk->length = SDL_ReadLE32(src); + chunk->data = (Uint8 *)malloc(chunk->length); + if ( chunk->data == NULL ) { + SDL_Error(SDL_ENOMEM); + return(-1); + } + if ( SDL_RWread(src, chunk->data, chunk->length, 1) != 1 ) { + SDL_Error(SDL_EFREAD); + free(chunk->data); + return(-1); + } + return(chunk->length); +} + +#endif /* ENABLE_FILE */ diff --git a/contrib/games/opentyrian/SDL/SDL_wave.h b/contrib/games/opentyrian/SDL/SDL_wave.h new file mode 100644 index 000000000..7f0247e3c --- /dev/null +++ b/contrib/games/opentyrian/SDL/SDL_wave.h @@ -0,0 +1,65 @@ +/* + SDL - Simple DirectMedia Layer + Copyright (C) 1997, 1998, 1999, 2000, 2001 Sam Lantinga + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Sam Lantinga + slouken@devolution.com +*/ + +#ifdef SAVE_RCSID +static char rcsid = + "@(#) $Id: SDL_wave.h,v 1.2 2001/04/26 16:50:17 hercules Exp $"; +#endif + +/* WAVE files are little-endian */ + +/*******************************************/ +/* Define values for Microsoft WAVE format */ +/*******************************************/ +#define RIFF 0x46464952 /* "RIFF" */ +#define WAVE 0x45564157 /* "WAVE" */ +#define FACT 0x74636166 /* "fact" */ +#define LIST 0x5453494c /* "LIST" */ +#define FMT 0x20746D66 /* "fmt " */ +#define DATA 0x61746164 /* "data" */ +#define PCM_CODE 0x0001 +#define MS_ADPCM_CODE 0x0002 +#define IMA_ADPCM_CODE 0x0011 +#define WAVE_MONO 1 +#define WAVE_STEREO 2 + +/* Normally, these three chunks come consecutively in a WAVE file */ +typedef struct WaveFMT { +/* Not saved in the chunk we read: + Uint32 FMTchunk; + Uint32 fmtlen; +*/ + Uint16 encoding; + Uint16 channels; /* 1 = mono, 2 = stereo */ + Uint32 frequency; /* One of 11025, 22050, or 44100 Hz */ + Uint32 byterate; /* Average bytes per second */ + Uint16 blockalign; /* Bytes per sample block */ + Uint16 bitspersample; /* One of 8, 12, 16, or 4 for ADPCM */ +} WaveFMT; + +/* The general chunk found in the WAVE file */ +typedef struct Chunk { + Uint32 magic; + Uint32 length; + Uint8 *data; /* Data includes magic and length */ +} Chunk; + diff --git a/contrib/games/opentyrian/SDL/joystick_stub.c b/contrib/games/opentyrian/SDL/joystick_stub.c new file mode 100755 index 000000000..6c0a3644d --- /dev/null +++ b/contrib/games/opentyrian/SDL/joystick_stub.c @@ -0,0 +1,153 @@ +/// JOYSTICK STUB FOR Wolfenstein 3D port to KolibriOS +/// Ported by maxcodehack and turbocat2001 + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +/** @file SDL_joystick.h + * @note In order to use these functions, SDL_Init() must have been called + * with the SDL_INIT_JOYSTICK flag. This causes SDL to scan the system + * for joysticks, and load appropriate drivers. + */ + +/** The joystick structure used to identify an SDL joystick */ +struct _SDL_Joystick; +typedef struct _SDL_Joystick SDL_Joystick; + +/* Function prototypes */ +/** + * Count the number of joysticks attached to the system + */ + int SDL_NumJoysticks(void){}; + +/** + * Get the implementation dependent name of a joystick. + * + * This can be called before any joysticks are opened. + * If no name can be found, this function returns NULL. + */ + const char * SDL_JoystickName(int device_index){}; + +/** + * Open a joystick for use. + * + * @param[in] device_index + * The index passed as an argument refers to + * the N'th joystick on the system. This index is the value which will + * identify this joystick in future joystick events. + * + * @return This function returns a joystick identifier, or NULL if an error occurred. + */ + SDL_Joystick * SDL_JoystickOpen(int device_index){}; + +/** + * Returns 1 if the joystick has been opened, or 0 if it has not. + */ + int SDL_JoystickOpened(int device_index){}; + +/** + * Get the device index of an opened joystick. + */ + int SDL_JoystickIndex(SDL_Joystick *joystick){}; + +/** + * Get the number of general axis controls on a joystick + */ + int SDL_JoystickNumAxes(SDL_Joystick *joystick){}; + +/** + * Get the number of trackballs on a joystick + * + * Joystick trackballs have only relative motion events associated + * with them and their state cannot be polled. + */ + int SDL_JoystickNumBalls(SDL_Joystick *joystick){}; + +/** + * Get the number of POV hats on a joystick + */ + int SDL_JoystickNumHats(SDL_Joystick *joystick){}; + +/** + * Get the number of buttons on a joystick + */ + int SDL_JoystickNumButtons(SDL_Joystick *joystick){}; + +/** + * Update the current state of the open joysticks. + * + * This is called automatically by the event loop if any joystick + * events are enabled. + */ + void SDL_JoystickUpdate(void){}; + +/** + * Enable/disable joystick event polling. + * + * If joystick events are disabled, you must call SDL_JoystickUpdate() + * yourself and check the state of the joystick when you want joystick + * information. + * + * @param[in] state The state can be one of SDL_QUERY, SDL_ENABLE or SDL_IGNORE. + */ + int SDL_JoystickEventState(int state){}; + +/** + * Get the current state of an axis control on a joystick + * + * @param[in] axis The axis indices start at index 0. + * + * @return The state is a value ranging from -32768 to 32767. + */ + int SDL_JoystickGetAxis(SDL_Joystick *joystick, int axis){}; + +/** + * @name Hat Positions + * The return value of SDL_JoystickGetHat() is one of the following positions: + */ +/*@{*/ +#define SDL_HAT_CENTERED 0x00 +#define SDL_HAT_UP 0x01 +#define SDL_HAT_RIGHT 0x02 +#define SDL_HAT_DOWN 0x04 +#define SDL_HAT_LEFT 0x08 +#define SDL_HAT_RIGHTUP (SDL_HAT_RIGHT|SDL_HAT_UP) +#define SDL_HAT_RIGHTDOWN (SDL_HAT_RIGHT|SDL_HAT_DOWN) +#define SDL_HAT_LEFTUP (SDL_HAT_LEFT|SDL_HAT_UP) +#define SDL_HAT_LEFTDOWN (SDL_HAT_LEFT|SDL_HAT_DOWN) +/*@}*/ + +/** + * Get the current state of a POV hat on a joystick + * + * @param[in] hat The hat indices start at index 0. + */ + int SDL_JoystickGetHat(SDL_Joystick *joystick, int hat){}; + +/** + * Get the ball axis change since the last poll + * + * @param[in] ball The ball indices start at index 0. + * + * @return This returns 0, or -1 if you passed it invalid parameters. + */ + int SDL_JoystickGetBall(SDL_Joystick *joystick, int ball, int *dx, int *dy){}; + +/** + * Get the current state of a button on a joystick + * + * @param[in] button The button indices start at index 0. + */ + int SDL_JoystickGetButton(SDL_Joystick *joystick, int button){}; + +/** + * Close a joystick previously opened with SDL_JoystickOpen() + */ + void SDL_JoystickClose(SDL_Joystick *joystick){}; + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif diff --git a/contrib/games/opentyrian/SDL/uSDL.c b/contrib/games/opentyrian/SDL/uSDL.c new file mode 100644 index 000000000..41b1a2dbf --- /dev/null +++ b/contrib/games/opentyrian/SDL/uSDL.c @@ -0,0 +1,53 @@ +#include +#include +#include + +#define asm_inline __asm__ __volatile__ + +#pragma pack(push,1) +typedef union{ + unsigned val; + struct{ + short h; + short w; + }; +}ksys_screen_t; +#pragma pack(pop) + + +static inline +void _ksys_change_window(int new_x, int new_y, int new_w, int new_h) +{ + asm_inline( + "int $0x40" + ::"a"(67), "b"(new_x), "c"(new_y), "d"(new_w),"S"(new_h) + ); +} + +static inline +ksys_screen_t _ksys_screen_size() +{ + ksys_screen_t size; + asm_inline( + "int $0x40" + :"=a"(size) + :"a"(14) + :"memory" + ); + return size; +} + +void uSDL_SetWinCenter(unsigned w, unsigned h){ + ksys_screen_t screen_size= _ksys_screen_size(); + int new_x = screen_size.w/2-w/2; + int new_y = screen_size.h/2-h/2; + _ksys_change_window(new_x, new_y, -1, -1); +} + + +void uSDL_Delay(unsigned ms){ + unsigned start = SDL_GetTicks(); + do{ + asm_inline("int $0x40" :: "a"(5),"b"(1)); + }while (SDL_GetTicks()-start < ms); +} \ No newline at end of file diff --git a/contrib/games/opentyrian/src/animlib.c b/contrib/games/opentyrian/src/animlib.c new file mode 100644 index 000000000..82dccce2d --- /dev/null +++ b/contrib/games/opentyrian/src/animlib.c @@ -0,0 +1,431 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "animlib.h" + +#include "file.h" +#include "keyboard.h" +#include "network.h" +#include "nortsong.h" +#include "palette.h" +#include "sizebuf.h" +#include "video.h" + +#include + +/*** Structs ***/ +/* The actual header has a lot of fields that are basically useless to us since + * we both set our own framerate and the format itself only allows for + * 320x200x8. Should a (nonexistent) ani be played that doesn't have the same + * assumed values we are going to use, TOO BAD. It'll just be treated as + * corrupt in playback. + */ +#define PALETTE_OFFSET 0x100 // 128 + sizeof(header) +#define PAGEHEADER_OFFSET 0x500 // PALETTE_OFFSET + sizeof(palette) +#define ANIM_OFFSET 0x0B00 // PAGEHEADER_OFFSET + sizeof(largepageheader) * 256 +#define ANI_PAGE_SIZE 0x10000 // 65536. +typedef struct anim_FileHeader_s +{ + unsigned int nlps; /* Number of 'pages', max 256. */ + unsigned int nRecords; /* Number of 'records', max 65535 */ +} anim_FileHeader_t; +typedef struct anim_LargePageHeader_s +{ + unsigned int baseRecord; /* The first record's number */ + unsigned int nRecords; /* Number of records. Supposedly there are bit flags but I saw no such code */ + unsigned int nBytes; /* Number of bytes used, excluding headers */ +} anim_LargePageHeader_t; + + +/*** Globals ***/ +Uint8 CurrentPageBuffer[65536]; +anim_LargePageHeader_t PageHeader[256]; +unsigned int CurrentPageRecordSizes[256]; + +anim_LargePageHeader_t CurrentPageHeader; +anim_FileHeader_t FileHeader; + +unsigned int Curlpnum; + +FILE * InFile; + + +/*** Function decs ***/ +int JE_playRunSkipDump( Uint8 *, unsigned int ); +void JE_closeAnim( void ); +int JE_loadAnim( const char * ); +int JE_renderFrame( unsigned int ); +int JE_findPage ( unsigned int ); +int JE_drawFrame( unsigned int ); +int JE_loadPage( unsigned int ); + +/*** Implementation ***/ + + +/* Loads the given page into memory. + * + * Returns 0 on success or nonzero on failure (bad data) + */ +int JE_loadPage( unsigned int pagenumber ) +{ + unsigned int i, pageSize; + + + if (Curlpnum == pagenumber) { return(0); } /* Already loaded */ + Curlpnum = pagenumber; + + /* We need to seek to the page and load it into our buffer. + * Pages have a fixed size of 0x10000; any left over space is padded + * unless it's the end of the file. + * + * Pages repeat their headers for some reason. They then have two bytes of + * padding folowed by a word for every record. THEN the data starts. + */ + fseek(InFile, ANIM_OFFSET + (pagenumber * ANI_PAGE_SIZE), SEEK_SET); + efread(&CurrentPageHeader.baseRecord, 2, 1, InFile); + efread(&CurrentPageHeader.nRecords, 2, 1, InFile); + efread(&CurrentPageHeader.nBytes, 2, 1, InFile); + + fseek(InFile, 2, SEEK_CUR); + for (i = 0; i < CurrentPageHeader.nRecords; i++) + { + efread(&CurrentPageRecordSizes[i], 2, 1, InFile); + } + + /* What remains is the 'compressed' data */ + efread(CurrentPageBuffer, 1, CurrentPageHeader.nBytes, InFile); + + /* Okay, we've succeeded in all our IO checks. Now, make sure the + * headers aren't lying or damaged or something. + */ + pageSize = 0; + for (i = 0; i < CurrentPageHeader.nRecords; i++) + { + pageSize += CurrentPageRecordSizes[i]; + } + + if(pageSize != CurrentPageHeader.nBytes) { return(-1); } + + /* So far, so good */ + return(0); +} + +int JE_drawFrame( unsigned int framenumber ) +{ + int ret; + + + ret = JE_loadPage(framenumber); + if (ret) { return(ret); } + + ret = JE_renderFrame (framenumber); + if (ret) { return(ret); } + + return(0); +} + +int JE_findPage( unsigned int framenumber ) +{ + unsigned int i; + + + for (i = 0; i < FileHeader.nlps; i++) + { + if (PageHeader[i].baseRecord <= framenumber + && PageHeader[i].baseRecord + PageHeader[i].nRecords > framenumber) + { + return(i); + } + } + + return(-1); /* Did not find */ +} + +int JE_renderFrame( unsigned int framenumber ) +{ + unsigned int i, offset, destframe; + + + destframe = framenumber - CurrentPageHeader.baseRecord; + + offset = 0; + for (i = 0; i < destframe; i++) + { + offset += CurrentPageRecordSizes[i]; + } + + return (JE_playRunSkipDump(CurrentPageBuffer + offset + 4, CurrentPageRecordSizes[destframe] - 4)); +} + +void JE_playAnim( const char *animfile, JE_byte startingframe, JE_byte speed ) +{ + unsigned int i; + int pageNum; + + if (JE_loadAnim(animfile) != 0) + { + return; /* Failed to open or process file */ + } + + /* Blank screen */ + JE_clr256(VGAScreen); + JE_showVGA(); + + + /* re FileHeader.nRecords-1: It's -1 in the pascal too. + * The final frame is a delta of the first, and we don't need that. + * We could also, if we ever ended up needing to loop anis, check + * the bools in the header to see if we should render the last + * frame. But that's never going to be encessary :) + */ + for (i = startingframe; i < FileHeader.nRecords-1; i++) + { + /* Handle boring crap */ + setjasondelay(speed); + + /* Load required frame. The loading function is smart enough to not re-load an already loaded frame */ + pageNum = JE_findPage(i); + if(pageNum == -1) { break; } + if (JE_loadPage(pageNum) != 0) { break; } + + /* render frame. */ + if (JE_renderFrame(i) != 0) { break; } + JE_showVGA(); + + + /* Return early if user presses a key */ + service_SDL_events(true); + if (newkey) + { + break; + } + + /* Wait until we need the next frame */ + NETWORK_KEEP_ALIVE(); + wait_delay(); + } + + JE_closeAnim(); +} + +/* loadAnim opens the file and loads data from it into the header structs. + * It should take care to clean up after itself should an error occur. + */ +int JE_loadAnim( const char *filename ) +{ + unsigned int i, fileSize; + char temp[4]; + + + Curlpnum = -1; + InFile = dir_fopen(data_dir(), filename, "rb"); + if(InFile == NULL) + { + return(-1); + } + + fileSize = ftell_eof(InFile); + if(fileSize < ANIM_OFFSET) + { + /* We don't know the exact size our file should be yet, + * but we do know it should be way more than this */ + fclose(InFile); + return(-1); + } + + /* Read in the header. The header is 256 bytes long or so, + * but that includes a lot of padding as well as several + * vars we really don't care about. We shall check the ID and extract + * the handful of vars we care about. Every value in the header that + * is constant will be ignored. + */ + + efread(&temp, 1, 4, InFile); /* The ID, should equal "LPF " */ + fseek(InFile, 2, SEEK_CUR); /* skip over this word */ + efread(&FileHeader.nlps, 2, 1, InFile); /* Number of pages */ + efread(&FileHeader.nRecords, 4, 1, InFile); /* Number of records */ + + if (memcmp(temp, "LPF ", 4) != 0 + || FileHeader.nlps == 0 || FileHeader.nRecords == 0 + || FileHeader.nlps > 256 || FileHeader.nRecords > 65535) + { + fclose(InFile); + return(-1); + } + + /* Read in headers */ + fseek(InFile, PAGEHEADER_OFFSET, SEEK_SET); + for (i = 0; i < FileHeader.nlps; i++) + { + efread(&PageHeader[i].baseRecord, 2, 1, InFile); + efread(&PageHeader[i].nRecords, 2, 1, InFile); + efread(&PageHeader[i].nBytes, 2, 1, InFile); + } + + + /* Now we have enough information to calculate the 'expected' file size. + * Our calculation SHOULD be equal to fileSize, but we won't begrudge + * padding */ + if (fileSize < (FileHeader.nlps-1) * ANI_PAGE_SIZE + ANIM_OFFSET + + PageHeader[FileHeader.nlps-1].nBytes + + PageHeader[FileHeader.nlps-1].nRecords * 2 + 8) + { + fclose(InFile); + return(-1); + } + + + /* Now read in the palette. */ + fseek(InFile, PALETTE_OFFSET, SEEK_SET); + for (i = 0; i < 256; i++) + { + efread(&colors[i].b, 1, 1, InFile); + efread(&colors[i].g, 1, 1, InFile); + efread(&colors[i].r, 1, 1, InFile); + efread(&colors[i].unused, 1, 1, InFile); + } + set_palette(colors, 0, 255); + + /* Whew! That was hard. Let's go grab some beers! */ + return(0); +} + +void JE_closeAnim( void ) +{ + fclose(InFile); +} + +/* RunSkipDump decompresses the video. There are three operations, run, skip, + * and dump. They can be used in either byte or word variations, making six + * possible actions, and there's a seventh 'stop' action, which looks + * like 0x80 0x00 0x00. + * + * Run is a memset. + * Dump is a memcpy. + * Skip leaves the old data intact and simply increments the pointers. + * + * returns 0 on success or 1 if decompressing failed. Failure to decompress + * indicates a broken or malicious file; playback should terminate. + */ +int JE_playRunSkipDump( Uint8 *incomingBuffer, unsigned int IncomingBufferLength ) +{ + sizebuf_t Buffer_IN, Buffer_OUT; + sizebuf_t * pBuffer_IN = &Buffer_IN, * pBuffer_OUT = &Buffer_OUT; + + #define ANI_SHORT_RLE 0x00 + #define ANI_SHORT_SKIP 0x80 + #define ANI_LONG_OP 0x80 + #define ANI_LONG_COPY_OR_RLE 0x8000 + #define ANI_LONG_RLE 0x4000 + #define ANI_STOP 0x0000 + + SZ_Init(pBuffer_IN, incomingBuffer, IncomingBufferLength); + SZ_Init(pBuffer_OUT, VGAScreen->pixels, VGAScreen->h * VGAScreen->pitch); + + + /* 320x200 is the only supported format. + * Assert is here as a hint should our screen size ever changes. + * As for how to decompress to the wrong screen size... */ + assert(VGAScreen->h * VGAScreen->pitch == 320 * 200); + + + while (1) + { + /* Get one byte. This byte may have flags that tell us more */ + unsigned int opcode = MSG_ReadByte(pBuffer_IN); + + /* Before we continue, check the error states/ + * We should *probably* check these after every read and write, but + * I've rigged it so that the buffers will never go out of bounds. + * So we can afford to be lazy; if the buffer overflows below it will + * silently fail its writes and we'll catch the failure on our next + * run through the loop. A failure means we should be + * leaving ANYWAY. The contents of our buffers doesn't matter. + */ + if (SZ_Error(pBuffer_IN) || SZ_Error(pBuffer_OUT)) + { + return(-1); + } + + /* Divide into 'short' and 'long' */ + if (opcode == ANI_LONG_OP) /* long ops */ + { + opcode = MSG_ReadWord(pBuffer_IN); + + if (opcode == ANI_STOP) /* We are done decompressing. Leave */ + { + break; + } + else if (!(opcode & ANI_LONG_COPY_OR_RLE)) /* If it's not those two, it's a skip */ + { + unsigned int count = opcode; + SZ_Seek(pBuffer_OUT, count, SEEK_CUR); + } + else /* Now things get a bit more interesting... */ + { + opcode &= ~ANI_LONG_COPY_OR_RLE; /* Clear that flag */ + + if (opcode & ANI_LONG_RLE) /* RLE */ + { + unsigned int count = opcode & ~ANI_LONG_RLE; /* Clear flag */ + + /* Extract another byte */ + unsigned int value = MSG_ReadByte(pBuffer_IN); + + /* The actual run */ + SZ_Memset(pBuffer_OUT, value, count); + } + else + { /* Long copy */ + unsigned int count = opcode; + + /* Copy */ + SZ_Memcpy2(pBuffer_OUT, pBuffer_IN, count); + } + } + } /* End of long ops */ + else /* short ops */ + { + if (opcode & ANI_SHORT_SKIP) /* Short skip, move pointer only */ + { + unsigned int count = opcode & ~ANI_SHORT_SKIP; /* clear flag to get count */ + SZ_Seek(pBuffer_OUT, count, SEEK_CUR); + } + else if (opcode == ANI_SHORT_RLE) /* Short RLE, memset the destination */ + { + /* Extract a few more bytes */ + unsigned int count = MSG_ReadByte(pBuffer_IN); + unsigned int value = MSG_ReadByte(pBuffer_IN); + + /* Run */ + SZ_Memset(pBuffer_OUT, value, count); + } + else /* Short copy, memcpy from src to dest. */ + { + unsigned int count = opcode; + + /* Dump */ + SZ_Memcpy2(pBuffer_OUT, pBuffer_IN, count); + } + } /* End of short ops */ + } + + /* And that's that */ + return(0); +} + diff --git a/contrib/games/opentyrian/src/animlib.h b/contrib/games/opentyrian/src/animlib.h new file mode 100644 index 000000000..502f002bc --- /dev/null +++ b/contrib/games/opentyrian/src/animlib.h @@ -0,0 +1,27 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef ANIMLIB_H +#define ANIMLIB_H + +#include "opentyr.h" + +void JE_playAnim( const char *animfile, JE_byte startingframe, JE_byte speed ); + +#endif /* ANIMLIB_H */ + diff --git a/contrib/games/opentyrian/src/arg_parse.c b/contrib/games/opentyrian/src/arg_parse.c new file mode 100644 index 000000000..549bfd857 --- /dev/null +++ b/contrib/games/opentyrian/src/arg_parse.c @@ -0,0 +1,248 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "arg_parse.h" + +#include "std_support.h" + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif + +#include +#include +#include + +static void permute( const char *argv[], int *first_nonopt, int *first_opt, int after_opt ); + +static int parse_short_opt( int argc, const char *const argv[], const Options *options, Option *option ); +static int parse_long_opt( int argc, const char *const argv[], const Options *options, Option *option ); + +Option parse_args( int argc, const char *argv[], const Options *options ) +{ + static int argn = 1; + static bool no_more_options = false; + + static int first_nonopt = 1; + + Option option = { NOT_OPTION, NULL, 0 }; + option.argn = first_nonopt; + + while (argn < argc) + { + size_t arg_len = strlen(argv[argn]); + + if (!no_more_options && + argv[argn][0] == '-' && // first char is '-' + arg_len > 1) // option is not "-" + { + option.argn = argn; + + if (argv[argn][1] == '-') // string begins with "--" + { + if (arg_len == 2) // "--" alone indicates end of options + { + ++argn; + no_more_options = true; + } + else + { + argn = parse_long_opt(argc, argv, options, &option); + } + } + else + { + argn = parse_short_opt(argc, argv, options, &option); + } + + // shift option in front of non-options + permute(argv, &first_nonopt, &option.argn, argn); + + // don't include "--" in non-options + if (no_more_options) + ++option.argn; + break; + } + else + { + // skip non-options, permute later when option encountered + ++argn; + } + } + + return option; +} + +static void permute( const char *argv[], int *first_nonopt, int *first_opt, int after_opt ) +{ + const int nonopts = *first_opt - *first_nonopt; + + // slide each of the options in front of the non-options + for (int i = *first_opt; i < after_opt; ++i) + { + for (int j = i; j > *first_nonopt; --j) + { + // swap argv[j] and argv[j - 1] + const char *temp = argv[j]; + argv[j] = argv[j - 1]; + argv[j - 1] = temp; + } + + // position of first non-option shifts right once for each option + ++(*first_nonopt); + } + + // position of first option is initial position of first non-option + *first_opt -= nonopts; +} + +static int parse_short_opt( int argc, const char *const argv[], const Options *options, Option *option ) +{ + static size_t offset = 1; // ignore the "-" + + int argn = option->argn; + + const char *arg = argv[argn]; + + const size_t arg_len = strlen(arg); + + const bool arg_attached = (offset + 1 < arg_len), // possible argument attached? + last_in_argv = (argn == argc - 1); + + option->value = INVALID_OPTION; + + for (; !(options->short_opt == 0 && + options->long_opt == NULL); ++options) + { + if (options->short_opt != 0 && + options->short_opt == arg[offset]) + { + option->value = options->value; + + if (options->has_arg) + { + if (arg_attached) // arg direclty follows option + { + option->arg = arg + offset + 1; + + offset = arg_len; + } + else if (!last_in_argv) // arg is next in argv + { + option->arg = argv[++argn]; + + offset = arg_len; + } + else + { + option->value = OPTION_MISSING_ARG; + break; + } + } + + break; + } + } + + switch (option->value) + { + case INVALID_OPTION: + fprintf(stderr, "%s: invalid option -- '%c'\n", argv[0], argv[option->argn][offset]); + break; + case OPTION_MISSING_ARG: + fprintf(stderr, "%s: option requires an argument -- '%c'\n", argv[0], argv[option->argn][offset]); + break; + } + + if (++offset >= arg_len) + { + ++argn; + offset = 1; + } + + return argn; // which arg in argv that parse_args() should examine when called again +} + +static int parse_long_opt( int argc, const char *const argv[], const Options *options, Option *option ) +{ + int argn = option->argn; + + const char *arg = argv[argn] + 2; // ignore the "--" + + const size_t arg_len = strlen(arg), + arg_opt_len = ot_strchrnul(arg, '=') - arg; // length before "=" + + const bool arg_attached = (arg_opt_len < arg_len), // argument attached using "="? + last_in_argv = (argn == argc - 1); + + option->value = INVALID_OPTION; + + for (; !(options->short_opt == 0 && + options->long_opt == NULL); ++options) + { + if (options->long_opt != NULL && + strncmp(options->long_opt, arg, arg_opt_len) == 0) // matches (partially, at least) + { + if (option->value != INVALID_OPTION) // other match already found + { + option->value = AMBIGUOUS_OPTION; + break; + } + + option->value = options->value; + + if (options->has_arg) + { + if (arg_attached) // arg is after "=" + { + option->arg = arg + arg_opt_len + 1; + } + else if (!last_in_argv) // arg is next in argv + { + option->arg = argv[++argn]; + } + else // arg is missing + { + option->value = OPTION_MISSING_ARG; + // can't break, gotta check for ambiguity + } + } + + if (arg_opt_len == strlen(options->long_opt)) // exact match + break; + // can't break for partial match, gotta check for ambiguity + } + } + + switch (option->value) + { + case INVALID_OPTION: + fprintf(stderr, "%s: unrecognized option '%s'\n", argv[0], argv[option->argn]); + break; + case AMBIGUOUS_OPTION: + fprintf(stderr, "%s: option '%s' is ambiguous\n", argv[0], argv[option->argn]); + break; + case OPTION_MISSING_ARG: + fprintf(stderr, "%s: option '%s' requires an argument\n", argv[0], argv[option->argn]); + break; + } + + ++argn; + + return argn; // which arg in argv that parse_args() should examine when called again +} diff --git a/contrib/games/opentyrian/src/arg_parse.h b/contrib/games/opentyrian/src/arg_parse.h new file mode 100644 index 000000000..fe15fd8f9 --- /dev/null +++ b/contrib/games/opentyrian/src/arg_parse.h @@ -0,0 +1,58 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef ARG_PARSE_H +#define ARG_PARSE_H + +#include + +// this is essentially a reimplementation of getopt_long() + +typedef struct +{ + int value; + char short_opt; + const char *long_opt; + bool has_arg; +} +Options; + +enum +{ + // indicates that argv[argn..argc) are not options + NOT_OPTION = 0, + + /* behavior of parse_args() is undefined after + it has returned any of the following values */ + INVALID_OPTION = -1, + AMBIGUOUS_OPTION = -2, + OPTION_MISSING_ARG = -3 +}; + +typedef struct +{ + int value; + const char *arg; + + int argn; +} +Option; + +Option parse_args( int argc, const char *argv[], const Options *options ); + +#endif /* ARG_PARSE_H */ diff --git a/contrib/games/opentyrian/src/backgrnd.c b/contrib/games/opentyrian/src/backgrnd.c new file mode 100644 index 000000000..e7bff5f46 --- /dev/null +++ b/contrib/games/opentyrian/src/backgrnd.c @@ -0,0 +1,527 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "backgrnd.h" + +#include "config.h" +#include "mtrand.h" +#include "opentyr.h" +#include "varz.h" +#include "video.h" + +#include + +/*Special Background 2 and Background 3*/ + +/*Back Pos 3*/ +JE_word backPos, backPos2, backPos3; +JE_word backMove, backMove2, backMove3; + +/*Main Maps*/ +JE_word mapX, mapY, mapX2, mapX3, mapY2, mapY3; +JE_byte **mapYPos, **mapY2Pos, **mapY3Pos; +JE_word mapXPos, oldMapXOfs, mapXOfs, mapX2Ofs, mapX2Pos, mapX3Pos, oldMapX3Ofs, mapX3Ofs, tempMapXOfs; +intptr_t mapXbpPos, mapX2bpPos, mapX3bpPos; +JE_byte map1YDelay, map1YDelayMax, map2YDelay, map2YDelayMax; + + +JE_boolean anySmoothies; +JE_byte smoothie_data[9]; /* [1..9] */ + +void JE_darkenBackground( JE_word neat ) /* wild detail level */ +{ + Uint8 *s = VGAScreen->pixels; /* screen pointer, 8-bit specific */ + int x, y; + + s += 24; + + for (y = 184; y; y--) + { + for (x = 264; x; x--) + { + *s = ((((*s & 0x0f) << 4) - (*s & 0x0f) + ((((x - neat - y) >> 2) + *(s-2) + (y == 184 ? 0 : *(s-(VGAScreen->pitch-1)))) & 0x0f)) >> 4) | (*s & 0xf0); + s++; + } + s += VGAScreen->pitch - 264; + } +} + +void blit_background_row( SDL_Surface *surface, int x, int y, Uint8 **map ) +{ + assert(surface->format->BitsPerPixel == 8); + + Uint8 *pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x, + *pixels_ll = (Uint8 *)surface->pixels, // lower limit + *pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit + + for (int y = 0; y < 28; y++) + { + // not drawing on screen yet; skip y + if ((pixels + (12 * 24)) < pixels_ll) + { + pixels += surface->pitch; + continue; + } + + for (int tile = 0; tile < 12; tile++) + { + Uint8 *data = *(map + tile); + + // no tile; skip tile + if (data == NULL) + { + pixels += 24; + continue; + } + + data += y * 24; + + for (int x = 24; x; x--) + { + if (pixels >= pixels_ul) + return; + if (pixels >= pixels_ll && *data != 0) + *pixels = *data; + + pixels++; + data++; + } + } + + pixels += surface->pitch - 12 * 24; + } +} + +void blit_background_row_blend( SDL_Surface *surface, int x, int y, Uint8 **map ) +{ + assert(surface->format->BitsPerPixel == 8); + + Uint8 *pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x, + *pixels_ll = (Uint8 *)surface->pixels, // lower limit + *pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit + + for (int y = 0; y < 28; y++) + { + // not drawing on screen yet; skip y + if ((pixels + (12 * 24)) < pixels_ll) + { + pixels += surface->pitch; + continue; + } + + for (int tile = 0; tile < 12; tile++) + { + Uint8 *data = *(map + tile); + + // no tile; skip tile + if (data == NULL) + { + pixels += 24; + continue; + } + + data += y * 24; + + for (int x = 24; x; x--) + { + if (pixels >= pixels_ul) + return; + if (pixels >= pixels_ll && *data != 0) + *pixels = (*data & 0xf0) | (((*pixels & 0x0f) + (*data & 0x0f)) / 2); + + pixels++; + data++; + } + } + + pixels += surface->pitch - 12 * 24; + } +} + +void draw_background_1( SDL_Surface *surface ) +{ + SDL_FillRect(surface, NULL, 0); + + Uint8 **map = (Uint8 **)mapYPos + mapXbpPos - 12; + + for (int i = -1; i < 7; i++) + { + blit_background_row(surface, mapXPos, (i * 28) + backPos, map); + + map += 14; + } +} + +void draw_background_2( SDL_Surface *surface ) +{ + if (map2YDelayMax > 1 && backMove2 < 2) + backMove2 = (map2YDelay == 1) ? 1 : 0; + + if (background2 != 0) + { + // water effect combines background 1 and 2 by syncronizing the x coordinate + int x = smoothies[1] ? mapXPos : mapX2Pos; + + Uint8 **map = (Uint8 **)mapY2Pos + (smoothies[1] ? mapXbpPos : mapX2bpPos) - 12; + + for (int i = -1; i < 7; i++) + { + blit_background_row(surface, x, (i * 28) + backPos2, map); + + map += 14; + } + } + + /*Set Movement of background*/ + if (--map2YDelay == 0) + { + map2YDelay = map2YDelayMax; + + backPos2 += backMove2; + + if (backPos2 > 27) + { + backPos2 -= 28; + mapY2--; + mapY2Pos -= 14; /*Map Width*/ + } + } +} + +void draw_background_2_blend( SDL_Surface *surface ) +{ + if (map2YDelayMax > 1 && backMove2 < 2) + backMove2 = (map2YDelay == 1) ? 1 : 0; + + Uint8 **map = (Uint8 **)mapY2Pos + mapX2bpPos - 12; + + for (int i = -1; i < 7; i++) + { + blit_background_row_blend(surface, mapX2Pos, (i * 28) + backPos2, map); + + map += 14; + } + + /*Set Movement of background*/ + if (--map2YDelay == 0) + { + map2YDelay = map2YDelayMax; + + backPos2 += backMove2; + + if (backPos2 > 27) + { + backPos2 -= 28; + mapY2--; + mapY2Pos -= 14; /*Map Width*/ + } + } +} + +void draw_background_3( SDL_Surface *surface ) +{ + /* Movement of background */ + backPos3 += backMove3; + + if (backPos3 > 27) + { + backPos3 -= 28; + mapY3--; + mapY3Pos -= 15; /*Map Width*/ + } + + Uint8 **map = (Uint8 **)mapY3Pos + mapX3bpPos - 12; + + for (int i = -1; i < 7; i++) + { + blit_background_row(surface, mapX3Pos, (i * 28) + backPos3, map); + + map += 15; + } +} + +void JE_filterScreen( JE_shortint col, JE_shortint int_) +{ + Uint8 *s = NULL; /* screen pointer, 8-bit specific */ + int x, y; + unsigned int temp; + + if (filterFade) + { + levelBrightness += levelBrightnessChg; + if ((filterFadeStart && levelBrightness < -14) || levelBrightness > 14) + { + levelBrightnessChg = -levelBrightnessChg; + filterFadeStart = false; + levelFilter = levelFilterNew; + } + if (!filterFadeStart && levelBrightness == 0) + { + filterFade = false; + levelBrightness = -99; + } + } + + if (col != -99 && filtrationAvail) + { + s = VGAScreen->pixels; + s += 24; + + col <<= 4; + + for (y = 184; y; y--) + { + for (x = 264; x; x--) + { + *s = col | (*s & 0x0f); + s++; + } + s += VGAScreen->pitch - 264; + } + } + + if (int_ != -99 && explosionTransparent) + { + s = VGAScreen->pixels; + s += 24; + + for (y = 184; y; y--) + { + for (x = 264; x; x--) + { + temp = (*s & 0x0f) + int_; + *s = (*s & 0xf0) | (temp >= 0x1f ? 0 : (temp >= 0x0f ? 0x0f : temp)); + s++; + } + s += VGAScreen->pitch - 264; + } + } +} + +void JE_checkSmoothies( void ) +{ + anySmoothies = (processorType > 2 && (smoothies[1-1] || smoothies[2-1])) || (processorType > 1 && (smoothies[3-1] || smoothies[4-1] || smoothies[5-1])); +} + +void lava_filter( SDL_Surface *dst, SDL_Surface *src ) +{ + assert(src->format->BitsPerPixel == 8 && dst->format->BitsPerPixel == 8); + + /* we don't need to check for over-reading the pixel surfaces since we only + * read from the top 185+1 scanlines, and there should be 320 */ + + const int dst_pitch = dst->pitch; + Uint8 *dst_pixel = (Uint8 *)dst->pixels + (185 * dst_pitch); + const Uint8 * const dst_pixel_ll = (Uint8 *)dst->pixels; // lower limit + + const int src_pitch = src->pitch; + const Uint8 *src_pixel = (Uint8 *)src->pixels + (185 * src->pitch); + const Uint8 * const src_pixel_ll = (Uint8 *)src->pixels; // lower limit + + int w = 320 * 185 - 1; + + for (int y = 185 - 1; y >= 0; --y) + { + dst_pixel -= (dst_pitch - 320); // in case pitch is not 320 + src_pixel -= (src_pitch - 320); // in case pitch is not 320 + + for (int x = 320 - 1; x >= 0; x -= 8) + { + int waver = abs(((w >> 9) & 0x0f) - 8) - 1; + w -= 8; + + for (int xi = 8 - 1; xi >= 0; --xi) + { + --dst_pixel; + --src_pixel; + + // value is average value of source pixel (2x), destination pixel above, and destination pixel below (all with waver) + // hue is red + Uint8 value = 0; + + if (src_pixel + waver >= src_pixel_ll) + value += (*(src_pixel + waver) & 0x0f) * 2; + value += *(dst_pixel + waver + dst_pitch) & 0x0f; + if (dst_pixel + waver - dst_pitch >= dst_pixel_ll) + value += *(dst_pixel + waver - dst_pitch) & 0x0f; + + *dst_pixel = (value / 4) | 0x70; + } + } + } +} + +void water_filter( SDL_Surface *dst, SDL_Surface *src ) +{ + assert(src->format->BitsPerPixel == 8 && dst->format->BitsPerPixel == 8); + + Uint8 hue = smoothie_data[1] << 4; + + /* we don't need to check for over-reading the pixel surfaces since we only + * read from the top 185+1 scanlines, and there should be 320 */ + + const int dst_pitch = dst->pitch; + Uint8 *dst_pixel = (Uint8 *)dst->pixels + (185 * dst_pitch); + + const Uint8 *src_pixel = (Uint8 *)src->pixels + (185 * src->pitch); + + int w = 320 * 185 - 1; + + for (int y = 185 - 1; y >= 0; --y) + { + dst_pixel -= (dst_pitch - 320); // in case pitch is not 320 + src_pixel -= (src->pitch - 320); // in case pitch is not 320 + + for (int x = 320 - 1; x >= 0; x -= 8) + { + int waver = abs(((w >> 10) & 0x07) - 4) - 1; + w -= 8; + + for (int xi = 8 - 1; xi >= 0; --xi) + { + --dst_pixel; + --src_pixel; + + // pixel is copied from source if not blue + // otherwise, value is average of value of source pixel and destination pixel below (with waver) + if ((*src_pixel & 0x30) == 0) + { + *dst_pixel = *src_pixel; + } + else + { + Uint8 value = *src_pixel & 0x0f; + value += *(dst_pixel + waver + dst_pitch) & 0x0f; + *dst_pixel = (value / 2) | hue; + } + } + } + } +} + +void iced_blur_filter( SDL_Surface *dst, SDL_Surface *src ) +{ + assert(src->format->BitsPerPixel == 8 && dst->format->BitsPerPixel == 8); + + Uint8 *dst_pixel = dst->pixels; + const Uint8 *src_pixel = src->pixels; + + for (int y = 0; y < 184; ++y) + { + for (int x = 0; x < 320; ++x) + { + // value is average value of source pixel and destination pixel + // hue is icy blue + + const Uint8 value = (*src_pixel & 0x0f) + (*dst_pixel & 0x0f); + *dst_pixel = (value / 2) | 0x80; + + ++dst_pixel; + ++src_pixel; + } + + dst_pixel += (dst->pitch - 320); // in case pitch is not 320 + src_pixel += (src->pitch - 320); // in case pitch is not 320 + } +} + +void blur_filter( SDL_Surface *dst, SDL_Surface *src ) +{ + assert(src->format->BitsPerPixel == 8 && dst->format->BitsPerPixel == 8); + + Uint8 *dst_pixel = dst->pixels; + const Uint8 *src_pixel = src->pixels; + + for (int y = 0; y < 184; ++y) + { + for (int x = 0; x < 320; ++x) + { + // value is average value of source pixel and destination pixel + // hue is source pixel hue + + const Uint8 value = (*src_pixel & 0x0f) + (*dst_pixel & 0x0f); + *dst_pixel = (value / 2) | (*src_pixel & 0xf0); + + ++dst_pixel; + ++src_pixel; + } + + dst_pixel += (dst->pitch - 320); // in case pitch is not 320 + src_pixel += (src->pitch - 320); // in case pitch is not 320 + } +} + + +/* Background Starfield */ +typedef struct +{ + Uint8 color; + JE_word position; // relies on overflow wrap-around + int speed; +} StarfieldStar; + +#define MAX_STARS 100 +#define STARFIELD_HUE 0x90 +static StarfieldStar starfield_stars[MAX_STARS]; +int starfield_speed; + +void initialize_starfield( void ) +{ + for (int i = MAX_STARS-1; i >= 0; --i) + { + starfield_stars[i].position = mt_rand() % 320 + mt_rand() % 200 * VGAScreen->pitch; + starfield_stars[i].speed = mt_rand() % 3 + 2; + starfield_stars[i].color = mt_rand() % 16 + STARFIELD_HUE; + } +} + +void update_and_draw_starfield( SDL_Surface* surface, int move_speed ) +{ + Uint8* p = (Uint8*)surface->pixels; + + for (int i = MAX_STARS-1; i >= 0; --i) + { + StarfieldStar* star = &starfield_stars[i]; + + star->position += (star->speed + move_speed) * surface->pitch; + + if (star->position < 177 * surface->pitch) + { + if (p[star->position] == 0) + { + p[star->position] = star->color; + } + + // If star is bright enough, draw surrounding pixels + if (star->color - 4 >= STARFIELD_HUE) + { + if (p[star->position + 1] == 0) + p[star->position + 1] = star->color - 4; + + if (star->position > 0 && p[star->position - 1] == 0) + p[star->position - 1] = star->color - 4; + + if (p[star->position + surface->pitch] == 0) + p[star->position + surface->pitch] = star->color - 4; + + if (star->position >= surface->pitch && p[star->position - surface->pitch] == 0) + p[star->position - surface->pitch] = star->color - 4; + } + } + } +} diff --git a/contrib/games/opentyrian/src/backgrnd.h b/contrib/games/opentyrian/src/backgrnd.h new file mode 100644 index 000000000..c227f7743 --- /dev/null +++ b/contrib/games/opentyrian/src/backgrnd.h @@ -0,0 +1,64 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef BACKGRND_H +#define BACKGRND_H + +#include "opentyr.h" + +#include "SDL.h" + +#include + +extern JE_word backPos, backPos2, backPos3; +extern JE_word backMove, backMove2, backMove3; +extern JE_word mapX, mapY, mapX2, mapX3, mapY2, mapY3; +extern JE_byte **mapYPos, **mapY2Pos, **mapY3Pos; +extern JE_word mapXPos, oldMapXOfs, mapXOfs, mapX2Ofs, mapX2Pos, mapX3Pos, oldMapX3Ofs, mapX3Ofs, tempMapXOfs; +extern intptr_t mapXbpPos, mapX2bpPos, mapX3bpPos; +extern JE_byte map1YDelay, map1YDelayMax, map2YDelay, map2YDelayMax; +extern JE_boolean anySmoothies; // if yes, I want one :D +extern JE_byte smoothie_data[9]; + +extern int starfield_speed; + +void JE_darkenBackground( JE_word neat ); + +void blit_background_row( SDL_Surface *surface, int x, int y, Uint8 **map ); +void blit_background_row_blend( SDL_Surface *surface, int x, int y, Uint8 **map ); + +void draw_background_1( SDL_Surface *surface ); +void draw_background_2( SDL_Surface *surface ); +void draw_background_2_blend( SDL_Surface *surface ); +void draw_background_3( SDL_Surface *surface ); + +void JE_filterScreen( JE_shortint col, JE_shortint generic_int ); + +void JE_checkSmoothies( void ); +void lava_filter( SDL_Surface *dst, SDL_Surface *src ); +void water_filter( SDL_Surface *dst, SDL_Surface *src ); +void iced_blur_filter( SDL_Surface *dst, SDL_Surface *src ); +void blur_filter( SDL_Surface *dst, SDL_Surface *src ); +/*smoothies #5 is used for 3*/ +/*smoothies #9 is a vertical flip*/ + +void initialize_starfield( void ); +void update_and_draw_starfield( SDL_Surface* surface, int move_speed ); + +#endif /* BACKGRND_H */ + diff --git a/contrib/games/opentyrian/src/config.c b/contrib/games/opentyrian/src/config.c new file mode 100644 index 000000000..1d58eaacd --- /dev/null +++ b/contrib/games/opentyrian/src/config.c @@ -0,0 +1,1014 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "config.h" + +#include "episodes.h" +#include "file.h" +#include "joystick.h" +#include "loudness.h" +#include "mtrand.h" +#include "nortsong.h" +#include "opentyr.h" +#include "player.h" +#include "varz.h" +#include "vga256d.h" +#include "video.h" +#include "video_scale.h" + +#include +#include + +#ifdef _MSC_VER +#include +#define mkdir _mkdir +#else +#include +#endif + +/* Configuration Load/Save handler */ + +const JE_byte cryptKey[10] = /* [1..10] */ +{ + 15, 50, 89, 240, 147, 34, 86, 9, 32, 208 +}; + +const JE_KeySettingType defaultKeySettings = +{ + SDLK_UP, SDLK_DOWN, SDLK_LEFT, SDLK_RIGHT, SDLK_SPACE, SDLK_RETURN, SDLK_LCTRL, SDLK_LALT +/* 72, 80, 75, 77, 57, 28, 29, 56*/ +}; + +const char defaultHighScoreNames[34][23] = /* [1..34] of string [22] */ +{/*1P*/ +/*TYR*/ "The Prime Chair", /*13*/ + "Transon Lohk", + "Javi Onukala", + "Mantori", + "Nortaneous", + "Dougan", + "Reid", + "General Zinglon", + "Late Gyges Phildren", + "Vykromod", + "Beppo", + "Borogar", + "ShipMaster Carlos", + +/*OTHER*/ "Jill", /*5*/ + "Darcy", + "Jake Stone", + "Malvineous Havershim", + "Marta Louise Velasquez", + +/*JAZZ*/ "Jazz Jackrabbit", /*3*/ + "Eva Earlong", + "Devan Shell", + +/*OMF*/ "Crystal Devroe", /*11*/ + "Steffan Tommas", + "Milano Angston", + "Christian", + "Shirro", + "Jean-Paul", + "Ibrahim Hothe", + "Angel", + "Cossette Akira", + "Raven", + "Hans Kreissack", + +/*DARE*/ "Tyler", /*2*/ + "Rennis the Rat Guard" +}; + +const char defaultTeamNames[22][25] = /* [1..22] of string [24] */ +{ + "Jackrabbits", + "Team Tyrian", + "The Elam Brothers", + "Dare to Dream Team", + "Pinball Freaks", + "Extreme Pinball Freaks", + "Team Vykromod", + "Epic All-Stars", + "Hans Keissack's WARriors", + "Team Overkill", + "Pied Pipers", + "Gencore Growlers", + "Microsol Masters", + "Beta Warriors", + "Team Loco", + "The Shellians", + "Jungle Jills", + "Murderous Malvineous", + "The Traffic Department", + "Clan Mikal", + "Clan Patrok", + "Carlos' Crawlers" +}; + + +const JE_EditorItemAvailType initialItemAvail = +{ + 1,1,1,0,0,1,1,0,1,1,1,1,1,0,1,0,1,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0, /* Front/Rear Weapons 1-38 */ + 0,0,0,0,0,0,0,0,0,0,1, /* Fill */ + 1,0,0,0,0,1,0,0,0,1,1,0,1,0,0,0,0,0, /* Sidekicks 51-68 */ + 0,0,0,0,0,0,0,0,0,0,0, /* Fill */ + 1,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* Special Weapons 81-93 */ + 0,0,0,0,0 /* Fill */ +}; + +/* Last 2 bytes = Word + * + * Max Value = 1680 + * X div 60 = Armor (1-28) + * X div 168 = Shield (1-12) + * X div 280 = Engine (1-06) + */ + + +JE_boolean smoothies[9] = /* [1..9] */ +{ 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + +JE_byte starShowVGASpecialCode; + +/* CubeData */ +JE_word lastCubeMax, cubeMax; +JE_word cubeList[4]; /* [1..4] */ + +/* High-Score Stuff */ +JE_boolean gameHasRepeated; // can only get highscore on first play-through + +/* Difficulty */ +JE_shortint difficultyLevel, oldDifficultyLevel, + initialDifficulty; // can only get highscore on initial episode + +/* Player Stuff */ +uint power, lastPower, powerAdd; +JE_byte shieldWait, shieldT; + +JE_byte shotRepeat[11], shotMultiPos[11]; +JE_boolean portConfigChange, portConfigDone; + +/* Level Data */ +char lastLevelName[11], levelName[11]; /* string [10] */ +JE_byte mainLevel, nextLevel, saveLevel; /*Current Level #*/ + +/* Keyboard Junk */ +JE_KeySettingType keySettings; + +/* Configuration */ +JE_shortint levelFilter, levelFilterNew, levelBrightness, levelBrightnessChg; +JE_boolean filtrationAvail, filterActive, filterFade, filterFadeStart; + +JE_boolean gameJustLoaded; + +JE_boolean galagaMode; + +JE_boolean extraGame; + +JE_boolean twoPlayerMode, twoPlayerLinked, onePlayerAction, superTyrian; +JE_boolean trentWin = false; +JE_byte superArcadeMode; + +JE_byte superArcadePowerUp; + +JE_real linkGunDirec; +JE_byte inputDevice[2] = { 1, 2 }; // 0:any 1:keyboard 2:mouse 3+:joystick + +JE_byte secretHint; +JE_byte background3over; +JE_byte background2over; +JE_byte gammaCorrection; +JE_boolean superPause = false; +JE_boolean explosionTransparent, + youAreCheating, + displayScore, + background2, smoothScroll, wild, superWild, starActive, + topEnemyOver, + skyEnemyOverAll, + background2notTransparent; + +JE_byte soundEffects; // dummy value for config +JE_byte versionNum; /* SW 1.0 and SW/Reg 1.1 = 0 or 1 + * EA 1.2 = 2 */ + +JE_byte fastPlay; +JE_boolean pentiumMode; + +/* Savegame files */ +JE_byte gameSpeed; +JE_byte processorType; /* 1=386 2=486 3=Pentium Hyper */ + +JE_SaveFilesType saveFiles; /*array[1..saveLevelnum] of savefiletype;*/ +JE_SaveGameTemp saveTemp; + +JE_word editorLevel; /*Initial value 800*/ + +Config opentyrian_config; // implicitly initialized + +bool load_opentyrian_config( void ) +{ + // defaults + fullscreen_enabled = false; + set_scaler_by_name("Scale2x"); + + Config *config = &opentyrian_config; + + FILE *file = dir_fopen_warn(get_user_directory(), "opentyrian.cfg", "r"); + if (file == NULL) + return false; + + if (!config_parse(config, file)) + { + fclose(file); + + return false; + } + + ConfigSection *section; + + section = config_find_section(config, "video", NULL); + if (section != NULL) + { + config_get_bool_option(section, "fullscreen", &fullscreen_enabled); + + const char *scaler; + if (config_get_string_option(section, "scaler", &scaler)) + set_scaler_by_name(scaler); + } + + fclose(file); + + return true; +} + +bool save_opentyrian_config( void ) +{ + Config *config = &opentyrian_config; + + ConfigSection *section; + + section = config_find_or_add_section(config, "video", NULL); + if (section == NULL) + exit(EXIT_FAILURE); // out of memory + + config_set_bool_option(section, "fullscreen", fullscreen_enabled, NO_YES); + + config_set_string_option(section, "scaler", scalers[scaler].name); + +#ifndef TARGET_WIN32 + mkdir(get_user_directory(), 0700); +#else + mkdir(get_user_directory()); +#endif + FILE *file = dir_fopen(get_user_directory(), "opentyrian.cfg", "wb"); + if (file == NULL) + return false; + + config_write(config, file); + printf("config write"); + +#if defined(TARGET_UNIX) + fsync(fileno(file)); +#endif + fclose(file); + + return true; +} + +static void playeritems_to_pitems( JE_PItemsType pItems, PlayerItems *items, JE_byte initial_episode_num ) +{ + pItems[0] = items->weapon[FRONT_WEAPON].id; + pItems[1] = items->weapon[REAR_WEAPON].id; + pItems[2] = items->super_arcade_mode; + pItems[3] = items->sidekick[LEFT_SIDEKICK]; + pItems[4] = items->sidekick[RIGHT_SIDEKICK]; + pItems[5] = items->generator; + pItems[6] = items->sidekick_level; + pItems[7] = items->sidekick_series; + pItems[8] = initial_episode_num; + pItems[9] = items->shield; + pItems[10] = items->special; + pItems[11] = items->ship; +} + +static void pitems_to_playeritems( PlayerItems *items, JE_PItemsType pItems, JE_byte *initial_episode_num ) +{ + items->weapon[FRONT_WEAPON].id = pItems[0]; + items->weapon[REAR_WEAPON].id = pItems[1]; + items->super_arcade_mode = pItems[2]; + items->sidekick[LEFT_SIDEKICK] = pItems[3]; + items->sidekick[RIGHT_SIDEKICK] = pItems[4]; + items->generator = pItems[5]; + items->sidekick_level = pItems[6]; + items->sidekick_series = pItems[7]; + if (initial_episode_num != NULL) + *initial_episode_num = pItems[8]; + items->shield = pItems[9]; + items->special = pItems[10]; + items->ship = pItems[11]; +} + +void JE_saveGame( JE_byte slot, const char *name ) +{ + saveFiles[slot-1].initialDifficulty = initialDifficulty; + saveFiles[slot-1].gameHasRepeated = gameHasRepeated; + saveFiles[slot-1].level = saveLevel; + + if (superTyrian) + player[0].items.super_arcade_mode = SA_SUPERTYRIAN; + else if (superArcadeMode == SA_NONE && onePlayerAction) + player[0].items.super_arcade_mode = SA_ARCADE; + else + player[0].items.super_arcade_mode = superArcadeMode; + + playeritems_to_pitems(saveFiles[slot-1].items, &player[0].items, initial_episode_num); + + if (twoPlayerMode) + playeritems_to_pitems(saveFiles[slot-1].lastItems, &player[1].items, 0); + else + playeritems_to_pitems(saveFiles[slot-1].lastItems, &player[0].last_items, 0); + + saveFiles[slot-1].score = player[0].cash; + saveFiles[slot-1].score2 = player[1].cash; + + memcpy(&saveFiles[slot-1].levelName, &lastLevelName, sizeof(lastLevelName)); + saveFiles[slot-1].cubes = lastCubeMax; + + if (strcmp(lastLevelName, "Completed") == 0) + { + temp = episodeNum - 1; + if (temp < 1) + { + temp = EPISODE_AVAILABLE; /* JE: {Episodemax is 4 for completion purposes} */ + } + saveFiles[slot-1].episode = temp; + } + else + { + saveFiles[slot-1].episode = episodeNum; + } + + saveFiles[slot-1].difficulty = difficultyLevel; + saveFiles[slot-1].secretHint = secretHint; + saveFiles[slot-1].input1 = inputDevice[0]; + saveFiles[slot-1].input2 = inputDevice[1]; + + strcpy(saveFiles[slot-1].name, name); + + for (uint port = 0; port < 2; ++port) + { + // if two-player, use first player's front and second player's rear weapon + saveFiles[slot-1].power[port] = player[twoPlayerMode ? port : 0].items.weapon[port].power; + } + + JE_saveConfiguration(); +} + +void JE_loadGame( JE_byte slot ) +{ + superTyrian = false; + onePlayerAction = false; + twoPlayerMode = false; + extraGame = false; + galagaMode = false; + + initialDifficulty = saveFiles[slot-1].initialDifficulty; + gameHasRepeated = saveFiles[slot-1].gameHasRepeated; + twoPlayerMode = (slot-1) > 10; + difficultyLevel = saveFiles[slot-1].difficulty; + + pitems_to_playeritems(&player[0].items, saveFiles[slot-1].items, &initial_episode_num); + + superArcadeMode = player[0].items.super_arcade_mode; + + if (superArcadeMode == SA_SUPERTYRIAN) + superTyrian = true; + if (superArcadeMode != SA_NONE) + onePlayerAction = true; + if (superArcadeMode > SA_NORTSHIPZ) + superArcadeMode = SA_NONE; + + if (twoPlayerMode) + { + onePlayerAction = false; + + pitems_to_playeritems(&player[1].items, saveFiles[slot-1].lastItems, NULL); + } + else + { + pitems_to_playeritems(&player[0].last_items, saveFiles[slot-1].lastItems, NULL); + } + + /* Compatibility with old version */ + if (player[1].items.sidekick_level < 101) + { + player[1].items.sidekick_level = 101; + player[1].items.sidekick_series = player[1].items.sidekick[LEFT_SIDEKICK]; + } + + player[0].cash = saveFiles[slot-1].score; + player[1].cash = saveFiles[slot-1].score2; + + mainLevel = saveFiles[slot-1].level; + cubeMax = saveFiles[slot-1].cubes; + lastCubeMax = cubeMax; + + secretHint = saveFiles[slot-1].secretHint; + inputDevice[0] = saveFiles[slot-1].input1; + inputDevice[1] = saveFiles[slot-1].input2; + + for (uint port = 0; port < 2; ++port) + { + // if two-player, use first player's front and second player's rear weapon + player[twoPlayerMode ? port : 0].items.weapon[port].power = saveFiles[slot-1].power[port]; + } + + int episode = saveFiles[slot-1].episode; + + memcpy(&levelName, &saveFiles[slot-1].levelName, sizeof(levelName)); + + if (strcmp(levelName, "Completed") == 0) + { + if (episode == EPISODE_AVAILABLE) + { + episode = 1; + } else if (episode < EPISODE_AVAILABLE) { + episode++; + } + /* Increment episode. Episode EPISODE_AVAILABLE goes to 1. */ + } + + JE_initEpisode(episode); + saveLevel = mainLevel; + memcpy(&lastLevelName, &levelName, sizeof(levelName)); +} + +void JE_initProcessorType( void ) +{ + /* SYN: Originally this proc looked at your hardware specs and chose appropriate options. We don't care, so I'll just set + decent defaults here. */ + + wild = false; + superWild = false; + smoothScroll = true; + explosionTransparent = true; + filtrationAvail = false; + background2 = true; + displayScore = true; + + switch (processorType) + { + case 1: /* 386 */ + background2 = false; + displayScore = false; + explosionTransparent = false; + break; + case 2: /* 486 - Default */ + break; + case 3: /* High Detail */ + smoothScroll = false; + break; + case 4: /* Pentium */ + wild = true; + filtrationAvail = true; + break; + case 5: /* Nonstandard VGA */ + smoothScroll = false; + break; + case 6: /* SuperWild */ + wild = true; + superWild = true; + filtrationAvail = true; + break; + } + + switch (gameSpeed) + { + case 1: /* Slug Mode */ + fastPlay = 3; + break; + case 2: /* Slower */ + fastPlay = 4; + break; + case 3: /* Slow */ + fastPlay = 5; + break; + case 4: /* Normal */ + fastPlay = 0; + break; + case 5: /* Pentium Hyper */ + fastPlay = 1; + break; + } + +} + +void JE_setNewGameSpeed( void ) +{ + pentiumMode = false; + + switch (fastPlay) + { + case 0: + speed = 0x4300; + smoothScroll = true; + frameCountMax = 2; + break; + case 1: + speed = 0x3000; + smoothScroll = true; + frameCountMax = 2; + break; + case 2: + speed = 0x2000; + smoothScroll = false; + frameCountMax = 2; + break; + case 3: + speed = 0x5300; + smoothScroll = true; + frameCountMax = 4; + break; + case 4: + speed = 0x4300; + smoothScroll = true; + frameCountMax = 3; + break; + case 5: + speed = 0x4300; + smoothScroll = true; + frameCountMax = 2; + pentiumMode = true; + break; + } + + frameCount = frameCountMax; + JE_resetTimerInt(); + JE_setTimerInt(); +} + +void JE_encryptSaveTemp( void ) +{ + JE_SaveGameTemp s3; + JE_word x; + JE_byte y; + + memcpy(&s3, &saveTemp, sizeof(s3)); + + y = 0; + for (x = 0; x < SAVE_FILE_SIZE; x++) + { + y += s3[x]; + } + saveTemp[SAVE_FILE_SIZE] = y; + + y = 0; + for (x = 0; x < SAVE_FILE_SIZE; x++) + { + y -= s3[x]; + } + saveTemp[SAVE_FILE_SIZE+1] = y; + + y = 1; + for (x = 0; x < SAVE_FILE_SIZE; x++) + { + y = (y * s3[x]) + 1; + } + saveTemp[SAVE_FILE_SIZE+2] = y; + + y = 0; + for (x = 0; x < SAVE_FILE_SIZE; x++) + { + y = y ^ s3[x]; + } + saveTemp[SAVE_FILE_SIZE+3] = y; + + for (x = 0; x < SAVE_FILE_SIZE; x++) + { + saveTemp[x] = saveTemp[x] ^ cryptKey[(x+1) % 10]; + if (x > 0) + { + saveTemp[x] = saveTemp[x] ^ saveTemp[x - 1]; + } + } +} + +void JE_decryptSaveTemp( void ) +{ + JE_boolean correct = true; + JE_SaveGameTemp s2; + int x; + JE_byte y; + + /* Decrypt save game file */ + for (x = (SAVE_FILE_SIZE - 1); x >= 0; x--) + { + s2[x] = (JE_byte)saveTemp[x] ^ (JE_byte)(cryptKey[(x+1) % 10]); + if (x > 0) + { + s2[x] ^= (JE_byte)saveTemp[x - 1]; + } + + } + + /* for (x = 0; x < SAVE_FILE_SIZE; x++) printf("%c", s2[x]); */ + + /* Check save file for correctitude */ + y = 0; + for (x = 0; x < SAVE_FILE_SIZE; x++) + { + y += s2[x]; + } + if (saveTemp[SAVE_FILE_SIZE] != y) + { + correct = false; + printf("Failed additive checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE], y); + } + + y = 0; + for (x = 0; x < SAVE_FILE_SIZE; x++) + { + y -= s2[x]; + } + if (saveTemp[SAVE_FILE_SIZE+1] != y) + { + correct = false; + printf("Failed subtractive checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE+1], y); + } + + y = 1; + for (x = 0; x < SAVE_FILE_SIZE; x++) + { + y = (y * s2[x]) + 1; + } + if (saveTemp[SAVE_FILE_SIZE+2] != y) + { + correct = false; + printf("Failed multiplicative checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE+2], y); + } + + y = 0; + for (x = 0; x < SAVE_FILE_SIZE; x++) + { + y = y ^ s2[x]; + } + if (saveTemp[SAVE_FILE_SIZE+3] != y) + { + correct = false; + printf("Failed XOR'd checksum: %d vs %d\n", saveTemp[SAVE_FILE_SIZE+3], y); + } + + /* Barf and die if save file doesn't validate */ + if (!correct) + { + fprintf(stderr, "Error reading save file!\n"); + exit(255); + } + + /* Keep decrypted version plz */ + memcpy(&saveTemp, &s2, sizeof(s2)); +} + +const char *get_user_directory( void ) +{ + static char user_dir[500] = ""; + + if (strlen(user_dir) == 0) + { +#if defined(TARGET_UNIX) + char *xdg_config_home = getenv("XDG_CONFIG_HOME"); + if (xdg_config_home != NULL) + { + snprintf(user_dir, sizeof(user_dir), "%s/opentyrian", xdg_config_home); + } + else + { + char *home = getenv("HOME"); + if (home != NULL) + { + snprintf(user_dir, sizeof(user_dir), "%s/.config/opentyrian", home); + } + else + { + strcpy(user_dir, "."); + } + } +#else + strcpy(user_dir, "."); +#endif + } + + return user_dir; +} + +// for compatibility +Uint8 joyButtonAssign[4] = {1, 4, 5, 5}; +Uint8 inputDevice_ = 0, jConfigure = 0, midiPort = 1; + +void JE_loadConfiguration( void ) +{ + FILE *fi; + int z; + JE_byte *p; + int y; + + fi = dir_fopen_warn(get_user_directory(), "tyrian.cfg", "rb"); + if (fi && ftell_eof(fi) == 20 + sizeof(keySettings)) + { + /* SYN: I've hardcoded the sizes here because the .CFG file format is fixed + anyways, so it's not like they'll change. */ + background2 = 0; + efread(&background2, 1, 1, fi); + efread(&gameSpeed, 1, 1, fi); + + efread(&inputDevice_, 1, 1, fi); + efread(&jConfigure, 1, 1, fi); + + efread(&versionNum, 1, 1, fi); + + efread(&processorType, 1, 1, fi); + efread(&midiPort, 1, 1, fi); + efread(&soundEffects, 1, 1, fi); + efread(&gammaCorrection, 1, 1, fi); + efread(&difficultyLevel, 1, 1, fi); + + efread(joyButtonAssign, 1, 4, fi); + + efread(&tyrMusicVolume, 2, 1, fi); + efread(&fxVolume, 2, 1, fi); + + efread(inputDevice, 1, 2, fi); + + efread(keySettings, sizeof(*keySettings), COUNTOF(keySettings), fi); + + fclose(fi); + } + else + { + printf("\nInvalid or missing TYRIAN.CFG! Continuing using defaults.\n\n"); + + soundEffects = 1; + memcpy(&keySettings, &defaultKeySettings, sizeof(keySettings)); + background2 = true; + tyrMusicVolume = fxVolume = 128; + gammaCorrection = 0; + processorType = 3; + gameSpeed = 4; + } + + load_opentyrian_config(); + + if (tyrMusicVolume > 255) + tyrMusicVolume = 255; + if (fxVolume > 255) + fxVolume = 255; + + JE_calcFXVol(); + + set_volume(tyrMusicVolume, fxVolume); + + fi = dir_fopen_warn(get_user_directory(), "tyrian.sav", "rb"); + if (fi) + { + + fseek(fi, 0, SEEK_SET); + efread(saveTemp, 1, sizeof(saveTemp), fi); + JE_decryptSaveTemp(); + + /* SYN: The original mostly blasted the save file into raw memory. However, our lives are not so + easy, because the C struct is necessarily a different size. So instead we have to loop + through each record and load fields manually. *emo tear* :'( */ + + p = saveTemp; + for (z = 0; z < SAVE_FILES_NUM; z++) + { + memcpy(&saveFiles[z].encode, p, sizeof(JE_word)); p += 2; + saveFiles[z].encode = SDL_SwapLE16(saveFiles[z].encode); + + memcpy(&saveFiles[z].level, p, sizeof(JE_word)); p += 2; + saveFiles[z].level = SDL_SwapLE16(saveFiles[z].level); + + memcpy(&saveFiles[z].items, p, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType); + + memcpy(&saveFiles[z].score, p, sizeof(JE_longint)); p += 4; + saveFiles[z].score = SDL_SwapLE32(saveFiles[z].score); + + memcpy(&saveFiles[z].score2, p, sizeof(JE_longint)); p += 4; + saveFiles[z].score2 = SDL_SwapLE32(saveFiles[z].score2); + + /* SYN: Pascal strings are prefixed by a byte holding the length! */ + memset(&saveFiles[z].levelName, 0, sizeof(saveFiles[z].levelName)); + memcpy(&saveFiles[z].levelName, &p[1], *p); + p += 10; + + /* This was a BYTE array, not a STRING, in the original. Go fig. */ + memcpy(&saveFiles[z].name, p, 14); + p += 14; + + memcpy(&saveFiles[z].cubes, p, sizeof(JE_byte)); p++; + memcpy(&saveFiles[z].power, p, sizeof(JE_byte) * 2); p += 2; + memcpy(&saveFiles[z].episode, p, sizeof(JE_byte)); p++; + memcpy(&saveFiles[z].lastItems, p, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType); + memcpy(&saveFiles[z].difficulty, p, sizeof(JE_byte)); p++; + memcpy(&saveFiles[z].secretHint, p, sizeof(JE_byte)); p++; + memcpy(&saveFiles[z].input1, p, sizeof(JE_byte)); p++; + memcpy(&saveFiles[z].input2, p, sizeof(JE_byte)); p++; + + /* booleans were 1 byte in pascal -- working around it */ + Uint8 temp; + memcpy(&temp, p, 1); p++; + saveFiles[z].gameHasRepeated = temp != 0; + + memcpy(&saveFiles[z].initialDifficulty, p, sizeof(JE_byte)); p++; + + memcpy(&saveFiles[z].highScore1, p, sizeof(JE_longint)); p += 4; + saveFiles[z].highScore1 = SDL_SwapLE32(saveFiles[z].highScore1); + + memcpy(&saveFiles[z].highScore2, p, sizeof(JE_longint)); p += 4; + saveFiles[z].highScore2 = SDL_SwapLE32(saveFiles[z].highScore2); + + memset(&saveFiles[z].highScoreName, 0, sizeof(saveFiles[z].highScoreName)); + memcpy(&saveFiles[z].highScoreName, &p[1], *p); + p += 30; + + memcpy(&saveFiles[z].highScoreDiff, p, sizeof(JE_byte)); p++; + } + + /* SYN: This is truncating to bytes. I have no idea what this is doing or why. */ + /* TODO: Figure out what this is about and make sure it isn't broked. */ + editorLevel = (saveTemp[SIZEOF_SAVEGAMETEMP - 5] << 8) | saveTemp[SIZEOF_SAVEGAMETEMP - 6]; + + fclose(fi); + } else { + /* We didn't have a save file! Let's make up random stuff! */ + editorLevel = 800; + + for (z = 0; z < 100; z++) + { + saveTemp[SAVE_FILES_SIZE + z] = initialItemAvail[z]; + } + + for (z = 0; z < SAVE_FILES_NUM; z++) + { + saveFiles[z].level = 0; + + for (y = 0; y < 14; y++) + { + saveFiles[z].name[y] = ' '; + } + saveFiles[z].name[14] = 0; + + saveFiles[z].highScore1 = ((mt_rand() % 20) + 1) * 1000; + + if (z % 6 > 2) + { + saveFiles[z].highScore2 = ((mt_rand() % 20) + 1) * 1000; + strcpy(saveFiles[z].highScoreName, defaultTeamNames[mt_rand() % 22]); + } else { + strcpy(saveFiles[z].highScoreName, defaultHighScoreNames[mt_rand() % 34]); + } + } + } + + JE_initProcessorType(); +} + +void JE_saveConfiguration( void ) +{ + FILE *f; + JE_byte *p; + int z; + + p = saveTemp; + for (z = 0; z < SAVE_FILES_NUM; z++) + { + JE_SaveFileType tempSaveFile; + memcpy(&tempSaveFile, &saveFiles[z], sizeof(tempSaveFile)); + + tempSaveFile.encode = SDL_SwapLE16(tempSaveFile.encode); + memcpy(p, &tempSaveFile.encode, sizeof(JE_word)); p += 2; + + tempSaveFile.level = SDL_SwapLE16(tempSaveFile.level); + memcpy(p, &tempSaveFile.level, sizeof(JE_word)); p += 2; + + memcpy(p, &tempSaveFile.items, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType); + + tempSaveFile.score = SDL_SwapLE32(tempSaveFile.score); + memcpy(p, &tempSaveFile.score, sizeof(JE_longint)); p += 4; + + tempSaveFile.score2 = SDL_SwapLE32(tempSaveFile.score2); + memcpy(p, &tempSaveFile.score2, sizeof(JE_longint)); p += 4; + + /* SYN: Pascal strings are prefixed by a byte holding the length! */ + memset(p, 0, sizeof(tempSaveFile.levelName)); + *p = strlen(tempSaveFile.levelName); + memcpy(&p[1], &tempSaveFile.levelName, *p); + p += 10; + + /* This was a BYTE array, not a STRING, in the original. Go fig. */ + memcpy(p, &tempSaveFile.name, 14); + p += 14; + + memcpy(p, &tempSaveFile.cubes, sizeof(JE_byte)); p++; + memcpy(p, &tempSaveFile.power, sizeof(JE_byte) * 2); p += 2; + memcpy(p, &tempSaveFile.episode, sizeof(JE_byte)); p++; + memcpy(p, &tempSaveFile.lastItems, sizeof(JE_PItemsType)); p += sizeof(JE_PItemsType); + memcpy(p, &tempSaveFile.difficulty, sizeof(JE_byte)); p++; + memcpy(p, &tempSaveFile.secretHint, sizeof(JE_byte)); p++; + memcpy(p, &tempSaveFile.input1, sizeof(JE_byte)); p++; + memcpy(p, &tempSaveFile.input2, sizeof(JE_byte)); p++; + + /* booleans were 1 byte in pascal -- working around it */ + Uint8 temp = tempSaveFile.gameHasRepeated != false; + memcpy(p, &temp, 1); p++; + + memcpy(p, &tempSaveFile.initialDifficulty, sizeof(JE_byte)); p++; + + tempSaveFile.highScore1 = SDL_SwapLE32(tempSaveFile.highScore1); + memcpy(p, &tempSaveFile.highScore1, sizeof(JE_longint)); p += 4; + + tempSaveFile.highScore2 = SDL_SwapLE32(tempSaveFile.highScore2); + memcpy(p, &tempSaveFile.highScore2, sizeof(JE_longint)); p += 4; + + memset(p, 0, sizeof(tempSaveFile.highScoreName)); + *p = strlen(tempSaveFile.highScoreName); + memcpy(&p[1], &tempSaveFile.highScoreName, *p); + p += 30; + + memcpy(p, &tempSaveFile.highScoreDiff, sizeof(JE_byte)); p++; + } + + saveTemp[SIZEOF_SAVEGAMETEMP - 6] = editorLevel >> 8; + saveTemp[SIZEOF_SAVEGAMETEMP - 5] = editorLevel; + + JE_encryptSaveTemp(); + +#ifndef TARGET_WIN32 + mkdir(get_user_directory(), 0700); +#else + mkdir(get_user_directory()); +#endif + + f = dir_fopen_warn(get_user_directory(), "tyrian.sav", "wb"); + if (f != NULL) + { + efwrite(saveTemp, 1, sizeof(saveTemp), f); + +#if defined(TARGET_UNIX) + fsync(fileno(f)); +#endif + fclose(f); + } + + JE_decryptSaveTemp(); + + f = dir_fopen_warn(get_user_directory(), "tyrian.cfg", "wb"); + if (f != NULL) + { + efwrite(&background2, 1, 1, f); + efwrite(&gameSpeed, 1, 1, f); + + efwrite(&inputDevice_, 1, 1, f); + efwrite(&jConfigure, 1, 1, f); + + efwrite(&versionNum, 1, 1, f); + efwrite(&processorType, 1, 1, f); + efwrite(&midiPort, 1, 1, f); + efwrite(&soundEffects, 1, 1, f); + efwrite(&gammaCorrection, 1, 1, f); + efwrite(&difficultyLevel, 1, 1, f); + efwrite(joyButtonAssign, 1, 4, f); + + efwrite(&tyrMusicVolume, 2, 1, f); + efwrite(&fxVolume, 2, 1, f); + + efwrite(inputDevice, 1, 2, f); + + efwrite(keySettings, sizeof(*keySettings), COUNTOF(keySettings), f); + +#if defined(TARGET_UNIX) + fsync(fileno(f)); +#endif + fclose(f); + } + + save_opentyrian_config(); +} + diff --git a/contrib/games/opentyrian/src/config.h b/contrib/games/opentyrian/src/config.h new file mode 100644 index 000000000..82064c1b5 --- /dev/null +++ b/contrib/games/opentyrian/src/config.h @@ -0,0 +1,148 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef CONFIG_H +#define CONFIG_H + +#include "opentyr.h" +#include "config_file.h" + +#include "SDL.h" + +#include + +#define SAVE_FILES_NUM (11 * 2) + +/* These are necessary because the size of the structure has changed from the original, but we + need to know the original sizes in order to find things in TYRIAN.SAV */ +#define SAVE_FILES_SIZE 2398 +#define SIZEOF_SAVEGAMETEMP SAVE_FILES_SIZE + 4 + 100 +#define SAVE_FILE_SIZE (SIZEOF_SAVEGAMETEMP - 4) + +/*#define SAVE_FILES_SIZE (2502 - 4) +#define SAVE_FILE_SIZE (SAVE_FILES_SIZE)*/ + +typedef SDLKey JE_KeySettingType[8]; /* [1..8] */ +typedef JE_byte JE_PItemsType[12]; /* [1..12] */ + +typedef JE_byte JE_EditorItemAvailType[100]; /* [1..100] */ + +typedef struct +{ + JE_word encode; + JE_word level; + JE_PItemsType items; + JE_longint score; + JE_longint score2; + char levelName[11]; /* string [9]; */ /* SYN: Added one more byte to match lastLevelName below */ + JE_char name[15]; /* [1..14] */ /* SYN: Added extra byte for null */ + JE_byte cubes; + JE_byte power[2]; /* [1..2] */ + JE_byte episode; + JE_PItemsType lastItems; + JE_byte difficulty; + JE_byte secretHint; + JE_byte input1; + JE_byte input2; + JE_boolean gameHasRepeated; /*See if you went from one episode to another*/ + JE_byte initialDifficulty; + + /* High Scores - Each episode has both sets of 1&2 player selections - with 3 in each */ + JE_longint highScore1, + highScore2; + char highScoreName[30]; /* string [29] */ + JE_byte highScoreDiff; +} JE_SaveFileType; + +typedef JE_SaveFileType JE_SaveFilesType[SAVE_FILES_NUM]; /* [1..savefilesnum] */ +typedef JE_byte JE_SaveGameTemp[SAVE_FILES_SIZE + 4 + 100]; /* [1..sizeof(savefilestype) + 4 + 100] */ + +extern const JE_byte cryptKey[10]; +extern const JE_KeySettingType defaultKeySettings; +extern const char defaultHighScoreNames[34][23]; +extern const char defaultTeamNames[22][25]; +extern const JE_EditorItemAvailType initialItemAvail; +extern JE_boolean smoothies[9]; +extern JE_byte starShowVGASpecialCode; +extern JE_word lastCubeMax, cubeMax; +extern JE_word cubeList[4]; +extern JE_boolean gameHasRepeated; +extern JE_shortint difficultyLevel, oldDifficultyLevel, initialDifficulty; +extern uint power, lastPower, powerAdd; +extern JE_byte shieldWait, shieldT; + +enum +{ + SHOT_FRONT, + SHOT_REAR, + SHOT_LEFT_SIDEKICK, + SHOT_RIGHT_SIDEKICK, + SHOT_MISC, + SHOT_P2_CHARGE, + SHOT_P1_SUPERBOMB, + SHOT_P2_SUPERBOMB, + SHOT_SPECIAL, + SHOT_NORTSPARKS, + SHOT_SPECIAL2 +}; + +extern JE_byte shotRepeat[11], shotMultiPos[11]; +extern JE_boolean portConfigChange, portConfigDone; +extern char lastLevelName[11], levelName[11]; +extern JE_byte mainLevel, nextLevel, saveLevel; +extern JE_KeySettingType keySettings; +extern JE_shortint levelFilter, levelFilterNew, levelBrightness, levelBrightnessChg; +extern JE_boolean filtrationAvail, filterActive, filterFade, filterFadeStart; +extern JE_boolean gameJustLoaded; +extern JE_boolean galagaMode; +extern JE_boolean extraGame; +extern JE_boolean twoPlayerMode, twoPlayerLinked, onePlayerAction, superTyrian, trentWin; +extern JE_byte superArcadeMode; +extern JE_byte superArcadePowerUp; +extern JE_real linkGunDirec; +extern JE_byte inputDevice[2]; +extern JE_byte secretHint; +extern JE_byte background3over; +extern JE_byte background2over; +extern JE_byte gammaCorrection; +extern JE_boolean superPause, explosionTransparent, youAreCheating, displayScore, background2, smoothScroll, wild, superWild, starActive, topEnemyOver, skyEnemyOverAll, background2notTransparent; +extern JE_byte versionNum; +extern JE_byte fastPlay; +extern JE_boolean pentiumMode; +extern JE_byte gameSpeed; +extern JE_byte processorType; +extern JE_SaveFilesType saveFiles; +extern JE_SaveGameTemp saveTemp; +extern JE_word editorLevel; + +extern Config opentyrian_config; + +void JE_initProcessorType( void ); +void JE_setNewGameSpeed( void ); +const char *get_user_directory( void ); +void JE_loadConfiguration( void ); +void JE_saveConfiguration( void ); + +void JE_saveGame( JE_byte slot, const char *name ); +void JE_loadGame( JE_byte slot ); + +void JE_encryptSaveTemp( void ); +void JE_decryptSaveTemp( void ); + +#endif /* CONFIG_H */ + diff --git a/contrib/games/opentyrian/src/config_file.c b/contrib/games/opentyrian/src/config_file.c new file mode 100644 index 000000000..075359f68 --- /dev/null +++ b/contrib/games/opentyrian/src/config_file.c @@ -0,0 +1,1033 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2015 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/*! + * \file config_file.c + * \author Carl Reinke + * \date 2015 + * \copyright GNU General Public License v2+ or Mozilla Public License 2.0 + */ +#include "config_file.h" + +#include +#include +#include +#include +#include + +/* potential size of decimal representation of type */ +#define udecsizeof(t) ((CHAR_BIT * sizeof(t) / 3) + 1) +#define sdecsizeof(t) (udecsizeof(t) + 1) + +extern void config_oom( void ); + +void config_oom( void ) +{ + fprintf(stderr, "out of memory\n"); + exit(EXIT_FAILURE); +} + +/* string manipulators */ + +static ConfigString string_init_len( const char *s, size_t n ) +{ + ConfigString string; + + if (s == NULL) + { + CONFIG_STRING_LONG_TAG(string) = true; + + string.long_buf = NULL; + } + else + { + char is_long = n >= COUNTOF(string.short_buf); + + CONFIG_STRING_LONG_TAG(string) = is_long; + + char *buffer = is_long ? + string.long_buf = malloc((n + 1) * sizeof(char)) : + string.short_buf; + if (buffer == NULL) + config_oom(); + + memcpy(buffer, s, n * sizeof(char)); + buffer[n] = '\0'; + } + + return string; +} + +static void string_deinit( ConfigString *string ) +{ + char is_long = CONFIG_STRING_LONG_TAG(*string); + + if (is_long) + { + free(string->long_buf); + string->long_buf = NULL; + } +} + +static bool string_equal_len( ConfigString *string, const char *s, size_t n ) +{ + const char *cstr = config_string_to_cstr(string); + return strncmp(cstr, s, n) == 0 && cstr[n] == '\0'; +} + +/* config manipulators */ + +static void deinit_section( ConfigSection *section ); +static void deinit_option( ConfigOption *option ); + +void config_init( Config *config ) +{ + assert(config != NULL); + + config->sections_count = 0; + config->sections = NULL; +} + +void config_deinit( Config *config ) +{ + assert(config != NULL); + + for (unsigned int s = 0; s < config->sections_count; ++s) + { + ConfigSection *section = &config->sections[s]; + + deinit_section(section); + } + + free(config->sections); + config->sections = NULL; +} + +/* config section manipulators -- internal */ + +static void init_section( ConfigSection *section, const char *type, size_t type_len, const char *name, size_t name_len ) +{ + section->type = string_init_len(type, type_len); + section->name = string_init_len(name, name_len); + section->options_count = 0; + section->options = NULL; +} + +static void deinit_section( ConfigSection *section ) +{ + for (unsigned int o = 0; o < section->options_count; ++o) + { + ConfigOption *option = §ion->options[o]; + + deinit_option(option); + } + + string_deinit(§ion->type); + string_deinit(§ion->name); + + free(section->options); + section->options = NULL; +} + +/* config section accessors/manipulators -- by type, name */ + +ConfigSection *config_add_section_len( Config *config, const char *type, size_t type_len, const char *name, size_t name_len ) +{ + assert(config != NULL); + assert(type != NULL); + + ConfigSection *sections = realloc(config->sections, (config->sections_count + 1) * sizeof(ConfigSection)); + if (sections == NULL) + return NULL; + + ConfigSection *section = §ions[config->sections_count]; + + config->sections_count += 1; + config->sections = sections; + + init_section(section, type, type_len, name, name_len); + + return section; +} + +ConfigSection *config_find_sections( Config *config, const char *type, ConfigSection **save ) +{ + assert(config != NULL); + assert(type != NULL); + + ConfigSection *sections_end = &config->sections[config->sections_count]; + + ConfigSection *section = save != NULL && *save != NULL ? + *save : + &config->sections[0]; + + for (; section < sections_end; ++section) + if (strcmp(config_string_to_cstr(§ion->type), type) == 0) + break; + + if (save != NULL) + *save = section; + + return section < sections_end ? section : NULL; +} + +ConfigSection *config_find_section( Config *config, const char *type, const char *name ) +{ + assert(config != NULL); + assert(type != NULL); + + ConfigSection *sections_end = &config->sections[config->sections_count]; + + for (ConfigSection *section = &config->sections[0]; section < sections_end; ++section) + { + if (strcmp(config_string_to_cstr(§ion->type), type) == 0) + { + const char *section_name = config_string_to_cstr(§ion->name); + if ((section_name == NULL || name == NULL) ? section_name == name : strcmp(config_string_to_cstr(§ion->name), name) == 0) + return section; + } + } + + return NULL; +} + +ConfigSection *config_find_or_add_section( Config *config, const char *type, const char *name ) +{ + assert(config != NULL); + assert(type != NULL); + + ConfigSection *section = config_find_section(config, type, name); + + if (section != NULL) + return section; + + return config_add_section(config, type, name); +} + +/* config option manipulators -- internal */ + +static void init_option_value( ConfigOption *option, const char *value, size_t value_len ) +{ + option->values_count = 0; + option->v.value = string_init_len(value, value_len); +} + +static void deinit_option_value( ConfigOption *option ) +{ + if (option->values_count != 0) + { + ConfigString *values_end = &option->v.values[option->values_count]; + for (ConfigString *value = &option->v.values[0]; value < values_end; ++value) + string_deinit(value); + + free(option->v.values); + option->v.values = NULL; + } + else + { + string_deinit(&option->v.value); + } +} + +static void init_option( ConfigOption *option, const char *key, size_t key_len, const char *value, size_t value_len ) +{ + option->key = string_init_len(key, key_len); + init_option_value(option, value, value_len); +} + +static void deinit_option( ConfigOption *option ) +{ + string_deinit(&option->key); + deinit_option_value(option); +} + +static ConfigOption *append_option( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len ) +{ + ConfigOption *options = realloc(section->options, (section->options_count + 1) * sizeof(ConfigSection)); + if (options == NULL) + return NULL; + + ConfigOption *option = &options[section->options_count]; + + section->options_count += 1; + section->options = options; + + init_option(option, key, key_len, value, value_len); + + return option; +} + +static ConfigOption *get_option_len( ConfigSection *section, const char *key, size_t key_len ) +{ + assert(section != NULL); + assert(key != NULL); + + ConfigOption *options_end = §ion->options[section->options_count]; + for (ConfigOption *option = §ion->options[0]; option < options_end; ++option) + if (string_equal_len(&option->key, key, key_len)) + return option; + + return NULL; +} + +/* config option accessors/manipulators -- by key */ + +ConfigOption *config_set_option_len( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len ) +{ + assert(section != NULL); + assert(key != NULL); + + ConfigOption *option = get_option_len(section, key, key_len); + + if (option != NULL) + return config_set_value_len(option, value, value_len); + + return append_option(section, key, key_len, value, value_len); +} + +ConfigOption *config_get_option( const ConfigSection *section, const char *key ) +{ + assert(section != NULL); + assert(key != NULL); + + ConfigOption *options_end = §ion->options[section->options_count]; + for (ConfigOption *option = §ion->options[0]; option < options_end; ++option) + if (strcmp(config_string_to_cstr(&option->key), key) == 0) + return option; + + return NULL; +} + +ConfigOption *config_get_or_set_option_len( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len ) +{ + assert(section != NULL); + assert(key != NULL); + + ConfigOption *option = get_option_len(section, key, key_len); + + if (option != NULL) + return option; + + return append_option(section, key, key_len, value, value_len); +} + +void config_set_string_option_len( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len ) +{ + if (config_set_option_len(section, key, key_len, value, value_len) == NULL) + config_oom(); +} + +bool config_get_string_option( const ConfigSection *section, const char *key, const char **out_value ) +{ + assert(section != NULL); + assert(key != NULL); + + ConfigOption *option = config_get_option(section, key); + if (option != NULL) + { + const char *value = config_get_value(option); + if (value != NULL) + { + *out_value = value; + return true; + } + } + + return false; +} + +const char *config_get_or_set_string_option( ConfigSection *section, const char *key, const char *value ) +{ + if (!config_get_string_option(section, key, &value)) + config_set_string_option_len(section, key, strlen(key), value, value == NULL ? 0 : strlen(value)); + return value; +} + +static const char *bool_values[][2] = +{ + { "0", "1" }, + { "no", "yes" }, + { "off", "on" }, + { "false", "true" }, +}; + +void config_set_bool_option( ConfigSection *section, const char *key, bool value, ConfigBoolStyle style ) +{ + if (config_set_option(section, key, bool_values[style][value ? 1 : 0]) == NULL) + config_oom(); +} + +bool config_get_bool_option( const ConfigSection *section, const char *key, bool *out_value ) +{ + assert(section != NULL); + assert(key != NULL); + assert(out_value != NULL); + + const char *value; + if (config_get_string_option(section, key, &value)) + { + for (size_t i = 0; i < COUNTOF(bool_values); ++i) + { + for (size_t j = 0; j < COUNTOF(bool_values[i]); ++j) + { + if (strcmp(value, bool_values[i][j]) == 0) + { + *out_value = j == 0 ? false : true; + return true; + } + } + } + } + + return false; +} + +bool config_get_or_set_bool_option( ConfigSection *section, const char *key, bool value, ConfigBoolStyle style ) +{ + if (!config_get_bool_option(section, key, &value)) + config_set_bool_option(section, key, value, style); + return value; +} + +void config_set_int_option( ConfigSection *section, const char *key, int value ) +{ + assert(key != NULL); + + char buffer[sdecsizeof(int) + 1]; + int buffer_len = snprintf(buffer, sizeof(buffer), "%i", value); + + if (config_set_option_len(section, key, strlen(key), buffer, buffer_len) == NULL) + config_oom(); +} + +bool config_get_int_option( const ConfigSection *section, const char *key, int *out_value ) +{ + assert(section != NULL); + assert(key != NULL); + assert(out_value != NULL); + + const char *value; + if (config_get_string_option(section, key, &value)) + { + int i; + int n; + if (sscanf(value, "%i%n", &i, &n) > 0 && value[n] == '\0') /* must be entire string */ + { + *out_value = i; + return true; + } + } + + return false; +} + +int config_get_or_set_int_option( ConfigSection *section, const char *key, int value ) +{ + if (!config_get_int_option(section, key, &value)) + config_set_int_option(section, key, value); + return value; +} + +void config_set_uint_option( ConfigSection *section, const char *key, unsigned int value ) +{ + assert(key != NULL); + + char buffer[udecsizeof(unsigned int) + 1]; + int buffer_len = snprintf(buffer, sizeof(buffer), "%u", value); + + if (config_set_option_len(section, key, strlen(key), buffer, buffer_len) == NULL) + config_oom(); +} + +bool config_get_uint_option( const ConfigSection *section, const char *key, unsigned int *out_value ) +{ + assert(section != NULL); + assert(key != NULL); + assert(out_value != NULL); + + const char *value; + if (config_get_string_option(section, key, &value)) + { + unsigned int u; + int n; + if (sscanf(value, "%u%n", &u, &n) > 0 && value[n] == '\0') /* must be entire string */ + { + *out_value = u; + return true; + } + } + + return false; +} + +unsigned int config_get_or_set_uint_option( ConfigSection *section, const char *key, unsigned int value ) +{ + if (!config_get_uint_option(section, key, &value)) + config_set_uint_option(section, key, value); + return value; +} + +/* config option accessors/manipulators -- by reference */ + +ConfigOption *config_set_value_len( ConfigOption *option, const char *value, size_t value_len ) +{ + assert(option != NULL); + + deinit_option_value(option); + + init_option_value(option, value, value_len); + + return option; +} + +ConfigOption *config_add_value_len( ConfigOption *option, const char *value, size_t value_len ) +{ + assert(option != NULL); + assert(value != NULL); + + /* convert 'item' to 'list' */ + if (option->values_count == 0 && config_string_to_cstr(&option->v.value) != NULL) + { + ConfigString option_value = option->v.value; + + ConfigString *values = malloc(2 * sizeof(ConfigString)); + if (values == NULL) + return NULL; + + option->v.values = values; + option->v.values[0] = option_value; + option->v.values[1] = string_init_len(value, value_len); + option->values_count = 2; + } + else + { + ConfigString *values = realloc(option->v.values, (option->values_count + 1) * sizeof(ConfigString)); + if (values == NULL) + return NULL; + + option->v.values = values; + option->v.values[option->values_count] = string_init_len(value, value_len); + option->values_count += 1; + } + + return option; +} + +ConfigOption *config_remove_value( ConfigOption *option, unsigned int i ) +{ + assert(option != NULL); + + if (!config_is_value_list(option)) + { + if (i > 0) + return NULL; + + config_set_value_len(option, NULL, 0); + } + else + { + if (i >= option->values_count) + return NULL; + + string_deinit(&option->v.values[i]); + memmove(&option->v.values[i], &option->v.values[i + 1], (option->values_count - i - 1) * sizeof(ConfigString)); + + if (option->values_count - 1 == 0) + { + option->v.value = string_init_len(NULL, 0); + option->values_count = 0; + } + else + { + ConfigString *values = realloc(option->v.values, (option->values_count - 1) * sizeof(ConfigString)); + if (values == NULL) + return NULL; + + option->v.values = values; + option->values_count -= 1; + } + } + + return option; +} + +const char *config_get_value( const ConfigOption *option ) +{ + if (option == NULL || option->values_count != 0) + return NULL; + + return config_string_to_cstr(&option->v.value); +} + +/* config parser */ + +static bool is_whitespace( char c ) +{ + return c == '\t' || c == ' '; +} + +static bool is_end( char c ) +{ + return c == '\0' || c == '\n' || c == '\r'; +} + +static bool is_whitespace_or_end( char c ) +{ + return is_whitespace(c) || is_end(c); +} + +typedef enum +{ + INVALID_DIRECTIVE = 0, + SECTION_DIRECTIVE, + ITEM_DIRECTIVE, + LIST_DIRECTIVE, +} Directive; + +static Directive match_directive( const char *buffer, size_t *index ) +{ + size_t i = *index; + + while (is_whitespace(buffer[i])) + ++i; + + Directive directive; + + if (strncmp("section", &buffer[i], 7) == 0) + { + directive = SECTION_DIRECTIVE; + i += 7; + } + else if (strncmp("item", &buffer[i], 4) == 0) + { + directive = ITEM_DIRECTIVE; + i += 4; + } + else if (strncmp("list", &buffer[i], 4) == 0) + { + directive = LIST_DIRECTIVE; + i += 4; + } + else + { + return INVALID_DIRECTIVE; + } + + if (!is_whitespace_or_end(buffer[i])) + return INVALID_DIRECTIVE; + + *index = i; + + return directive; +} + +static bool match_nonquote_field( const char *buffer, size_t *index, size_t *length ) +{ + size_t i = *index; + + for (; ; ++i) + { + char c = buffer[i]; + + if (is_whitespace_or_end(c)) + { + break; + } + else if (c <= ' ' || c > '~' || c == '#' || c == '\'' || c == '"') + { + return false; + } + } + + *length = i - *index; + *index = i; + + return *length > 0; +} + +static bool parse_quote_field( char *buffer, size_t *index, size_t *length ) +{ + size_t i = *index; + size_t o = *index; + + char quote = buffer[i]; + + for (; ; ) + { + char c = buffer[++i]; + + if (c == quote) + { + ++i; + break; + } + else if (c == '\\') + { + c = buffer[++i]; + if (c == quote) + { + buffer[o++] = quote; + } + else + { + switch (c) + { + case 't': + buffer[o++] = '\t'; + break; + case 'n': + buffer[o++] = '\n'; + break; + case 'r': + buffer[o++] = '\r'; + break; + case '\\': + buffer[o++] = '\\'; + break; + case 'x': + /* parse two hex digits */ + c = buffer[++i]; + char m = (c >= '0' && c <= '9') ? '0' : + (c >= 'a' && c <= 'f') ? 'a' - 10 : + (c >= 'A' && c <= 'F') ? 'A' - 10 : 0; + if (m == 0) + return false; + char h = c - m; + c = buffer[++i]; + m = (c >= '0' && c <= '9') ? '0' : + (c >= 'a' && c <= 'f') ? 'a' - 10 : + (c >= 'A' && c <= 'F') ? 'A' - 10 : 0; + if (m == 0) + return false; + buffer[o++] = (h << 4) | (c - m); + break; + default: + return false; + } + } + } + else if (c >= ' ' && c <= '~') + { + buffer[o++] = c; + } + else + { + return false; + } + } + + *length = o - *index; + *index = i; + + return true; +} + +static bool parse_field( char *buffer, size_t *index, size_t *start, size_t *length ) +{ + size_t i = *index; + + while (is_whitespace(buffer[i])) + ++i; + + *start = i; + + if (buffer[i] == '"' || buffer[i] == '\'') + { + if (!parse_quote_field(buffer, &i, length)) + return false; + } + else + { + if (!match_nonquote_field(buffer, &i, length)) + return false; + } + + if (!is_whitespace_or_end(buffer[i])) + return INVALID_DIRECTIVE; + + *index = i; + + return true; +} + +bool config_parse( Config *config, FILE *file ) +{ + assert(config != NULL); + assert(file != NULL); + + config_init(config); + + ConfigSection *section = NULL; + ConfigOption *option = NULL; + + size_t buffer_cap = 128; + char *buffer = malloc(buffer_cap * sizeof(char)); + if (buffer == NULL) + config_oom(); + size_t buffer_end = 1; + buffer[buffer_end - 1] = '\0'; + + for (size_t line = 0, next_line = 0; ; line = next_line) + { + /* find begining of next line */ + while (next_line < buffer_end) + { + char c = buffer[next_line]; + + if (c == '\0' && next_line == buffer_end - 1) + { + if (line > 0) + { + /* shift to front */ + memmove(&buffer[0], &buffer[line], buffer_end - line); + buffer_end -= line; + next_line -= line; + line = 0; + } + else if (buffer_end > 1) + { + /* need larger capacity */ + buffer_cap *= 2; + char *new_buffer = realloc(buffer, buffer_cap * sizeof(char)); + if (new_buffer == NULL) + config_oom(); + buffer = new_buffer; + } + + size_t read = fread(&buffer[buffer_end - 1], sizeof(char), buffer_cap - buffer_end, file); + if (read == 0) + break; + + buffer_end += read; + buffer[buffer_end - 1] = '\0'; + } + else + { + ++next_line; + + if (c == '\n' || c == '\r') + break; + } + } + + /* if at end of file */ + if (next_line == line) + break; + + size_t i = line; + + Directive directive = match_directive(buffer, &i); + + switch (directive) + { + case INVALID_DIRECTIVE: + continue; + case SECTION_DIRECTIVE: + { + size_t type_start; + size_t type_length; + + if (!parse_field(buffer, &i, &type_start, &type_length)) + continue; + + size_t name_start; + size_t name_length; + + bool has_name = parse_field(buffer, &i, &name_start, &name_length); + + section = config_add_section_len(config, + &buffer[type_start], type_length, + has_name ? &buffer[name_start] : NULL, has_name ? name_length : 0); + if (section == NULL) + config_oom(); + option = NULL; + } + break; + case ITEM_DIRECTIVE: + case LIST_DIRECTIVE: + { + if (section == NULL) + continue; + + size_t key_start; + size_t key_length; + + if (!parse_field(buffer, &i, &key_start, &key_length)) + continue; + + size_t value_start; + size_t value_length; + + if (!parse_field(buffer, &i, &value_start, &value_length)) + continue; + + if (directive == ITEM_DIRECTIVE) + { + option = config_set_option_len(section, + &buffer[key_start], key_length, + &buffer[value_start], value_length); + } + else + { + if (option == NULL || !string_equal_len(&option->key, &buffer[key_start], key_length)) + option = config_get_or_set_option_len(section, + &buffer[key_start], key_length, + NULL, 0); + if (option != NULL) + option = config_add_value_len(option, + &buffer[value_start], value_length); + } + if (option == NULL) + config_oom(); + } + break; + } + + assert(i <= next_line); + } + + free(buffer); + + return config; +} + +/* config writer */ + +static void write_field( const ConfigString *field, FILE *file ) +{ + fputc('\'', file); + + char buffer[128]; + size_t o = 0; + + for (const char *ci = config_string_to_cstr(field); *ci != '\0'; ++ci) + { + char c = *ci; + + size_t l; + switch (c) + { + case '\t': + case '\n': + case '\r': + case '\'': + case '\\': + l = 2; + break; + default: + l = (c >= ' ' && c <= '~') ? 1 : 4; + break; + } + + if (o + l > COUNTOF(buffer)) + { + fwrite(buffer, sizeof(*buffer), o, file); + o = 0; + } + + switch (l) + { + case 1: + buffer[o++] = c; + break; + case 2: + switch (c) + { + case '\t': + buffer[o++] = '\\'; + buffer[o++] = 't'; + break; + case '\n': + buffer[o++] = '\\'; + buffer[o++] = 'n'; + break; + case '\r': + buffer[o++] = '\\'; + buffer[o++] = 'r'; + break; + case '\'': + case '\\': + buffer[o++] = '\\'; + buffer[o++] = c; + break; + } + break; + case 4: + buffer[o++] = '\\'; + buffer[o++] = 'x'; + char n = (c >> 4) & 0x0f; + buffer[o++] = (n < 10 ? '0' : ('a' - 10)) + n; + n = c & 0x0f; + buffer[o++] = (n < 10 ? '0' : ('a' - 10)) + n; + break; + } + } + + if (o > 0) + fwrite(buffer, sizeof(*buffer), o, file); + + fputc('\'', file); +} + +void config_write( const Config *config, FILE *file ) +{ + assert(config != NULL); + assert(file != NULL); + + for (unsigned int s = 0; s < config->sections_count; ++s) + { + ConfigSection *section = &config->sections[s]; + + fputs("section ", file); + write_field(§ion->type, file); + if (config_string_to_cstr(§ion->name) != NULL) + { + fputc(' ', file); + write_field(§ion->name, file); + } + fputc('\n', file); + + for (unsigned int o = 0; o < section->options_count; ++o) + { + ConfigOption *option = §ion->options[o]; + + if (option->values_count == 0 && config_string_to_cstr(&option->v.value) != NULL) + { + fputs("\titem ", file); + write_field(&option->key, file); + fputc(' ', file); + write_field(&option->v.value, file); + fputc('\n', file); + } + else + { + ConfigString *values_end = &option->v.values[option->values_count]; + for (ConfigString *value = &option->v.values[0]; value < values_end; ++value) + { + fputs("\tlist ", file); + write_field(&option->key, file); + fputc(' ', file); + write_field(value, file); + fputc('\n', file); + } + } + } + + fputc('\n', file); + } +} diff --git a/contrib/games/opentyrian/src/config_file.h b/contrib/games/opentyrian/src/config_file.h new file mode 100644 index 000000000..6707b40c9 --- /dev/null +++ b/contrib/games/opentyrian/src/config_file.h @@ -0,0 +1,596 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2015 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +/*! + * \file config_file.h + * \author Carl Reinke + * \date 2015 + * \copyright GNU General Public License v2+ or Mozilla Public License 2.0 + */ +#ifndef CONFIG_FILE_H +#define CONFIG_FILE_H + +#include +#include +#include +#include +#include + +#ifndef COMPILE_TIME_ASSERT +/*! + * \brief Cause compile error if compile-time computable condition fails. + * + * \param[in] name the unique identifier of the assertion + * \param[in] cond the condition + */ +#define COMPILE_TIME_ASSERT(name, cond) typedef int assert_ ## name[(cond) * 2 - 1] +#endif + +#ifndef COUNTOF +/*! + * \brief Calculate the number of elements in a fixed-length array. + * + * \param[in] a the fixed-length array + * \return the number of elements in the array + */ +#define COUNTOF(a) (sizeof(a) / sizeof(*(a))) +#endif + +/* string type */ + +/*! + * \brief A short-string-optimizing string type. + * + * This struct allows for storing up to 15 characters (plus a terminating \c '\0') inline. For + * longer strings memory will be allocated. + * + * The tag for this union is the last character of \p short_buf: + * \li if \c '\0' then \p short_buf is valid, + * \li otherwise \p long_buf is valid. + */ +typedef union +{ + /*! + * \brief The inline buffer for short strings. + */ + char short_buf[16]; + + /*! + * \brief The buffer for long strings. + * + * May be \c NULL. + */ + char *long_buf; +} ConfigString; + +/*! \cond suppress_doxygen */ +COMPILE_TIME_ASSERT(string_short_buf_sufficient, sizeof(char *) + 1 <= COUNTOF(((ConfigString *)NULL)->short_buf)); +/*! \endcond */ + +/*! \cond suppress_doxygen */ +#define CONFIG_STRING_LONG_TAG(s) ((s).short_buf[COUNTOF((s).short_buf) - 1]) +/*! \endcond */ + +/*! + * \brief Return a C-string backed by a string. + * + * \param[in] string the string + * \return the C-string + */ +static inline const char *config_string_to_cstr( const ConfigString *string ) +{ + assert(string != NULL); + char is_long = CONFIG_STRING_LONG_TAG(*string); + return is_long ? + string->long_buf : + string->short_buf; +} + +/* config types */ + +/*! + * \brief An option consisting of one (an item) or many (a list) values. + */ +typedef struct +{ + /*! + * \brief The key of the option. + */ + ConfigString key; + + /*! + * \brief The number of values in the option if it is a 'list' option. + * + * If \c 0 then the option \e may be an 'item' option. + * + * \see ::ConfigOption::value + */ + unsigned int values_count; + + /*! + * \brief The value or values. + * + * The tag for this union is \p value_count: + * \li if \c 0 then \p value is valid, + * \li otherwise \p values is valid. + */ + union + { + /*! + * \brief The value of an 'item' option or an empty 'list' option. + * + * If this field is \c NULL then the option is an empty 'list' option. + */ + ConfigString value; + + /*! + * \brief The values of a non-empty 'list' option. + */ + ConfigString *values; + } v; +} ConfigOption; + +/*! + * \brief A section consisting of options. + */ +typedef struct +{ + /*! + * \brief The type of the section. + */ + ConfigString type; + + /*! + * \brief The optional name of the section. + * + * May be \c NULL. + */ + ConfigString name; + + /*! + * \brief The number of options in the section. + */ + unsigned int options_count; + + /*! + * \brief The options in the section. + * + * \c NULL if \p options_count is \c 0. + */ + ConfigOption *options; +} ConfigSection; + +/*! + * \brief A configuration consisting of sections. + */ +typedef struct +{ + /*! + * \brief The number of sections in the configuration. + */ + unsigned int sections_count; + + /*! + * \brief The sections in the configuration. + * + * \c NULL if \p sections_count is \c 0. + */ + ConfigSection *sections; +} Config; + +/* config manipulators */ + +/*! + * \brief Initialize a configuration. + * + * \param[in] config the configuration + * \return void + */ +extern void config_init( Config *config ); + +/*! + * \brief Release any memory allocated inside a configuration. + * + * \param[in] config the configuration + * \return void + */ +extern void config_deinit( Config *config ); + +/*! + * \brief Parse a configuration from a file. + * + * \param[in] config the uninitalized configuration + * \param[in] file the file handle + * \return whether parsing succeeded + */ +extern bool config_parse( Config *config, FILE *file ); + +/*! + * \brief Write a configuration to a file. + * + * \param[in] config the configuration + * \param[in] file the file handle + * \return void + */ +extern void config_write( const Config *config, FILE *file ); + +/* config section accessors/manipulators -- by type, name */ + +/*! \see ::config_add_section() */ +extern ConfigSection *config_add_section_len( Config *config, const char *type, size_t type_len, const char *name, size_t name_len ); + +/*! + * \brief Add a section to a configuration. + * + * \param[in] config the configuration to contain the section + * \param[in] type the type of the section + * \param[in] name the name of the section; may be \c NULL + * \return the added section; \c NULL if out of memory + */ +static inline ConfigSection *config_add_section( Config *config, const char *type, const char *name) +{ + assert(type != NULL); + return config_add_section_len(config, type, strlen(type), name, name == NULL ? 0 : strlen(name)); +} + +// TODO: extern Config *config_remove_section( Config *config, unsigned int i ); + +/*! + * \brief Iterate sections by type. + * + * \param[in] config the configuration containing the sections + * \param[in] type the type of the section + * \param[in,out] save the saved state of the iterator; initialize \c *save to \c NULL before + * iteration + * \return the section; \c NULL if iteration finished + */ +extern ConfigSection *config_find_sections( Config *config, const char *type, ConfigSection **save ); + +/*! + * \brief Find a section by type and name. + * + * \param[in] config the configuration containing the section + * \param[in] type the type of the section + * \param[in] name the name of the section + * \return the section; \c NULL if it does not exist + */ +extern ConfigSection *config_find_section( Config *config, const char *type, const char *name ); + +/*! + * \brief Find a section by type and name, creating the section if it did not exist. + * + * \param[in] config the configuration containing the section + * \param[in] type the type of the section + * \param[in] name the name of the section; may be \c NULL + * \return the section; \c NULL if out of memory + */ +extern ConfigSection *config_find_or_add_section( Config *config, const char *type, const char *name ); + +/* config option accessors/manipulators -- by key */ + +/*! \see ::config_set_option() */ +extern ConfigOption *config_set_option_len( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len ); + +/*! + * \brief Set a value of an 'item' option by key, creating the option if necessary. + * + * \param[in] section the section containing the option + * \param[in] key the option key + * \param[in] value the item value; \c NULL to set an emtpy 'list' option instead of an 'item' + * option (can be used to delete an 'item' option) + * \return the option; \c NULL if out of memory + */ +static inline ConfigOption *config_set_option( ConfigSection *section, const char *key, const char *value) +{ + assert(key != NULL); + return config_set_option_len(section, key, strlen(key), value, value == NULL ? 0 : strlen(value)); +} + +/*! + * \brief Get an option by key. + * + * \param[in] section the section containing the option + * \param[in] key the option key + * \return the option; \c NULL if it does not exist + */ +extern ConfigOption *config_get_option( const ConfigSection *section, const char *key ); + +/*! \see ::config_get_or_set_option() */ +extern ConfigOption *config_get_or_set_option_len( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len ); + +/*! + * \brief Get an option by key, creating an 'item' option if the option did not exist. + * + * \param[in] section the section containing the option + * \param[in] key the option key + * \param[in] value the default item value; \c NULL to set an empty 'list' option instead of an + * 'item' option + * \return the option; \c NULL if out of memory + */ +static inline ConfigOption *config_get_or_set_option( ConfigSection *section, const char *key, const char *value ) +{ + assert(key != NULL); + return config_get_or_set_option_len(section, key, strlen(key), value, value == NULL ? 0 : strlen(value)); +} + +/*! \see ::config_set_string_option() */ +extern void config_set_string_option_len( ConfigSection *section, const char *key, size_t key_len, const char *value, size_t value_len ); + +/*! + * \brief Set a string value of an 'item' option by key, creating the option if necessary. + * + * \param[in] section the section containing the option + * \param[in] key the option key + * \param[in] value the item value + * \return void + */ +static inline void config_set_string_option( ConfigSection *section, const char *key, const char *value ) +{ + assert(key != NULL); + config_set_string_option_len(section, key, strlen(key), value, value == NULL ? 0 : strlen(value)); +} + +/*! + * \brief Get a string value of an 'item' option by key. + * + * \param[in] section the section containing the option + * \param[in] key the option key + * \param[out] out_value the item value if a valid option exists; otherwise unset + * \return whether \p out_value was set + */ +extern bool config_get_string_option( const ConfigSection *section, const char *key, const char **out_value ); + +/*! + * \brief Get a string value of an 'item' option by key, setting the option if it was invalid or + * creating the option if it did not exist. + * + * \param[in] section the section containing the option + * \param[in] key the option key + * \param[in] value the default item value + * \return the value + */ +extern const char *config_get_or_set_string_option( ConfigSection *section, const char *key, const char *value ); + +/*! + * \brief The styles of boolean values. + */ +typedef enum +{ + ZERO_ONE = 0, + NO_YES = 1, + OFF_ON = 2, + FALSE_TRUE = 3, +} ConfigBoolStyle; + +/*! + * \brief Set a boolean value of an 'item' option by key, creating the option if necessary. + * + * \param[in] section the section containing the option + * \param[in] key the option key + * \param[in] value the item value + * \param[in] style the style of boolean value + * \return void + */ +extern void config_set_bool_option( ConfigSection *section, const char *key, bool value, ConfigBoolStyle style ); + +/*! + * \brief Get a boolean value of an 'item' option by key. + * + * \param[in] section the section containing the option + * \param[in] key the option key + * \param[out] out_value the item value if a valid option exists; otherwise unset + * \return whether \p out_value was set + */ +extern bool config_get_bool_option( const ConfigSection *section, const char *key, bool *out_value ); + +/*! + * \brief Get a boolean value of an 'item' option by key, setting the option if it was invalid or + * creating the option if it did not exist. + * + * \param[in] section the section containing the option + * \param[in] key the option key + * \param[in] value the default item value + * \param[in] style the style of boolean value + * \return the value + */ +extern bool config_get_or_set_bool_option( ConfigSection *section, const char *key, bool value, ConfigBoolStyle style ); + +/*! + * \brief Set an integer value of an 'item' option by key, creating the option if necessary. + * + * \param[in] section the section containing the option + * \param[in] key the option key + * \param[in] value the item value + * \return void + */ +extern void config_set_int_option( ConfigSection *section, const char *key, int value ); + +/*! + * \brief Get an integer value of an 'item' option by key. + * + * \param[in] section the section containing the option + * \param[in] key the option key + * \param[out] out_value the item value if a valid option exists; otherwise unset + * \return whether \p out_value was set + */ +extern bool config_get_int_option( const ConfigSection *section, const char *key, int *out_value ); + +/*! + * \brief Get an integer value of an 'item' option by key, setting the option if it was invalid or + * creating the option if it did not exist. + * + * \param[in] section the section containing the option + * \param[in] key the option key + * \param[in] value the default item value + * \return the value + */ +extern int config_get_or_set_int_option( ConfigSection *section, const char *key, int value ); + +/*! + * \brief Set an unsigned integer value of an 'item' option by key, creating the option if + * necessary. + * + * \param[in] section the section containing the option + * \param[in] key the option key + * \param[in] value the item value + * \return void + */ +extern void config_set_uint_option( ConfigSection *section, const char *key, unsigned int value ); + +/*! + * \brief Get an unsigned integer value of an 'item' option by key. + * + * \param[in] section the section containing the option + * \param[in] key the option key + * \param[out] out_value the item value if a valid option exists; otherwise unset + * \return whether \p out_value was set + */ +extern bool config_get_uint_option( const ConfigSection *section, const char *key, unsigned int *out_value ); + +/*! + * \brief Get an unsigned integer value of an 'item' option by key, setting the option if it was + * invalid or creating the option if it did not exist. + * + * \param[in] section the section containing the option + * \param[in] key the option key + * \param[in] value the default item value + * \return the value + */ +extern unsigned int config_get_or_set_uint_option( ConfigSection *section, const char *key, unsigned int value ); + +/* config option accessors/manipulators -- by reference */ + +/*! \see ::config_set_value() */ +extern ConfigOption *config_set_value_len( ConfigOption *option, const char *value, size_t value_len ); + +/*! + * \brief Set the value of an 'item' option. + * + * \param[in] option the option + * \param[in] value the value + * \return the option; \c NULL if out of memory + */ +static inline ConfigOption *config_set_value( ConfigOption *option, const char *value ) +{ + return config_set_value_len(option, value, value == NULL ? 0 : strlen(value)); +} + +/*! \see ::config_add_value() */ +extern ConfigOption *config_add_value_len( ConfigOption *option, const char *value, size_t value_len ); + +/*! + * \brief Add a value to a 'list' option. + * + * \param[in] option the option + * \param[in] value the value + * \return the option; \c NULL if out of memory + */ +static inline ConfigOption *config_add_value( ConfigOption *option, const char *value ) +{ + assert(value != NULL); + return config_add_value_len(option, value, strlen(value)); +} + +/*! + * \brief Remove a value from a 'list' option. + * + * \param[in] option the option + * \param[in] i the index of the value + * \return the option; \c NULL if out of memory or invalid \p index + */ +extern ConfigOption *config_remove_value( ConfigOption *option, unsigned int i ); + +/*! + * \brief Get the value of an 'item' option. + * + * \param[in] option the option + * \return the value; \c NULL if \p option was \c NULL or was a 'list' option + */ +extern const char *config_get_value( const ConfigOption *option ); + +/*! + * \brief Get the value that indicates whether the option is a 'list' option. + * + * \param[in] option the option + * \return whether the option is a 'list' option + */ +static inline bool config_is_value_list( const ConfigOption *option ) +{ + assert(option != NULL); + return option->values_count > 0 || + config_string_to_cstr(&option->v.value) == NULL; +} + +/*! + * \brief Get the number of values assigned to the option. + * + * \param[in] option the option + * \return \c 1 if the option is an 'item' option; the number of elements if the option is a 'list' + * option + */ +static inline unsigned int config_get_value_count( const ConfigOption *option ) +{ + assert(option != NULL); + return option->values_count == 0 ? + (config_string_to_cstr(&option->v.value) == NULL ? 0 : 1) : + option->values_count; +} + +/*! + * \brief Iterate over the values assigned to the option. + * + * \param[out] string_value the value variable to declare + * \param[in] option the option + */ +#define foreach_option_value( string_value, option ) \ + for (ConfigOption *_option = (option); _option != NULL; _option = NULL) \ + for (ConfigString *_values_begin = _option->values_count == 0 ? &_option->v.value : &_option->v.values[0], \ + *_values_end = _option->values_count == 0 ? _values_begin + 1 : &_option->v.values[_option->values_count], \ + *_value = _values_begin; _value < _values_end; ++_value) \ + for (const char *(string_value) = config_string_to_cstr(_value); (string_value) != NULL; (string_value) = NULL) + +/*! + * \brief Iterate over the values assigned to the option. + * + * \param[out] i the index variable to declare + * \param[out] string_value the value variable to declare + * \param[in] option the option + */ +#define foreach_option_i_value( i, string_value, option ) \ + for (unsigned int (i) = 0; (i) == 0; (i) = ~0) \ + for (ConfigOption *_option = (option); _option != NULL; _option = NULL) \ + for (ConfigString *_values_begin = _option->values_count == 0 ? &_option->v.value : &_option->v.values[0], \ + *_values_end = _option->values_count == 0 ? _values_begin + 1 : &_option->v.values[_option->values_count], \ + *_value = _values_begin; _value < _values_end; ++_value, (i) = _value - _values_begin) \ + for (const char *(string_value) = config_string_to_cstr(_value); (string_value) != NULL; (string_value) = NULL) + +/*! + * \brief Remove a value from an option during iteration. Should be followed by \c continue. + */ +#define foreach_remove_option_value() \ + { \ + extern void config_oom( void ); \ + unsigned int _value_i = _value - _values_begin; \ + if (config_remove_value(_option, _value_i) == NULL) \ + config_oom(); \ + _values_begin = _option->values_count == 0 ? &_option->v.value : &_option->v.values[0]; \ + _values_end = _option->values_count == 0 ? _values_begin + 1 : &_option->v.values[_option->values_count]; \ + _value = _values_begin + _value_i - 1; \ + } + +#endif diff --git a/contrib/games/opentyrian/src/destruct.c b/contrib/games/opentyrian/src/destruct.c new file mode 100644 index 000000000..1831cf1a2 --- /dev/null +++ b/contrib/games/opentyrian/src/destruct.c @@ -0,0 +1,2690 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* File notes: + * Two players duke it out in a Scorched Earth style game. + * Most of the variables referring to the players are global as + * they are often edited and that's how the original was written. + * + * Currently this file is at its final stage for vanilla destruct. + * Almost all of the left/right code duplications is gone. Most of the + * functions have been examined and tightened up, none of the enums + * start with '1', and the various large functions have been divided into + * smaller chunks. + * + * Destruct also supports some 'hidden' configuration that's just too awesome + * to not have available. Destruct has no configuration options in game, but + * that doesn't stop us from changing various limiting vars and letting + * people remap the keyboard. AIs may also be introduced here; fighting a + * stateless AI isn't really challenging afterall. + * + * This hidden config also allows for a hidden game mode! Though as a custom + * game mode wouldn't show up in the data files it forces us to distinguish + * between the constant DESTRUCT_MODES (5) and MAX_MODES (6). DESTRUCT_MODES + * is only used with loaded data. + * + * Things I wanted to do but can't: Remove references to VGAScreen. For + * a multitude of reasons this just isn't feasable. It would have been nice + * to increase the playing field though... + */ + +/*** Headers ***/ +#include "destruct.h" + +#include "config.h" +#include "config_file.h" +#include "fonthand.h" +#include "helptext.h" +#include "keyboard.h" +#include "loudness.h" +#include "mtrand.h" +#include "nortsong.h" +#include "opentyr.h" +#include "palette.h" +#include "picload.h" +#include "sprite.h" +#include "varz.h" +#include "vga256d.h" +#include "video.h" + +#include + +/*** Defines ***/ +#define UNIT_HEIGHT 12 +#define MAX_KEY_OPTIONS 4 + +/*** Enums ***/ +enum de_state_t { STATE_INIT, STATE_RELOAD, STATE_CONTINUE }; +enum de_player_t { PLAYER_LEFT = 0, PLAYER_RIGHT = 1, MAX_PLAYERS = 2 }; +enum de_team_t { TEAM_LEFT = 0, TEAM_RIGHT = 1, MAX_TEAMS = 2 }; +enum de_mode_t { MODE_5CARDWAR = 0, MODE_TRADITIONAL, MODE_HELIASSAULT, + MODE_HELIDEFENSE, MODE_OUTGUNNED, MODE_CUSTOM, + MODE_FIRST = MODE_5CARDWAR, MODE_LAST = MODE_CUSTOM, + MAX_MODES = 6, MODE_NONE = -1 }; +enum de_unit_t { UNIT_TANK = 0, UNIT_NUKE, UNIT_DIRT, UNIT_SATELLITE, + UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER, UNIT_HELI, + UNIT_FIRST = UNIT_TANK, UNIT_LAST = UNIT_HELI, + MAX_UNITS = 8, UNIT_NONE = -1 }; +enum de_shot_t { SHOT_TRACER = 0, SHOT_SMALL, SHOT_LARGE, SHOT_MICRO, + SHOT_SUPER, SHOT_DEMO, SHOT_SMALLNUKE, SHOT_LARGENUKE, + SHOT_SMALLDIRT, SHOT_LARGEDIRT, SHOT_MAGNET, SHOT_MINILASER, + SHOT_MEGALASER, SHOT_LASERTRACER, SHOT_MEGABLAST, SHOT_MINI, + SHOT_BOMB, + SHOT_FIRST = SHOT_TRACER, SHOT_LAST = SHOT_BOMB, + MAX_SHOT_TYPES = 17, SHOT_INVALID = -1 }; +enum de_expl_t { EXPL_NONE, EXPL_MAGNET, EXPL_DIRT, EXPL_NORMAL }; /* this needs a better name */ +enum de_trails_t { TRAILS_NONE, TRAILS_NORMAL, TRAILS_FULL }; +enum de_pixel_t { PIXEL_BLACK = 0, PIXEL_DIRT = 25 }; +enum de_mapflags_t { MAP_NORMAL = 0x00, MAP_WALLS = 0x01, MAP_RINGS = 0x02, + MAP_HOLES = 0x04, MAP_FUZZY = 0x08, MAP_TALL = 0x10 }; + +/* keys and moves should line up. */ +enum de_keys_t { KEY_LEFT = 0, KEY_RIGHT, KEY_UP, KEY_DOWN, KEY_CHANGE, KEY_FIRE, KEY_CYUP, KEY_CYDN, MAX_KEY = 8}; +enum de_move_t { MOVE_LEFT = 0, MOVE_RIGHT, MOVE_UP, MOVE_DOWN, MOVE_CHANGE, MOVE_FIRE, MOVE_CYUP, MOVE_CYDN, MAX_MOVE = 8}; + +/* The tracerlaser is dummied out. It works but (probably due to the low + * MAX_SHOTS) is not assigned to anything. The bomb does not work. + */ + + +/*** Structs ***/ +struct destruct_config_s { + + unsigned int max_shots; + unsigned int min_walls; + unsigned int max_walls; + unsigned int max_explosions; + unsigned int max_installations; + bool allow_custom; + bool alwaysalias; + bool jumper_straight[2]; + bool ai[2]; +}; +struct destruct_unit_s { + + /* Positioning/movement */ + unsigned int unitX; /* yep, one's an int and the other is a real */ + float unitY; + float unitYMov; + bool isYInAir; + + /* What it is and what it fires */ + enum de_unit_t unitType; + enum de_shot_t shotType; + + /* What it's pointed */ + float angle; + float power; + + /* Misc */ + int lastMove; + unsigned int ani_frame; + int health; +}; +struct destruct_shot_s { + + bool isAvailable; + + float x; + float y; + float xmov; + float ymov; + bool gravity; + unsigned int shottype; + //int shotdur; /* This looks to be unused */ + unsigned int trailx[4], traily[4], trailc[4]; +}; +struct destruct_explo_s { + + bool isAvailable; + + unsigned int x, y; + unsigned int explowidth; + unsigned int explomax; + unsigned int explofill; + enum de_expl_t exploType; +}; +struct destruct_moves_s { + bool actions[MAX_MOVE]; +}; +struct destruct_keys_s { + SDLKey Config[MAX_KEY][MAX_KEY_OPTIONS]; +}; +struct destruct_ai_s { + + int c_Angle, c_Power, c_Fire; + unsigned int c_noDown; +}; +struct destruct_player_s { + + bool is_cpu; + struct destruct_ai_s aiMemory; + + struct destruct_unit_s * unit; + struct destruct_moves_s moves; + struct destruct_keys_s keys; + + enum de_team_t team; + unsigned int unitsRemaining; + unsigned int unitSelected; + unsigned int shotDelay; + unsigned int score; +}; +struct destruct_wall_s { + + bool wallExist; + unsigned int wallX, wallY; +}; +struct destruct_world_s { + + /* Map data & screen pointer */ + unsigned int baseMap[320]; + SDL_Surface * VGAScreen; + struct destruct_wall_s * mapWalls; + + /* Map configuration */ + enum de_mode_t destructMode; + unsigned int mapFlags; +}; + +/*** Function decs ***/ +//Prep functions +static void JE_destructMain( void ); +static void JE_introScreen( void ); +static enum de_mode_t JE_modeSelect( void ); +static void JE_helpScreen( void ); +static void JE_pauseScreen( void ); + +//level generating functions +static void JE_generateTerrain( void ); +static void DE_generateBaseTerrain( unsigned int, unsigned int *); +static void DE_drawBaseTerrain( unsigned int * ); +static void DE_generateUnits( unsigned int * ); +static void DE_generateWalls( struct destruct_world_s * ); +static void DE_generateRings(SDL_Surface *, Uint8 ); +static void DE_ResetLevel( void ); +static unsigned int JE_placementPosition( unsigned int, unsigned int, unsigned int * ); + +//drawing functions +static void JE_aliasDirt( SDL_Surface * ); +static void DE_RunTickDrawCrosshairs( void ); +static void DE_RunTickDrawHUD( void ); +static void DE_GravityDrawUnit( enum de_player_t, struct destruct_unit_s * ); +static void DE_RunTickAnimate( void ); +static void DE_RunTickDrawWalls( void ); +static void DE_DrawTrails( struct destruct_shot_s *, unsigned int, unsigned int, unsigned int ); +static void JE_tempScreenChecking( void ); +static void JE_superPixel( unsigned int, unsigned int ); +static void JE_pixCool( unsigned int, unsigned int, Uint8 ); + +//player functions +static void DE_RunTickGetInput( void ); +static void DE_ProcessInput( void ); +static void DE_ResetPlayers( void ); +static void DE_ResetAI( void ); +static void DE_ResetActions( void ); +static void DE_RunTickAI( void ); + +//unit functions +static void DE_RaiseAngle( struct destruct_unit_s * ); +static void DE_LowerAngle( struct destruct_unit_s * ); +static void DE_RaisePower( struct destruct_unit_s * ); +static void DE_LowerPower( struct destruct_unit_s * ); +static void DE_CycleWeaponUp( struct destruct_unit_s * ); +static void DE_CycleWeaponDown( struct destruct_unit_s * ); +static void DE_RunMagnet( enum de_player_t, struct destruct_unit_s * ); +static void DE_GravityFlyUnit( struct destruct_unit_s * ); +static void DE_GravityLowerUnit( struct destruct_unit_s * ); +static void DE_DestroyUnit( enum de_player_t, struct destruct_unit_s * ); +static void DE_ResetUnits( void ); +static inline bool DE_isValidUnit( struct destruct_unit_s *); + +//weapon functions +static void DE_ResetWeapons( void ); +static void DE_RunTickShots( void ); +static void DE_RunTickExplosions( void ); +static void DE_TestExplosionCollision( unsigned int, unsigned int); +static void JE_makeExplosion( unsigned int, unsigned int, enum de_shot_t ); +static void DE_MakeShot( enum de_player_t, const struct destruct_unit_s *, int ); + +//gameplay functions +static enum de_state_t DE_RunTick( void ); +static void DE_RunTickCycleDeadUnits( void ); +static void DE_RunTickGravity( void ); +static bool DE_RunTickCheckEndgame( void ); +static bool JE_stabilityCheck( unsigned int, unsigned int ); + +//sound +static void DE_RunTickPlaySounds( void ); +static void JE_eSound( unsigned int ); + + + +/*** Weapon configurations ***/ + +/* Part of me wants to leave these as bytes to save space. */ +static const bool demolish[MAX_SHOT_TYPES] = {false, false, false, false, false, true, true, true, false, false, false, false, true, false, true, false, true}; +//static const int shotGr[MAX_SHOT_TYPES] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 101}; +static const int shotTrail[MAX_SHOT_TYPES] = {TRAILS_NONE, TRAILS_NONE, TRAILS_NONE, TRAILS_NORMAL, TRAILS_NORMAL, TRAILS_NORMAL, TRAILS_FULL, TRAILS_FULL, TRAILS_NONE, TRAILS_NONE, TRAILS_NONE, TRAILS_NORMAL, TRAILS_FULL, TRAILS_NORMAL, TRAILS_FULL, TRAILS_NORMAL, TRAILS_NONE}; +//static const int shotFuse[MAX_SHOT_TYPES] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}; +static const int shotDelay[MAX_SHOT_TYPES] = {10, 30, 80, 20, 60, 100, 140, 200, 20, 60, 5, 15, 50, 5, 80, 16, 0}; +static const int shotSound[MAX_SHOT_TYPES] = {S_SELECT, S_WEAPON_2, S_WEAPON_1, S_WEAPON_7, S_WEAPON_7, S_EXPLOSION_9, S_EXPLOSION_22, S_EXPLOSION_22, S_WEAPON_5, S_WEAPON_13, S_WEAPON_10, S_WEAPON_15, S_WEAPON_15, S_WEAPON_26, S_WEAPON_14, S_WEAPON_7, S_WEAPON_7}; +static const int exploSize[MAX_SHOT_TYPES] = {4, 20, 30, 14, 22, 16, 40, 60, 10, 30, 0, 5, 10, 3, 15, 7, 0}; +static const bool shotBounce[MAX_SHOT_TYPES] = {false, false, false, false, false, false, false, false, false, false, false, true, true, true, true, false, true}; +static const int exploDensity[MAX_SHOT_TYPES] = { 2, 5, 10, 15, 20, 15, 25, 30, 40, 80, 0, 30, 30, 4, 30, 5, 0}; +static const int shotDirt[MAX_SHOT_TYPES] = {EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_DIRT, EXPL_DIRT, EXPL_MAGNET, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NORMAL, EXPL_NONE}; +static const int shotColor[MAX_SHOT_TYPES] = {16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 10, 10, 10, 10, 16, 0}; + +static const int defaultWeapon[MAX_UNITS] = {SHOT_SMALL, SHOT_MICRO, SHOT_SMALLDIRT, SHOT_INVALID, SHOT_MAGNET, SHOT_MINILASER, SHOT_MICRO, SHOT_MINI}; +static const int defaultCpuWeapon[MAX_UNITS] = {SHOT_SMALL, SHOT_MICRO, SHOT_DEMO, SHOT_INVALID, SHOT_MAGNET, SHOT_MINILASER, SHOT_MICRO, SHOT_MINI}; +static const int defaultCpuWeaponB[MAX_UNITS] = {SHOT_DEMO, SHOT_SMALLNUKE, SHOT_DEMO, SHOT_INVALID, SHOT_MAGNET, SHOT_MEGALASER, SHOT_MICRO, SHOT_MINI}; +static const int systemAngle[MAX_UNITS] = {true, true, true, false, false, true, false, false}; +static const int baseDamage[MAX_UNITS] = {200, 120, 400, 300, 80, 150, 600, 40}; +static const int systemAni[MAX_UNITS] = {false, false, false, true, false, false, false, true}; + +static bool weaponSystems[MAX_UNITS][MAX_SHOT_TYPES] = +{ + {1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // normal + {0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // nuke + {0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, // dirt + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, // worthless + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, // magnet + {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0}, // laser + {1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, // jumper + {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0} // helicopter +}; + +/* More constant configuration settings. */ +/* Music that destruct will play. You can check out musmast.c to see what is what. */ +static const JE_byte goodsel[14] /*[1..14]*/ = {1, 2, 6, 12, 13, 14, 17, 23, 24, 26, 28, 29, 32, 33}; + +/* Unit creation. Need to move this later: Doesn't belong here */ +static JE_byte basetypes[10][11] /*[1..8, 1..11]*/ = /* [0] is amount of units*/ +{ + {5, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_DIRT, UNIT_DIRT, UNIT_SATELLITE, UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER, UNIT_HELI}, /*Normal*/ + {1, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK}, /*Traditional*/ + {4, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI}, /*Weak Heli attack fleet*/ + {8, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_NUKE, UNIT_NUKE, UNIT_DIRT, UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER}, /*Strong Heli defense fleet*/ + {8, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI, UNIT_HELI}, /*Strong Heli attack fleet*/ + {4, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_NUKE, UNIT_DIRT, UNIT_MAGNET, UNIT_JUMPER, UNIT_JUMPER}, /*Weak Heli defense fleet*/ + {8, UNIT_TANK, UNIT_NUKE, UNIT_DIRT, UNIT_SATELLITE, UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER, UNIT_HELI, UNIT_TANK, UNIT_NUKE}, /*Overpowering fleet*/ + {4, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_DIRT, UNIT_TANK, UNIT_LASER, UNIT_JUMPER, UNIT_HELI, UNIT_NUKE, UNIT_JUMPER}, /*Weak fleet*/ + {5, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_DIRT, UNIT_DIRT, UNIT_SATELLITE, UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER, UNIT_HELI}, /*Left custom*/ + {5, UNIT_TANK, UNIT_TANK, UNIT_NUKE, UNIT_DIRT, UNIT_DIRT, UNIT_SATELLITE, UNIT_MAGNET, UNIT_LASER, UNIT_JUMPER, UNIT_HELI}, /*Right custom*/ +}; +static const unsigned int baseLookup[MAX_PLAYERS][MAX_MODES] = +{ + {0, 1, 3, 4, 6, 8}, + {0, 1, 2, 5, 7, 9} +}; + + +static const JE_byte GraphicBase[MAX_PLAYERS][MAX_UNITS] = +{ + { 1, 6, 11, 58, 63, 68, 96, 153}, + { 20, 25, 30, 77, 82, 87, 115, 172} +}; + +static const JE_byte ModeScore[MAX_PLAYERS][MAX_MODES] = +{ + {1, 0, 0, 5, 0, 1}, + {1, 0, 5, 0, 1, 1} +}; + +static SDLKey defaultKeyConfig[MAX_PLAYERS][MAX_KEY][MAX_KEY_OPTIONS] = +{ + { {SDLK_c}, + {SDLK_v}, + {SDLK_a}, + {SDLK_z}, + {SDLK_LALT}, + {SDLK_x, SDLK_LSHIFT}, + {SDLK_LCTRL}, + {SDLK_SPACE} + }, + { {SDLK_LEFT, SDLK_KP4}, + {SDLK_RIGHT, SDLK_KP6}, + {SDLK_UP, SDLK_KP8}, + {SDLK_DOWN, SDLK_KP2}, + {SDLK_BACKSLASH, SDLK_KP5}, + {SDLK_INSERT, SDLK_RETURN, SDLK_KP0, SDLK_KP_ENTER}, + {SDLK_PAGEUP, SDLK_KP9}, + {SDLK_PAGEDOWN, SDLK_KP3} + } +}; + + +/*** Globals ***/ +static SDL_Surface *destructTempScreen; +static JE_boolean destructFirstTime; + +static struct destruct_config_s config = { 40, 20, 20, 40, 10, false, false, {true, false}, {true, false} }; +static struct destruct_player_s destruct_player[MAX_PLAYERS]; +static struct destruct_world_s world; +static struct destruct_shot_s * shotRec; +static struct destruct_explo_s * exploRec; + + +static const char *player_names[] = +{ + "left", "right", +}; + +static const char *key_names[] = +{ + "left", "right", "up", "down", + "change", "fire", "previous weapon", "next weapon", +}; + +static const char *unit_names[] = +{ + "tank", "nuke", "dirt", "satellite", + "magnet", "laser", "jumper", "heli", +}; + +static enum de_unit_t get_unit_by_name( const char *unit_name ) +{ + for (enum de_unit_t unit = UNIT_FIRST; unit < MAX_UNITS; ++unit) + if (strcmp(unit_name, unit_names[unit]) == 0) + return unit; + + return UNIT_NONE; +} + +static SDLKey get_SDLKey_by_name( const char *key_name ) +{ + for (SDLKey key = SDLK_FIRST; key < SDLK_LAST; ++key) + if (strcmp(key_name, SDL_GetKeyName(key)) == 0) + return key; + + return SDLK_UNKNOWN; +} + +static void load_destruct_config( Config *config_ ) +{ + ConfigSection *section; + + section = config_find_or_add_section(config_, "destruct", NULL); + if (section == NULL) + exit(EXIT_FAILURE); // out of memory + + config.alwaysalias = config_get_or_set_bool_option(section, "antialias craters", false, NO_YES); + + weaponSystems[UNIT_LASER][SHOT_LASERTRACER] = config_get_or_set_bool_option(section, "tracer laser", false, OFF_ON); + + config.max_shots = config_get_or_set_int_option(section, "max shots", 40); + config.max_explosions = config_get_or_set_int_option(section, "max explosions", 40); + config.min_walls = config_get_or_set_int_option(section, "min walls", 20); + config.max_walls = config_get_or_set_int_option(section, "max walls", 20); + + config.ai[0] = config_get_or_set_bool_option(section, "left ai", true, NO_YES); + config.jumper_straight[0] = config_get_or_set_bool_option(section, "left jumper fires straight", true, NO_YES); + config.ai[1] = config_get_or_set_bool_option(section, "right ai", false, NO_YES); + config.jumper_straight[1] = config_get_or_set_bool_option(section, "right jumper fires straight", false, NO_YES); + + // keyboard controls + + for (int p = 0; p < MAX_PLAYERS; ++p) + { + section = config_find_section(config_, "destruct keyboard", player_names[p]); + if (section == NULL) + if ((section = config_add_section(config_, "destruct keyboard", player_names[p])) == NULL) + exit(-1); + + ConfigOption *option; + + for (int k = 0; k < MAX_KEY; ++k) + { + if ((option = config_get_or_set_option(section, key_names[k], NULL)) == NULL) + exit(-1); + + foreach_option_i_value(i, value, option) + { + SDLKey key = get_SDLKey_by_name(value); + if (key != SDLK_LAST && i < COUNTOF(defaultKeyConfig[p][k])) + { + defaultKeyConfig[p][k][i] = key; + } + else // invalid or excess + { + foreach_remove_option_value(); + continue; + } + } + + if (config_get_value_count(option) > 0) + { + // unset remaining defaults + for (unsigned int i = config_get_value_count(option); i < COUNTOF(defaultKeyConfig[p][k]); ++i) + defaultKeyConfig[p][k][i] = SDLK_UNKNOWN; + } + else + { + // set defaults + for (unsigned int i = 0; i < COUNTOF(defaultKeyConfig[p][k]); ++i) + if (defaultKeyConfig[p][k][i] != SDLK_UNKNOWN) + config_add_value(option, SDL_GetKeyName(defaultKeyConfig[p][k][i])); + } + } + } + + // custom destruct mode + + section = config_find_section(config_, "destruct custom", NULL); + if (section == NULL) + if ((section = config_add_section(config_, "destruct custom", NULL)) == NULL) + exit(-1); + + config.allow_custom = config_get_or_set_bool_option(section, "enable", false, NO_YES); + + char buffer[15 + 1]; + + for (int p = 0; p < MAX_PLAYERS; ++p) + { + snprintf(buffer, sizeof(buffer), "%s num units", player_names[p]); + basetypes[8 + p][0] = config_get_or_set_int_option(section, buffer, basetypes[8 + p][0]); + + ConfigOption *option; + + snprintf(buffer, sizeof(buffer), "%s unit", player_names[p]); + if ((option = config_get_or_set_option(section, buffer, NULL)) == NULL) + exit(-1); + + foreach_option_i_value(i, value, option) + { + enum de_unit_t unit = get_unit_by_name(value); + if (unit != UNIT_NONE && 1 + i < COUNTOF(basetypes[8 + p])) + { + basetypes[8 + p][1 + i] = unit; + } + else // invalid or excess + { + foreach_remove_option_value(); + continue; + } + } + + if (config_get_value_count(option) > 0) + { + // set remaining units to tank + for (unsigned int i = config_get_value_count(option); 1 + i < COUNTOF(basetypes[8 + p]); ++i) + { + basetypes[8 + p][1 + i] = UNIT_TANK; + config_add_value(option, unit_names[UNIT_TANK]); + } + } + else + { + // set defaults + for (unsigned int i = 0; 1 + i < COUNTOF(basetypes[8 + p]); ++i) + config_add_value(option, unit_names[basetypes[8 + p][1 + i]]); + } + } +} + +/*** Startup ***/ + +void JE_destructGame( void ) +{ + unsigned int i; + + /* This is the entry function. Any one-time actions we need to + * perform can go in here. */ + JE_clr256(VGAScreen); + JE_showVGA(); + + load_destruct_config(&opentyrian_config); + + //malloc things that have customizable sizes + shotRec = malloc(sizeof(struct destruct_shot_s) * config.max_shots); + exploRec = malloc(sizeof(struct destruct_explo_s) * config.max_explosions); + world.mapWalls = malloc(sizeof(struct destruct_wall_s) * config.max_walls); + + //Malloc enough structures to cover all of this session's possible needs. + for(i = 0; i < 10; i++) { + config.max_installations = MAX(config.max_installations, basetypes[i][0]); + } + destruct_player[PLAYER_LEFT ].unit = malloc(sizeof(struct destruct_unit_s) * config.max_installations); + destruct_player[PLAYER_RIGHT].unit = malloc(sizeof(struct destruct_unit_s) * config.max_installations); + + destructTempScreen = game_screen; + world.VGAScreen = VGAScreen; + + JE_loadCompShapes(&eShapes[0], '~'); + fade_black(1); + + JE_destructMain(); + + //and of course exit actions go here. + free(shotRec); + free(exploRec); + free(world.mapWalls); + free(destruct_player[PLAYER_LEFT ].unit); + free(destruct_player[PLAYER_RIGHT].unit); +} + +static void JE_destructMain( void ) +{ + enum de_state_t curState; + + + JE_loadPic(VGAScreen, 11, false); + JE_introScreen(); + + DE_ResetPlayers(); + + destruct_player[PLAYER_LEFT ].is_cpu = config.ai[PLAYER_LEFT]; + destruct_player[PLAYER_RIGHT].is_cpu = config.ai[PLAYER_RIGHT]; + + while(1) + { + world.destructMode = JE_modeSelect(); + + if(world.destructMode == MODE_NONE) { + break; /* User is quitting */ + } + + do + { + + destructFirstTime = true; + JE_loadPic(VGAScreen, 11, false); + + DE_ResetUnits(); + DE_ResetLevel(); + do { + curState = DE_RunTick(); + } while(curState == STATE_CONTINUE); + + fade_black(25); + } + while (curState == STATE_RELOAD); + } +} + +static void JE_introScreen( void ) +{ + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch); + JE_outText(VGAScreen, JE_fontCenter(specialName[7], TINY_FONT), 90, specialName[7], 12, 5); + JE_outText(VGAScreen, JE_fontCenter(miscText[64], TINY_FONT), 180, miscText[64], 15, 2); + JE_outText(VGAScreen, JE_fontCenter(miscText[65], TINY_FONT), 190, miscText[65], 15, 2); + JE_showVGA(); + fade_palette(colors, 15, 0, 255); + + newkey = false; + while (!newkey) + { + service_SDL_events(false); + uSDL_Delay(16); + } + + fade_black(15); + memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch); + JE_showVGA(); +} + +/* JE_modeSelect + * + * This function prints the DESTRUCT mode selection menu. + * The return value is the selected mode, or -1 (MODE_NONE) + * if the user quits. + */ +static void DrawModeSelectMenu( enum de_mode_t mode ) { + + int i; + + /* Helper function of JE_modeSelect. Do not use elsewhere. */ + for (i = 0; i < DESTRUCT_MODES; i++) + { /* What a large function call. */ + JE_textShade(VGAScreen, JE_fontCenter(destructModeName[i], TINY_FONT), 82 + i * 12, destructModeName[i], 12, (i == mode) * 4, FULL_SHADE); + } + if (config.allow_custom == true) + { + JE_textShade(VGAScreen, JE_fontCenter("Custom", TINY_FONT), 82 + i * 12, "Custom", 12, (i == mode) * 4, FULL_SHADE); + } +} +static enum de_mode_t JE_modeSelect( void ) +{ + enum de_mode_t mode; + + + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch); + mode = MODE_5CARDWAR; + + // Draw the menu and fade us in + DrawModeSelectMenu(mode); + + JE_showVGA(); + fade_palette(colors, 15, 0, 255); + + /* Get input in a loop. */ + while(1) + { + /* Re-draw the menu every iteration */ + DrawModeSelectMenu(mode); + JE_showVGA(); + + /* Grab keys */ + newkey = false; + do { + service_SDL_events(false); + uSDL_Delay(16); + } while(!newkey); + + /* See what was pressed */ + if (keysactive[SDLK_ESCAPE]) + { + mode = MODE_NONE; /* User is quitting, return failure */ + break; + } + if (keysactive[SDLK_RETURN]) + { + break; /* User has selected, return choice */ + } + if (keysactive[SDLK_UP]) + { + if(mode == MODE_FIRST) + { + if (config.allow_custom == true) + { + mode = MODE_LAST; + } else { + mode = MODE_LAST-1; + } + } else { + mode--; + } + } + if (keysactive[SDLK_DOWN]) + { + if(mode >= MODE_LAST-1) + { + if (config.allow_custom == true && mode == MODE_LAST-1) + { + mode++; + } else { + mode = MODE_FIRST; + } + } else { + mode++; + } + } + } + + fade_black(15); + memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch); + JE_showVGA(); + return(mode); +} + +static void JE_generateTerrain( void ) +{ + /* The unique modifiers: + Altered generation (really tall) + Fuzzy hills + Rings of dirt + + The non-unique ones;: + Rings of not dirt (holes) + Walls + */ + + world.mapFlags = MAP_NORMAL; + + if(mt_rand() % 2 == 0) + { + world.mapFlags |= MAP_WALLS; + } + if(mt_rand() % 4 == 0) + { + world.mapFlags |= MAP_HOLES; + } + switch(mt_rand() % 4) + { + case 0: + world.mapFlags |= MAP_FUZZY; + break; + + case 1: + world.mapFlags |= MAP_TALL; + break; + + case 2: + world.mapFlags |= MAP_RINGS; + break; + } + + play_song(goodsel[mt_rand() % 14] - 1); + + DE_generateBaseTerrain(world.mapFlags, world.baseMap); + DE_generateUnits(world.baseMap); + DE_generateWalls(&world); + DE_drawBaseTerrain(world.baseMap); + + if (world.mapFlags & MAP_RINGS) + { + DE_generateRings(world.VGAScreen, PIXEL_DIRT); + } + if (world.mapFlags & MAP_HOLES) + { + DE_generateRings(world.VGAScreen, PIXEL_BLACK); + } + + JE_aliasDirt(world.VGAScreen); + JE_showVGA(); + + memcpy(destructTempScreen->pixels, VGAScreen->pixels, destructTempScreen->pitch * destructTempScreen->h); +} +static void DE_generateBaseTerrain( unsigned int mapFlags, unsigned int * baseWorld) +{ + unsigned int i; + unsigned int newheight, HeightMul; + float sinewave, sinewave2, cosinewave, cosinewave2; + + + /* The 'terrain' is actually the video buffer :). If it's brown, flu... er, + * brown pixels are what we check for collisions with. */ + + /* The ranges here are between .01 and roughly 0.07283...*/ + sinewave = mt_rand_lt1() * M_PI / 50 + 0.01f; + sinewave2 = mt_rand_lt1() * M_PI / 50 + 0.01f; + cosinewave = mt_rand_lt1() * M_PI / 50 + 0.01f; + cosinewave2 = mt_rand_lt1() * M_PI / 50 + 0.01f; + HeightMul = 20; + + /* This block just exists to mix things up. */ + if(mapFlags & MAP_FUZZY) + { + sinewave = M_PI - mt_rand_lt1() * 0.3f; + sinewave2 = M_PI - mt_rand_lt1() * 0.3f; + } + if(mapFlags & MAP_TALL) + { + HeightMul = 100; + } + + /* Now compute a height for each of our lines. */ + for (i = 1; i <= 318; i++) + { + newheight = roundf(sinf(sinewave * i) * HeightMul + sinf(sinewave2 * i) * 15 + + cosf(cosinewave * i) * 10 + sinf(cosinewave2 * i) * 15) + 130; + + /* Bind it; we have mins and maxs */ + if (newheight < 40) + { + newheight = 40; + } + else if (newheight > 195) { + newheight = 195; + } + baseWorld[i] = newheight; + } + /* The base world has been created. */ +} +static void DE_drawBaseTerrain( unsigned int * baseWorld) +{ + unsigned int i; + + + for (i = 1; i <= 318; i++) + { + JE_rectangle(VGAScreen, i, baseWorld[i], i, 199, PIXEL_DIRT); + } +} + +static void DE_generateUnits( unsigned int * baseWorld ) +{ + unsigned int i, j, numSatellites; + + + for (i = 0; i < MAX_PLAYERS; i++) + { + numSatellites = 0; + destruct_player[i].unitsRemaining = 0; + + for (j = 0; j < basetypes[baseLookup[i][world.destructMode]][0]; j++) + { + /* Not everything is the same between players */ + if(i == PLAYER_LEFT) + { + destruct_player[i].unit[j].unitX = (mt_rand() % 120) + 10; + } + else + { + destruct_player[i].unit[j].unitX = 320 - ((mt_rand() % 120) + 22); + } + + destruct_player[i].unit[j].unitY = JE_placementPosition(destruct_player[i].unit[j].unitX - 1, 14, baseWorld); + destruct_player[i].unit[j].unitType = basetypes[baseLookup[i][world.destructMode]][(mt_rand() % 10) + 1]; + + /* Sats are special cases since they are useless. They don't count + * as active units and we can't have a team of all sats */ + if (destruct_player[i].unit[j].unitType == UNIT_SATELLITE) + { + if (numSatellites == basetypes[baseLookup[i][world.destructMode]][0]) + { + destruct_player[i].unit[j].unitType = UNIT_TANK; + destruct_player[i].unitsRemaining++; + } else { + /* Place the satellite. Note: Earlier we cleared + * space with JE_placementPosition. Now we are randomly + * placing the sat's Y. It can be generated in hills + * and there is a clearing underneath it. This CAN + * be fixed but won't be for classic. + */ + destruct_player[i].unit[j].unitY = 30 + (mt_rand() % 40); + numSatellites++; + } + } + else + { + destruct_player[i].unitsRemaining++; + } + + /* Now just fill in the rest of the unit's values. */ + destruct_player[i].unit[j].lastMove = 0; + destruct_player[i].unit[j].unitYMov = 0; + destruct_player[i].unit[j].isYInAir = false; + destruct_player[i].unit[j].angle = 0; + destruct_player[i].unit[j].power = (destruct_player[i].unit[j].unitType == UNIT_LASER) ? 6 : 3; + destruct_player[i].unit[j].shotType = defaultWeapon[destruct_player[i].unit[j].unitType]; + destruct_player[i].unit[j].health = baseDamage[destruct_player[i].unit[j].unitType]; + destruct_player[i].unit[j].ani_frame = 0; + } + } +} +static void DE_generateWalls( struct destruct_world_s * gameWorld ) +{ + unsigned int i, j, wallX; + unsigned int wallHeight, remainWalls; + unsigned int tries; + bool isGood; + + + if ((world.mapFlags & MAP_WALLS) == false) + { + /* Just clear them out */ + for (i = 0; i < config.max_walls; i++) + { + gameWorld->mapWalls[i].wallExist = false; + } + return; + } + + remainWalls = (rand() % (config.max_walls - config.min_walls + 1)) + config.min_walls; + + do { + + /* Create a wall. Decide how tall the wall will be */ + wallHeight = (mt_rand() % 5) + 1; + if(wallHeight > remainWalls) + { + wallHeight = remainWalls; + } + + /* Now find a good place to put the wall. */ + tries = 0; + do { + + isGood = true; + wallX = (mt_rand() % 300) + 10; + + /* Is this X already occupied? In the original Tyrian we only + * checked to make sure four units on each side were unobscured. + * That's not very scalable; instead I will check every unit, + * but I'll only try plotting an unobstructed X four times. + * After that we'll cover up what may; having a few units + * stuck behind walls makes things mildly interesting. + */ + for (i = 0; i < MAX_PLAYERS; i++) + { + for (j = 0; j < config.max_installations; j++) + { + if ((wallX > destruct_player[i].unit[j].unitX - 12) + && (wallX < destruct_player[i].unit[j].unitX + 13)) + { + isGood = false; + goto label_outer_break; /* I do feel that outer breaking is a legitimate goto use. */ + } + } + } + +label_outer_break: + tries++; + + } while(isGood == false && tries < 5); + + + /* We now have a valid X. Create the wall. */ + for (i = 1; i <= wallHeight; i++) + { + gameWorld->mapWalls[remainWalls - i].wallExist = true; + gameWorld->mapWalls[remainWalls - i].wallX = wallX; + gameWorld->mapWalls[remainWalls - i].wallY = JE_placementPosition(wallX, 12, gameWorld->baseMap) - 14 * i; + } + + remainWalls -= wallHeight; + + } while (remainWalls != 0); +} + +static void DE_generateRings( SDL_Surface * screen, Uint8 pixel ) +{ + unsigned int i, j, tempSize, rings; + int tempPosX1, tempPosY1, tempPosX2, tempPosY2; + float tempRadian; + + + rings = mt_rand() % 6 + 1; + for (i = 1; i <= rings; i++) + { + tempPosX1 = (mt_rand() % 320); + tempPosY1 = (mt_rand() % 160) + 20; + tempSize = (mt_rand() % 40) + 10; /*Size*/ + + for (j = 1; j <= tempSize * tempSize * 2; j++) + { + tempRadian = mt_rand_lt1() * (2 * M_PI); + tempPosY2 = tempPosY1 + roundf(cosf(tempRadian) * (mt_rand_lt1() * 0.1f + 0.9f) * tempSize); + tempPosX2 = tempPosX1 + roundf(sinf(tempRadian) * (mt_rand_lt1() * 0.1f + 0.9f) * tempSize); + if ((tempPosY2 > 12) && (tempPosY2 < 200) + && (tempPosX2 > 0) && (tempPosX2 < 319)) + { + ((Uint8 *)screen->pixels)[tempPosX2 + tempPosY2 * screen->pitch] = pixel; + } + } + } +} + +static unsigned int aliasDirtPixel(const SDL_Surface * screen, unsigned int x, unsigned int y, const Uint8 * s) { + + //A helper function used when aliasing dirt. That's a messy process; + //let's contain the mess here. + unsigned int newColor = PIXEL_BLACK; + + + if ((y > 0) && (*(s - screen->pitch) == PIXEL_DIRT)) { // look up + newColor += 1; + } + if ((y < screen->h - 1u) && (*(s + screen->pitch) == PIXEL_DIRT)) { // look down + newColor += 3; + } + if ((x > 0) && (*(s - 1) == PIXEL_DIRT)) { // look left + newColor += 2; + } + if ((x < screen->pitch - 1u) && (*(s + 1) == PIXEL_DIRT)) { // look right + newColor += 2; + } + if (newColor != PIXEL_BLACK) { + return(newColor + 16); // 16 must be the start of the brown pixels. + } + + return(PIXEL_BLACK); +} +static void JE_aliasDirt( SDL_Surface * screen ) +{ + /* This complicated looking function goes through the whole screen + * looking for brown pixels which just happen to be next to non-brown + * pixels. It's an aliaser, just like it says. */ + unsigned int x, y; + + + /* This is a pointer to a screen. If you don't like pointer arithmetic, + * you won't like this function. */ + Uint8 *s = screen->pixels; + s += 12 * screen->pitch; + + for (y = 12; y < (unsigned)screen->h; y++) { + for (x = 0; x < screen->pitch; x++) { + if (*s == PIXEL_BLACK) { + *s = aliasDirtPixel(screen, x, y, s); + } + + s++; + } + } +} + +static unsigned int JE_placementPosition( unsigned int passed_x, unsigned int width, unsigned int * world ) +{ + unsigned int i, new_y; + + + /* This is the function responsible for carving out chunks of land. + * There's a bug here, but it's a pretty major gameplay altering one: + * areas can be carved out for units that are aerial or in mountains. + * This can result in huge caverns. Ergo, it's a feature :) + * + * I wondered if it might be better to not carve out land at all. + * On testing I determined that was distracting and added nothing. */ + new_y = 0; + for (i = passed_x; i <= passed_x + width - 1; i++) + { + if (new_y < world[i]) + new_y = world[i]; + } + + for (i = passed_x; i <= passed_x + width - 1; i++) + { + world[i] = new_y; + } + + return new_y; +} + +static bool JE_stabilityCheck( unsigned int x, unsigned int y ) +{ + unsigned int i, numDirtPixels; + Uint8 * s; + + + numDirtPixels = 0; + s = destructTempScreen->pixels; + s += x + (y * destructTempScreen->pitch) - 1; + + /* Check the 12 pixels on the bottom border of our object */ + for (i = 0; i < 12; i++) + { + if (*s == PIXEL_DIRT) + numDirtPixels++; + + s++; + } + + /* If there are fewer than 10 brown pixels we don't consider it a solid base */ + return (numDirtPixels < 10); +} + +static void JE_tempScreenChecking( void ) /*and copy to vgascreen*/ +{ + Uint8 *s = VGAScreen->pixels; + s += 12 * VGAScreen->pitch; + + Uint8 *temps = destructTempScreen->pixels; + temps += 12 * destructTempScreen->pitch; + + for (int y = 12; y < VGAScreen->h; y++) + { + for (int x = 0; x < VGAScreen->pitch; x++) + { + // This block is what fades out explosions. The palette from 241 + // to 255 fades from a very dark red to a very bright yellow. + if (*temps >= 241) + { + if (*temps == 241) + *temps = PIXEL_BLACK; + else + (*temps)--; + } + + // This block is for aliasing dirt. Computers are fast these days, + // and it's fun. + if (config.alwaysalias == true && *temps == PIXEL_BLACK) { + *temps = aliasDirtPixel(VGAScreen, x, y, temps); + } + + /* This is copying from our temp screen to VGAScreen */ + *s = *temps; + + s++; + temps++; + } + } +} + +static void JE_makeExplosion( unsigned int tempPosX, unsigned int tempPosY, enum de_shot_t shottype ) +{ + unsigned int i, tempExploSize; + + + /* First find an open explosion. If we can't find one, return.*/ + for (i = 0; i < config.max_explosions; i++) + { + if (exploRec[i].isAvailable == true) + break; + } + if (i == config.max_explosions) /* No empty slots */ + { + return; + } + + + exploRec[i].isAvailable = false; + exploRec[i].x = tempPosX; + exploRec[i].y = tempPosY; + exploRec[i].explowidth = 2; + + if(shottype != SHOT_INVALID) + { + tempExploSize = exploSize[shottype]; + if (tempExploSize < 5) + JE_eSound(3); + else if (tempExploSize < 15) + JE_eSound(4); + else if (tempExploSize < 20) + JE_eSound(12); + else if (tempExploSize < 40) + JE_eSound(11); + else + { + JE_eSound(12); + JE_eSound(11); + } + + exploRec[i].explomax = tempExploSize; + exploRec[i].explofill = exploDensity[shottype]; + exploRec[i].exploType = shotDirt[shottype]; + } + else + { + JE_eSound(4); + exploRec[i].explomax = (mt_rand() % 40) + 10; + exploRec[i].explofill = (mt_rand() % 60) + 20; + exploRec[i].exploType = EXPL_NORMAL; + } +} + +static void JE_eSound( unsigned int sound ) +{ + static int exploSoundChannel = 0; + + if (++exploSoundChannel > 5) + { + exploSoundChannel = 1; + } + + soundQueue[exploSoundChannel] = sound; +} + +static void JE_superPixel( unsigned int tempPosX, unsigned int tempPosY ) +{ + const unsigned int starPattern[5][5] = { + { 0, 0, 246, 0, 0 }, + { 0, 247, 249, 247, 0 }, + { 246, 249, 252, 249, 246 }, + { 0, 247, 249, 247, 0 }, + { 0, 0, 246, 0, 0 } + }; + const unsigned int starIntensity[5][5] = { + { 0, 0, 1, 0, 0 }, + { 0, 1, 2, 1, 0 }, + { 1, 2, 4, 2, 1 }, + { 0, 1, 2, 1, 0 }, + { 0, 0, 1, 0, 0 } + }; + + int x, y, maxX, maxY; + unsigned int rowLen; + Uint8 *s; + + + maxX = destructTempScreen->pitch; + maxY = destructTempScreen->h; + + rowLen = destructTempScreen->pitch; + s = destructTempScreen->pixels; + s += (rowLen * (tempPosY - 2)) + (tempPosX - 2); + + for (y = 0; y < 5; y++, s += rowLen - 5) + { + if ((signed)tempPosY + y - 2 < 0 /* would be out of bounds */ + || (signed)tempPosY + y - 2 >= maxY) { continue; } + + for (x = 0; x < 5; x++, s++) + { + if ((signed)tempPosX + x - 2 < 0 + || (signed)tempPosX + x - 2 >= maxX) { continue; } + + if (starPattern[y][x] == 0) { continue; } /* this is just to speed it up */ + + /* at this point *s is our pixel. Our constant arrays tell us what + * to do with it. */ + if (*s < starPattern[y][x]) + { + *s = starPattern[y][x]; + } + else if (*s + starIntensity[y][x] > 255) + { + *s = 255; + } + else + { + *s += starIntensity[y][x]; + } + } + } +} + +static void JE_helpScreen( void ) +{ + unsigned int i, j; + + + //JE_getVGA(); didn't do anything anyway? + fade_black(15); + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch); + JE_clr256(VGAScreen); + + for(i = 0; i < 2; i++) + { + JE_outText(VGAScreen, 100, 5 + i * 90, destructHelp[i * 12 + 0], 2, 4); + JE_outText(VGAScreen, 100, 15 + i * 90, destructHelp[i * 12 + 1], 2, 1); + for (j = 3; j <= 12; j++) + { + JE_outText(VGAScreen, ((j - 1) % 2) * 160 + 10, 15 + ((j - 1) / 2) * 12 + i * 90, destructHelp[i * 12 + j-1], 1, 3); + } + } + JE_outText(VGAScreen, 30, 190, destructHelp[24], 3, 4); + JE_showVGA(); + fade_palette(colors, 15, 0, 255); + + do /* wait until user hits a key */ + { + service_SDL_events(true); + uSDL_Delay(16); + } + while (!newkey); + + fade_black(15); + memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch); + JE_showVGA(); + fade_palette(colors, 15, 0, 255); +} + + +static void JE_pauseScreen( void ) +{ + set_volume(tyrMusicVolume / 2, fxVolume); + + /* Save our current screen/game world. We don't want to screw it up while paused. */ + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->h * VGAScreen2->pitch); + JE_outText(VGAScreen, JE_fontCenter(miscText[22], TINY_FONT), 90, miscText[22], 12, 5); + JE_showVGA(); + + do /* wait until user hits a key */ + { + service_SDL_events(true); + uSDL_Delay(16); + } + while (!newkey); + + /* Restore current screen & volume*/ + memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->h * VGAScreen->pitch); + JE_showVGA(); + + set_volume(tyrMusicVolume, fxVolume); +} + +/* DE_ResetX + * + * The reset functions clear the state of whatefer they are assigned to. + */ +static void DE_ResetUnits( void ) +{ + unsigned int p, u; + + + for (p = 0; p < MAX_PLAYERS; ++p) + for (u = 0; u < config.max_installations; ++u) + destruct_player[p].unit[u].health = 0; +} +static void DE_ResetPlayers( void ) +{ + unsigned int i; + + + for (i = 0; i < MAX_PLAYERS; ++i) + { + destruct_player[i].is_cpu = false; + destruct_player[i].unitSelected = 0; + destruct_player[i].shotDelay = 0; + destruct_player[i].score = 0; + destruct_player[i].aiMemory.c_Angle = 0; + destruct_player[i].aiMemory.c_Power = 0; + destruct_player[i].aiMemory.c_Fire = 0; + destruct_player[i].aiMemory.c_noDown = 0; + memcpy(destruct_player[i].keys.Config, defaultKeyConfig[i], sizeof(destruct_player[i].keys.Config)); + } +} +static void DE_ResetWeapons( void ) +{ + unsigned int i; + + + for (i = 0; i < config.max_shots; i++) + shotRec[i].isAvailable = true; + + for (i = 0; i < config.max_explosions; i++) + exploRec[i].isAvailable = true; +} +static void DE_ResetLevel( void ) +{ + /* Okay, let's prep the arena */ + + DE_ResetWeapons(); + + JE_generateTerrain(); + DE_ResetAI(); +} +static void DE_ResetAI( void ) +{ + unsigned int i, j; + struct destruct_unit_s * ptr; + + + for (i = PLAYER_LEFT; i < MAX_PLAYERS; i++) + { + if (destruct_player[i].is_cpu == false) { continue; } + ptr = destruct_player[i].unit; + + for( j = 0; j < config.max_installations; j++, ptr++) + { + if(DE_isValidUnit(ptr) == false) + continue; + + if (systemAngle[ptr->unitType] || ptr->unitType == UNIT_HELI) + ptr->angle = M_PI_4; + else + ptr->angle = 0; + + ptr->power = (ptr->unitType == UNIT_LASER) ? 6 : 4; + + if (world.mapFlags & MAP_WALLS) + ptr->shotType = defaultCpuWeaponB[ptr->unitType]; + else + ptr->shotType = defaultCpuWeapon[ptr->unitType]; + } + } +} +static void DE_ResetActions( void ) +{ + unsigned int i; + + + for(i = 0; i < MAX_PLAYERS; i++) + { /* Zero it all. A memset would do the trick */ + memset(&(destruct_player[i].moves), 0, sizeof(destruct_player[i].moves)); + } +} +/* DE_RunTick + * + * Runs one tick. One tick involves handling physics, drawing crap, + * moving projectiles and explosions, and getting input. + * Returns true while the game is running or false if the game is + * to be terminated. + */ +static enum de_state_t DE_RunTick( void ) +{ + static unsigned int endDelay; + + + setjasondelay(1); + + memset(soundQueue, 0, sizeof(soundQueue)); + JE_tempScreenChecking(); + + DE_ResetActions(); + DE_RunTickCycleDeadUnits(); + + + DE_RunTickGravity(); + DE_RunTickAnimate(); + DE_RunTickDrawWalls(); + DE_RunTickExplosions(); + DE_RunTickShots(); + DE_RunTickAI(); + DE_RunTickDrawCrosshairs(); + DE_RunTickDrawHUD(); + JE_showVGA(); + + if (destructFirstTime) + { + fade_palette(colors, 25, 0, 255); + destructFirstTime = false; + endDelay = 0; + } + + DE_RunTickGetInput(); + DE_ProcessInput(); + + if (endDelay > 0) + { + if(--endDelay == 0) + { + return(STATE_RELOAD); + } + } + else if ( DE_RunTickCheckEndgame() == true) + { + endDelay = 80; + } + + DE_RunTickPlaySounds(); + + /* The rest of this cruft needs to be put in appropriate sections */ + if (keysactive[SDLK_F10]) + { + destruct_player[PLAYER_LEFT].is_cpu = !destruct_player[PLAYER_LEFT].is_cpu; + keysactive[SDLK_F10] = false; + } + if (keysactive[SDLK_F11]) + { + destruct_player[PLAYER_RIGHT].is_cpu = !destruct_player[PLAYER_RIGHT].is_cpu; + keysactive[SDLK_F11] = false; + } + if (keysactive[SDLK_p]) + { + JE_pauseScreen(); + keysactive[lastkey_sym] = false; + } + + if (keysactive[SDLK_F1]) + { + JE_helpScreen(); + keysactive[lastkey_sym] = false; + } + + wait_delay(); + + if (keysactive[SDLK_ESCAPE]) + { + keysactive[SDLK_ESCAPE] = false; + return(STATE_INIT); /* STATE_INIT drops us to the mode select */ + } + + if (keysactive[SDLK_BACKSPACE]) + { + keysactive[SDLK_BACKSPACE] = false; + return(STATE_RELOAD); /* STATE_RELOAD creates a new map */ + } + + return(STATE_CONTINUE); +} + +/* DE_RunTickX + * + * Handles something that we do once per tick, such as + * track ammo and move asplosions. + */ +static void DE_RunTickCycleDeadUnits( void ) +{ + unsigned int i; + struct destruct_unit_s * unit; + + + /* This code automatically switches the active unit if it is destroyed + * and skips over the useless satellite */ + for (i = 0; i < MAX_PLAYERS; i++) + { + if (destruct_player[i].unitsRemaining == 0) { continue; } + + unit = &(destruct_player[i].unit[destruct_player[i].unitSelected]); + while(DE_isValidUnit(unit) == false + || unit->shotType == SHOT_INVALID) + { + destruct_player[i].unitSelected++; + unit++; + if (destruct_player[i].unitSelected >= config.max_installations) + { + destruct_player[i].unitSelected = 0; + unit = destruct_player[i].unit; + } + } + } +} +static void DE_RunTickGravity( void ) +{ + unsigned int i, j; + struct destruct_unit_s * unit; + + + for (i = 0; i < MAX_PLAYERS; i++) + { + + unit = destruct_player[i].unit; + for (j = 0; j < config.max_installations; j++, unit++) + { + if (DE_isValidUnit(unit) == false) /* invalid unit */ + continue; + + switch(unit->unitType) + { + case UNIT_SATELLITE: /* satellites don't fall down */ + break; + + case UNIT_HELI: + case UNIT_JUMPER: + if (unit->isYInAir == true) /* unit is falling down, at least in theory */ + { + DE_GravityFlyUnit(unit); + break; + } + /* else treat as a normal unit */ + /* fall through */ + default: + DE_GravityLowerUnit(unit); + } + + /* Draw the unit. */ + DE_GravityDrawUnit(i, unit); + } + } +} +static void DE_GravityDrawUnit( enum de_player_t team, struct destruct_unit_s * unit ) +{ + unsigned int anim_index; + + + anim_index = GraphicBase[team][unit->unitType] + unit->ani_frame; + if (unit->unitType == UNIT_HELI) + { + /* Adjust animation index if we are travelling right or left. */ + if (unit->lastMove < -2) + anim_index += 5; + else if (unit->lastMove > 2) + anim_index += 10; + } + else /* This handles our cannons and the like */ + { + anim_index += floorf(unit->angle * 9.99f / M_PI); + } + + blit_sprite2(VGAScreen, unit->unitX, roundf(unit->unitY) - 13, eShapes[0], anim_index); +} +static void DE_GravityLowerUnit( struct destruct_unit_s * unit ) +{ + /* units fall at a constant speed. The heli is an odd case though; + * we simply give it a downward velocity, but due to a buggy implementation + * the chopper didn't lower until you tried to fly it up. Tyrian 2000 fixes + * this by not making the chopper a special case. I've decided to actually + * mix both; the chopper is given a slight downward acceleration (simulating + * a 'rocky' takeoff), and it is lowered like a regular unit, but not as + * quickly. + */ + if(unit->unitY < 199) { /* checking takes time, don't check if it's at the bottom */ + if (JE_stabilityCheck(unit->unitX, roundf(unit->unitY))) + { + switch(unit->unitType) + { + case UNIT_HELI: + unit->unitYMov = 1.5f; + unit->unitY += 0.2f; + break; + + default: + unit->unitY += 1; + } + + if (unit->unitY > 199) /* could be possible */ + unit->unitY = 199; + } + } +} +static void DE_GravityFlyUnit( struct destruct_unit_s * unit ) +{ + if (unit->unitY + unit->unitYMov > 199) /* would hit bottom of screen */ + { + unit->unitY = 199; + unit->unitYMov = 0; + unit->isYInAir = false; + return; + } + + /* move the unit and alter acceleration */ + unit->unitY += unit->unitYMov; + if (unit->unitY < 24) /* This stops units from going above the screen */ + { + unit->unitYMov = 0; + unit->unitY = 24; + } + + if (unit->unitType == UNIT_HELI) /* helicopters fall more slowly */ + unit->unitYMov += 0.0001f; + else + unit->unitYMov += 0.03f; + + if (!JE_stabilityCheck(unit->unitX, roundf(unit->unitY))) + { + unit->unitYMov = 0; + unit->isYInAir = false; + } +} +static void DE_RunTickAnimate( void ) +{ + unsigned int p, u; + struct destruct_unit_s * ptr; + + + for (p = 0; p < MAX_PLAYERS; ++p) + { + ptr = destruct_player[p].unit; + for (u = 0; u < config.max_installations; ++u, ++ptr) + { + /* Don't mess with any unit that is unallocated + * or doesn't animate and is set to frame 0 */ + if(DE_isValidUnit(ptr) == false) { continue; } + if(systemAni[ptr->unitType] == false && ptr->ani_frame == 0) { continue; } + + if (++(ptr->ani_frame) > 3) + { + ptr->ani_frame = 0; + } + } + } +} +static void DE_RunTickDrawWalls( void ) +{ + unsigned int i; + + + for (i = 0; i < config.max_walls; i++) + { + if (world.mapWalls[i].wallExist) + { + blit_sprite2(VGAScreen, world.mapWalls[i].wallX, world.mapWalls[i].wallY, eShapes[0], 42); + } + } +} +static void DE_RunTickExplosions( void ) +{ + unsigned int i, j; + int tempPosX, tempPosY; + float tempRadian; + + + /* Run through all open explosions. They are not sorted in any way */ + for (i = 0; i < config.max_explosions; i++) + { + if (exploRec[i].isAvailable == true) { continue; } /* Nothing to do */ + + for (j = 0; j < exploRec[i].explofill; j++) + { + /* An explosion is comprised of multiple 'flares' that fan out. + Calculate where this 'flare' will end up */ + tempRadian = mt_rand_lt1() * (2 * M_PI); + tempPosY = exploRec[i].y + roundf(cosf(tempRadian) * mt_rand_lt1() * exploRec[i].explowidth); + tempPosX = exploRec[i].x + roundf(sinf(tempRadian) * mt_rand_lt1() * exploRec[i].explowidth); + + /* Our game allows explosions to wrap around. This looks to have + * originally been a bug that was left in as being fun, but we are + * going to replicate it w/o risking out of bound arrays. */ + + while(tempPosX < 0) { tempPosX += 320; } + while(tempPosX > 320) { tempPosX -= 320; } + + /* We don't draw our explosion if it's out of bounds vertically */ + if (tempPosY >= 200 || tempPosY <= 15) { continue; } + + /* And now the drawing. There are only two types of explosions + * right now; dirt and flares. Dirt simply draws a brown pixel; + * flares explode and have a star formation. */ + switch(exploRec[i].exploType) + { + case EXPL_DIRT: + ((Uint8 *)destructTempScreen->pixels)[tempPosX + tempPosY * destructTempScreen->pitch] = PIXEL_DIRT; + break; + + case EXPL_NORMAL: + JE_superPixel(tempPosX, tempPosY); + DE_TestExplosionCollision(tempPosX, tempPosY); + break; + + default: + assert(false); + break; + } + } + + /* Widen the explosion and delete it if necessary. */ + exploRec[i].explowidth++; + if (exploRec[i].explowidth == exploRec[i].explomax) + { + exploRec[i].isAvailable = true; + } + } +} +static void DE_TestExplosionCollision( unsigned int PosX, unsigned int PosY) +{ + unsigned int i, j; + struct destruct_unit_s * unit; + + + for (i = PLAYER_LEFT; i < MAX_PLAYERS; i++) + { + unit = destruct_player[i].unit; + for (j = 0; j < config.max_installations; j++, unit++) + { + if (DE_isValidUnit(unit) == true + && PosX > unit->unitX && PosX < unit->unitX + 11 + && PosY < unit->unitY && PosY > unit->unitY - 11) + { + unit->health--; + if (unit->health <= 0) + { + DE_DestroyUnit(i, unit); + } + } + } + } +} +static void DE_DestroyUnit( enum de_player_t playerID, struct destruct_unit_s * unit ) +{ + /* This function call was an evil evil piece of brilliance before. Go on. + * Look at the older revisions. It passed the result of a comparison. + * MULTIPLIED. This is at least a little clearer... */ + JE_makeExplosion(unit->unitX + 5, roundf(unit->unitY) - 5, (unit->unitType == UNIT_HELI) ? SHOT_SMALL : SHOT_INVALID); /* Helicopters explode like small shots do. Invalids are their own special case. */ + + if (unit->unitType != UNIT_SATELLITE) /* increment score */ + { /* todo: change when teams are created. Hacky kludge for now.*/ + destruct_player[playerID].unitsRemaining--; + destruct_player[((playerID == PLAYER_LEFT) ? PLAYER_RIGHT : PLAYER_LEFT)].score++; + } +} + +static void DE_RunTickShots( void ) +{ + unsigned int i, j, k; + unsigned int tempTrails; + unsigned int tempPosX, tempPosY; + struct destruct_unit_s * unit; + + + for (i = 0; i < config.max_shots; i++) + { + if (shotRec[i].isAvailable == true) { continue; } /* Nothing to do */ + + /* Move the shot. Simple displacement */ + shotRec[i].x += shotRec[i].xmov; + shotRec[i].y += shotRec[i].ymov; + + /* If the shot can bounce off the map, bounce it */ + if (shotBounce[shotRec[i].shottype]) + { + if (shotRec[i].y > 199 || shotRec[i].y < 14) + { + shotRec[i].y -= shotRec[i].ymov; + shotRec[i].ymov = -shotRec[i].ymov; + } + if (shotRec[i].x < 1 || shotRec[i].x > 318) + { + shotRec[i].x -= shotRec[i].xmov; + shotRec[i].xmov = -shotRec[i].xmov; + } + } + else /* If it cannot, apply normal physics */ + { + shotRec[i].ymov += 0.05f; /* add gravity */ + + if (shotRec[i].y > 199) /* We hit the floor */ + { + shotRec[i].y -= shotRec[i].ymov; + shotRec[i].ymov = -shotRec[i].ymov * 0.8f; /* bounce at reduced velocity */ + + /* Don't allow a bouncing shot to bounce straight up and down */ + if (shotRec[i].xmov == 0) + { + shotRec[i].xmov += mt_rand_lt1() - 0.5f; + } + } + } + + /* Shot has gone out of bounds. Eliminate it. */ + if (shotRec[i].x > 318 || shotRec[i].x < 1) + { + shotRec[i].isAvailable = true; + continue; + } + + /* Now check for collisions. */ + + /* Don't bother checking for collisions above the map :) */ + if (shotRec[i].y <= 14) + continue; + + tempPosX = roundf(shotRec[i].x); + tempPosY = roundf(shotRec[i].y); + + /*Check building hits*/ + for(j = 0; j < MAX_PLAYERS; j++) + { + unit = destruct_player[j].unit; + for(k = 0; k < config.max_installations; k++, unit++) + { + if (DE_isValidUnit(unit) == false) + continue; + + if (tempPosX > unit->unitX && tempPosX < unit->unitX + 11 + && tempPosY < unit->unitY && tempPosY > unit->unitY - 13) + { + shotRec[i].isAvailable = true; + JE_makeExplosion(tempPosX, tempPosY, shotRec[i].shottype); + } + } + } + + tempTrails = (shotColor[shotRec[i].shottype] << 4) - 3; + JE_pixCool(tempPosX, tempPosY, tempTrails); + + /*Draw the shot trail (if applicable) */ + switch (shotTrail[shotRec[i].shottype]) + { + case TRAILS_NONE: + break; + case TRAILS_NORMAL: + DE_DrawTrails( &(shotRec[i]), 2, 4, tempTrails - 3 ); + break; + case TRAILS_FULL: + DE_DrawTrails( &(shotRec[i]), 4, 3, tempTrails - 1 ); + break; + } + + /* Bounce off of or destroy walls */ + for (j = 0; j < config.max_walls; j++) + { + if (world.mapWalls[j].wallExist == true + && tempPosX >= world.mapWalls[j].wallX && tempPosX <= world.mapWalls[j].wallX + 11 + && tempPosY >= world.mapWalls[j].wallY && tempPosY <= world.mapWalls[j].wallY + 14) + { + if (demolish[shotRec[i].shottype]) + { + /* Blow up the wall and remove the shot. */ + world.mapWalls[j].wallExist = false; + shotRec[i].isAvailable = true; + JE_makeExplosion(tempPosX, tempPosY, shotRec[i].shottype); + continue; + } + else + { + /* Otherwise, bounce. */ + if (shotRec[i].x - shotRec[i].xmov < world.mapWalls[j].wallX + || shotRec[i].x - shotRec[i].xmov > world.mapWalls[j].wallX + 11) + { + shotRec[i].xmov = -shotRec[i].xmov; + } + if (shotRec[i].y - shotRec[i].ymov < world.mapWalls[j].wallY + || shotRec[i].y - shotRec[i].ymov > world.mapWalls[j].wallY + 14) + { + if (shotRec[i].ymov < 0) + shotRec[i].ymov = -shotRec[i].ymov; + else + shotRec[i].ymov = -shotRec[i].ymov * 0.8f; + } + + tempPosX = roundf(shotRec[i].x); + tempPosY = roundf(shotRec[i].y); + } + } + } + + /* Our last collision check, at least for now. We hit dirt. */ + if((((Uint8 *)destructTempScreen->pixels)[tempPosX + tempPosY * destructTempScreen->pitch]) == PIXEL_DIRT) + { + shotRec[i].isAvailable = true; + JE_makeExplosion(tempPosX, tempPosY, shotRec[i].shottype); + continue; + } + } +} +static void DE_DrawTrails( struct destruct_shot_s * shot, unsigned int count, unsigned int decay, unsigned int startColor ) +{ + int i; + + + for (i = count-1; i >= 0; i--) /* going in reverse is important as it affects how we draw */ + { + if (shot->trailc[i] > 0 && shot->traily[i] > 12) /* If it exists and if it's not out of bounds, draw it. */ + { + JE_pixCool(shot->trailx[i], shot->traily[i], shot->trailc[i]); + } + + if (i == 0) /* The first trail we create. */ + { + shot->trailx[i] = roundf(shot->x); + shot->traily[i] = roundf(shot->y); + shot->trailc[i] = startColor; + } + else /* The newer trails decay into the older trails.*/ + { + shot->trailx[i] = shot->trailx[i-1]; + shot->traily[i] = shot->traily[i-1]; + if (shot->trailc[i-1] > 0) + { + shot->trailc[i] = shot->trailc[i-1] - decay; + } + } + } +} +static void DE_RunTickAI( void ) +{ + unsigned int i, j; + struct destruct_player_s * ptrPlayer, * ptrTarget; + struct destruct_unit_s * ptrUnit, * ptrCurUnit; + + + for (i = 0; i < MAX_PLAYERS; i++) + { + ptrPlayer = &(destruct_player[i]); + if (ptrPlayer->is_cpu == false) + { + continue; + } + + + /* I've been thinking, purely hypothetically, about what it would take + * to have multiple computer opponents. The answer? A lot of crap + * and a 'target' variable in the destruct_player struct. */ + j = i + 1; + if (j >= MAX_PLAYERS) + { + j = 0; + } + + ptrTarget = &(destruct_player[j]); + ptrCurUnit = &(ptrPlayer->unit[ptrPlayer->unitSelected]); + + + /* This is the start of the original AI. Heh. AI. */ + + if (ptrPlayer->aiMemory.c_noDown > 0) + ptrPlayer->aiMemory.c_noDown--; + + /* Until all structs are properly divvied up this must only apply to player1 */ + if (mt_rand() % 100 > 80) + { + ptrPlayer->aiMemory.c_Angle += (mt_rand() % 3) - 1; + + if (ptrPlayer->aiMemory.c_Angle > 1) + ptrPlayer->aiMemory.c_Angle = 1; + else + if (ptrPlayer->aiMemory.c_Angle < -1) + ptrPlayer->aiMemory.c_Angle = -1; + } + if (mt_rand() % 100 > 90) + { + if (ptrPlayer->aiMemory.c_Angle > 0 && ptrCurUnit->angle > (M_PI_2) - (M_PI / 9)) + ptrPlayer->aiMemory.c_Angle = 0; + else + if (ptrPlayer->aiMemory.c_Angle < 0 && ptrCurUnit->angle < M_PI / 8) + ptrPlayer->aiMemory.c_Angle = 0; + } + + if (mt_rand() % 100 > 93) + { + ptrPlayer->aiMemory.c_Power += (mt_rand() % 3) - 1; + + if (ptrPlayer->aiMemory.c_Power > 1) + ptrPlayer->aiMemory.c_Power = 1; + else + if (ptrPlayer->aiMemory.c_Power < -1) + ptrPlayer->aiMemory.c_Power = -1; + } + if (mt_rand() % 100 > 90) + { + if (ptrPlayer->aiMemory.c_Power > 0 && ptrCurUnit->power > 4) + ptrPlayer->aiMemory.c_Power = 0; + else + if (ptrPlayer->aiMemory.c_Power < 0 && ptrCurUnit->power < 3) + ptrPlayer->aiMemory.c_Power = 0; + else + if (ptrCurUnit->power < 2) + ptrPlayer->aiMemory.c_Power = 1; + } + + // prefer helicopter + ptrUnit = ptrPlayer->unit; + for (j = 0; j < config.max_installations; j++, ptrUnit++) + { + if (DE_isValidUnit(ptrUnit) && ptrUnit->unitType == UNIT_HELI) + { + ptrPlayer->unitSelected = j; + break; + } + } + + if (ptrCurUnit->unitType == UNIT_HELI) + { + if (ptrCurUnit->isYInAir == false) + { + ptrPlayer->aiMemory.c_Power = 1; + } + if (mt_rand() % ptrCurUnit->unitX > 100) + { + ptrPlayer->aiMemory.c_Power = 1; + } + if (mt_rand() % 240 > ptrCurUnit->unitX) + { + ptrPlayer->moves.actions[MOVE_RIGHT] = true; + } + else if ((mt_rand() % 20) + 300 < ptrCurUnit->unitX) + { + ptrPlayer->moves.actions[MOVE_LEFT] = true; + } + else if (mt_rand() % 30 == 1) + { + ptrPlayer->aiMemory.c_Angle = (mt_rand() % 3) - 1; + } + if (ptrCurUnit->unitX > 295 && ptrCurUnit->lastMove > 1) + { + ptrPlayer->moves.actions[MOVE_LEFT] = true; + ptrPlayer->moves.actions[MOVE_RIGHT] = false; + } + if (ptrCurUnit->unitType != UNIT_HELI || ptrCurUnit->lastMove > 3 || (ptrCurUnit->unitX > 160 && ptrCurUnit->lastMove > -3)) + { + if (mt_rand() % (int)roundf(ptrCurUnit->unitY) < 150 && ptrCurUnit->unitYMov < 0.01f && (ptrCurUnit->unitX < 160 || ptrCurUnit->lastMove < 2)) + { + ptrPlayer->moves.actions[MOVE_FIRE] = true; + } + ptrPlayer->aiMemory.c_noDown = (5 - abs(ptrCurUnit->lastMove)) * (5 - abs(ptrCurUnit->lastMove)) + 3; + ptrPlayer->aiMemory.c_Power = 1; + } else { + ptrPlayer->moves.actions[MOVE_FIRE] = false; + } + + ptrUnit = ptrTarget->unit; + for (j = 0; j < config.max_installations; j++, ptrUnit++) + { + if (abs(ptrUnit->unitX - ptrCurUnit->unitX) < 8) + { + /* I get it. This makes helicoptors hover over + * their enemies. */ + if (ptrUnit->unitType == UNIT_SATELLITE) + { + ptrPlayer->moves.actions[MOVE_FIRE] = false; + } + else + { + ptrPlayer->moves.actions[MOVE_LEFT] = false; + ptrPlayer->moves.actions[MOVE_RIGHT] = false; + if (ptrCurUnit->lastMove < -1) + { + ptrCurUnit->lastMove++; + } + else if (ptrCurUnit->lastMove > 1) + { + ptrCurUnit->lastMove--; + } + } + } + } + } else { + ptrPlayer->moves.actions[MOVE_FIRE] = 1; + } + + if (mt_rand() % 200 > 198) + { + ptrPlayer->moves.actions[MOVE_CHANGE] = true; + ptrPlayer->aiMemory.c_Angle = 0; + ptrPlayer->aiMemory.c_Power = 0; + ptrPlayer->aiMemory.c_Fire = 0; + } + + if (mt_rand() % 100 > 98 || ptrCurUnit->shotType == SHOT_TRACER) + { /* Clearly the CPU doesn't like the tracer :) */ + ptrPlayer->moves.actions[MOVE_CYDN] = true; + } + if (ptrPlayer->aiMemory.c_Angle > 0) + { + ptrPlayer->moves.actions[MOVE_LEFT] = true; + } + if (ptrPlayer->aiMemory.c_Angle < 0) + { + ptrPlayer->moves.actions[MOVE_RIGHT] = true; + } + if (ptrPlayer->aiMemory.c_Power > 0) + { + ptrPlayer->moves.actions[MOVE_UP] = true; + } + if (ptrPlayer->aiMemory.c_Power < 0 && ptrPlayer->aiMemory.c_noDown == 0) + { + ptrPlayer->moves.actions[MOVE_DOWN] = true; + } + if (ptrPlayer->aiMemory.c_Fire > 0) + { + ptrPlayer->moves.actions[MOVE_FIRE] = true; + } + + if (ptrCurUnit->unitYMov < -0.1f && ptrCurUnit->unitType == UNIT_HELI) + { + ptrPlayer->moves.actions[MOVE_FIRE] = false; + } + + /* This last hack was down in the processing section. + * What exactly it was doing there I do not know */ + if(ptrCurUnit->unitType == UNIT_LASER || ptrCurUnit->isYInAir == true) { + ptrPlayer->aiMemory.c_Power = 0; + } + } +} +static void DE_RunTickDrawCrosshairs( void ) +{ + unsigned int i; + int tempPosX, tempPosY; + int direction; + struct destruct_unit_s * curUnit; + + + /* Draw the crosshairs. Most vehicles aim left or right. Helis can aim + * either way and this must be accounted for. + */ + for (i = 0; i < MAX_PLAYERS; i++) + { + direction = (i == PLAYER_LEFT) ? -1 : 1; + curUnit = &(destruct_player[i].unit[destruct_player[i].unitSelected]); + + if (curUnit->unitType == UNIT_HELI) + { + tempPosX = curUnit->unitX + roundf(0.1f * curUnit->lastMove * curUnit->lastMove * curUnit->lastMove) + 5; + tempPosY = roundf(curUnit->unitY) + 1; + } else { + tempPosX = roundf(curUnit->unitX + 6 - cosf(curUnit->angle) * (curUnit->power * 8 + 7) * direction); + tempPosY = roundf(curUnit->unitY - 7 - sinf(curUnit->angle) * (curUnit->power * 8 + 7)); + } + + /* Draw it. Clip away from the HUD though. */ + if(tempPosY > 9) + { + if(tempPosY > 11) + { + if(tempPosY > 13) + { + /* Top pixel */ + JE_pix(VGAScreen, tempPosX, tempPosY - 2, 3); + } + /* Middle three pixels */ + JE_pix(VGAScreen, tempPosX + 3, tempPosY, 3); + JE_pix(VGAScreen, tempPosX, tempPosY, 14); + JE_pix(VGAScreen, tempPosX - 3, tempPosY, 3); + } + /* Bottom pixel */ + JE_pix(VGAScreen, tempPosX, tempPosY + 2, 3); + } + } +} +static void DE_RunTickDrawHUD( void ) +{ + unsigned int i; + unsigned int startX; + char tempstr[16]; /* Max size needed: 16 assuming 10 digit int max. */ + struct destruct_unit_s * curUnit; + + + for (i = 0; i < MAX_PLAYERS; i++) + { + curUnit = &(destruct_player[i].unit[destruct_player[i].unitSelected]); + startX = ((i == PLAYER_LEFT) ? 0 : 320 - 150); + + fill_rectangle_xy(VGAScreen, startX + 5, 3, startX + 14, 8, 241); + JE_rectangle(VGAScreen, startX + 4, 2, startX + 15, 9, 242); + JE_rectangle(VGAScreen, startX + 3, 1, startX + 16, 10, 240); + fill_rectangle_xy(VGAScreen, startX + 18, 3, startX + 140, 8, 241); + JE_rectangle(VGAScreen, startX + 17, 2, startX + 143, 9, 242); + JE_rectangle(VGAScreen, startX + 16, 1, startX + 144, 10, 240); + + blit_sprite2(VGAScreen, startX + 4, 0, eShapes[0], 191 + curUnit->shotType); + + JE_outText (VGAScreen, startX + 20, 3, weaponNames[curUnit->shotType], 15, 2); + sprintf (tempstr, "dmg~%d~", curUnit->health); + JE_outText (VGAScreen, startX + 75, 3, tempstr, 15, 0); + sprintf (tempstr, "pts~%d~", destruct_player[i].score); + JE_outText (VGAScreen, startX + 110, 3, tempstr, 15, 0); + } +} +static void DE_RunTickGetInput( void ) +{ + unsigned int player_index, key_index, slot_index; + SDLKey key; + + /* destruct_player.keys holds our key config. Players will eventually be + * allowed to can change their key mappings. destruct_player.moves and + * destruct_player.keys line up; rather than manually checking left and + * right we can just loop through the indexes and set the actions as + * needed. */ + service_SDL_events(true); + + for(player_index = 0; player_index < MAX_PLAYERS; player_index++) + { + for(key_index = 0; key_index < MAX_KEY; key_index++) + { + for(slot_index = 0; slot_index < MAX_KEY_OPTIONS; slot_index++) + { + key = destruct_player[player_index].keys.Config[key_index][slot_index]; + if(key == SDLK_UNKNOWN) { break; } + if(keysactive[key] == true) + { + /* The right key was clearly pressed */ + destruct_player[player_index].moves.actions[key_index] = true; + + /* Some keys we want to toggle afterwards */ + if(key_index == KEY_CHANGE || + key_index == KEY_CYUP || + key_index == KEY_CYDN) + { + keysactive[key] = false; + } + break; + } + } + } + } +} +static void DE_ProcessInput( void ) +{ + int direction; + + unsigned int player_index; + struct destruct_unit_s * curUnit; + + + for (player_index = 0; player_index < MAX_PLAYERS; player_index++) + { + if (destruct_player[player_index].unitsRemaining <= 0) { continue; } + + direction = (player_index == PLAYER_LEFT) ? -1 : 1; + curUnit = &(destruct_player[player_index].unit[destruct_player[player_index].unitSelected]); + + if (systemAngle[curUnit->unitType] == true) /* selected unit may change shot angle */ + { + if (destruct_player[player_index].moves.actions[MOVE_LEFT] == true) + { + (player_index == PLAYER_LEFT) ? DE_RaiseAngle(curUnit) : DE_LowerAngle(curUnit); + } + if (destruct_player[player_index].moves.actions[MOVE_RIGHT] == true) + { + (player_index == PLAYER_LEFT) ? DE_LowerAngle(curUnit) : DE_RaiseAngle(curUnit); + + } + } else if (curUnit->unitType == UNIT_HELI) { + if (destruct_player[player_index].moves.actions[MOVE_LEFT] == true && curUnit->unitX > 5) + if (JE_stabilityCheck(curUnit->unitX - 5, roundf(curUnit->unitY))) + { + if (curUnit->lastMove > -5) + { + curUnit->lastMove--; + } + curUnit->unitX--; + if (JE_stabilityCheck(curUnit->unitX, roundf(curUnit->unitY))) + { + curUnit->isYInAir = true; + } + } + if (destruct_player[player_index].moves.actions[MOVE_RIGHT] == true && curUnit->unitX < 305) + { + if (JE_stabilityCheck(curUnit->unitX + 5, roundf(curUnit->unitY))) + { + if (curUnit->lastMove < 5) + { + curUnit->lastMove++; + } + curUnit->unitX++; + if (JE_stabilityCheck(curUnit->unitX, roundf(curUnit->unitY))) + { + curUnit->isYInAir = true; + } + } + } + } + + if (curUnit->unitType != UNIT_LASER) + + { /*increasepower*/ + if (destruct_player[player_index].moves.actions[MOVE_UP] == true) + { + if (curUnit->unitType == UNIT_HELI) + { + curUnit->isYInAir = true; + curUnit->unitYMov -= 0.1f; + } + else if (curUnit->unitType == UNIT_JUMPER + && curUnit->isYInAir == false) { + curUnit->unitYMov = -3; + curUnit->isYInAir = true; + } + else { + DE_RaisePower(curUnit); + } + } + /*decreasepower*/ + if (destruct_player[player_index].moves.actions[MOVE_DOWN] == true) + { + if (curUnit->unitType == UNIT_HELI && curUnit->isYInAir == true) + { + curUnit->unitYMov += 0.1f; + } else { + DE_LowerPower(curUnit); + } + } + } + + /*up/down weapon. These just cycle until a valid weapon is found */ + if (destruct_player[player_index].moves.actions[MOVE_CYUP] == true) + { + DE_CycleWeaponUp(curUnit); + } + if (destruct_player[player_index].moves.actions[MOVE_CYDN] == true) + { + DE_CycleWeaponDown(curUnit); + } + + /* Change. Since change would change out curUnit pointer, let's just do it last. + * Validity checking is performed at the beginning of the tick. */ + if (destruct_player[player_index].moves.actions[MOVE_CHANGE] == true) + { + destruct_player[player_index].unitSelected++; + if (destruct_player[player_index].unitSelected >= config.max_installations) + { + destruct_player[player_index].unitSelected = 0; + } + } + + /*Newshot*/ + if (destruct_player[player_index].shotDelay > 0) + { + destruct_player[player_index].shotDelay--; + } + if (destruct_player[player_index].moves.actions[MOVE_FIRE] == true + && (destruct_player[player_index].shotDelay == 0)) + { + destruct_player[player_index].shotDelay = shotDelay[curUnit->shotType]; + + switch(shotDirt[curUnit->shotType]) + { + case EXPL_NONE: + break; + + case EXPL_MAGNET: + DE_RunMagnet(player_index, curUnit); + break; + + case EXPL_DIRT: + case EXPL_NORMAL: + DE_MakeShot(player_index, curUnit, direction); + break; + + default: + assert(false); + } + } + } +} + +static void DE_CycleWeaponUp( struct destruct_unit_s * unit ) +{ + do + { + unit->shotType++; + if (unit->shotType > SHOT_LAST) + { + unit->shotType = SHOT_FIRST; + } + } while (weaponSystems[unit->unitType][unit->shotType] == 0); +} +static void DE_CycleWeaponDown( struct destruct_unit_s * unit ) +{ + do + { + unit->shotType--; + if (unit->shotType < SHOT_FIRST) + { + unit->shotType = SHOT_LAST; + } + } while (weaponSystems[unit->unitType][unit->shotType] == 0); +} + + +static void DE_MakeShot( enum de_player_t curPlayer, const struct destruct_unit_s * curUnit, int direction ) +{ + unsigned int i; + unsigned int shotIndex; + + + /* First, find an empty shot struct we can use */ + for (i = 0; ; i++) + { + if (i >= config.max_shots) { return; } /* no empty slots. Do nothing. */ + + if (shotRec[i].isAvailable) + { + shotIndex = i; + break; + } + } + if (curUnit->unitType == UNIT_HELI && curUnit->isYInAir == false) + { /* Helis can't fire when they are on the ground. */ + return; + } + + /* Play the firing sound */ + soundQueue[curPlayer] = shotSound[curUnit->shotType]; + + /* Create our shot. Some units have differing logic here */ + switch (curUnit->unitType) + { + case UNIT_HELI: + + shotRec[shotIndex].x = curUnit->unitX + curUnit->lastMove * 2 + 5; + shotRec[shotIndex].xmov = 0.02f * curUnit->lastMove * curUnit->lastMove * curUnit->lastMove; + + /* If we are trying in vain to move up off the screen, act differently.*/ + if (destruct_player[curPlayer].moves.actions[MOVE_UP] && curUnit->unitY < 30) + { + shotRec[shotIndex].y = curUnit->unitY; + shotRec[shotIndex].ymov = 0.1f; + + if (shotRec[shotIndex].xmov < 0) + { + shotRec[shotIndex].xmov += 0.1f; + } + else if (shotRec[shotIndex].xmov > 0) + { + shotRec[shotIndex].xmov -= 0.1f; + } + } + else + { + shotRec[shotIndex].y = curUnit->unitY + 1; + shotRec[shotIndex].ymov = 0.5f + curUnit->unitYMov * 0.1f; + } + break; + + case UNIT_JUMPER: /* Jumpers are normally only special for the left hand player. Bug? Or feature? */ + + if(config.jumper_straight[curPlayer]) + { + /* This is identical to the default case. + * I considered letting the switch fall through + * but that's more confusing to people who aren't used + * to that quirk of switch. */ + + shotRec[shotIndex].x = curUnit->unitX + 6 - cosf(curUnit->angle) * 10 * direction; + shotRec[shotIndex].y = curUnit->unitY - 7 - sinf(curUnit->angle) * 10; + shotRec[shotIndex].xmov = -cosf(curUnit->angle) * curUnit->power * direction; + shotRec[shotIndex].ymov = -sinf(curUnit->angle) * curUnit->power; + } + else + { + /* This is not identical to the default case. */ + + shotRec[shotIndex].x = curUnit->unitX + 2; + shotRec[shotIndex].xmov = -cosf(curUnit->angle) * curUnit->power * direction; + + if (curUnit->isYInAir == true) + { + shotRec[shotIndex].ymov = 1; + shotRec[shotIndex].y = curUnit->unitY + 2; + } else { + shotRec[shotIndex].ymov = -2; + shotRec[shotIndex].y = curUnit->unitY - 12; + } + } + break; + + default: + + shotRec[shotIndex].x = curUnit->unitX + 6 - cosf(curUnit->angle) * 10 * direction; + shotRec[shotIndex].y = curUnit->unitY - 7 - sinf(curUnit->angle) * 10; + shotRec[shotIndex].xmov = -cosf(curUnit->angle) * curUnit->power * direction; + shotRec[shotIndex].ymov = -sinf(curUnit->angle) * curUnit->power; + break; + } + + /* Now set/clear out a few last details. */ + shotRec[shotIndex].isAvailable = false; + + shotRec[shotIndex].shottype = curUnit->shotType; + //shotRec[shotIndex].shotdur = shotFuse[shotRec[shotIndex].shottype]; + + shotRec[shotIndex].trailc[0] = 0; + shotRec[shotIndex].trailc[1] = 0; + shotRec[shotIndex].trailc[2] = 0; + shotRec[shotIndex].trailc[3] = 0; +} +static void DE_RunMagnet( enum de_player_t curPlayer, struct destruct_unit_s * magnet ) +{ + unsigned int i; + enum de_player_t curEnemy; + int direction; + struct destruct_unit_s * enemyUnit; + + + curEnemy = (curPlayer == PLAYER_LEFT) ? PLAYER_RIGHT : PLAYER_LEFT; + direction = (curPlayer == PLAYER_LEFT) ? -1 : 1; + + /* Push all shots that are in front of the magnet */ + for (i = 0; i < config.max_shots; i++) + { + if (shotRec[i].isAvailable == false) + { + if ((curPlayer == PLAYER_LEFT && shotRec[i].x > magnet->unitX) + || (curPlayer == PLAYER_RIGHT && shotRec[i].x < magnet->unitX)) + { + shotRec[i].xmov += magnet->power * 0.1f * -direction; + } + } + } + + enemyUnit = destruct_player[curEnemy].unit; + for (i = 0; i < config.max_installations; i++, enemyUnit++) /* magnets push coptors */ + { + if (DE_isValidUnit(enemyUnit) + && enemyUnit->unitType == UNIT_HELI + && enemyUnit->isYInAir == true) + { + if ((curEnemy == PLAYER_RIGHT && destruct_player[curEnemy].unit[i].unitX + 11 < 318) + || (curEnemy == PLAYER_LEFT && destruct_player[curEnemy].unit[i].unitX > 1)) + { + enemyUnit->unitX -= 2 * direction; + } + } + } + magnet->ani_frame = 1; +} +static void DE_RaiseAngle( struct destruct_unit_s * unit ) +{ + unit->angle += 0.01f; + if (unit->angle > M_PI_2 - 0.01f) + { + unit->angle = M_PI_2 - 0.01f; + } +} +static void DE_LowerAngle( struct destruct_unit_s * unit ) +{ + unit->angle -= 0.01f; + if (unit->angle < 0) + { + unit->angle = 0; + } +} +static void DE_RaisePower( struct destruct_unit_s * unit ) +{ + unit->power += 0.05f; + if (unit->power > 5) + { + unit->power = 5; + } +} +static void DE_LowerPower( struct destruct_unit_s * unit ) +{ + unit->power -= 0.05f; + if (unit->power < 1) + { + unit->power = 1; + } +} + +/* DE_isValidUnit + * + * Returns true if the unit's health is above 0 and false + * otherwise. This mainly exists because the 'health' var + * serves two roles and that can get confusing. + */ +static inline bool DE_isValidUnit( struct destruct_unit_s * unit ) +{ + return(unit->health > 0); +} + + +static bool DE_RunTickCheckEndgame( void ) +{ + if (destruct_player[PLAYER_LEFT].unitsRemaining == 0) + { + destruct_player[PLAYER_RIGHT].score += ModeScore[PLAYER_LEFT][world.destructMode]; + soundQueue[7] = V_CLEARED_PLATFORM; + return(true); + } + if (destruct_player[PLAYER_RIGHT].unitsRemaining == 0) + { + destruct_player[PLAYER_LEFT].score += ModeScore[PLAYER_RIGHT][world.destructMode]; + soundQueue[7] = V_CLEARED_PLATFORM; + return(true); + } + return(false); +} +static void DE_RunTickPlaySounds( void ) +{ + unsigned int i, tempSampleIndex, tempVolume; + + + for (i = 0; i < COUNTOF(soundQueue); i++) + { + if (soundQueue[i] != S_NONE) + { + tempSampleIndex = soundQueue[i]; + if (i == 7) + { + tempVolume = fxPlayVol; + } + else + { + tempVolume = fxPlayVol / 2; + } + + JE_multiSamplePlay(digiFx[tempSampleIndex-1], fxSize[tempSampleIndex-1], i, tempVolume); + soundQueue[i] = S_NONE; + } + } +} + +static void JE_pixCool( unsigned int x, unsigned int y, Uint8 c ) +{ + JE_pix(VGAScreen, x, y, c); + JE_pix(VGAScreen, x - 1, y, c - 2); + JE_pix(VGAScreen, x + 1, y, c - 2); + JE_pix(VGAScreen, x, y - 1, c - 2); + JE_pix(VGAScreen, x, y + 1, c - 2); +} diff --git a/contrib/games/opentyrian/src/destruct.h b/contrib/games/opentyrian/src/destruct.h new file mode 100644 index 000000000..df7ff7108 --- /dev/null +++ b/contrib/games/opentyrian/src/destruct.h @@ -0,0 +1,27 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef DESTRUCT_H +#define DESTRUCT_H + +#include "opentyr.h" + +void JE_destructGame( void ); + +#endif /* DESTRUCT_H */ + diff --git a/contrib/games/opentyrian/src/editship.c b/contrib/games/opentyrian/src/editship.c new file mode 100644 index 000000000..311bb6386 --- /dev/null +++ b/contrib/games/opentyrian/src/editship.c @@ -0,0 +1,92 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "editship.h" + +#include "config.h" +#include "file.h" +#include "opentyr.h" + +#define SAS (sizeof(JE_ShipsType) - 4) + +const JE_byte extraCryptKey[10] = { 58, 23, 16, 192, 254, 82, 113, 147, 62, 99 }; + +JE_boolean extraAvail; +JE_ShipsType extraShips; +void *extraShapes; +JE_word extraShapeSize; + +void JE_decryptShips( void ) +{ + JE_boolean correct = true; + JE_ShipsType s2; + JE_byte y; + + for (int x = SAS - 1; x >= 0; x--) + { + s2[x] = extraShips[x] ^ extraCryptKey[(x + 1) % 10]; + if (x > 0) + s2[x] ^= extraShips[x - 1]; + } /* <= Key Decryption Test (Reversed key) */ + + y = 0; + for (uint x = 0; x < SAS; x++) + y += s2[x]; + if (extraShips[SAS + 0] != y) + correct = false; + + y = 0; + for (uint x = 0; x < SAS; x++) + y -= s2[x]; + if (extraShips[SAS + 1] != y) + correct = false; + + y = 1; + for (uint x = 0; x < SAS; x++) + y = y * s2[x] + 1; + if (extraShips[SAS + 2] != y) + correct = false; + + y = 0; + for (uint x = 0; x < SAS; x++) + y ^= s2[x]; + if (extraShips[SAS + 3] != y) + correct = false; + + if (!correct) + exit(255); + + memcpy(extraShips, s2, sizeof(extraShips)); +} + +void JE_loadExtraShapes( void ) +{ + FILE *f = dir_fopen(get_user_directory(), "newsh$.shp", "rb"); + + if (f) + { + extraAvail = true; + extraShapeSize = ftell_eof(f) - sizeof(extraShips); + extraShapes = malloc(extraShapeSize); + efread(extraShapes, extraShapeSize, 1, f); + efread(extraShips, sizeof(extraShips), 1, f); + JE_decryptShips(); + fclose(f); + } +} + diff --git a/contrib/games/opentyrian/src/editship.h b/contrib/games/opentyrian/src/editship.h new file mode 100644 index 000000000..210291241 --- /dev/null +++ b/contrib/games/opentyrian/src/editship.h @@ -0,0 +1,36 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef EDITSHIP_H +#define EDITSHIP_H + +#include "opentyr.h" + + +typedef JE_byte JE_ShipsType[154]; /* [1..154] */ + +extern JE_boolean extraAvail; +extern JE_ShipsType extraShips; +extern void *extraShapes; +extern JE_word extraShapeSize; + +void JE_decryptShips( void ); +void JE_loadExtraShapes( void ); + +#endif /* EDITSHIP_H */ + diff --git a/contrib/games/opentyrian/src/episodes.c b/contrib/games/opentyrian/src/episodes.c new file mode 100644 index 000000000..86b2cef13 --- /dev/null +++ b/contrib/games/opentyrian/src/episodes.c @@ -0,0 +1,267 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "episodes.h" + +#include "config.h" +#include "file.h" +#include "lvllib.h" +#include "lvlmast.h" +#include "opentyr.h" + + +/* MAIN Weapons Data */ +JE_WeaponPortType weaponPort; +JE_WeaponType weapons[WEAP_NUM + 1]; /* [0..weapnum] */ + +/* Items */ +JE_PowerType powerSys; +JE_ShipType ships; +JE_OptionType options[OPTION_NUM + 1]; /* [0..optionnum] */ +JE_ShieldType shields; +JE_SpecialType special; + +/* Enemy data */ +JE_EnemyDatType enemyDat; + +/* EPISODE variables */ +JE_byte initial_episode_num, episodeNum = 0; +JE_boolean episodeAvail[EPISODE_MAX]; /* [1..episodemax] */ +char episode_file[13], cube_file[13]; + +JE_longint episode1DataLoc; + +/* Tells the game whether the level currently loaded is a bonus level. */ +JE_boolean bonusLevel; + +/* Tells if the game jumped back to Episode 1 */ +JE_boolean jumpBackToEpisode1; + +void JE_loadItemDat( void ) +{ + FILE *f = NULL; + + if (episodeNum <= 3) + { + f = dir_fopen_die(data_dir(), "tyrian.hdt", "rb"); + efread(&episode1DataLoc, sizeof(JE_longint), 1, f); + fseek(f, episode1DataLoc, SEEK_SET); + } + else + { + // episode 4 stores item data in the level file + f = dir_fopen_die(data_dir(), levelFile, "rb"); + fseek(f, lvlPos[lvlNum-1], SEEK_SET); + } + + JE_word itemNum[7]; /* [1..7] */ + efread(&itemNum, sizeof(JE_word), 7, f); + + for (int i = 0; i < WEAP_NUM + 1; ++i) + { + efread(&weapons[i].drain, sizeof(JE_word), 1, f); + efread(&weapons[i].shotrepeat, sizeof(JE_byte), 1, f); + efread(&weapons[i].multi, sizeof(JE_byte), 1, f); + efread(&weapons[i].weapani, sizeof(JE_word), 1, f); + efread(&weapons[i].max, sizeof(JE_byte), 1, f); + efread(&weapons[i].tx, sizeof(JE_byte), 1, f); + efread(&weapons[i].ty, sizeof(JE_byte), 1, f); + efread(&weapons[i].aim, sizeof(JE_byte), 1, f); + efread(&weapons[i].attack, sizeof(JE_byte), 8, f); + efread(&weapons[i].del, sizeof(JE_byte), 8, f); + efread(&weapons[i].sx, sizeof(JE_shortint), 8, f); + efread(&weapons[i].sy, sizeof(JE_shortint), 8, f); + efread(&weapons[i].bx, sizeof(JE_shortint), 8, f); + efread(&weapons[i].by, sizeof(JE_shortint), 8, f); + efread(&weapons[i].sg, sizeof(JE_word), 8, f); + efread(&weapons[i].acceleration, sizeof(JE_shortint), 1, f); + efread(&weapons[i].accelerationx, sizeof(JE_shortint), 1, f); + efread(&weapons[i].circlesize, sizeof(JE_byte), 1, f); + efread(&weapons[i].sound, sizeof(JE_byte), 1, f); + efread(&weapons[i].trail, sizeof(JE_byte), 1, f); + efread(&weapons[i].shipblastfilter, sizeof(JE_byte), 1, f); + } + + for (int i = 0; i < PORT_NUM + 1; ++i) + { + fseek(f, 1, SEEK_CUR); /* skip string length */ + efread(&weaponPort[i].name, 1, 30, f); + weaponPort[i].name[30] = '\0'; + efread(&weaponPort[i].opnum, sizeof(JE_byte), 1, f); + for (int j = 0; j < 2; ++j) + { + efread(&weaponPort[i].op[j], sizeof(JE_word), 11, f); + } + efread(&weaponPort[i].cost, sizeof(JE_word), 1, f); + efread(&weaponPort[i].itemgraphic, sizeof(JE_word), 1, f); + efread(&weaponPort[i].poweruse, sizeof(JE_word), 1, f); + } + + for (int i = 0; i < SPECIAL_NUM + 1; ++i) + { + fseek(f, 1, SEEK_CUR); /* skip string length */ + efread(&special[i].name, 1, 30, f); + special[i].name[30] = '\0'; + efread(&special[i].itemgraphic, sizeof(JE_word), 1, f); + efread(&special[i].pwr, sizeof(JE_byte), 1, f); + efread(&special[i].stype, sizeof(JE_byte), 1, f); + efread(&special[i].wpn, sizeof(JE_word), 1, f); + } + + for (int i = 0; i < POWER_NUM + 1; ++i) + { + fseek(f, 1, SEEK_CUR); /* skip string length */ + efread(&powerSys[i].name, 1, 30, f); + powerSys[i].name[30] = '\0'; + efread(&powerSys[i].itemgraphic, sizeof(JE_word), 1, f); + efread(&powerSys[i].power, sizeof(JE_shortint), 1, f); + efread(&powerSys[i].speed, sizeof(JE_byte), 1, f); + efread(&powerSys[i].cost, sizeof(JE_word), 1, f); + } + + for (int i = 0; i < SHIP_NUM + 1; ++i) + { + fseek(f, 1, SEEK_CUR); /* skip string length */ + efread(&ships[i].name, 1, 30, f); + ships[i].name[30] = '\0'; + efread(&ships[i].shipgraphic, sizeof(JE_word), 1, f); + efread(&ships[i].itemgraphic, sizeof(JE_word), 1, f); + efread(&ships[i].ani, sizeof(JE_byte), 1, f); + efread(&ships[i].spd, sizeof(JE_shortint), 1, f); + efread(&ships[i].dmg, sizeof(JE_byte), 1, f); + efread(&ships[i].cost, sizeof(JE_word), 1, f); + efread(&ships[i].bigshipgraphic, sizeof(JE_byte), 1, f); + } + + for (int i = 0; i < OPTION_NUM + 1; ++i) + { + fseek(f, 1, SEEK_CUR); /* skip string length */ + efread(&options[i].name, 1, 30, f); + options[i].name[30] = '\0'; + efread(&options[i].pwr, sizeof(JE_byte), 1, f); + efread(&options[i].itemgraphic, sizeof(JE_word), 1, f); + efread(&options[i].cost, sizeof(JE_word), 1, f); + efread(&options[i].tr, sizeof(JE_byte), 1, f); + efread(&options[i].option, sizeof(JE_byte), 1, f); + efread(&options[i].opspd, sizeof(JE_shortint), 1, f); + efread(&options[i].ani, sizeof(JE_byte), 1, f); + efread(&options[i].gr, sizeof(JE_word), 20, f); + efread(&options[i].wport, sizeof(JE_byte), 1, f); + efread(&options[i].wpnum, sizeof(JE_word), 1, f); + efread(&options[i].ammo, sizeof(JE_byte), 1, f); + efread(&options[i].stop, 1, 1, f); /* override sizeof(JE_boolean) */ + efread(&options[i].icongr, sizeof(JE_byte), 1, f); + } + + for (int i = 0; i < SHIELD_NUM + 1; ++i) + { + fseek(f, 1, SEEK_CUR); /* skip string length */ + efread(&shields[i].name, 1, 30, f); + shields[i].name[30] = '\0'; + efread(&shields[i].tpwr, sizeof(JE_byte), 1, f); + efread(&shields[i].mpwr, sizeof(JE_byte), 1, f); + efread(&shields[i].itemgraphic, sizeof(JE_word), 1, f); + efread(&shields[i].cost, sizeof(JE_word), 1, f); + } + + for (int i = 0; i < ENEMY_NUM + 1; ++i) + { + efread(&enemyDat[i].ani, sizeof(JE_byte), 1, f); + efread(&enemyDat[i].tur, sizeof(JE_byte), 3, f); + efread(&enemyDat[i].freq, sizeof(JE_byte), 3, f); + efread(&enemyDat[i].xmove, sizeof(JE_shortint), 1, f); + efread(&enemyDat[i].ymove, sizeof(JE_shortint), 1, f); + efread(&enemyDat[i].xaccel, sizeof(JE_shortint), 1, f); + efread(&enemyDat[i].yaccel, sizeof(JE_shortint), 1, f); + efread(&enemyDat[i].xcaccel, sizeof(JE_shortint), 1, f); + efread(&enemyDat[i].ycaccel, sizeof(JE_shortint), 1, f); + efread(&enemyDat[i].startx, sizeof(JE_integer), 1, f); + efread(&enemyDat[i].starty, sizeof(JE_integer), 1, f); + efread(&enemyDat[i].startxc, sizeof(JE_shortint), 1, f); + efread(&enemyDat[i].startyc, sizeof(JE_shortint), 1, f); + efread(&enemyDat[i].armor, sizeof(JE_byte), 1, f); + efread(&enemyDat[i].esize, sizeof(JE_byte), 1, f); + efread(&enemyDat[i].egraphic, sizeof(JE_word), 20, f); + efread(&enemyDat[i].explosiontype, sizeof(JE_byte), 1, f); + efread(&enemyDat[i].animate, sizeof(JE_byte), 1, f); + efread(&enemyDat[i].shapebank, sizeof(JE_byte), 1, f); + efread(&enemyDat[i].xrev, sizeof(JE_shortint), 1, f); + efread(&enemyDat[i].yrev, sizeof(JE_shortint), 1, f); + efread(&enemyDat[i].dgr, sizeof(JE_word), 1, f); + efread(&enemyDat[i].dlevel, sizeof(JE_shortint), 1, f); + efread(&enemyDat[i].dani, sizeof(JE_shortint), 1, f); + efread(&enemyDat[i].elaunchfreq, sizeof(JE_byte), 1, f); + efread(&enemyDat[i].elaunchtype, sizeof(JE_word), 1, f); + efread(&enemyDat[i].value, sizeof(JE_integer), 1, f); + efread(&enemyDat[i].eenemydie, sizeof(JE_word), 1, f); + } + + fclose(f); +} + +void JE_initEpisode( JE_byte newEpisode ) +{ + if (newEpisode == episodeNum) + return; + + episodeNum = newEpisode; + + sprintf(levelFile, "tyrian%d.lvl", episodeNum); + sprintf(cube_file, "cubetxt%d.dat", episodeNum); + sprintf(episode_file, "levels%d.dat", episodeNum); + + JE_analyzeLevel(); + JE_loadItemDat(); +} + +void JE_scanForEpisodes( void ) +{ + for (int i = 0; i < EPISODE_MAX; ++i) + { + char ep_file[20]; + snprintf(ep_file, sizeof(ep_file), "tyrian%d.lvl", i + 1); + episodeAvail[i] = dir_file_exists(data_dir(), ep_file); + } +} + +unsigned int JE_findNextEpisode( void ) +{ + unsigned int newEpisode = episodeNum; + + jumpBackToEpisode1 = false; + + while (true) + { + newEpisode++; + + if (newEpisode > EPISODE_MAX) + { + newEpisode = 1; + jumpBackToEpisode1 = true; + gameHasRepeated = true; + } + + if (episodeAvail[newEpisode-1] || newEpisode == episodeNum) + { + break; + } + } + + return newEpisode; +} + diff --git a/contrib/games/opentyrian/src/episodes.h b/contrib/games/opentyrian/src/episodes.h new file mode 100644 index 000000000..799001d97 --- /dev/null +++ b/contrib/games/opentyrian/src/episodes.h @@ -0,0 +1,173 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef EPISODES_H +#define EPISODES_H + +#include "opentyr.h" + +#include "lvlmast.h" + + +/* Episodes and general data */ + +#define FIRST_LEVEL 1 +#define EPISODE_MAX 5 +#define EPISODE_AVAILABLE 4 + +typedef struct +{ + JE_word drain; + JE_byte shotrepeat; + JE_byte multi; + JE_word weapani; + JE_byte max; + JE_byte tx, ty, aim; + JE_byte attack[8], del[8]; /* [1..8] */ + JE_shortint sx[8], sy[8]; /* [1..8] */ + JE_shortint bx[8], by[8]; /* [1..8] */ + JE_word sg[8]; /* [1..8] */ + JE_shortint acceleration, accelerationx; + JE_byte circlesize; + JE_byte sound; + JE_byte trail; + JE_byte shipblastfilter; +} JE_WeaponType; + +typedef struct +{ + char name[31]; /* string [30] */ + JE_byte opnum; + JE_word op[2][11]; /* [1..2, 1..11] */ + JE_word cost; + JE_word itemgraphic; + JE_word poweruse; +} JE_WeaponPortType[PORT_NUM + 1]; /* [0..portnum] */ + +typedef struct +{ + char name[31]; /* string [30] */ + JE_word itemgraphic; + JE_byte power; + JE_shortint speed; + JE_word cost; +} JE_PowerType[POWER_NUM + 1]; /* [0..powernum] */ + +typedef struct +{ + char name[31]; /* string [30] */ + JE_word itemgraphic; + JE_byte pwr; + JE_byte stype; + JE_word wpn; +} JE_SpecialType[SPECIAL_NUM + 1]; /* [0..specialnum] */ + +typedef struct +{ + char name[31]; /* string [30] */ + JE_byte pwr; + JE_word itemgraphic; + JE_word cost; + JE_byte tr, option; + JE_shortint opspd; + JE_byte ani; + JE_word gr[20]; /* [1..20] */ + JE_byte wport; + JE_word wpnum; + JE_byte ammo; + JE_boolean stop; + JE_byte icongr; +} JE_OptionType; + +typedef struct +{ + char name[31]; /* string [30] */ + JE_byte tpwr; + JE_byte mpwr; + JE_word itemgraphic; + JE_word cost; +} JE_ShieldType[SHIELD_NUM + 1]; /* [0..shieldnum] */ + +typedef struct +{ + char name[31]; /* string [30] */ + JE_word shipgraphic; + JE_word itemgraphic; + JE_byte ani; + JE_shortint spd; + JE_byte dmg; + JE_word cost; + JE_byte bigshipgraphic; +} JE_ShipType[SHIP_NUM + 1]; /* [0..shipnum] */ + +/* EnemyData */ +typedef struct +{ + JE_byte ani; + JE_byte tur[3]; /* [1..3] */ + JE_byte freq[3]; /* [1..3] */ + JE_shortint xmove; + JE_shortint ymove; + JE_shortint xaccel; + JE_shortint yaccel; + JE_shortint xcaccel; + JE_shortint ycaccel; + JE_integer startx; + JE_integer starty; + JE_shortint startxc; + JE_shortint startyc; + JE_byte armor; + JE_byte esize; + JE_word egraphic[20]; /* [1..20] */ + JE_byte explosiontype; + JE_byte animate; /* 0:Not Yet 1:Always 2:When Firing Only */ + JE_byte shapebank; /* See LEVELMAK.DOC */ + JE_shortint xrev, yrev; + JE_word dgr; + JE_shortint dlevel; + JE_shortint dani; + JE_byte elaunchfreq; + JE_word elaunchtype; + JE_integer value; + JE_word eenemydie; +} JE_EnemyDatType[ENEMY_NUM + 1]; /* [0..enemynum] */ + +extern JE_WeaponPortType weaponPort; +extern JE_WeaponType weapons[WEAP_NUM + 1]; /* [0..weapnum] */ +extern JE_PowerType powerSys; +extern JE_ShipType ships; +extern JE_OptionType options[OPTION_NUM + 1]; /* [0..optionnum] */ +extern JE_ShieldType shields; +extern JE_SpecialType special; +extern JE_EnemyDatType enemyDat; +extern JE_byte initial_episode_num, episodeNum; +extern JE_boolean episodeAvail[EPISODE_MAX]; + +extern char episode_file[13], cube_file[13]; + +extern JE_longint episode1DataLoc; +extern JE_boolean bonusLevel; +extern JE_boolean jumpBackToEpisode1; + +void JE_loadItemDat( void ); +void JE_initEpisode( JE_byte newEpisode ); +unsigned int JE_findNextEpisode( void ); +void JE_scanForEpisodes( void ); + +#endif /* EPISODES_H */ + diff --git a/contrib/games/opentyrian/src/file.c b/contrib/games/opentyrian/src/file.c new file mode 100644 index 000000000..0f0428f39 --- /dev/null +++ b/contrib/games/opentyrian/src/file.c @@ -0,0 +1,208 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "file.h" + +#include "opentyr.h" +#include "varz.h" + +#include "SDL.h" + +#include +#include +#include + +const char *custom_data_dir = NULL; + +// finds the Tyrian data directory +const char *data_dir( void ) +{ + const char *dirs[] = + { + custom_data_dir, + TYRIAN_DIR, + "data", + ".", + }; + + static const char *dir = NULL; + + if (dir != NULL) + return dir; + + for (uint i = 0; i < COUNTOF(dirs); ++i) + { + if (dirs[i] == NULL) + continue; + + FILE *f = dir_fopen(dirs[i], "tyrian1.lvl", "rb"); + if (f) + { + fclose(f); + + dir = dirs[i]; + break; + } + } + + if (dir == NULL) // data not found + dir = ""; + + return dir; +} + +// prepend directory and fopen +FILE *dir_fopen( const char *dir, const char *file, const char *mode ) +{ + char *path = malloc(strlen(dir) + 1 + strlen(file) + 1); + sprintf(path, "%s/%s", dir, file); + + FILE *f = fopen(path, mode); + fprintf(stderr, "%s\n", path); + free(path); + + return f; +} + +// warn when dir_fopen fails +FILE *dir_fopen_warn( const char *dir, const char *file, const char *mode ) +{ + FILE *f = dir_fopen(dir, file, mode); + + if (f == NULL) + fprintf(stderr, "warning: failed to open '%s': %s\n", file, strerror(errno)); + + return f; +} + +// die when dir_fopen fails +FILE *dir_fopen_die( const char *dir, const char *file, const char *mode ) +{ + FILE *f = dir_fopen(dir, file, mode); + + if (f == NULL) + { + fprintf(stderr, "error: failed to open '%s': %s\n", file, strerror(errno)); + fprintf(stderr, "error: One or more of the required Tyrian " TYRIAN_VERSION " data files could not be found.\n" + " Please read the README file.\n"); + JE_tyrianHalt(1); + } + + return f; +} + +// check if file can be opened for reading +bool dir_file_exists( const char *dir, const char *file ) +{ + FILE *f = dir_fopen(dir, file, "rb"); + if (f != NULL) + fclose(f); + return (f != NULL); +} + +// returns end-of-file position +long ftell_eof( FILE *f ) +{ + long pos = ftell(f); + + fseek(f, 0, SEEK_END); + long size = ftell(f); + + fseek(f, pos, SEEK_SET); + + return size; +} + +// endian-swapping fread that dies if the expected amount cannot be read +size_t efread( void *buffer, size_t size, size_t num, FILE *stream ) +{ + size_t num_read = fread(buffer, size, num, stream); + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + switch (size) + { + case 2: + for (size_t i = 0; i < num; i++) + ((Uint16 *)buffer)[i] = SDL_Swap16(((Uint16 *)buffer)[i]); + break; + case 4: + for (size_t i = 0; i < num; i++) + ((Uint32 *)buffer)[i] = SDL_Swap32(((Uint32 *)buffer)[i]); + break; + case 8: + for (size_t i = 0; i < num; i++) + ((Uint64 *)buffer)[i] = SDL_Swap64(((Uint64 *)buffer)[i]); + break; + default: + break; + } +#endif + + if (num_read != num) + { + fprintf(stderr, "error: An unexpected problem occurred while reading from a file.\n"); + JE_tyrianHalt(1); + } + + return num_read; +} + +// endian-swapping fwrite that dies if the expected amount cannot be written +size_t efwrite( const void *buffer, size_t size, size_t num, FILE *stream ) +{ + void *swap_buffer = NULL; + +#if SDL_BYTEORDER == SDL_BIG_ENDIAN + switch (size) + { + case 2: + swap_buffer = malloc(size * num); + for (size_t i = 0; i < num; i++) + ((Uint16 *)swap_buffer)[i] = SDL_SwapLE16(((Uint16 *)buffer)[i]); + buffer = swap_buffer; + break; + case 4: + swap_buffer = malloc(size * num); + for (size_t i = 0; i < num; i++) + ((Uint32 *)swap_buffer)[i] = SDL_SwapLE32(((Uint32 *)buffer)[i]); + buffer = swap_buffer; + break; + case 8: + swap_buffer = malloc(size * num); + for (size_t i = 0; i < num; i++) + ((Uint64 *)swap_buffer)[i] = SDL_SwapLE64(((Uint64 *)buffer)[i]); + buffer = swap_buffer; + break; + default: + break; + } +#endif + + size_t num_written = fwrite(buffer, size, num, stream); + + if (swap_buffer != NULL) + free(swap_buffer); + + if (num_written != num) + { + fprintf(stderr, "error: An unexpected problem occurred while writing to a file.\n"); + JE_tyrianHalt(1); + } + + return num_written; +} diff --git a/contrib/games/opentyrian/src/file.h b/contrib/games/opentyrian/src/file.h new file mode 100644 index 000000000..595fc7496 --- /dev/null +++ b/contrib/games/opentyrian/src/file.h @@ -0,0 +1,44 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef FILE_H +#define FILE_H + +#include "SDL_endian.h" + +#include +#include + +extern const char *custom_data_dir; + +const char *data_dir( void ); + +FILE *dir_fopen( const char *dir, const char *file, const char *mode ); +FILE *dir_fopen_warn( const char *dir, const char *file, const char *mode ); +FILE *dir_fopen_die( const char *dir, const char *file, const char *mode ); + +bool dir_file_exists( const char *dir, const char *file ); + +long ftell_eof( FILE *f ); + +// endian-swapping fread/fwrite that die if the expected amount cannot be read/written +size_t efread( void *buffer, size_t size, size_t num, FILE *stream ); +size_t efwrite( const void *buffer, size_t size, size_t num, FILE *stream ); + +#endif // FILE_H + diff --git a/contrib/games/opentyrian/src/font.c b/contrib/games/opentyrian/src/font.c new file mode 100644 index 000000000..7875db856 --- /dev/null +++ b/contrib/games/opentyrian/src/font.c @@ -0,0 +1,275 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "font.h" + +#include "fonthand.h" +#include "sprite.h" + +/** + * \file font.c + * \brief Text drawing routines. + */ + +/** + * \brief Draws text in a color specified by hue and value and with a drop + * shadow. + * + * A '~' in the text is not drawn but instead toggles highlighting which + * increases \c value by 4. + * + * \li like JE_dString() if (black == false && shadow_dist == 2 && hue == 15) + * \li like JE_textShade() with PART_SHADE if (black == true && shadow_dist == 1) + * \li like JE_outTextAndDarken() if (black == false && shadow_dist == 1) + * \li like JE_outTextAdjust() with shadow if (black == false && shadow_dist == 2) + * + * @param surface destination surface + * @param x initial x-position in pixels; which direction(s) the text is drawn + * from this position depends on the alignment + * @param y initial upper y-position in pixels + * @param text text to be drawn + * @param font style/size of text + * @param alignment left_aligned, centered, or right_aligned + * @param hue hue component of text color + * @param value value component of text color + * @param black if true the shadow is drawn as solid black, if false the shadow + * is drawn by darkening the pixels of the destination surface + * @param shadow_dist distance in pixels that the shadow will be drawn away from + * the text. (This is added to both the x and y positions, so a value of + * 1 causes the shadow to be drawn 1 pixel right and 1 pixel lower than + * the text.) + */ +void draw_font_hv_shadow( SDL_Surface *surface, int x, int y, const char *text, Font font, FontAlignment alignment, Uint8 hue, Sint8 value, bool black, int shadow_dist ) +{ + draw_font_dark(surface, x + shadow_dist, y + shadow_dist, text, font, alignment, black); + + draw_font_hv(surface, x, y, text, font, alignment, hue, value); +} + +/** + * \brief Draws text in a color specified by hue and value and with a + * surrounding shadow. + * + * A '~' in the text is not drawn but instead toggles highlighting which + * increases \c value by 4. + * + * \li like JE_textShade() with FULL_SHADE if (black == true && shadow_dist == 1) + * + * @param surface destination surface + * @param x initial x-position in pixels; which direction(s) the text is drawn + * from this position depends on the alignment + * @param y initial upper y-position in pixels + * @param text text to be drawn + * @param font style/size of text + * @param alignment left_aligned, centered, or right_aligned + * @param hue hue component of text color + * @param value value component of text color + * @param black if true the shadow is drawn as solid black, if false the shadow + * is drawn by darkening the pixels of the destination surface + * @param shadow_dist distance in pixels that the shadows will be drawn away + * from the text. (This distance is separately added to and subtracted + * from the x position and y position, resulting in four shadows -- one + * in each cardinal direction. If this shadow distance is small enough, + * this produces a shadow that outlines the text.) + */ +void draw_font_hv_full_shadow( SDL_Surface *surface, int x, int y, const char *text, Font font, FontAlignment alignment, Uint8 hue, Sint8 value, bool black, int shadow_dist ) +{ + draw_font_dark(surface, x, y - shadow_dist, text, font, alignment, black); + draw_font_dark(surface, x + shadow_dist, y, text, font, alignment, black); + draw_font_dark(surface, x, y + shadow_dist, text, font, alignment, black); + draw_font_dark(surface, x - shadow_dist, y, text, font, alignment, black); + + draw_font_hv(surface, x, y, text, font, alignment, hue, value); +} + +/** + * \brief Draws text in a color specified by hue and value. + * + * A '~' in the text is not drawn but instead toggles highlighting which + * increases \c value by 4. + * + * \li like JE_outText() with (brightness >= 0) + * \li like JE_outTextAdjust() without shadow + * + * @param surface destination surface + * @param x initial x-position in pixels; which direction(s) the text is drawn + * from this position depends on the alignment + * @param y initial upper y-position in pixels + * @param text text to be drawn + * @param font style/size of text + * @param alignment left_aligned, centered, or right_aligned + * @param hue hue component of text color + * @param value value component of text color + */ +void draw_font_hv( SDL_Surface *surface, int x, int y, const char *text, Font font, FontAlignment alignment, Uint8 hue, Sint8 value ) +{ + switch (alignment) + { + case left_aligned: + break; + case centered: + x -= JE_textWidth(text, font) / 2; + break; + case right_aligned: + x -= JE_textWidth(text, font); + break; + } + + bool highlight = false; + + for (; *text != '\0'; ++text) + { + int sprite_id = font_ascii[(unsigned char)*text]; + + switch (*text) + { + case ' ': + x += 6; + break; + + case '~': + highlight = !highlight; + if (highlight) + value += 4; + else + value -= 4; + break; + + default: + if (sprite_id != -1 && sprite_exists(font, sprite_id)) + { + blit_sprite_hv(surface, x, y, font, sprite_id, hue, value); + + x += sprite(font, sprite_id)->width + 1; + } + break; + } + } +} + +/** + * \brief Draws blended text in a color specified by hue and value. + * + * Corresponds to blit_sprite_hv_blend() + * + * \li like JE_outTextModify() + * + * @param surface destination surface + * @param x initial x-position in pixels; which direction(s) the text is drawn + * from this position depends on the alignment + * @param y initial upper y-position in pixels + * @param text text to be drawn + * @param font style/size of text + * @param alignment left_aligned, centered, or right_aligned + * @param hue hue component of text color + * @param value value component of text color + */ +void draw_font_hv_blend( SDL_Surface *surface, int x, int y, const char *text, Font font, FontAlignment alignment, Uint8 hue, Sint8 value ) +{ + switch (alignment) + { + case left_aligned: + break; + case centered: + x -= JE_textWidth(text, font) / 2; + break; + case right_aligned: + x -= JE_textWidth(text, font); + break; + } + + for (; *text != '\0'; ++text) + { + int sprite_id = font_ascii[(unsigned char)*text]; + + switch (*text) + { + case ' ': + x += 6; + break; + + case '~': + break; + + default: + if (sprite_id != -1 && sprite_exists(font, sprite_id)) + { + blit_sprite_hv_blend(surface, x, y, font, sprite_id, hue, value); + + x += sprite(font, sprite_id)->width + 1; + } + break; + } + } +} + +/** + * \brief Draws darkened text. + * + * Corresponds to blit_sprite_dark() + * + * \li like JE_outText() with (brightness < 0) if (black == true) + * + * @param surface destination surface + * @param x initial x-position in pixels; which direction(s) the text is drawn + * from this position depends on the alignment + * @param y initial upper y-position in pixels + * @param text text to be drawn + * @param font style/size of text + * @param alignment left_aligned, centered, or right_aligned + * @param black if true text is drawn as solid black, if false text is drawn by + * darkening the pixels of the destination surface + */ +void draw_font_dark( SDL_Surface *surface, int x, int y, const char *text, Font font, FontAlignment alignment, bool black ) +{ + switch (alignment) + { + case left_aligned: + break; + case centered: + x -= JE_textWidth(text, font) / 2; + break; + case right_aligned: + x -= JE_textWidth(text, font); + break; + } + + for (; *text != '\0'; ++text) + { + int sprite_id = font_ascii[(unsigned char)*text]; + + switch (*text) + { + case ' ': + x += 6; + break; + + case '~': + break; + + default: + if (sprite_id != -1 && sprite_exists(font, sprite_id)) + { + blit_sprite_dark(surface, x, y, font, sprite_id, black); + + x += sprite(font, sprite_id)->width + 1; + } + break; + } + } +} diff --git a/contrib/games/opentyrian/src/font.h b/contrib/games/opentyrian/src/font.h new file mode 100644 index 000000000..49423d949 --- /dev/null +++ b/contrib/games/opentyrian/src/font.h @@ -0,0 +1,49 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef FONT_H +#define FONT_H + +#include "SDL.h" + +#include + +typedef enum +{ + large_font = 0, + normal_font = 1, + small_font = 2 +} +Font; + +typedef enum +{ + left_aligned, + centered, + right_aligned +} +FontAlignment; + +void draw_font_hv_shadow( SDL_Surface *, int x, int y, const char *text, Font, FontAlignment, Uint8 hue, Sint8 value, bool black, int shadow_dist ); +void draw_font_hv_full_shadow( SDL_Surface *, int x, int y, const char *text, Font, FontAlignment, Uint8 hue, Sint8 value, bool black, int shadow_dist ); + +void draw_font_hv( SDL_Surface *, int x, int y, const char *text, Font, FontAlignment, Uint8 hue, Sint8 value ); +void draw_font_hv_blend( SDL_Surface *, int x, int y, const char *text, Font, FontAlignment, Uint8 hue, Sint8 value ); +void draw_font_dark( SDL_Surface *, int x, int y, const char *text, Font, FontAlignment, bool black ); + +#endif // FONT_H diff --git a/contrib/games/opentyrian/src/fonthand.c b/contrib/games/opentyrian/src/fonthand.c new file mode 100644 index 000000000..6eb94431b --- /dev/null +++ b/contrib/games/opentyrian/src/fonthand.c @@ -0,0 +1,334 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "fonthand.h" + +#include "network.h" +#include "nortsong.h" +#include "nortvars.h" +#include "opentyr.h" +#include "params.h" +#include "sprite.h" +#include "vga256d.h" +#include "video.h" + +const int font_ascii[256] = +{ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 26, 33, 60, 61, 62, -1, 32, 64, 65, 63, 84, 29, 83, 28, 80, // !"#$%&'()*+,-./ + 79, 70, 71, 72, 73, 74, 75, 76, 77, 78, 31, 30, -1, 85, -1, 27, // 0123456789:;<=>? + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // @ABCDEFGHIJKLMNO + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 68, 82, 69, -1, -1, // PQRSTUVWXYZ[\]^_ + -1, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, // `abcdefghijklmno + 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 66, 81, 67, -1, -1, // pqrstuvwxyz{|}~⌂ + + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, // ÇüéâäàåçêëèïîìÄÅ + 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, // ÉæÆôöòûùÿÖÜ¢£¥₧ƒ + 118, 119, 120, 121, 122, 123, 124, 125, 126, -1, -1, -1, -1, -1, -1, -1, // áíóúñѪº¿ + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, +}; + +/* shape constants included in newshape.h */ + +JE_byte textGlowFont, textGlowBrightness = 6; + +JE_boolean levelWarningDisplay; +JE_byte levelWarningLines; +char levelWarningText[10][61]; /* [1..10] of string [60] */ +JE_boolean warningRed; + +JE_byte warningSoundDelay; +JE_word armorShipDelay; +JE_byte warningCol; +JE_shortint warningColChange; + +void JE_dString( SDL_Surface * screen, int x, int y, const char *s, unsigned int font ) +{ + const int defaultBrightness = -3; + + int bright = 0; + + for (int i = 0; s[i] != '\0'; ++i) + { + int sprite_id = font_ascii[(unsigned char)s[i]]; + + switch (s[i]) + { + case ' ': + x += 6; + break; + + case '~': + bright = (bright == 0) ? 2 : 0; + break; + + default: + if (sprite_id != -1) + { + blit_sprite_dark(screen, x + 2, y + 2, font, sprite_id, false); + blit_sprite_hv_unsafe(screen, x, y, font, sprite_id, 0xf, defaultBrightness + bright); + + x += sprite(font, sprite_id)->width + 1; + } + break; + } + } +} + +int JE_fontCenter( const char *s, unsigned int font ) +{ + return 160 - (JE_textWidth(s, font) / 2); +} + +int JE_textWidth( const char *s, unsigned int font ) +{ + int x = 0; + + for (int i = 0; s[i] != '\0'; ++i) + { + int sprite_id = font_ascii[(unsigned char)s[i]]; + + if (s[i] == ' ') + x += 6; + else if (sprite_id != -1) + x += sprite(font, sprite_id)->width + 1; + } + + return x; +} + +void JE_textShade( SDL_Surface * screen, int x, int y, const char *s, unsigned int colorbank, int brightness, unsigned int shadetype ) +{ + switch (shadetype) + { + case PART_SHADE: + JE_outText(screen, x+1, y+1, s, 0, -1); + JE_outText(screen, x, y, s, colorbank, brightness); + break; + case FULL_SHADE: + JE_outText(screen, x-1, y, s, 0, -1); + JE_outText(screen, x+1, y, s, 0, -1); + JE_outText(screen, x, y-1, s, 0, -1); + JE_outText(screen, x, y+1, s, 0, -1); + JE_outText(screen, x, y, s, colorbank, brightness); + break; + case DARKEN: + JE_outTextAndDarken(screen, x+1, y+1, s, colorbank, brightness, TINY_FONT); + break; + case TRICK: + JE_outTextModify(screen, x, y, s, colorbank, brightness, TINY_FONT); + break; + } +} + +void JE_outText( SDL_Surface * screen, int x, int y, const char *s, unsigned int colorbank, int brightness ) +{ + int bright = 0; + + for (int i = 0; s[i] != '\0'; ++i) + { + int sprite_id = font_ascii[(unsigned char)s[i]]; + + switch (s[i]) + { + case ' ': + x += 6; + break; + + case '~': + bright = (bright == 0) ? 4 : 0; + break; + + default: + if (sprite_id != -1 && sprite_exists(TINY_FONT, sprite_id)) + { + if (brightness >= 0) + blit_sprite_hv_unsafe(screen, x, y, TINY_FONT, sprite_id, colorbank, brightness + bright); + else + blit_sprite_dark(screen, x, y, TINY_FONT, sprite_id, true); + + x += sprite(TINY_FONT, sprite_id)->width + 1; + } + break; + } + } +} + +void JE_outTextModify( SDL_Surface * screen, int x, int y, const char *s, unsigned int filter, unsigned int brightness, unsigned int font ) +{ + for (int i = 0; s[i] != '\0'; ++i) + { + int sprite_id = font_ascii[(unsigned char)s[i]]; + + if (s[i] == ' ') + { + x += 6; + } + else if (sprite_id != -1) + { + blit_sprite_hv_blend(screen, x, y, font, sprite_id, filter, brightness); + + x += sprite(font, sprite_id)->width + 1; + } + } +} + +void JE_outTextAdjust( SDL_Surface * screen, int x, int y, const char *s, unsigned int filter, int brightness, unsigned int font, JE_boolean shadow ) +{ + int bright = 0; + + for (int i = 0; s[i] != '\0'; ++i) + { + int sprite_id = font_ascii[(unsigned char)s[i]]; + + switch (s[i]) + { + case ' ': + x += 6; + break; + + case '~': + bright = (bright == 0) ? 4 : 0; + break; + + default: + if (sprite_id != -1 && sprite_exists(TINY_FONT, sprite_id)) + { + if (shadow) + blit_sprite_dark(screen, x + 2, y + 2, font, sprite_id, false); + blit_sprite_hv(screen, x, y, font, sprite_id, filter, brightness + bright); + + x += sprite(font, sprite_id)->width + 1; + } + break; + } + } +} + +void JE_outTextAndDarken( SDL_Surface * screen, int x, int y, const char *s, unsigned int colorbank, unsigned int brightness, unsigned int font ) +{ + int bright = 0; + + for (int i = 0; s[i] != '\0'; ++i) + { + int sprite_id = font_ascii[(unsigned char)s[i]]; + + switch (s[i]) + { + case ' ': + x += 6; + break; + + case '~': + bright = (bright == 0) ? 4 : 0; + break; + + default: + if (sprite_id != -1 && sprite_exists(TINY_FONT, sprite_id)) + { + blit_sprite_dark(screen, x + 1, y + 1, font, sprite_id, false); + blit_sprite_hv_unsafe(screen, x, y, font, sprite_id, colorbank, brightness + bright); + + x += sprite(font, sprite_id)->width + 1; + } + break; + } + } +} + +void JE_updateWarning( SDL_Surface * screen ) +{ + if (delaycount2() == 0) + { /*Update Color Bars*/ + + warningCol += warningColChange; + if (warningCol > 14 * 16 + 10 || warningCol < 14 * 16 + 4) + { + warningColChange = -warningColChange; + } + fill_rectangle_xy(screen, 0, 0, 319, 5, warningCol); + fill_rectangle_xy(screen, 0, 194, 319, 199, warningCol); + JE_showVGA(); + + setjasondelay2(6); + + if (warningSoundDelay > 0) + { + warningSoundDelay--; + } + else + { + warningSoundDelay = 14; + JE_playSampleNum(S_WARNING); + } + } +} + +void JE_outTextGlow( SDL_Surface * screen, int x, int y, const char *s ) +{ + JE_integer z; + JE_byte c = 15; + + if (warningRed) + { + c = 7; + } + + JE_outTextAdjust(screen, x - 1, y, s, 0, -12, textGlowFont, false); + JE_outTextAdjust(screen, x, y - 1, s, 0, -12, textGlowFont, false); + JE_outTextAdjust(screen, x + 1, y, s, 0, -12, textGlowFont, false); + JE_outTextAdjust(screen, x, y + 1, s, 0, -12, textGlowFont, false); + if (frameCountMax > 0) + for (z = 1; z <= 12; z++) + { + setjasondelay(frameCountMax); + JE_outTextAdjust(screen, x, y, s, c, z - 10, textGlowFont, false); + if (JE_anyButton()) + { + frameCountMax = 0; + } + + NETWORK_KEEP_ALIVE(); + + JE_showVGA(); + + wait_delay(); + } + for (z = (frameCountMax == 0) ? 6 : 12; z >= textGlowBrightness; z--) + { + setjasondelay(frameCountMax); + JE_outTextAdjust(screen, x, y, s, c, z - 10, textGlowFont, false); + if (JE_anyButton()) + { + frameCountMax = 0; + } + + NETWORK_KEEP_ALIVE(); + + JE_showVGA(); + + wait_delay(); + } + textGlowBrightness = 6; +} + diff --git a/contrib/games/opentyrian/src/fonthand.h b/contrib/games/opentyrian/src/fonthand.h new file mode 100644 index 000000000..ecb801b2f --- /dev/null +++ b/contrib/games/opentyrian/src/fonthand.h @@ -0,0 +1,58 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef FONTHAND_H +#define FONTHAND_H + +#include "opentyr.h" + +#include "SDL.h" + +#define PART_SHADE 0 +#define FULL_SHADE 1 +#define DARKEN 2 +#define TRICK 3 +#define NO_SHADE 255 + +extern const int font_ascii[256]; + +extern JE_byte textGlowFont, textGlowBrightness; +extern JE_boolean levelWarningDisplay; +extern JE_byte levelWarningLines; +extern char levelWarningText[10][61]; +extern JE_boolean warningRed; +extern JE_byte warningSoundDelay; +extern JE_word armorShipDelay; +extern JE_byte warningCol; +extern JE_shortint warningColChange; + +void JE_dString( SDL_Surface * screen, int x, int y, const char *s, unsigned int font ); + +int JE_fontCenter( const char *s, unsigned int font ); +int JE_textWidth( const char *s, unsigned int font ); +void JE_textShade( SDL_Surface * screen, int x, int y, const char *s, unsigned int colorbank, int brightness, unsigned int shadetype ); +void JE_outText( SDL_Surface * screen, int x, int y, const char *s, unsigned int colorbank, int brightness ); +void JE_outTextModify( SDL_Surface * screen, int x, int y, const char *s, unsigned int filter, unsigned int brightness, unsigned int font ); +void JE_outTextAdjust( SDL_Surface * screen, int x, int y, const char *s, unsigned int filter, int brightness, unsigned int font, bool shadow ); +void JE_outTextAndDarken( SDL_Surface * screen, int x, int y, const char *s, unsigned int colorbank, unsigned int brightness, unsigned int font ); + +void JE_updateWarning( SDL_Surface * screen ); +void JE_outTextGlow( SDL_Surface * screen, int x, int y, const char *s ); + +#endif /* FONTHAND_H */ + diff --git a/contrib/games/opentyrian/src/game_menu.c b/contrib/games/opentyrian/src/game_menu.c new file mode 100644 index 000000000..6cce2baad --- /dev/null +++ b/contrib/games/opentyrian/src/game_menu.c @@ -0,0 +1,3243 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "game_menu.h" + +#include "backgrnd.h" +#include "config.h" +#include "file.h" +#include "fonthand.h" +#include "joystick.h" +#include "keyboard.h" +#include "loudness.h" +#include "mainint.h" +#include "mouse.h" +#include "musmast.h" +#include "network.h" +#include "nortsong.h" +#include "nortvars.h" +#include "params.h" +#include "pcxmast.h" +#include "picload.h" +#include "player.h" +#include "shots.h" +#include "sprite.h" +#include "tyrian2.h" +#include "varz.h" +#include "vga256d.h" +#include "video.h" + +#include + +/*** Structs ***/ +struct cube_struct +{ + char title[81]; + char header[13]; + int face_sprite; + char text[90][36]; + int last_line; +}; + +/*** Globals ***/ +static int joystick_config = 0; // which joystick is being configured in menu + +static JE_word yLoc; +static JE_shortint yChg; +static int newPal, curPal, oldPal; +static JE_boolean quikSave; +static JE_byte oldMenu; +static JE_boolean backFromHelp; +static JE_integer lastDirection; +static JE_boolean firstMenu9, paletteChanged; +static JE_MenuChoiceType menuChoices; +static JE_integer col, colC; +static JE_byte lastCurSel; +static JE_integer curMenu; +static JE_byte curSel[MENU_MAX]; /* [1..maxmenu] */ +static JE_byte curItemType, curItem, cursor; +static JE_boolean leftPower, rightPower, rightPowerAfford; +static JE_byte currentCube; +static JE_boolean keyboardUsed; + +static JE_byte planetAni, planetAniWait; +static JE_byte currentDotNum, currentDotWait; +static JE_real navX, navY, newNavX, newNavY; +static JE_integer tempNavX, tempNavY; +static JE_byte planetDots[5]; /* [1..5] */ +static JE_integer planetDotX[5][10], planetDotY[5][10]; /* [1..5, 1..10] */ +static PlayerItems old_items[2]; // TODO: should not be global if possible + +static struct cube_struct cube[4]; + +static const JE_MenuChoiceType menuChoicesDefault = { 7, 9, 8, 0, 0, 11, (SAVE_FILES_NUM / 2) + 2, 0, 0, 6, 4, 6, 7, 5 }; +static const JE_byte menuEsc[MENU_MAX] = { 0, 1, 1, 1, 2, 3, 3, 1, 8, 0, 0, 11, 3, 0 }; +static const JE_byte itemAvailMap[7] = { 1, 2, 3, 9, 4, 6, 7 }; +static const JE_word planetX[21] = { 200, 150, 240, 300, 270, 280, 320, 260, 220, 150, 160, 210, 80, 240, 220, 180, 310, 330, 150, 240, 200 }; +static const JE_word planetY[21] = { 40, 90, 90, 80, 170, 30, 50, 130, 120, 150, 220, 200, 80, 50, 160, 10, 55, 55, 90, 90, 40 }; +static const uint cube_line_chars = sizeof(*cube->text) - 1; +static const uint cube_line_width = 150; + + +/*** Functions ***/ +static uint *playeritem_map( PlayerItems *items, uint i ) +{ + uint * const map[] = { &items->ship, &items->weapon[FRONT_WEAPON].id, &items->weapon[REAR_WEAPON].id, &items->shield, &items->generator, &items->sidekick[LEFT_SIDEKICK], &items->sidekick[RIGHT_SIDEKICK] }; + assert(i < COUNTOF(map)); + return map[i]; +} + + +JE_longint JE_cashLeft( void ) +{ + JE_longint tempL = player[0].cash; + JE_word itemNum = *playeritem_map(&player[0].items, curSel[1] - 2); + + tempL -= JE_getCost(curSel[1], itemNum); + + tempW = 0; + + switch (curSel[1]) + { + case 3: + case 4: + for (uint i = 1; i < player[0].items.weapon[curSel[1]-3].power; ++i) + { + tempW += weaponPort[itemNum].cost * i; + tempL -= tempW; + } + break; + } + + return tempL; +} + +void JE_itemScreen( void ) +{ + bool quit = false; + + /* SYN: Okay, here's the menu numbers. All are reindexed by -1 from the original code. + 0: full game menu + 1: upgrade ship main + 2: full game options + 3: play next level + 4: upgrade ship submenus + 5: keyboard settings + 6: load/save menu + 7: data cube menu + 8: read data cube + 9: 2 player arcade game menu + 10: 1 player arcade game menu + 11: network game options + 12: joystick settings + 13: super tyrian + */ + + free_sprite2s(&shapes6); + JE_loadCompShapes(&shapes6, '1'); // item sprites + + load_cubes(); + + VGAScreen = VGAScreenSeg; + + memcpy(menuChoices, menuChoicesDefault, sizeof(menuChoices)); + + play_song(songBuy); + + JE_loadPic(VGAScreen, 1, false); + + curPal = 1; + newPal = 0; + + JE_showVGA(); + + set_palette(colors, 0, 255); + + col = 1; + gameLoaded = false; + curItemType = 1; + cursor = 1; + curItem = 0; + + for (unsigned int i = 0; i < COUNTOF(curSel); ++i) + curSel[i] = 2; + + curMenu = 0; + + int temp_weapon_power[7]; // assumes there'll never be more than 6 weapons to choose from, 7th is "Done" + + /* JE: (* Check for where Pitems and Select match up - if no match then add to the itemavail list *) */ + for (int i = 0; i < 7; i++) + { + int item = *playeritem_map(&player[0].last_items, i); + + int slot = 0; + + for ( ; slot < itemAvailMax[itemAvailMap[i]-1]; ++slot) + { + if (itemAvail[itemAvailMap[i]-1][slot] == item) + break; + } + + if (slot == itemAvailMax[itemAvailMap[i]-1]) + { + itemAvail[itemAvailMap[i]-1][slot] = item; + itemAvailMax[itemAvailMap[i]-1]++; + } + } + + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); + + keyboardUsed = false; + firstMenu9 = false; + backFromHelp = false; + + /* JE: Sort items in merchant inventory */ + for (int x = 0; x < 9; x++) + { + if (itemAvailMax[x] > 1) + { + for (temp = 0; temp < itemAvailMax[x] - 1; temp++) + { + for (temp2 = temp; temp2 < itemAvailMax[x]; temp2++) + { + if (itemAvail[x][temp] == 0 || (itemAvail[x][temp] > itemAvail[x][temp2] && itemAvail[x][temp2] != 0)) + { + temp3 = itemAvail[x][temp]; + itemAvail[x][temp] = itemAvail[x][temp2]; + itemAvail[x][temp2] = temp3; + } + } + } + } + } + + do + { + quit = false; + + JE_getShipInfo(); + + /* JE: If curMenu==1 and twoPlayerMode is on, then force move to menu 10 */ + if (curMenu == 0) + { + if (twoPlayerMode) + curMenu = 9; + + if (isNetworkGame || onePlayerAction) + curMenu = 10; + + if (superTyrian) + curMenu = 13; + } + + paletteChanged = false; + + leftPower = false; + rightPower = false; + + /* SYN: note reindexing... "firstMenu9" refers to Menu 8 here :( */ + if (curMenu != 8 || firstMenu9) + { + memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->pitch * VGAScreen->h); + } + + if (curMenu == 1 && (curSel[curMenu] == 3 || curSel[curMenu] == 4)) + { + // reset temp_weapon_power[] every time we select upgrading front or back + const uint item = player[0].items.weapon[curSel[1] - 3].id, + item_power = player[0].items.weapon[curSel[1] - 3].power, + i = curSel[1] - 2; // 1 or 2 (front or rear) + + // set power level of owned weapon + for (int slot = 0; slot < itemAvailMax[itemAvailMap[i]-1]; ++slot) + { + if (itemAvail[itemAvailMap[i]-1][slot] == item) + temp_weapon_power[slot] = item_power; + else + temp_weapon_power[slot] = 1; + } + + // set power level for "Done" + temp_weapon_power[itemAvailMax[itemAvailMap[i]-1]] = item_power; + } + + /* play next level menu */ + if (curMenu == 3) + { + planetAni = 0; + keyboardUsed = false; + currentDotNum = 0; + currentDotWait = 8; + planetAniWait = 3; + JE_updateNavScreen(); + } + + /* Draw menu title for everything but upgrade ship submenus */ + if (curMenu != 4) + { + JE_drawMenuHeader(); + } + + /* Draw menu choices for simple menus */ + if ((curMenu >= 0 && curMenu <= 3) || (curMenu >= 9 && curMenu <= 11) || curMenu == 13) + { + JE_drawMenuChoices(); + } + + /* Data cube icons */ + if (curMenu == 0) + { + for (int i = 1; i <= cubeMax; i++) + { + blit_sprite_dark(VGAScreen, 190 + i * 18 + 2, 37 + 1, OPTION_SHAPES, 34, false); + blit_sprite(VGAScreen, 190 + i * 18, 37, OPTION_SHAPES, 34); // data cube + } + } + + /* load/save menu */ + if (curMenu == 6) + { + int min, max; + + if (twoPlayerMode) + { + min = 13; + max = 24; + } + else + { + min = 2; + max = 13; + } + + for (int x = min; x <= max; x++) + { + /* Highlight if current selection */ + temp2 = (x - min + 2 == curSel[curMenu]) ? 15 : 28; + + /* Write save game slot */ + if (x == max) + strcpy(tempStr, miscText[6-1]); + else if (saveFiles[x-2].level == 0) + strcpy(tempStr, miscText[3-1]); + else + strcpy(tempStr, saveFiles[x-2].name); + + int tempY = 38 + (x - min)*11; + + JE_textShade(VGAScreen, 163, tempY, tempStr, temp2 / 16, temp2 % 16 - 8, DARKEN); + + /* If selected with keyboard, move mouse pointer to match? Or something. */ + if (x - min + 2 == curSel[curMenu]) + { + if (keyboardUsed) + set_mouse_position(305, 38 + (x - min) * 11); + } + + if (x < max) /* x == max isn't a save slot */ + { + /* Highlight if current selection */ + temp2 = (x - min + 2 == curSel[curMenu]) ? 252 : 250; + + if (saveFiles[x-2].level == 0) + { + strcpy(tempStr, "-----"); /* Empty save slot */ + } + else + { + char buf[20]; + + strcpy(tempStr, saveFiles[x-2].levelName); + + snprintf(buf, sizeof buf, "%s%d", miscTextB[1-1], saveFiles[x-2].episode); + JE_textShade(VGAScreen, 297, tempY, buf, temp2 / 16, temp2 % 16 - 8, DARKEN); + } + + JE_textShade(VGAScreen, 245, tempY, tempStr, temp2 / 16, temp2 % 16 - 8, DARKEN); + } + + JE_drawMenuHeader(); + } + } + + /* keyboard settings menu */ + if (curMenu == 5) + { + for (int x = 2; x <= 11; x++) + { + if (x == curSel[curMenu]) + { + temp2 = 15; + if (keyboardUsed) + set_mouse_position(305, 38 + (x - 2) * 12); + } + else + { + temp2 = 28; + } + + JE_textShade(VGAScreen, 166, 38 + (x - 2)*12, menuInt[curMenu + 1][x-1], temp2 / 16, temp2 % 16 - 8, DARKEN); + + if (x < 10) /* 10 = reset to defaults, 11 = done */ + { + temp2 = (x == curSel[curMenu]) ? 252 : 250; + JE_textShade(VGAScreen, 236, 38 + (x - 2)*12, SDL_GetKeyName(keySettings[x-2]), temp2 / 16, temp2 % 16 - 8, DARKEN); + } + } + + menuChoices[5] = 11; + } + + /* Joystick settings menu */ + if (curMenu == 12) + { + const char *menu_item[] = + { + "JOYSTICK", + "ANALOG AXES", + " SENSITIVITY", + " THRESHOLD", + menuInt[6][1], + menuInt[6][4], + menuInt[6][2], + menuInt[6][3], + menuInt[6][5], + menuInt[6][6], + menuInt[6][7], + menuInt[6][8], + "MENU", + "PAUSE", + menuInt[6][9], + menuInt[6][10] + }; + + for (uint i = 0; i < COUNTOF(menu_item); i++) + { + int temp = (i == curSel[curMenu] - 2u) ? 15 : 28; + + JE_textShade(VGAScreen, 166, 38 + i * 8, menu_item[i], temp / 16, temp % 16 - 8, DARKEN); + + temp = (i == curSel[curMenu] - 2u) ? 252 : 250; + + char value[30] = ""; + if (joysticks == 0 && i < 14) // no joysticks, everything disabled + { + sprintf(value, "-"); + } + else if (i == 0) // joystick number + { + sprintf(value, "%d", joystick_config + 1); + } + else if (i == 1) // joystick is analog + { + sprintf(value, "%s", joystick[joystick_config].analog ? "TRUE" : "FALSE"); + } + else if (i < 4) // joystick analog settings + { + if (!joystick[joystick_config].analog) + temp -= 3; + sprintf(value, "%d", i == 2 ? joystick[joystick_config].sensitivity : joystick[joystick_config].threshold); + } + else if (i < 14) // assignments + { + joystick_assignments_to_string(value, sizeof(value), joystick[joystick_config].assignment[i - 4]); + } + + JE_textShade(VGAScreen, 236, 38 + i * 8, value, temp / 16, temp % 16 - 8, DARKEN); + } + + menuChoices[curMenu] = COUNTOF(menu_item) + 1; + } + + /* Upgrade weapon submenus, with weapon sim */ + if (curMenu == 4) + { + /* Move cursor until we hit either "Done" or a weapon the player can afford */ + while (curSel[4] < menuChoices[4] && JE_getCost(curSel[1], itemAvail[itemAvailMap[curSel[1]-2]-1][curSel[4]-2]) > player[0].cash) + { + curSel[4] += lastDirection; + if (curSel[4] < 2) + curSel[4] = menuChoices[4]; + else if (curSel[4] > menuChoices[4]) + curSel[4] = 2; + } + + if (curSel[4] == menuChoices[4]) + { + /* If cursor on "Done", use previous weapon */ + *playeritem_map(&player[0].items, curSel[1] - 2) = *playeritem_map(&old_items[0], curSel[1] - 2); + } + else + { + /* Otherwise display the selected weapon */ + *playeritem_map(&player[0].items, curSel[1] - 2) = itemAvail[itemAvailMap[curSel[1]-2]-1][curSel[4]-2]; + } + + /* Get power level info for front and rear weapons */ + if ((curSel[1] == 3 && curSel[4] < menuChoices[4]) || (curSel[1] == 4 && curSel[4] < menuChoices[4]-1)) + { + const uint port = curSel[1] - 3, // 0 or 1 (front or back) + item_level = player[0].items.weapon[port].power; + + // calculate upgradeCost + JE_getCost(curSel[1], itemAvail[itemAvailMap[curSel[1]-2]-1][curSel[5]-2]); + + leftPower = item_level > 1; // can downgrade + rightPower = item_level < 11; // can upgrade + + if (rightPower) + rightPowerAfford = JE_cashLeft() >= upgradeCost; // can afford upgrade + } + else + { + /* Nothing else can be upgraded / downgraded */ + leftPower = false; + rightPower = false; + } + + /* submenu title e.g., "Left Sidekick" */ + JE_dString(VGAScreen, 74 + JE_fontCenter(menuInt[2][curSel[1]-1], FONT_SHAPES), 10, menuInt[2][curSel[1]-1], FONT_SHAPES); + + /* Iterate through all submenu options */ + for (tempW = 1; tempW < menuChoices[curMenu]; tempW++) + { + int tempY = 40 + (tempW-1) * 26; /* Calculate y position */ + uint temp_cost; + + /* Is this a item or None/DONE? */ + if (tempW < menuChoices[4] - 1) + { + /* Get base cost for choice */ + temp_cost = JE_getCost(curSel[1], itemAvail[itemAvailMap[curSel[1]-2]-1][tempW-1]); + } + else + { + /* "None" is free :) */ + temp_cost = 0; + } + + int afford_shade = (temp_cost > player[0].cash) ? 4 : 0; // can player afford current weapon at all + + temp = itemAvail[itemAvailMap[curSel[1]-2]-1][tempW-1]; /* Item ID */ + switch (curSel[1]-1) + { + case 1: /* ship */ + if (temp > 90) + { + snprintf(tempStr, sizeof(tempStr), "Custom Ship %d", temp - 90); + } else { + strcpy(tempStr, ships[temp].name); + } + break; + case 2: /* front and rear weapon */ + case 3: + strcpy(tempStr, weaponPort[temp].name); + break; + case 4: /* shields */ + strcpy(tempStr, shields[temp].name); + break; + case 5: /* generator */ + strcpy(tempStr, powerSys[temp].name); + break; + case 6: /* sidekicks */ + case 7: + strcpy(tempStr, options[temp].name); + break; + } + if (tempW == curSel[curMenu]-1) + { + if (keyboardUsed) + { + set_mouse_position(305, tempY + 10); + } + temp2 = 15; + } else { + temp2 = 28; + } + + JE_getShipInfo(); + + /* item-owned marker */ + if (temp == *playeritem_map(&old_items[0], curSel[1] - 2) && temp != 0 && tempW != menuChoices[curMenu]-1) + { + fill_rectangle_xy(VGAScreen, 160, tempY+7, 300, tempY+11, 227); + blit_sprite2(VGAScreen, 298, tempY+2, shapes6, 247); + } + + /* Draw DONE */ + if (tempW == menuChoices[curMenu]-1) + { + strcpy(tempStr, miscText[13]); + } + JE_textShade(VGAScreen, 185, tempY, tempStr, temp2 / 16, temp2 % 16 - 8 - afford_shade, DARKEN); + + /* Draw icon if not DONE. NOTE: None is a normal item with a blank icon. */ + if (tempW < menuChoices[curMenu]-1) + { + JE_drawItem(curSel[1]-1, temp, 160, tempY-4); + } + + /* Make selected text brigther */ + temp2 = (tempW == curSel[curMenu]-1) ? 15 : 28; + + /* Draw Cost: if it's not the DONE option */ + if (tempW != menuChoices[curMenu]-1) + { + char buf[20]; + + snprintf(buf, sizeof buf, "Cost: %d", temp_cost); + JE_textShade(VGAScreen, 187, tempY+10, buf, temp2 / 16, temp2 % 16 - 8 - afford_shade, DARKEN); + } + } + } /* /weapon upgrade */ + + /* Draw current money and shield/armor bars, when appropriate */ + /* YKS: Ouch */ + if (((curMenu <= 2 || curMenu == 5 || curMenu == 6 || curMenu >= 10) && !twoPlayerMode) || (curMenu == 4 && (curSel[1] >= 1 && curSel[1] <= 6))) + { + if (curMenu != 4) + { + char buf[20]; + + snprintf(buf, sizeof buf, "%lu", player[0].cash); + JE_textShade(VGAScreen, 65, 173, buf, 1, 6, DARKEN); + } + JE_barDrawShadow(VGAScreen, 42, 152, 3, 14, player[0].armor, 2, 13); + JE_barDrawShadow(VGAScreen, 104, 152, 2, 14, shields[player[0].items.shield].mpwr * 2, 2, 13); + } + + /* Draw crap on the left side of the screen, i.e. two player scores, ship graphic, etc. */ + if (((curMenu >= 0 && curMenu <= 2) || curMenu == 5 || curMenu == 6 || curMenu >= 9) || (curMenu == 4 && (curSel[1] == 2 || curSel[1] == 5))) + { + if (twoPlayerMode) + { + char buf[50]; + + for (uint i = 0; i < 2; ++i) + { + snprintf(buf, sizeof(buf), "%s %lu", miscText[40 + i], player[i].cash); + JE_textShade(VGAScreen, 25, 50 + 10 * i, buf, 15, 0, FULL_SHADE); + } + } + else if (superArcadeMode != SA_NONE || superTyrian) + { + helpBoxColor = 15; + helpBoxBrightness = 4; + if (!superTyrian) + JE_helpBox(VGAScreen, 35, 25, superShips[superArcadeMode], 18); + else + JE_helpBox(VGAScreen, 35, 25, superShips[SA+3], 18); + helpBoxBrightness = 1; + + JE_textShade(VGAScreen, 25, 50, superShips[SA+1], 15, 0, FULL_SHADE); + JE_helpBox(VGAScreen, 25, 60, weaponPort[player[0].items.weapon[FRONT_WEAPON].id].name, 22); + JE_textShade(VGAScreen, 25, 120, superShips[SA+2], 15, 0, FULL_SHADE); + JE_helpBox(VGAScreen, 25, 130, special[player[0].items.special].name, 22); + } + else + { + draw_ship_illustration(); + } + } + + /* Changing the volume? */ + if ((curMenu == 2) || (curMenu == 11)) + { + JE_barDrawShadow(VGAScreen, 225, 70, 1, music_disabled ? 12 : 16, tyrMusicVolume / 12, 3, 13); + JE_barDrawShadow(VGAScreen, 225, 86, 1, samples_disabled ? 12 : 16, fxVolume / 12, 3, 13); + } + + /* 7 is data cubes menu, 8 is reading a data cube, "firstmenu9" refers to menu 8 because of reindexing */ + if (curMenu == 7 || ( curMenu == 8 && (firstMenu9 || backFromHelp) ) ) + { + firstMenu9 = false; + menuChoices[7] = cubeMax + 2; + fill_rectangle_xy(VGAScreen, 1, 1, 145, 170, 0); + + blit_sprite(VGAScreenSeg, 1, 1, OPTION_SHAPES, 20); /* Portrait area background */ + + if (curMenu == 7) + { + if (cubeMax == 0) + { + JE_helpBox(VGAScreen, 166, 80, miscText[16 - 1], 30); + tempW = 160; + temp2 = 252; + } + else + { + for (int x = 1; x <= cubeMax; x++) + { + JE_drawCube(VGAScreenSeg, 166, 38 + (x - 1) * 28, 13, 0); + if (x + 1 == curSel[curMenu]) + { + if (keyboardUsed) + set_mouse_position(305, 38 + (x - 1) * 28 + 6); + temp2 = 252; + } + else + { + temp2 = 250; + } + + helpBoxColor = temp2 / 16; + helpBoxBrightness = (temp2 % 16) - 8; + helpBoxShadeType = DARKEN; + JE_helpBox(VGAScreen, 192, 44 + (x - 1) * 28, cube[x - 1].title, 24); + } + int x = cubeMax + 1; + if (x + 1 == curSel[curMenu]) + { + if (keyboardUsed) + set_mouse_position(305, 38 + (x - 1) * 28 + 6); + temp2 = 252; + } + else + { + temp2 = 250; + } + tempW = 44 + (x - 1) * 28; + } + + JE_textShade(VGAScreen, 172, tempW, miscText[6 - 1], temp2 / 16, (temp2 % 16) - 8, DARKEN); + } + + if (curSel[7] < menuChoices[7]) + { + const int face_sprite = cube[curSel[7] - 2].face_sprite; + + if (face_sprite != -1) + { + const int face_x = 77 - (sprite(FACE_SHAPES, face_sprite)->width / 2), + face_y = 92 - (sprite(FACE_SHAPES, face_sprite)->height / 2); + + blit_sprite(VGAScreenSeg, face_x, face_y, FACE_SHAPES, face_sprite); // datacube face + + // modify pallete for face + paletteChanged = true; + temp2 = facepal[face_sprite]; + newPal = 0; + + for (temp = 1; temp <= 255 - (3 * 16); temp++) + colors[temp] = palettes[temp2][temp]; + } + } + } + + /* 2 player input devices */ + if (curMenu == 9) + { + for (uint i = 0; i < COUNTOF(inputDevice); i++) + { + if (inputDevice[i] > 2 + joysticks) + inputDevice[i] = inputDevice[i == 0 ? 1 : 0] == 1 ? 2 : 1; + + char temp[64]; + if (joysticks > 1 && inputDevice[i] > 2) + sprintf(temp, "%s %d", inputDevices[2], inputDevice[i] - 2); + else + sprintf(temp, "%s", inputDevices[inputDevice[i] - 1]); + JE_dString(VGAScreen, 186, 38 + 2 * (i + 1) * 16, temp, SMALL_FONT_SHAPES); + } + } + + /* JE: { - Step VI - Help text for current cursor location } */ + + flash = false; + + /* JE: {Reset player weapons} */ + memset(shotMultiPos, 0, sizeof(shotMultiPos)); + + JE_drawScore(); + + JE_drawMainMenuHelpText(); + + if (newPal > 0) /* can't reindex this :( */ + { + curPal = newPal; + memcpy(colors, palettes[newPal - 1], sizeof(colors)); + set_palette(palettes[newPal - 1], 0, 255); + newPal = 0; + } + + /* datacube title under face */ + if ( ( (curMenu == 7) || (curMenu == 8) ) && (curSel[7] < menuChoices[7]) ) + JE_textShade (VGAScreen, 75 - JE_textWidth(cube[curSel[7] - 2].header, TINY_FONT) / 2, 173, cube[curSel[7] - 2].header, 14, 3, DARKEN); + + /* SYN: Everything above was just drawing the screen. In the rest of it, we process + any user input (and do a few other things) */ + + /* SYN: Let's start by getting fresh events from SDL */ + service_SDL_events(true); + + if (constantPlay) + { + mainLevel = mapSection[mapPNum-1]; + jumpSection = true; + } + else + { + do + { + /* Inner loop -- this handles animations on menus that need them and handles + some keyboard events. Events it can't handle end the loop and fall through + to the main keyboard handler below. + + Also, I think all timing is handled in here. Somehow. */ + + NETWORK_KEEP_ALIVE(); + + mouseCursor = 0; + + col += colC; + if (col < -2 || col > 6) + { + colC = (-1 * colC); + } + + // data cube reading + if (curMenu == 8) + { + if (mouseX > 164 && mouseX < 299 && mouseY > 47 && mouseY < 153) + { + if (mouseY > 100) + mouseCursor = 2; + else + mouseCursor = 1; + } + + fill_rectangle_xy(VGAScreen, 160, 49, 310, 158, 228); + if (yLoc + yChg < 0) + { + yChg = 0; + yLoc = 0; + } + + yLoc += yChg; + temp = yLoc / 12; + temp2 = yLoc % 12; + tempW = 38 + 12 - temp2; + temp3 = cube[curSel[7] - 2].last_line; + + for (int x = temp + 1; x <= temp + 10; x++) + { + if (x <= temp3) + { + JE_outTextAndDarken(VGAScreen, 161, tempW, cube[curSel[7] - 2].text[x-1], 14, 3, TINY_FONT); + tempW += 12; + } + } + + fill_rectangle_xy(VGAScreen, 160, 39, 310, 48, 228); + fill_rectangle_xy(VGAScreen, 160, 157, 310, 166, 228); + + int percent_read = (cube[currentCube].last_line <= 9) + ? 100 + : (yLoc * 100) / ((cube[currentCube].last_line - 9) * 12); + + char buf[20]; + snprintf(buf, sizeof(buf), "%s %d%%", miscText[11], percent_read); + JE_outTextAndDarken(VGAScreen, 176, 160, buf, 14, 1, TINY_FONT); + + JE_dString(VGAScreen, 260, 160, miscText[12], SMALL_FONT_SHAPES); + + if (temp2 == 0) + yChg = 0; + + JE_mouseStart(); + + JE_showVGA(); + + if (backFromHelp) + { + fade_palette(colors, 10, 0, 255); + backFromHelp = false; + } + JE_mouseReplace(); + + setjasondelay(1); + } + else + { + /* current menu is not 8 (read data cube) */ + + if (curMenu == 3) + { + JE_updateNavScreen(); + JE_drawMainMenuHelpText(); + JE_drawMenuHeader(); + JE_drawMenuChoices(); + if (extraGame) + JE_dString(VGAScreen, 170, 140, miscText[68 - 1], FONT_SHAPES); + } + + if (curMenu == 7 && curSel[7] < menuChoices[7]) + { + /* Draw flashy cube */ + blit_sprite_hv_blend(VGAScreenSeg, 166, 38 + (curSel[7] - 2) * 28, OPTION_SHAPES, 25, 13, col); + } + + /* IF (curmenu = 5) AND (cursel [2] IN [3, 4, 6, 7, 8]) */ + if (curMenu == 4 && ( curSel[1] == 3 || curSel[1] == 4 || ( curSel[1] >= 6 && curSel[1] <= 8) ) ) + { + setjasondelay(3); + JE_weaponSimUpdate(); + JE_drawScore(); + service_SDL_events(false); + + if (newPal > 0) + { + curPal = newPal; + set_palette(palettes[newPal - 1], 0, 255); + newPal = 0; + } + + JE_mouseStart(); + + if (paletteChanged) + { + set_palette(colors, 0, 255); + paletteChanged = false; + } + + JE_showVGA(); /* SYN: This is where it updates the screen for the weapon sim */ + + if (backFromHelp) + { + fade_palette(colors, 10, 0, 255); + backFromHelp = false; + } + + JE_mouseReplace(); + + } else { /* current menu is anything but weapon sim or datacube */ + + setjasondelay(2); + + JE_drawScore(); + //JE_waitRetrace(); didn't do anything anyway? + + if (newPal > 0) + { + curPal = newPal; + set_palette(palettes[newPal - 1], 0, 255); + newPal = 0; + } + + JE_mouseStart(); + + if (paletteChanged) + { + set_palette(colors, 0, 255); + paletteChanged = false; + } + + JE_showVGA(); /* SYN: This is the where the screen updates for most menus */ + + JE_mouseReplace(); + + if (backFromHelp) + { + fade_palette(colors, 10, 0, 255); + backFromHelp = false; + } + + } + } + + wait_delay(); + + push_joysticks_as_keyboard(); + service_SDL_events(false); + mouseButton = JE_mousePosition(&mouseX, &mouseY); + inputDetected = newkey || mouseButton > 0; + + if (curMenu != 6) + { + if (keysactive[SDLK_s] && (keysactive[SDLK_LALT] || keysactive[SDLK_RALT]) ) + { + if (curMenu == 8 || curMenu == 7) + { + curMenu = 0; + } + quikSave = true; + oldMenu = curMenu; + curMenu = 6; + performSave = true; + newPal = 1; + oldPal = curPal; + } + if (keysactive[SDLK_l] && (keysactive[SDLK_LALT] || keysactive[SDLK_RALT]) ) + { + if (curMenu == 8 || curMenu == 7) + { + curMenu = 0; + } + quikSave = true; + oldMenu = curMenu; + curMenu = 6; + performSave = false; + newPal = 1; + oldPal = curPal; + } + } + + if (curMenu == 8) + { + if (mouseButton > 0 && mouseCursor >= 1) + { + inputDetected = false; + if (mouseCursor == 1) + { + yChg = -1; + } else { + yChg = 1; + } + } + + if (keysactive[SDLK_PAGEUP]) + { + yChg = -2; + inputDetected = false; + } + if (keysactive[SDLK_PAGEDOWN]) + { + yChg = 2; + inputDetected = false; + } + + bool joystick_up = false, joystick_down = false; + for (int j = 0; j < joysticks; j++) + { + joystick_up |= joystick[j].direction[0]; + joystick_down |= joystick[j].direction[2]; + } + + if (keysactive[SDLK_UP] || joystick_up) + { + yChg = -1; + inputDetected = false; + } + + if (keysactive[SDLK_DOWN] || joystick_down) + { + yChg = 1; + inputDetected = false; + } + + if (yChg < 0 && yLoc == 0) + { + yChg = 0; + } + if (yChg > 0 && (yLoc / 12) > cube[currentCube].last_line - 10) + { + yChg = 0; + } + } + + } while (!inputDetected); + } + + keyboardUsed = false; + + /* The rest of this just grabs input events, handles them, then proceeds on. */ + + if (mouseButton > 0) + { + lastDirection = 1; + + mouseButton = JE_mousePosition(&mouseX, &mouseY); + + if (curMenu == 7 && cubeMax == 0) + { + curMenu = 0; + JE_playSampleNum(S_SPRING); + newPal = 1; + JE_wipeKey(); + } + + if (curMenu == 8) + { + if ((mouseX > 258) && (mouseX < 290) && (mouseY > 159) && (mouseY < 171)) + { + curMenu = 7; + JE_playSampleNum(S_SPRING); + } + } + + if (curMenu == 2 || curMenu == 11) + { + if ((mouseX >= (225 - 4)) && (mouseY >= 70) && (mouseY <= 82)) + { + if (music_disabled) + { + music_disabled = false; + restart_song(); + } + + curSel[2] = 4; + + tyrMusicVolume = (mouseX - (225 - 4)) / 4 * 12; + if (tyrMusicVolume > 255) + tyrMusicVolume = 255; + } + + if ((mouseX >= (225 - 4)) && (mouseY >= 86) && (mouseY <= 98)) + { + samples_disabled = false; + + curSel[2] = 5; + + fxVolume = (mouseX - (225 - 4)) / 4 * 12; + if (fxVolume > 255) + fxVolume = 255; + } + + JE_calcFXVol(); + + set_volume(tyrMusicVolume, fxVolume); + + JE_playSampleNum(S_CURSOR); + } + + if ((mouseY > 20) && (mouseX > 170) && (mouseX < 308) && (curMenu != 8)) + { + const JE_byte mouseSelectionY[MENU_MAX] = { 16, 16, 16, 16, 26, 12, 11, 28, 0, 16, 16, 16, 8, 16 }; + + int selection = (mouseY - 38) / mouseSelectionY[curMenu]+2; + + if (curMenu == 9) + { + if (selection > 5) + selection--; + if (selection > 3) + selection--; + } + + if (curMenu == 0) + { + if (selection > 7) + selection = 7; + } + + // is play next level screen? + if (curMenu == 3) + { + if (selection == menuChoices[curMenu] + 1) + selection = menuChoices[curMenu]; + } + + if (selection <= menuChoices[curMenu]) + { + if ((curMenu == 4) && (selection == menuChoices[4])) + { + player[0].cash = JE_cashLeft(); + curMenu = 1; + JE_playSampleNum(S_ITEM); + } + else + { + JE_playSampleNum(S_CLICK); + if (curSel[curMenu] == selection) + { + JE_menuFunction(curSel[curMenu]); + } + else + { + if ((curMenu == 4) && (JE_getCost(curSel[1], itemAvail[itemAvailMap[curSel[1]-2]-1][selection-2]) > player[0].cash)) + { + JE_playSampleNum(S_CLINK); + } + else + { + if (curSel[1] == 4) + player[0].weapon_mode = 1; + + curSel[curMenu] = selection; + } + + // in front or rear weapon upgrade screen? + if ((curMenu == 4) && ((curSel[1] == 3) || (curSel[1] == 4))) + player[0].items.weapon[curSel[1]-3].power = temp_weapon_power[curSel[4]-2]; + } + } + } + + wait_noinput(false, true, false); + } + + if ((curMenu == 4) && ((curSel[1] == 3) || (curSel[1] == 4))) + { + if ((mouseX >= 23) && (mouseX <= 36) && (mouseY >= 149) && (mouseY <= 168)) + { + JE_playSampleNum(S_CURSOR); + switch (curSel[1]) + { + case 3: + case 4: + if (leftPower) + player[0].items.weapon[curSel[1]-3].power = --temp_weapon_power[curSel[4]-2]; + else + JE_playSampleNum(S_CLINK); + + break; + } + wait_noinput(false, true, false); + } + + if ((mouseX >= 119) && (mouseX <= 131) && (mouseY >= 149) && (mouseY <= 168)) + { + JE_playSampleNum(S_CURSOR); + switch (curSel[1]) + { + case 3: + case 4: + if (rightPower && rightPowerAfford) + player[0].items.weapon[curSel[1]-3].power = ++temp_weapon_power[curSel[4]-2]; + else + JE_playSampleNum(S_CLINK); + + break; + } + wait_noinput(false, true, false); + } + } + } + else if (newkey) + { + switch (lastkey_sym) + { + case SDLK_SLASH: + // if in rear weapon upgrade screen + if ( (curMenu == 4) && (curSel[1] == 4)) + { + // cycle weapon modes + if (++player[0].weapon_mode > weaponPort[player[0].items.weapon[REAR_WEAPON].id].opnum) + player[0].weapon_mode = 1; + } + break; + + case SDLK_SPACE: + case SDLK_RETURN: + keyboardUsed = true; + + // if front or rear weapon, update "Done" power level + if (curMenu == 4 && (curSel[1] == 3 || curSel[1] == 4)) + temp_weapon_power[itemAvailMax[itemAvailMap[curSel[1]-2]-1]] = player[0].items.weapon[curSel[1]-3].power; + + JE_menuFunction(curSel[curMenu]); + break; + + case SDLK_ESCAPE: + keyboardUsed = true; + + JE_playSampleNum(S_SPRING); + if ( (curMenu == 6) && quikSave) + { + curMenu = oldMenu; + newPal = oldPal; + } + else if (menuEsc[curMenu] == 0) + { + if (JE_quitRequest()) + { + gameLoaded = true; + mainLevel = 0; + } + } + else + { + if (curMenu == 4) // leaving upgrade menu without buying + { + player[0].items = old_items[0]; + curSel[4] = lastCurSel; + player[0].cash = JE_cashLeft(); + } + + if (curMenu != 8) // not data cube + newPal = 1; + + curMenu = menuEsc[curMenu] - 1; + } + break; + + case SDLK_F1: + if (!isNetworkGame) + { + JE_helpSystem(2); + fade_black(10); + + play_song(songBuy); + + JE_loadPic(VGAScreen, 1, false); + newPal = 1; + + switch (curMenu) + { + case 3: + newPal = 18; + break; + case 7: + case 8: + break; + } + + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); + + curPal = newPal; + memcpy(colors, palettes[newPal-1], sizeof(colors)); + JE_showVGA(); + newPal = 0; + backFromHelp = true; + } + break; + + case SDLK_UP: + keyboardUsed = true; + lastDirection = -1; + + if (curMenu != 8) // not data cube + JE_playSampleNum(S_CURSOR); + + curSel[curMenu]--; + if (curSel[curMenu] < 2) + curSel[curMenu] = menuChoices[curMenu]; + + // if in front or rear weapon upgrade screen + if (curMenu == 4 && (curSel[1] == 3 || curSel[1] == 4)) + { + player[0].items.weapon[curSel[1]-3].power = temp_weapon_power[curSel[4]-2]; + if (curSel[curMenu] == 4) + player[0].weapon_mode = 1; + } + + // if joystick config, skip disabled items when digital + if (curMenu == 12 && joysticks > 0 && !joystick[joystick_config].analog && curSel[curMenu] == 5) + curSel[curMenu] = 3; + + break; + + case SDLK_DOWN: + keyboardUsed = true; + lastDirection = 1; + + if (curMenu != 8) // not data cube + JE_playSampleNum(S_CURSOR); + + curSel[curMenu]++; + if (curSel[curMenu] > menuChoices[curMenu]) + curSel[curMenu] = 2; + + // if in front or rear weapon upgrade screen + if (curMenu == 4 && (curSel[1] == 3 || curSel[1] == 4)) + { + player[0].items.weapon[curSel[1]-3].power = temp_weapon_power[curSel[4]-2]; + if (curSel[curMenu] == 4) + player[0].weapon_mode = 1; + } + + // if in joystick config, skip disabled items when digital + if (curMenu == 12 && joysticks > 0 && !joystick[joystick_config].analog && curSel[curMenu] == 4) + curSel[curMenu] = 6; + + break; + + case SDLK_HOME: + if (curMenu == 8) // data cube + yLoc = 0; + break; + + case SDLK_END: + if (curMenu == 8) // data cube + yLoc = (cube[currentCube].last_line - 9) * 12; + break; + + case SDLK_LEFT: + if (curMenu == 12) // joystick settings menu + { + if (joysticks > 0) + { + switch (curSel[curMenu]) + { + case 2: + if (joystick_config == 0) + joystick_config = joysticks; + joystick_config--; + break; + case 3: + joystick[joystick_config].analog = !joystick[joystick_config].analog; + break; + case 4: + if (joystick[joystick_config].sensitivity == 0) + joystick[joystick_config].sensitivity = 10; + else + joystick[joystick_config].sensitivity--; + break; + case 5: + if (joystick[joystick_config].threshold == 0) + joystick[joystick_config].threshold = 10; + else + joystick[joystick_config].threshold--; + break; + default: + break; + } + } + } + + if (curMenu == 9) + { + switch (curSel[curMenu]) + { + case 3: + case 4: + JE_playSampleNum(S_CURSOR); + + int temp = curSel[curMenu] - 3; + do { + if (joysticks == 0) + { + inputDevice[temp == 0 ? 1 : 0] = inputDevice[temp]; // swap controllers + } + if (inputDevice[temp] <= 1) + { + inputDevice[temp] = 2 + joysticks; + } else { + inputDevice[temp]--; + } + } while (inputDevice[temp] == inputDevice[temp == 0 ? 1 : 0]); + break; + } + } + + if (curMenu == 2 || curMenu == 4 || curMenu == 11) + { + JE_playSampleNum(S_CURSOR); + } + + switch (curMenu) + { + case 2: + case 11: + switch (curSel[curMenu]) + { + case 4: + JE_changeVolume(&tyrMusicVolume, -12, &fxVolume, 0); + if (music_disabled) + { + music_disabled = false; + restart_song(); + } + break; + case 5: + JE_changeVolume(&tyrMusicVolume, 0, &fxVolume, -12); + samples_disabled = false; + break; + } + break; + case 4: + switch (curSel[1]) + { + case 3: + case 4: + if (leftPower) + player[0].items.weapon[curSel[1]-3].power = --temp_weapon_power[curSel[4]-2]; + else + JE_playSampleNum(S_CLINK); + + break; + } + break; + } + break; + + case SDLK_RIGHT: + if (curMenu == 12) // joystick settings menu + { + if (joysticks > 0) + { + switch (curSel[curMenu]) + { + case 2: + joystick_config++; + joystick_config %= joysticks; + break; + case 3: + joystick[joystick_config].analog = !joystick[joystick_config].analog; + break; + case 4: + joystick[joystick_config].sensitivity++; + joystick[joystick_config].sensitivity %= 11; + break; + case 5: + joystick[joystick_config].threshold++; + joystick[joystick_config].threshold %= 11; + break; + default: + break; + } + } + } + + if (curMenu == 9) + { + switch (curSel[curMenu]) + { + case 3: + case 4: + JE_playSampleNum(S_CURSOR); + + int temp = curSel[curMenu] - 3; + do { + if (joysticks == 0) + { + inputDevice[temp == 0 ? 1 : 0] = inputDevice[temp]; // swap controllers + } + if (inputDevice[temp] >= 2 + joysticks) + { + inputDevice[temp] = 1; + } else { + inputDevice[temp]++; + } + } while (inputDevice[temp] == inputDevice[temp == 0 ? 1 : 0]); + break; + } + } + + if (curMenu == 2 || curMenu == 4 || curMenu == 11) + { + JE_playSampleNum(S_CURSOR); + } + + switch (curMenu) + { + case 2: + case 11: + switch (curSel[curMenu]) + { + case 4: + JE_changeVolume(&tyrMusicVolume, 12, &fxVolume, 0); + if (music_disabled) + { + music_disabled = false; + restart_song(); + } + break; + case 5: + JE_changeVolume(&tyrMusicVolume, 0, &fxVolume, 12); + samples_disabled = false; + break; + } + break; + case 4: + switch (curSel[1]) + { + case 3: + case 4: + if (rightPower && rightPowerAfford) + player[0].items.weapon[curSel[1]-3].power = ++temp_weapon_power[curSel[4]-2]; + else + JE_playSampleNum(S_CLINK); + + break; + } + break; + } + break; + + default: + break; + } + } + + } while (!(quit || gameLoaded || jumpSection)); + +#ifdef WITH_NETWORK + if (!quit && isNetworkGame) + { + JE_barShade(VGAScreen, 3, 3, 316, 196); + JE_barShade(VGAScreen, 1, 1, 318, 198); + JE_dString(VGAScreen, 10, 160, "Waiting for other player.", SMALL_FONT_SHAPES); + + network_prepare(PACKET_WAITING); + network_send(4); // PACKET_WAITING + + while (true) + { + service_SDL_events(false); + JE_showVGA(); + + if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_WAITING) + { + network_update(); + break; + } + + network_update(); + network_check(); + + uSDL_Delay(16); + } + + network_state_reset(); + } + + if (isNetworkGame) + { + while (!network_is_sync()) + { + service_SDL_events(false); + JE_showVGA(); + + network_check(); + uSDL_Delay(16); + } + } +#endif + + if (gameLoaded) + fade_black(10); +} + +void draw_ship_illustration( void ) +{ + // full of evil hardcoding + + // ship + { + assert(player[0].items.ship > 0); + + const int sprite_id = (player[0].items.ship < COUNTOF(ships)) // shipedit ships get a default + ? ships[player[0].items.ship].bigshipgraphic - 1 + : 31; + + const int ship_x[6] = { 31, 0, 0, 0, 35, 31 }, + ship_y[6] = { 36, 0, 0, 0, 33, 35 }; + + const int x = ship_x[sprite_id - 27], + y = ship_y[sprite_id - 27]; + + blit_sprite(VGAScreenSeg, x, y, OPTION_SHAPES, sprite_id); + } + + // generator + { + assert(player[0].items.generator > 0 && player[0].items.generator < 7); + + const int sprite_id = (player[0].items.generator == 1) // generator 1 and generator 2 have the same sprite + ? player[0].items.generator + 15 + : player[0].items.generator + 14; + + const int generator_x[5] = { 62, 64, 67, 66, 63 }, + generator_y[5] = { 84, 85, 86, 84, 97 }; + const int x = generator_x[sprite_id - 16], + y = generator_y[sprite_id - 16]; + + blit_sprite(VGAScreenSeg, x, y, WEAPON_SHAPES, sprite_id); + } + + const int weapon_sprites[43] = + { + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, + 9, 10, 11, 21, 5, 13, -1, 14, 15, 0, + 14, 9, 8, 2, 15, 0, 13, 0, 8, 8, + 11, 1, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 2, 1 + }; + + // front weapon + if (player[0].items.weapon[FRONT_WEAPON].id > 0) + { + const int front_weapon_xy_list[43] = + { + -1, 4, 9, 3, 8, 2, 5, 10, 1, -1, + -1, -1, -1, 7, 8, -1, -1, 0, -1, 4, + 0, -1, -1, 3, -1, 4, -1, 4, -1, -1, + -1, 9, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 3, 9 + }; + + const int front_weapon_x[12] = { 59, 66, 66, 54, 61, 51, 58, 51, 61, 52, 53, 58 }; + const int front_weapon_y[12] = { 38, 53, 41, 36, 48, 35, 41, 35, 53, 41, 39, 31 }; + const int x = front_weapon_x[front_weapon_xy_list[player[0].items.weapon[FRONT_WEAPON].id]], + y = front_weapon_y[front_weapon_xy_list[player[0].items.weapon[FRONT_WEAPON].id]]; + + blit_sprite(VGAScreenSeg, x, y, WEAPON_SHAPES, weapon_sprites[player[0].items.weapon[FRONT_WEAPON].id]); // ship illustration: front weapon + } + + // rear weapon + if (player[0].items.weapon[REAR_WEAPON].id > 0) + { + const int rear_weapon_xy_list[43] = + { + -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, + 1, 2, 3, -1, 4, 5, -1, -1, 6, -1, + -1, 1, 0, -1, 6, -1, 5, -1, 0, 0, + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, -1, -1 + }; + + const int rear_weapon_x[7] = { 41, 27, 49, 43, 51, 39, 41 }; + const int rear_weapon_y[7] = { 92, 92, 113, 102, 97, 96, 76 }; + const int x = rear_weapon_x[rear_weapon_xy_list[player[0].items.weapon[REAR_WEAPON].id]], + y = rear_weapon_y[rear_weapon_xy_list[player[0].items.weapon[REAR_WEAPON].id]]; + + blit_sprite(VGAScreenSeg, x, y, WEAPON_SHAPES, weapon_sprites[player[0].items.weapon[REAR_WEAPON].id]); + } + + // sidekicks + JE_drawItem(6, player[0].items.sidekick[LEFT_SIDEKICK], 3, 84); + JE_drawItem(7, player[0].items.sidekick[RIGHT_SIDEKICK], 129, 84); + + // shield + blit_sprite_hv(VGAScreenSeg, 28, 23, OPTION_SHAPES, 26, 15, shields[player[0].items.shield].mpwr - 10); +} + +void load_cubes( void ) +{ + for (int cube_slot = 0; cube_slot < cubeMax; ++cube_slot) + { + memset(cube[cube_slot].text, 0, sizeof(cube->text)); + + load_cube(cube_slot, cubeList[cube_slot]); + } +} + +bool load_cube( int cube_slot, int cube_index ) +{ + FILE *f = dir_fopen_die(data_dir(), cube_file, "rb"); + + char buf[256]; + + // seek to the cube + while (cube_index > 0) + { + read_encrypted_pascal_string(buf, sizeof(buf), f); + if (buf[0] == '*') + --cube_index; + + if (feof(f)) + { + fclose(f); + + return false; + } + } + + str_pop_int(&buf[4], &cube[cube_slot].face_sprite); + --cube[cube_slot].face_sprite; + + read_encrypted_pascal_string(cube[cube_slot].title, sizeof(cube[cube_slot].title), f); + read_encrypted_pascal_string(cube[cube_slot].header, sizeof(cube[cube_slot].header), f); + + uint line = 0, line_chars = 0, line_width = 0; + + // for each line of decrypted text, split the line into words + // and add them individually to the lines of wrapped text + for (; ; ) + { + read_encrypted_pascal_string(buf, sizeof(buf), f); + + // end of data + if (feof(f) || buf[0] == '*') + break; + + // new paragraph + if (strlen(buf) == 0) + { + if (line_chars == 0) + line += 4; // subsequent new paragaphs indicate 4-line break + else + ++line; + line_chars = 0; + line_width = 0; + + continue; + } + + uint word_start = 0; + for (uint i = 0; ; ++i) + { + bool end_of_line = (buf[i] == '\0'), + end_of_word = end_of_line || (buf[i] == ' '); + + if (end_of_word) + { + buf[i] = '\0'; + + char *word = &buf[word_start]; + word_start = i + 1; + + uint word_chars = strlen(word), + word_width = JE_textWidth(word, TINY_FONT); + + // word won't fit; no can do + if (word_chars > cube_line_chars || word_width > cube_line_width) + break; + + bool prepend_space = true; + + line_chars += word_chars + (prepend_space ? 1 : 0); + line_width += word_width + (prepend_space ? 6 : 0); + + // word won't fit on current line; use next + if (line_chars > cube_line_chars || line_width > cube_line_width) + { + ++line; + line_chars = word_chars; + line_width = word_width; + + prepend_space = false; + } + + // append word + if (line < COUNTOF(cube->text)) + { + if (prepend_space) + strcat(cube[cube_slot].text[line], " "); + strcat(cube[cube_slot].text[line], word); + + // track last line with text + cube[cube_slot].last_line = line + 1; + } + } + + if (end_of_line) + break; + } + } + + fclose(f); + + return true; +} + +void JE_drawItem( JE_byte itemType, JE_word itemNum, JE_word x, JE_word y ) +{ + JE_word tempW = 0; + + if (itemNum > 0) + { + switch (itemType) + { + case 2: + case 3: + tempW = weaponPort[itemNum].itemgraphic; + break; + case 5: + tempW = powerSys[itemNum].itemgraphic; + break; + case 6: + case 7: + tempW = options[itemNum].itemgraphic; + break; + case 4: + tempW = shields[itemNum].itemgraphic; + break; + } + + if (itemType == 1) + { + if (itemNum > 90) + { + shipGrPtr = &shapes9; + shipGr = JE_SGr(itemNum - 90, &shipGrPtr); + blit_sprite2x2(VGAScreen, x, y, *shipGrPtr, shipGr); + } + else + { + blit_sprite2x2(VGAScreen, x, y, shapes9, ships[itemNum].shipgraphic); + } + } + else if (tempW > 0) + { + blit_sprite2x2(VGAScreen, x, y, shapes6, tempW); + } + } +} + +void JE_drawMenuHeader( void ) +{ + switch (curMenu) + { + case 8: + strcpy(tempStr, cube[curSel[7]-2].header); + break; + case 7: + strcpy(tempStr, menuInt[1][1]); + break; + case 6: + strcpy(tempStr, menuInt[3][performSave + 1]); + break; + default: + strcpy(tempStr, menuInt[curMenu + 1][0]); + break; + } + JE_dString(VGAScreen, 74 + JE_fontCenter(tempStr, FONT_SHAPES), 10, tempStr, FONT_SHAPES); +} + +void JE_drawMenuChoices( void ) +{ + JE_byte x; + char *str; + + for (x = 2; x <= menuChoices[curMenu]; x++) + { + int tempY = 38 + (x-1) * 16; + + if (curMenu == 0) + { + if (x == 7) + { + tempY += 16; + } + } + + if (curMenu == 9) + { + if (x > 3) + { + tempY += 16; + } + if (x > 4) + { + tempY += 16; + } + } + + if (!(curMenu == 3 && x == menuChoices[curMenu])) + { + tempY -= 16; + } + + str = malloc(strlen(menuInt[curMenu + 1][x-1])+2); + if (curSel[curMenu] == x) + { + str[0] = '~'; + strcpy(str+1, menuInt[curMenu + 1][x-1]); + } else { + strcpy(str, menuInt[curMenu + 1][x-1]); + } + JE_dString(VGAScreen, 166, tempY, str, SMALL_FONT_SHAPES); + free(str); + + if (keyboardUsed && curSel[curMenu] == x) + { + set_mouse_position(305, tempY + 6); + } + } +} + +void JE_updateNavScreen( void ) +{ + JE_byte x; + + /* minor issues: */ + /* TODO: The scroll to the new planet is too fast, I think */ + /* TODO: The starting coordinates for the scrolling effect may be wrong, the + yellowish planet below Tyrian isn't visible for as many frames as in the + original. */ + + tempNavX = roundf(navX); + tempNavY = roundf(navY); + fill_rectangle_xy(VGAScreen, 19, 16, 135, 169, 2); + JE_drawNavLines(true); + JE_drawNavLines(false); + JE_drawDots(); + + for (x = 0; x < 11; x++) + JE_drawPlanet(x); + + for (x = 0; x < menuChoices[3]-1; x++) + { + if (mapPlanet[x] > 11) + JE_drawPlanet(mapPlanet[x] - 1); + } + + if (mapOrigin > 11) + JE_drawPlanet(mapOrigin - 1); + + blit_sprite(VGAScreenSeg, 0, 0, OPTION_SHAPES, 28); // navigation screen interface + + if (curSel[3] < menuChoices[3]) + { + const unsigned int origin_x_offset = sprite(PLANET_SHAPES, PGR[mapOrigin-1]-1)->width / 2, + origin_y_offset = sprite(PLANET_SHAPES, PGR[mapOrigin-1]-1)->height / 2, + dest_x_offset = sprite(PLANET_SHAPES, PGR[mapPlanet[curSel[3]-2] - 1]-1)->width / 2, + dest_y_offset = sprite(PLANET_SHAPES, PGR[mapPlanet[curSel[3]-2] - 1]-1)->height / 2; + + newNavX = (planetX[mapOrigin-1] - origin_x_offset + + planetX[mapPlanet[curSel[3]-2] - 1] - dest_x_offset) / 2.0f; + newNavY = (planetY[mapOrigin-1] - origin_y_offset + + planetY[mapPlanet[curSel[3]-2] - 1] - dest_y_offset) / 2.0f; + } + + navX = navX + (newNavX - navX) / 2.0f; + navY = navY + (newNavY - navY) / 2.0f; + + if (fabsf(newNavX - navX) < 1) + navX = newNavX; + if (fabsf(newNavY - navY) < 1) + navY = newNavY; + + fill_rectangle_xy(VGAScreen, 314, 0, 319, 199, 230); + + if (planetAniWait > 0) + { + planetAniWait--; + } + else + { + planetAni++; + if (planetAni > 14) + planetAni = 0; + planetAniWait = 3; + } + + if (currentDotWait > 0) + { + currentDotWait--; + } + else + { + if (currentDotNum < planetDots[curSel[3]-2]) + currentDotNum++; + currentDotWait = 5; + } +} + +void JE_drawLines( SDL_Surface *surface, JE_boolean dark ) +{ + JE_byte x, y; + JE_integer tempX, tempY; + JE_integer tempX2, tempY2; + JE_word tempW, tempW2; + + tempX2 = -10; + tempY2 = 0; + + tempW = 0; + for (x = 0; x < 20; x++) + { + tempW += 15; + tempX = tempW - tempX2; + + if (tempX > 18 && tempX < 135) + { + if (dark) + { + JE_rectangle(surface, tempX + 1, 0, tempX + 1, 199, 32+3); + } else { + JE_rectangle(surface, tempX, 0, tempX, 199, 32+5); + } + } + } + + tempW = 0; + for (y = 0; y < 20; y++) + { + tempW += 15; + tempY = tempW - tempY2; + + if (tempY > 15 && tempY < 169) + { + if (dark) + { + JE_rectangle(surface, 0, tempY + 1, 319, tempY + 1, 32+3); + } else { + JE_rectangle(surface, 0, tempY, 319, tempY, 32+5); + } + + tempW2 = 0; + + for (x = 0; x < 20; x++) + { + tempW2 += 15; + tempX = tempW2 - tempX2; + if (tempX > 18 && tempX < 135) + { + JE_pix3(surface, tempX, tempY, 32+6); + } + } + } + } +} + +/* SYN: This was originally PROC drawlines... yes, there were two different procs called + drawlines in different scopes in the same file. Dammit, Jason, why do you do this to me? */ + +void JE_drawNavLines( JE_boolean dark ) +{ + JE_byte x, y; + JE_integer tempX, tempY; + JE_integer tempX2, tempY2; + JE_word tempW, tempW2; + + tempX2 = tempNavX >> 1; + tempY2 = tempNavY >> 1; + + tempW = 0; + for (x = 1; x <= 20; x++) + { + tempW += 15; + tempX = tempW - tempX2; + + if (tempX > 18 && tempX < 135) + { + if (dark) + JE_rectangle(VGAScreen, tempX + 1, 16, tempX + 1, 169, 1); + else + JE_rectangle(VGAScreen, tempX, 16, tempX, 169, 5); + } + } + + tempW = 0; + for (y = 1; y <= 20; y++) + { + tempW += 15; + tempY = tempW - tempY2; + + if (tempY > 15 && tempY < 169) + { + if (dark) + JE_rectangle(VGAScreen, 19, tempY + 1, 135, tempY + 1, 1); + else + JE_rectangle(VGAScreen, 8, tempY, 160, tempY, 5); + + tempW2 = 0; + + for (x = 0; x < 20; x++) + { + tempW2 += 15; + tempX = tempW2 - tempX2; + if (tempX > 18 && tempX < 135) + JE_pix3(VGAScreen, tempX, tempY, 7); + } + } + } +} + +void JE_drawDots( void ) +{ + JE_byte x, y; + JE_integer tempX, tempY; + + for (x = 0; x < mapPNum; x++) + { + for (y = 0; y < planetDots[x]; y++) + { + tempX = planetDotX[x][y] - tempNavX + 66 - 2; + tempY = planetDotY[x][y] - tempNavY + 85 - 2; + if (tempX > 0 && tempX < 140 && tempY > 0 && tempY < 168) + blit_sprite(VGAScreenSeg, tempX, tempY, OPTION_SHAPES, (x == curSel[3]-2 && y < currentDotNum) ? 30 : 29); // navigation dots + } + } +} + +void JE_drawPlanet( JE_byte planetNum ) +{ + JE_integer tempZ = PGR[planetNum]-1, + tempX = planetX[planetNum] + 66 - tempNavX - sprite(PLANET_SHAPES, tempZ)->width / 2, + tempY = planetY[planetNum] + 85 - tempNavY - sprite(PLANET_SHAPES, tempZ)->height / 2; + + if (tempX > -7 && tempX + sprite(PLANET_SHAPES, tempZ)->width < 170 && tempY > 0 && tempY < 160) + { + if (PAni[planetNum]) + tempZ += planetAni; + + blit_sprite_dark(VGAScreenSeg, tempX + 3, tempY + 3, PLANET_SHAPES, tempZ, false); + blit_sprite(VGAScreenSeg, tempX, tempY, PLANET_SHAPES, tempZ); // planets + } +} + +void JE_scaleBitmap( SDL_Surface *dst_bitmap, const SDL_Surface *src_bitmap, int x1, int y1, int x2, int y2 ) +{ + /* This function scales one screen and writes the result to another. + * The only code that calls it is the code run when you select 'ship + * specs' from the main menu. + * + * Originally this used fixed point math. I haven't seen that in ages :). + * But we're well past the point of needing that.*/ + + assert(src_bitmap != NULL && dst_bitmap != NULL); + assert(x1 >= 0 && y1 >= 0 && x2 < src_bitmap->pitch && y2 < src_bitmap->h); + + int w = x2 - x1 + 1, + h = y2 - y1 + 1; + float base_skip_w = src_bitmap->pitch / (float)w, + base_skip_h = src_bitmap->h / (float)h; + float cumulative_skip_w, cumulative_skip_h; + + + //Okay, it's time to loop through and add bits of A to a rectangle in B + Uint8 *dst = dst_bitmap->pixels; /* 8-bit specific */ + const Uint8 *src, *src_w; /* 8-bit specific */ + + dst += y1 * dst_bitmap->pitch + x1; + cumulative_skip_h = 0; + + for (int i = 0; i < h; i++) + { + //this sets src to the beginning of our desired line + src = src_w = (Uint8 *)(src_bitmap->pixels) + (src_bitmap->w * ((unsigned int)cumulative_skip_h)); + cumulative_skip_h += base_skip_h; + cumulative_skip_w = 0; + + for (int j = 0; j < w; j++) + { + //copy and move pointers + *dst = *src; + dst++; + + cumulative_skip_w += base_skip_w; + src = src_w + ((unsigned int)cumulative_skip_w); //value is floored + } + + dst += dst_bitmap->pitch - w; + } +} + +void JE_initWeaponView( void ) +{ + fill_rectangle_xy(VGAScreen, 8, 8, 144, 177, 0); + + player[0].sidekick[LEFT_SIDEKICK].x = 72 - 15; + player[0].sidekick[LEFT_SIDEKICK].y = 120; + player[0].sidekick[RIGHT_SIDEKICK].x = 72 + 15; + player[0].sidekick[RIGHT_SIDEKICK].y = 120; + + player[0].x = 72; + player[0].y = 110; + player[0].delta_x_shot_move = 0; + player[0].delta_y_shot_move = 0; + player[0].last_x_explosion_follow = 72; + player[0].last_y_explosion_follow = 110; + power = 500; + lastPower = 500; + + memset(shotAvail, 0, sizeof(shotAvail)); + + memset(shotRepeat, 1, sizeof(shotRepeat)); + memset(shotMultiPos, 0, sizeof(shotMultiPos)); + + initialize_starfield(); +} + +void JE_computeDots( void ) +{ + JE_integer tempX, tempY; + JE_longint distX, distY; + JE_byte x, y; + + for (x = 0; x < mapPNum; x++) + { + distX = (int)(planetX[mapPlanet[x]-1]) - (int)(planetX[mapOrigin-1]); + distY = (int)(planetY[mapPlanet[x]-1]) - (int)(planetY[mapOrigin-1]); + tempX = abs(distX) + abs(distY); + + if (tempX != 0) + { + planetDots[x] = roundf(sqrtf(sqrtf((distX * distX) + (distY * distY)))) - 1; + } else { + planetDots[x] = 0; + } + + if (planetDots[x] > 10) + { + planetDots[x] = 10; + } + + for (y = 0; y < planetDots[x]; y++) + { + tempX = JE_partWay(planetX[mapOrigin-1], planetX[mapPlanet[x]-1], planetDots[x], y); + tempY = JE_partWay(planetY[mapOrigin-1], planetY[mapPlanet[x]-1], planetDots[x], y); + /* ??? Why does it use temp? =P */ + planetDotX[x][y] = tempX; + planetDotY[x][y] = tempY; + } + } +} + +JE_integer JE_partWay( JE_integer start, JE_integer finish, JE_byte dots, JE_byte dist ) +{ + return (finish - start) / (dots + 2) * (dist + 1) + start; +} + +void JE_doShipSpecs( void ) +{ + /* This function is called whenever you select 'ship specs' in the + * game menu. It draws the nice green tech screen and scales it onto + * the main window. To do this we need two temp buffers, so we're going + * to use VGAScreen and game_screen for the purpose (making things more + * complex than they would be if we just malloc'd, but faster) + * + * Originally the whole system was pretty oddly designed. So I changed it. + * Currently drawFunkyScreen creates the image, scaleInPicture draws it, + * and doFunkyScreen ties everything together. Before it was more like + * an oddly designed, unreusable, global sharing hierarchy. */ + + //create the image we want + wait_noinput(true, true, true); + JE_drawShipSpecs(game_screen, VGAScreen2); + + //reset VGAScreen2, which we clobbered + JE_loadPic(VGAScreen2, 1, false); + + //draw it + JE_playSampleNum(S_SPRING); + JE_scaleInPicture(VGAScreen, game_screen); + wait_input(true, true, true); +} + +void JE_drawMainMenuHelpText( void ) +{ + char tempStr[67]; + JE_byte temp; + + temp = curSel[curMenu] - 2; + if (curMenu == 12) // joystick settings menu help + { + int help[16] = { 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 24, 11 }; + memcpy(tempStr, mainMenuHelp[help[curSel[curMenu] - 2]], sizeof(tempStr)); + } + else if (curMenu < 3 || curMenu == 9 || curMenu > 10) + { + memcpy(tempStr, mainMenuHelp[(menuHelp[curMenu][temp])-1], sizeof(tempStr)); + } + else if (curMenu == 5 && curSel[5] == 10) + { + memcpy(tempStr, mainMenuHelp[25-1], sizeof(tempStr)); + } + else if (leftPower || rightPower) + { + memcpy(tempStr, mainMenuHelp[24-1], sizeof(tempStr)); + } + else if ( (temp == menuChoices[curMenu] - 1) || ( (curMenu == 7) && (cubeMax == 0) ) ) + { + memcpy(tempStr, mainMenuHelp[12-1], sizeof(tempStr)); + } + else + { + memcpy(tempStr, mainMenuHelp[17 + curMenu - 3], sizeof(tempStr)); + } + + JE_textShade(VGAScreen, 10, 187, tempStr, 14, 1, DARKEN); +} + +JE_boolean JE_quitRequest( void ) +{ + bool quit_selected = true, done = false; + + JE_clearKeyboard(); + JE_wipeKey(); + wait_noinput(true, true, true); + + JE_barShade(VGAScreen, 65, 55, 255, 155); + + while (!done) + { + Uint8 col = 8; + int colC = 1; + + do + { + service_SDL_events(true); + setjasondelay(4); + + blit_sprite(VGAScreen, 50, 50, OPTION_SHAPES, 35); // message box + JE_textShade(VGAScreen, 70, 60, miscText[28], 0, 5, FULL_SHADE); + JE_helpBox(VGAScreen, 70, 90, miscText[30], 30); + + col += colC; + if (col > 8 || col < 2) + colC = -colC; + + int temp_x, temp_c; + + temp_x = 54 + 45 - (JE_textWidth(miscText[9], FONT_SHAPES) / 2); + temp_c = quit_selected ? col - 12 : -5; + + JE_outTextAdjust(VGAScreen, temp_x, 128, miscText[9], 15, temp_c, FONT_SHAPES, true); + + temp_x = 149 + 45 - (JE_textWidth(miscText[10], FONT_SHAPES) / 2); + temp_c = !quit_selected ? col - 12 : -5; + + JE_outTextAdjust(VGAScreen, temp_x, 128, miscText[10], 15, temp_c, FONT_SHAPES, true); + + if (has_mouse) + { + JE_mouseStart(); + JE_showVGA(); + JE_mouseReplace(); + } + else + { + JE_showVGA(); + } + + wait_delay(); + + push_joysticks_as_keyboard(); + service_SDL_events(false); + + } while (!newkey && !mousedown); + + if (mousedown) + { + if (lastmouse_y > 123 && lastmouse_y < 149) + { + if (lastmouse_x > 56 && lastmouse_x < 142) + { + quit_selected = true; + done = true; + } + else if (lastmouse_x > 151 && lastmouse_x < 237) + { + quit_selected = false; + done = true; + } + } + mousedown = false; + } + else if (newkey) + { + switch (lastkey_sym) + { + case SDLK_LEFT: + case SDLK_RIGHT: + case SDLK_TAB: + quit_selected = !quit_selected; + JE_playSampleNum(S_CURSOR); + break; + case SDLK_RETURN: + case SDLK_SPACE: + done = true; + break; + case SDLK_ESCAPE: + quit_selected = false; + done = true; + break; + default: + break; + } + } + } + + JE_playSampleNum(quit_selected ? S_SPRING : S_CLICK); + +#ifdef WITH_NETWORK + if (isNetworkGame && quit_selected) + { + network_prepare(PACKET_QUIT); + network_send(4); // PACKET QUIT + + network_tyrian_halt(0, true); + } +#endif + + return quit_selected; +} + +void JE_genItemMenu( JE_byte itemNum ) +{ + menuChoices[4] = itemAvailMax[itemAvailMap[itemNum - 2] - 1] + 2; + + temp3 = 2; + temp2 = *playeritem_map(&player[0].items, itemNum - 2); + + strcpy(menuInt[5][0], menuInt[2][itemNum - 1]); + + for (tempW = 0; tempW < itemAvailMax[itemAvailMap[itemNum - 2] - 1]; tempW++) + { + temp = itemAvail[itemAvailMap[itemNum - 2] - 1][tempW]; + switch (itemNum) + { + case 2: + strcpy(tempStr, ships[temp].name); + break; + case 3: + case 4: + strcpy(tempStr, weaponPort[temp].name); + break; + case 5: + strcpy(tempStr, shields[temp].name); + break; + case 6: + strcpy(tempStr, powerSys[temp].name); + break; + case 7: + case 8: + strcpy(tempStr, options[temp].name); + break; + } + if (temp == temp2) + { + temp3 = tempW + 2; + } + strcpy(menuInt[5][tempW], tempStr); + } + + strcpy(menuInt[5][tempW], miscText[13]); + + curSel[4] = temp3; +} + +void JE_scaleInPicture( SDL_Surface *dst, const SDL_Surface *src ) +{ + for (int i = 2; i <= 160; i += 2) + { + if (JE_anyButton()) { break; } + + JE_scaleBitmap(dst, src, 160 - i, 0, 160 + i - 1, 100 + roundf(i * 0.625f) - 1); + JE_showVGA(); + + uSDL_Delay(1); + } +} + + +void JE_drawScore( void ) +{ + char cl[24]; + if (curMenu == 4) + { + sprintf(cl, "%d", JE_cashLeft()); + JE_textShade(VGAScreen, 65, 173, cl, 1, 6, DARKEN); + } +} + +void JE_menuFunction( JE_byte select ) +{ + JE_byte x; + JE_word curSelect; + + col = 0; + colC = -1; + JE_playSampleNum(S_CLICK); + + curSelect = curSel[curMenu]; + + switch (curMenu) + { + case 0: //root menu + switch (select) + { + case 2: //cubes + curMenu = 7; + curSel[7] = 2; + break; + case 3: //shipspecs + JE_doShipSpecs(); + break; + case 4://upgradeship + curMenu = 1; + break; + case 5: //options + curMenu = 2; + break; + case 6: //nextlevel + curMenu = 3; + newPal = 18; + JE_computeDots(); + navX = planetX[mapOrigin - 1]; + navY = planetY[mapOrigin - 1]; + newNavX = navX; + newNavY = navY; + menuChoices[3] = mapPNum + 2; + curSel[3] = 2; + strcpy(menuInt[4][0], "Next Level"); + for (x = 0; x < mapPNum; x++) + { + temp = mapPlanet[x]; + strcpy(menuInt[4][x + 1], pName[temp - 1]); + } + strcpy(menuInt[4][x + 1], miscText[5]); + break; + case 7: //quit + if (JE_quitRequest()) + { + gameLoaded = true; + mainLevel = 0; + } + break; + } + break; + + case 1: //upgradeship + if (select == 9) //done + { + curMenu = 0; + } + else // selected item to upgrade + { + old_items[0] = player[0].items; + + lastDirection = 1; + JE_genItemMenu(select); + JE_initWeaponView(); + curMenu = 4; + lastCurSel = curSel[4]; + player[0].cash = player[0].cash * 2 - JE_cashLeft(); + } + break; + + case 2: //options + switch (select) + { + case 2: + curMenu = 6; + performSave = false; + quikSave = false; + break; + case 3: + curMenu = 6; + performSave = true; + quikSave = false; + break; + case 6: + curMenu = 12; + break; + case 7: + curMenu = 5; + break; + case 8: + curMenu = 0; + break; + } + break; + + case 3: //nextlevel + if (select == menuChoices[3]) //exit + { + curMenu = 0; + newPal = 1; + } else { + mainLevel = mapSection[curSelect - 2]; + jumpSection = true; + } + break; + + case 4: //buying + if (curSel[4] < menuChoices[4]) + { + // select done + curSel[4] = menuChoices[4]; + } + else // if done is selected + { + JE_playSampleNum(S_ITEM); + + player[0].cash = JE_cashLeft(); + curMenu = 1; + } + break; + + case 5: /* keyboard settings */ + if (curSelect == 10) /* reset to defaults */ + { + memcpy(keySettings, defaultKeySettings, sizeof(keySettings)); + } + else if (curSelect == 11) /* done */ + { + if (isNetworkGame || onePlayerAction) + { + curMenu = 11; + } else { + curMenu = 2; + } + } + else /* change key */ + { + temp2 = 254; + int tempY = 38 + (curSelect - 2) * 12; + JE_textShade(VGAScreen, 236, tempY, SDL_GetKeyName(keySettings[curSelect-2]), (temp2 / 16), (temp2 % 16) - 8, DARKEN); + JE_showVGA(); + + wait_noinput(true, true, true); + + col = 248; + colC = 1; + + do { + setjasondelay(1); + + col += colC; + if (col < 243 || col > 248) + { + colC *= -1; + } + JE_rectangle(VGAScreen, 230, tempY - 2, 300, tempY + 7, col); + + poll_joysticks(); + service_SDL_events(true); + + JE_showVGA(); + + wait_delay(); + } while (!newkey && !mousedown && !joydown); + + if (newkey) + { + // already used? then swap + for (uint i = 0; i < COUNTOF(keySettings); ++i) + { + if (keySettings[i] == lastkey_sym) + { + keySettings[i] = keySettings[curSelect-2]; + break; + } + } + + if (lastkey_sym != SDLK_ESCAPE && // reserved for menu + lastkey_sym != SDLK_F11 && // reserved for gamma + lastkey_sym != SDLK_p) // reserved for pause + { + JE_playSampleNum(S_CLICK); + keySettings[curSelect-2] = lastkey_sym; + ++curSelect; + } + + JE_wipeKey(); + } + } + break; + + case 6: //save + if (curSelect == 13) + { + if (quikSave) + { + curMenu = oldMenu; + newPal = oldPal; + } else { + curMenu = 2; + } + } else { + if (twoPlayerMode) + { + temp = 11; + } else { + temp = 0; + } + JE_operation(curSelect - 1 + temp); + if (quikSave) + { + curMenu = oldMenu; + newPal = oldPal; + } + } + break; + + case 7: //cubes + if (curSelect == menuChoices[curMenu]) + { + curMenu = 0; + newPal = 1; + } else { + if (cubeMax > 0) + { + firstMenu9 = true; + curMenu = 8; + yLoc = 0; + yChg = 0; + currentCube = curSel[7] - 2; + } else { + curMenu = 0; + newPal = 1; + } + } + break; + + case 8: //cubes 2 + curMenu = 7; + break; + + case 9: //2player + switch (curSel[curMenu]) + { + case 2: + mainLevel = mapSection[mapPNum-1]; + jumpSection = true; + break; + case 3: + case 4: + JE_playSampleNum(S_CURSOR); + + int temp = curSel[curMenu] - 3; + do { + if (joysticks == 0) + { + inputDevice[temp == 0 ? 1 : 0] = inputDevice[temp]; // swap controllers + } + if (inputDevice[temp] >= 2 + joysticks) + { + inputDevice[temp] = 1; + } else { + inputDevice[temp]++; + } + } while (inputDevice[temp] == inputDevice[temp == 0 ? 1 : 0]); + break; + case 5: + curMenu = 2; + break; + case 6: + if (JE_quitRequest()) + { + gameLoaded = true; + mainLevel = 0; + } + break; + } + break; + + case 10: //arcade + switch (curSel[curMenu]) + { + case 2: + mainLevel = mapSection[mapPNum-1]; + jumpSection = true; + break; + case 3: + curMenu = 2; + break; + case 4: + if (JE_quitRequest()) + { + gameLoaded = true; + mainLevel = 0; + } + break; + } + break; + + case 11: //dunno, possibly online multiplayer + switch (select) + { + case 2: + curMenu = 12; + break; + case 3: + curMenu = 5; + break; + case 6: + curMenu = 10; + break; + } + break; + + case 12: //joy + if (joysticks == 0 && select != 17) + break; + + switch (select) + { + case 2: + joystick_config++; + joystick_config %= joysticks; + break; + case 3: + joystick[joystick_config].analog = !joystick[joystick_config].analog; + break; + case 4: + if (joystick[joystick_config].analog) + { + joystick[joystick_config].sensitivity++; + joystick[joystick_config].sensitivity %= 11; + } + break; + case 5: + if (joystick[joystick_config].analog) + { + joystick[joystick_config].threshold++; + joystick[joystick_config].threshold %= 11; + } + break; + case 16: + reset_joystick_assignments(joystick_config); + break; + case 17: + if (isNetworkGame || onePlayerAction) + { + curMenu = 11; + } else { + curMenu = 2; + } + break; + default: + if (joysticks == 0) + break; + + // int temp = 254; + // JE_textShade(VGAScreen, 236, 38 + i * 8, value, temp / 16, temp % 16 - 8, DARKEN); + + JE_rectangle(VGAScreen, 235, 21 + select * 8, 310, 30 + select * 8, 248); + + Joystick_assignment temp; + if (detect_joystick_assignment(joystick_config, &temp)) + { + // if the detected assignment was already set, unset it + for (uint i = 0; i < COUNTOF(*joystick->assignment); i++) + { + if (joystick_assignment_cmp(&temp, &joystick[joystick_config].assignment[select - 6][i])) + { + joystick[joystick_config].assignment[select - 6][i].type = NONE; + goto joystick_assign_done; + } + } + + // if there is an empty assignment, set it + for (uint i = 0; i < COUNTOF(*joystick->assignment); i++) + { + if (joystick[joystick_config].assignment[select - 6][i].type == NONE) + { + joystick[joystick_config].assignment[select - 6][i] = temp; + goto joystick_assign_done; + } + } + + // if no assignments are empty, shift them all forward and set the last one + for (uint i = 0; i < COUNTOF(*joystick->assignment); i++) + { + if (i == COUNTOF(*joystick->assignment) - 1) + joystick[joystick_config].assignment[select - 6][i] = temp; + else + joystick[joystick_config].assignment[select - 6][i] = joystick[joystick_config].assignment[select - 6][i + 1]; + } + +joystick_assign_done: + curSelect++; + + poll_joysticks(); + } + } + break; + + case 13: //engage + switch (curSel[curMenu]) + { + case 2: + mainLevel = mapSection[mapPNum-1]; + jumpSection = true; + break; + case 3: + JE_doShipSpecs(); + break; + case 4: + curMenu = 2; + break; + case 5: + if (JE_quitRequest()) + { + if (isNetworkGame) + { + JE_tyrianHalt(0); + } + gameLoaded = true; + mainLevel = 0; + } + } + break; + } + + old_items[0] = player[0].items; +} + +void JE_drawShipSpecs( SDL_Surface * screen, SDL_Surface * temp_screen ) +{ + /* In this function we create our ship description image. + * + * We use a temp screen for convenience. Bad design maybe (Jason!), + * but it'll be okay (and the alternative is malloc/a large stack) */ + + int temp_x = 0, temp_y = 0, temp_index; + Uint8 *src, *dst; + + + //first, draw the text and other assorted flavoring. + JE_clr256(screen); + JE_drawLines(screen, true); + JE_drawLines(screen, false); + JE_rectangle(screen, 0, 0, 319, 199, 37); + JE_rectangle(screen, 1, 1, 318, 198, 35); + + verticalHeight = 9; + JE_outText(screen, 10, 2, ships[player[0].items.ship].name, 12, 3); + JE_helpBox(screen, 100, 20, shipInfo[player[0].items.ship-1][0], 40); + JE_helpBox(screen, 100, 100, shipInfo[player[0].items.ship-1][1], 40); + verticalHeight = 7; + + JE_outText(screen, JE_fontCenter(miscText[4], TINY_FONT), 190, miscText[4], 12, 2); + + + //now draw the green ship over that. + //This hardcoded stuff is for positioning our little ship graphic + if (player[0].items.ship > 90) + { + temp_index = 32; + } + else if (player[0].items.ship > 0) + { + temp_index = ships[player[0].items.ship].bigshipgraphic; + } + else + { + temp_index = ships[old_items[0].ship].bigshipgraphic; + } + + switch (temp_index) + { + case 32: + temp_x = 35; + temp_y = 33; + break; + case 28: + temp_x = 31; + temp_y = 36; + break; + case 33: + temp_x = 31; + temp_y = 35; + break; + default: + assert(0); + } + temp_x -= 30; + + + //draw the ship into our temp buffer. + JE_clr256(temp_screen); + blit_sprite(temp_screen, temp_x, temp_y, OPTION_SHAPES, temp_index - 1); // ship illustration + + /* But wait! Our ship is fully colored, not green! + * With a little work we could get the sprite dimensions and greenify + * the area it resides in. For now, let's just greenify the (almost + * entirely) black screen. + + * We can't work in place. In fact we'll need to overlay the result + * To avoid our temp screen dependence this has been rewritten to + * only write one line at a time.*/ + dst = screen->pixels; + src = temp_screen->pixels; + for (int y = 0; y < screen->h; y++) + { + for (int x = 0; x < screen->pitch; x++) + { + int avg = 0; + if (y > 0) + avg += *(src - screen->pitch) & 0x0f; + if (y < screen->h - 1) + avg += *(src + screen->pitch) & 0x0f; + if (x > 0) + avg += *(src - 1) & 0x0f; + if (x < screen->pitch - 1) + avg += *(src + 1) & 0x0f; + avg /= 4; + + if ((*src & 0x0f) > avg) + { + *dst = (*src & 0x0f) | 0xc0; + //} else { + // *dst = 0; + } + + src++; + dst++; + } + } +} + +void JE_weaponSimUpdate( void ) +{ + char buf[32]; + + JE_weaponViewFrame(); + + if ( (curSel[1] == 3 && curSel[4] < menuChoices[4]) || (curSel[1] == 4 && curSel[4] < menuChoices[4] - 1) ) + { + if (leftPower) + { + sprintf(buf, "%d", downgradeCost); + JE_outText(VGAScreen, 26, 137, buf, 1, 4); + } + else + { + blit_sprite(VGAScreenSeg, 24, 149, OPTION_SHAPES, 13); // downgrade disabled + } + + if (rightPower) + { + if (!rightPowerAfford) + { + sprintf(buf, "%d", upgradeCost); + JE_outText(VGAScreen, 108, 137, buf, 7, 4); + blit_sprite(VGAScreenSeg, 119, 149, OPTION_SHAPES, 14); // upgrade disabled + } + else + { + sprintf(buf, "%d", upgradeCost); + JE_outText(VGAScreen, 108, 137, buf, 1, 4); + } + } + else + { + blit_sprite(VGAScreenSeg, 119, 149, OPTION_SHAPES, 14); // upgrade disabled + } + + temp = player[0].items.weapon[curSel[1]-3].power; + + for (int x = 1; x <= temp; x++) + { + fill_rectangle_xy(VGAScreen, 39 + x * 6, 151, 39 + x * 6 + 4, 151, 251); + JE_pix(VGAScreen, 39 + x * 6, 151, 252); + fill_rectangle_xy(VGAScreen, 39 + x * 6, 152, 39 + x * 6 + 4, 164, 250); + fill_rectangle_xy(VGAScreen, 39 + x * 6, 165, 39 + x * 6 + 4, 165, 249); + } + + sprintf(buf, "POWER: %d", temp); + JE_outText(VGAScreen, 58, 137, buf, 15, 4); + } + else + { + leftPower = false; + rightPower = false; + blit_sprite(VGAScreenSeg, 20, 146, OPTION_SHAPES, 17); // hide power level interface + } + + JE_drawItem(1, player[0].items.ship, player[0].x - 5, player[0].y - 7); +} + +void JE_weaponViewFrame( void ) +{ + fill_rectangle_xy(VGAScreen, 8, 8, 143, 182, 0); + + /* JE: (* Port Configuration Display *) + (* drawportconfigbuttons;*/ + + update_and_draw_starfield(VGAScreen, 1); + + mouseX = player[0].x; + mouseY = player[0].y; + + // create shots in weapon simulator + for (uint i = 0; i < 2; ++i) + { + if (shotRepeat[i] > 0) + { + --shotRepeat[i]; + } + else + { + const uint item = player[0].items.weapon[i].id, + item_power = player[0].items.weapon[i].power - 1, + item_mode = (i == REAR_WEAPON) ? player[0].weapon_mode - 1 : 0; + + b = player_shot_create(item, i, player[0].x, player[0].y, mouseX, mouseY, weaponPort[item].op[item_mode][item_power], 1); + } + } + + if (options[player[0].items.sidekick[LEFT_SIDEKICK]].wport > 0) + { + if (shotRepeat[SHOT_LEFT_SIDEKICK] > 0) + { + --shotRepeat[SHOT_LEFT_SIDEKICK]; + } + else + { + const uint item = player[0].items.sidekick[LEFT_SIDEKICK]; + const int x = player[0].sidekick[LEFT_SIDEKICK].x, + y = player[0].sidekick[LEFT_SIDEKICK].y; + + b = player_shot_create(options[item].wport, SHOT_LEFT_SIDEKICK, x, y, mouseX, mouseY, options[item].wpnum, 1); + } + } + + if (options[player[0].items.sidekick[RIGHT_SIDEKICK]].tr == 2) + { + player[0].sidekick[RIGHT_SIDEKICK].x = player[0].x; + player[0].sidekick[RIGHT_SIDEKICK].y = MAX(10, player[0].y - 20); + } + else + { + player[0].sidekick[RIGHT_SIDEKICK].x = 72 + 15; + player[0].sidekick[RIGHT_SIDEKICK].y = 120; + } + + if (options[player[0].items.sidekick[RIGHT_SIDEKICK]].wport > 0) + { + if (shotRepeat[SHOT_RIGHT_SIDEKICK] > 0) + { + --shotRepeat[SHOT_RIGHT_SIDEKICK]; + } + else + { + const uint item = player[0].items.sidekick[RIGHT_SIDEKICK]; + const int x = player[0].sidekick[RIGHT_SIDEKICK].x, + y = player[0].sidekick[RIGHT_SIDEKICK].y; + + b = player_shot_create(options[item].wport, SHOT_RIGHT_SIDEKICK, x, y, mouseX, mouseY, options[item].wpnum, 1); + } + } + + simulate_player_shots(); + + blit_sprite(VGAScreenSeg, 0, 0, OPTION_SHAPES, 12); // upgrade interface + + + /*========================Power Bar=========================*/ + + power += powerAdd; + if (power > 900) + power = 900; + + temp = power / 10; + + for (temp = 147 - temp; temp <= 146; temp++) + { + temp2 = 113 + (146 - temp) / 9 + 2; + temp3 = (temp + 1) % 6; + if (temp3 == 1) + temp2 += 3; + else if (temp3 != 0) + temp2 += 2; + + JE_pix(VGAScreen, 141, temp, temp2 - 3); + JE_pix(VGAScreen, 142, temp, temp2 - 3); + JE_pix(VGAScreen, 143, temp, temp2 - 2); + JE_pix(VGAScreen, 144, temp, temp2 - 1); + fill_rectangle_xy(VGAScreen, 145, temp, 149, temp, temp2); + + if (temp2 - 3 < 112) + temp2++; + } + + temp = 147 - (power / 10); + temp2 = 113 + (146 - temp) / 9 + 4; + + JE_pix(VGAScreen, 141, temp - 1, temp2 - 1); + JE_pix(VGAScreen, 142, temp - 1, temp2 - 1); + JE_pix(VGAScreen, 143, temp - 1, temp2 - 1); + JE_pix(VGAScreen, 144, temp - 1, temp2 - 1); + + fill_rectangle_xy(VGAScreen, 145, temp-1, 149, temp-1, temp2); + + lastPower = temp; + + //JE_waitFrameCount(); TODO: didn't do anything? +} + diff --git a/contrib/games/opentyrian/src/game_menu.h b/contrib/games/opentyrian/src/game_menu.h new file mode 100644 index 000000000..bbeb81b58 --- /dev/null +++ b/contrib/games/opentyrian/src/game_menu.h @@ -0,0 +1,58 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef GAME_MENU_H +#define GAME_MENU_H + +#include "helptext.h" +#include "opentyr.h" + +typedef JE_byte JE_MenuChoiceType[MENU_MAX]; + +JE_longint JE_cashLeft( void ); +void JE_itemScreen( void ); + +void load_cubes( void ); +bool load_cube( int cube_slot, int cube_index ); + +void JE_drawItem( JE_byte itemType, JE_word itemNum, JE_word x, JE_word y ); +void JE_drawMenuHeader( void ); +void JE_drawMenuChoices( void ); +void JE_updateNavScreen( void ); +void JE_drawNavLines( JE_boolean dark ); +void JE_drawLines( SDL_Surface *surface, JE_boolean dark ); +void JE_drawDots( void ); +void JE_drawPlanet( JE_byte planetNum ); +void draw_ship_illustration( void ); +void JE_scaleBitmap( SDL_Surface *dst, const SDL_Surface *src, int x1, int y1, int x2, int y2 ); +void JE_initWeaponView( void ); +void JE_computeDots( void ); +JE_integer JE_partWay( JE_integer start, JE_integer finish, JE_byte dots, JE_byte dist ); +void JE_doShipSpecs( void ); +void JE_drawMainMenuHelpText( void ); +JE_boolean JE_quitRequest( void ); +void JE_genItemMenu( JE_byte itemnum ); +void JE_scaleInPicture( SDL_Surface *dst, const SDL_Surface *src ); +void JE_drawScore( void ); +void JE_menuFunction( JE_byte select ); +void JE_drawShipSpecs( SDL_Surface *, SDL_Surface * ); +void JE_weaponSimUpdate( void ); +void JE_weaponViewFrame( void ); + +#endif // GAME_MENU_H + diff --git a/contrib/games/opentyrian/src/helptext.c b/contrib/games/opentyrian/src/helptext.c new file mode 100644 index 000000000..a5923c28a --- /dev/null +++ b/contrib/games/opentyrian/src/helptext.c @@ -0,0 +1,381 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "helptext.h" + +#include "config.h" +#include "episodes.h" +#include "file.h" +#include "fonthand.h" +#include "menus.h" +#include "opentyr.h" +#include "video.h" + +#include +#include + + +const JE_byte menuHelp[MENU_MAX][11] = /* [1..maxmenu, 1..11] */ +{ + { 1, 34, 2, 3, 4, 5, 0, 0, 0, 0, 0 }, + { 6, 7, 8, 9, 10, 11, 11, 12, 0, 0, 0 }, + { 13, 14, 15, 15, 16, 17, 12, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 4, 30, 30, 3, 5, 0, 0, 0, 0, 0, 0 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 16, 17, 15, 15, 12, 0, 0, 0, 0, 0, 0 }, + { 31, 31, 31, 31, 32, 12, 0, 0, 0, 0, 0 }, + { 4, 34, 3, 5, 0, 0, 0, 0, 0, 0, 0 } +}; + +JE_byte verticalHeight = 7; +JE_byte helpBoxColor = 12; +JE_byte helpBoxBrightness = 1; +JE_byte helpBoxShadeType = FULL_SHADE; + +char helpTxt[39][231]; /* [1..39] of string [230] */ +char pName[21][16]; /* [1..21] of string [15] */ +char miscText[HELPTEXT_MISCTEXT_COUNT][42]; /* [1..68] of string [41] */ +char miscTextB[HELPTEXT_MISCTEXTB_COUNT][HELPTEXT_MISCTEXTB_SIZE]; /* [1..5] of string [10] */ +char keyName[8][18]; /* [1..8] of string [17] */ +char menuText[7][HELPTEXT_MENUTEXT_SIZE]; /* [1..7] of string [20] */ +char outputs[9][31]; /* [1..9] of string [30] */ +char topicName[6][21]; /* [1..6] of string [20] */ +char mainMenuHelp[HELPTEXT_MAINMENUHELP_COUNT][66]; /* [1..34] of string [65] */ +char inGameText[6][21]; /* [1..6] of string [20] */ +char detailLevel[6][13]; /* [1..6] of string [12] */ +char gameSpeedText[5][13]; /* [1..5] of string [12] */ +char inputDevices[3][13]; /* [1..3] of string [12] */ +char networkText[HELPTEXT_NETWORKTEXT_COUNT][HELPTEXT_NETWORKTEXT_SIZE]; /* [1..4] of string [20] */ +char difficultyNameB[11][21]; /* [0..9] of string [20] */ +char joyButtonNames[5][21]; /* [1..5] of string [20] */ +char superShips[HELPTEXT_SUPERSHIPS_COUNT][26]; /* [0..10] of string [25] */ +char specialName[HELPTEXT_SPECIALNAME_COUNT][10]; /* [1..9] of string [9] */ +char destructHelp[25][22]; /* [1..25] of string [21] */ +char weaponNames[17][17]; /* [1..17] of string [16] */ +char destructModeName[DESTRUCT_MODES][13]; /* [1..destructmodes] of string [12] */ +char shipInfo[HELPTEXT_SHIPINFO_COUNT][2][256]; /* [1..13, 1..2] of string */ +char menuInt[MENU_MAX+1][11][18]; /* [0..14, 1..11] of string [17] */ + + +void decrypt_pascal_string( char *s, int len ) +{ + static const unsigned char crypt_key[] = { 204, 129, 63, 255, 71, 19, 25, 62, 1, 99 }; + + for (int i = len - 1; i >= 0; --i) + { + s[i] ^= crypt_key[i % sizeof(crypt_key)]; + if (i > 0) + s[i] ^= s[i - 1]; + } +} + +void read_encrypted_pascal_string( char *s, int size, FILE *f ) +{ + int len = getc(f); + if (len != EOF) + { + int skip = MAX((len + 1) - size, 0); + assert(skip == 0); + + len -= skip; + efread(s, 1, len, f); + if (size > 0) + s[len] = '\0'; + fseek(f, skip, SEEK_CUR); + + decrypt_pascal_string(s, len); + } +} + +void skip_pascal_string( FILE *f ) +{ + int len = getc(f); + fseek(f, len, SEEK_CUR); +} + +void JE_helpBox( SDL_Surface *screen, int x, int y, const char *message, unsigned int boxwidth ) +{ + JE_byte startpos, endpos, pos; + JE_boolean endstring; + + char substring[256]; + + if (strlen(message) == 0) + { + return; + } + + pos = 1; + endpos = 0; + endstring = false; + + do + { + startpos = endpos + 1; + + do + { + endpos = pos; + do + { + pos++; + if (pos == strlen(message)) + { + endstring = true; + if ((unsigned)(pos - startpos) < boxwidth) + { + endpos = pos + 1; + } + } + + } while (!(message[pos-1] == ' ' || endstring)); + + } while (!((unsigned)(pos - startpos) > boxwidth || endstring)); + + SDL_strlcpy(substring, message + startpos - 1, MIN((size_t)(endpos - startpos + 1), sizeof(substring))); + JE_textShade(screen, x, y, substring, helpBoxColor, helpBoxBrightness, helpBoxShadeType); + + y += verticalHeight; + + } while (!endstring); + + if (endpos != pos + 1) + { + JE_textShade(screen, x, y, message + endpos, helpBoxColor, helpBoxBrightness, helpBoxShadeType); + } + + helpBoxColor = 12; + helpBoxShadeType = FULL_SHADE; +} + +void JE_HBox( SDL_Surface *screen, int x, int y, unsigned int messagenum, unsigned int boxwidth ) +{ + JE_helpBox(screen, x, y, helpTxt[messagenum-1], boxwidth); +} + +void JE_loadHelpText( void ) +{ + const unsigned int menuInt_entries[MENU_MAX + 1] = { -1, 7, 9, 8, -1, -1, 11, -1, -1, -1, 6, 4, 6, 7, 5 }; + + FILE *f = dir_fopen_die(data_dir(), "tyrian.hdt", "rb"); + efread(&episode1DataLoc, sizeof(JE_longint), 1, f); + + /*Online Help*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(helpTxt); ++i) + read_encrypted_pascal_string(helpTxt[i], sizeof(helpTxt[i]), f); + skip_pascal_string(f); + + /*Planet names*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(pName); ++i) + read_encrypted_pascal_string(pName[i], sizeof(pName[i]), f); + skip_pascal_string(f); + + /*Miscellaneous text*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(miscText); ++i) + read_encrypted_pascal_string(miscText[i], sizeof(miscText[i]), f); + skip_pascal_string(f); + + /*Little Miscellaneous text*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(miscTextB); ++i) + read_encrypted_pascal_string(miscTextB[i], sizeof(miscTextB[i]), f); + skip_pascal_string(f); + + /*Key names*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < menuInt_entries[6]; ++i) + read_encrypted_pascal_string(menuInt[6][i], sizeof(menuInt[6][i]), f); + skip_pascal_string(f); + + /*Main Menu*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(menuText); ++i) + read_encrypted_pascal_string(menuText[i], sizeof(menuText[i]), f); + skip_pascal_string(f); + + /*Event text*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(outputs); ++i) + read_encrypted_pascal_string(outputs[i], sizeof(outputs[i]), f); + skip_pascal_string(f); + + /*Help topics*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(topicName); ++i) + read_encrypted_pascal_string(topicName[i], sizeof(topicName[i]), f); + skip_pascal_string(f); + + /*Main Menu Help*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(mainMenuHelp); ++i) + read_encrypted_pascal_string(mainMenuHelp[i], sizeof(mainMenuHelp[i]), f); + skip_pascal_string(f); + + /*Menu 1 - Main*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < menuInt_entries[1]; ++i) + read_encrypted_pascal_string(menuInt[1][i], sizeof(menuInt[1][i]), f); + skip_pascal_string(f); + + /*Menu 2 - Items*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < menuInt_entries[2]; ++i) + read_encrypted_pascal_string(menuInt[2][i], sizeof(menuInt[2][i]), f); + skip_pascal_string(f); + + /*Menu 3 - Options*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < menuInt_entries[3]; ++i) + read_encrypted_pascal_string(menuInt[3][i], sizeof(menuInt[3][i]), f); + skip_pascal_string(f); + + /*InGame Menu*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(inGameText); ++i) + read_encrypted_pascal_string(inGameText[i], sizeof(inGameText[i]), f); + skip_pascal_string(f); + + /*Detail Level*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(detailLevel); ++i) + read_encrypted_pascal_string(detailLevel[i], sizeof(detailLevel[i]), f); + skip_pascal_string(f); + + /*Game speed text*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(gameSpeedText); ++i) + read_encrypted_pascal_string(gameSpeedText[i], sizeof(gameSpeedText[i]), f); + skip_pascal_string(f); + + // episode names + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(episode_name); ++i) + read_encrypted_pascal_string(episode_name[i], sizeof(episode_name[i]), f); + skip_pascal_string(f); + + // difficulty names + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(difficulty_name); ++i) + read_encrypted_pascal_string(difficulty_name[i], sizeof(difficulty_name[i]), f); + skip_pascal_string(f); + + // gameplay mode names + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(gameplay_name); ++i) + read_encrypted_pascal_string(gameplay_name[i], sizeof(gameplay_name[i]), f); + skip_pascal_string(f); + + /*Menu 10 - 2Player Main*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < menuInt_entries[10]; ++i) + read_encrypted_pascal_string(menuInt[10][i], sizeof(menuInt[10][i]), f); + skip_pascal_string(f); + + /*Input Devices*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(inputDevices); ++i) + read_encrypted_pascal_string(inputDevices[i], sizeof(inputDevices[i]), f); + skip_pascal_string(f); + + /*Network text*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(networkText); ++i) + read_encrypted_pascal_string(networkText[i], sizeof(networkText[i]), f); + skip_pascal_string(f); + + /*Menu 11 - 2Player Network*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < menuInt_entries[11]; ++i) + read_encrypted_pascal_string(menuInt[11][i], sizeof(menuInt[11][i]), f); + skip_pascal_string(f); + + /*HighScore Difficulty Names*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(difficultyNameB); ++i) + read_encrypted_pascal_string(difficultyNameB[i], sizeof(difficultyNameB[i]), f); + skip_pascal_string(f); + + /*Menu 12 - Network Options*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < menuInt_entries[12]; ++i) + read_encrypted_pascal_string(menuInt[12][i], sizeof(menuInt[12][i]), f); + skip_pascal_string(f); + + /*Menu 13 - Joystick*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < menuInt_entries[13]; ++i) + read_encrypted_pascal_string(menuInt[13][i], sizeof(menuInt[13][i]), f); + skip_pascal_string(f); + + /*Joystick Button Assignments*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(joyButtonNames); ++i) + read_encrypted_pascal_string(joyButtonNames[i], sizeof(joyButtonNames[i]), f); + skip_pascal_string(f); + + /*SuperShips - For Super Arcade Mode*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(superShips); ++i) + read_encrypted_pascal_string(superShips[i], sizeof(superShips[i]), f); + skip_pascal_string(f); + + /*SuperShips - For Super Arcade Mode*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(specialName); ++i) + read_encrypted_pascal_string(specialName[i], sizeof(specialName[i]), f); + skip_pascal_string(f); + + /*Secret DESTRUCT game*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(destructHelp); ++i) + read_encrypted_pascal_string(destructHelp[i], sizeof(destructHelp[i]), f); + skip_pascal_string(f); + + /*Secret DESTRUCT weapons*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(weaponNames); ++i) + read_encrypted_pascal_string(weaponNames[i], sizeof(weaponNames[i]), f); + skip_pascal_string(f); + + /*Secret DESTRUCT modes*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(destructModeName); ++i) + read_encrypted_pascal_string(destructModeName[i], sizeof(destructModeName[i]), f); + skip_pascal_string(f); + + /*NEW: Ship Info*/ + skip_pascal_string(f); + for (unsigned int i = 0; i < COUNTOF(shipInfo); ++i) + { + read_encrypted_pascal_string(shipInfo[i][0], sizeof(shipInfo[i][0]), f); + read_encrypted_pascal_string(shipInfo[i][1], sizeof(shipInfo[i][1]), f); + } + skip_pascal_string(f); + + fclose(f); +} + diff --git a/contrib/games/opentyrian/src/helptext.h b/contrib/games/opentyrian/src/helptext.h new file mode 100644 index 000000000..2b79b010c --- /dev/null +++ b/contrib/games/opentyrian/src/helptext.h @@ -0,0 +1,80 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef HELPTEXT_H +#define HELPTEXT_H + +#include "opentyr.h" + +#include "SDL.h" + +#include + +#define MENU_MAX 14 + +#define DESTRUCT_MODES 5 + +extern const JE_byte menuHelp[MENU_MAX][11]; /* [1..14, 1..11] */ + +extern JE_byte verticalHeight; +extern JE_byte helpBoxColor, helpBoxBrightness, helpBoxShadeType; + +#define HELPTEXT_MISCTEXT_COUNT 68 +#define HELPTEXT_MISCTEXTB_COUNT 5 +#define HELPTEXT_MISCTEXTB_SIZE 11 +#define HELPTEXT_MENUTEXT_SIZE 21 +#define HELPTEXT_MAINMENUHELP_COUNT 34 +#define HELPTEXT_NETWORKTEXT_COUNT 4 +#define HELPTEXT_NETWORKTEXT_SIZE 22 +#define HELPTEXT_SUPERSHIPS_COUNT 11 +#define HELPTEXT_SPECIALNAME_COUNT 9 +#define HELPTEXT_SHIPINFO_COUNT 13 + +extern char helpTxt[39][231]; +extern char pName[21][16]; +extern char miscText[HELPTEXT_MISCTEXT_COUNT][42]; +extern char miscTextB[HELPTEXT_MISCTEXTB_COUNT][HELPTEXT_MISCTEXTB_SIZE]; +extern char keyName[8][18]; +extern char menuText[7][HELPTEXT_MENUTEXT_SIZE]; +extern char outputs[9][31]; +extern char topicName[6][21]; +extern char mainMenuHelp[HELPTEXT_MAINMENUHELP_COUNT][66]; +extern char inGameText[6][21]; +extern char detailLevel[6][13]; +extern char gameSpeedText[5][13]; +extern char inputDevices[3][13]; +extern char networkText[HELPTEXT_NETWORKTEXT_COUNT][HELPTEXT_NETWORKTEXT_SIZE]; +extern char difficultyNameB[11][21]; +extern char joyButtonNames[5][21]; +extern char superShips[HELPTEXT_SUPERSHIPS_COUNT][26]; +extern char specialName[HELPTEXT_SPECIALNAME_COUNT][10]; +extern char destructHelp[25][22]; +extern char weaponNames[17][17]; +extern char destructModeName[DESTRUCT_MODES][13]; +extern char shipInfo[HELPTEXT_SHIPINFO_COUNT][2][256]; +extern char menuInt[MENU_MAX+1][11][18]; + +void read_encrypted_pascal_string( char *s, int size, FILE *f ); +void skip_pascal_string( FILE *f ); + +void JE_helpBox( SDL_Surface *screen, int x, int y, const char *message, unsigned int boxwidth ); +void JE_HBox( SDL_Surface *screen, int x, int y, unsigned int messagenum, unsigned int boxwidth ); +void JE_loadHelpText( void ); + +#endif /* HELPTEXT_H */ + diff --git a/contrib/games/opentyrian/src/joystick.c b/contrib/games/opentyrian/src/joystick.c new file mode 100644 index 000000000..6fad1ef67 --- /dev/null +++ b/contrib/games/opentyrian/src/joystick.c @@ -0,0 +1,653 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "joystick.h" + +#include "config.h" +#include "config_file.h" +#include "file.h" +#include "keyboard.h" +#include "nortsong.h" +#include "opentyr.h" +#include "params.h" +#include "varz.h" +#include "video.h" + +#include +#include +#include + +int joystick_axis_threshold( int j, int value ); +int check_assigned( SDL_Joystick *joystick_handle, const Joystick_assignment assignment[2] ); + +const char *assignment_to_code( const Joystick_assignment *assignment ); +void code_to_assignment( Joystick_assignment *assignment, const char *buffer ); + +int joystick_repeat_delay = 300; // milliseconds, repeat delay for buttons +bool joydown = false; // any joystick buttons down, updated by poll_joysticks() +bool ignore_joystick = false; + +int joysticks = 0; +Joystick *joystick = NULL; + +static const int joystick_analog_max = 32767; + +// eliminates axis movement below the threshold +int joystick_axis_threshold( int j, int value ) +{ + assert(j < joysticks); + + bool negative = value < 0; + if (negative) + value = -value; + + if (value <= joystick[j].threshold * 1000) + return 0; + + value -= joystick[j].threshold * 1000; + + return negative ? -value : value; +} + +// converts joystick axis to sane Tyrian-usable value (based on sensitivity) +int joystick_axis_reduce( int j, int value ) +{ + assert(j < joysticks); + + value = joystick_axis_threshold(j, value); + + if (value == 0) + return 0; + + return value / (3000 - 200 * joystick[j].sensitivity); +} + +// converts analog joystick axes to an angle +// returns false if axes are centered (there is no angle) +bool joystick_analog_angle( int j, float *angle ) +{ + assert(j < joysticks); + + float x = joystick_axis_threshold(j, joystick[j].x), y = joystick_axis_threshold(j, joystick[j].y); + + if (x != 0) + { + *angle += atanf(-y / x); + *angle += (x < 0) ? -M_PI_2 : M_PI_2; + return true; + } + else if (y != 0) + { + *angle += y < 0 ? M_PI : 0; + return true; + } + + return false; +} + +/* gives back value 0..joystick_analog_max indicating that one of the assigned + * buttons has been pressed or that one of the assigned axes/hats has been moved + * in the assigned direction + */ +int check_assigned( SDL_Joystick *joystick_handle, const Joystick_assignment assignment[2] ) +{ + int result = 0; + + for (int i = 0; i < 2; i++) + { + int temp = 0; + + switch (assignment[i].type) + { + case NONE: + continue; + + case AXIS: + temp = SDL_JoystickGetAxis(joystick_handle, assignment[i].num); + + if (assignment[i].negative_axis) + temp = -temp; + break; + + case BUTTON: + temp = SDL_JoystickGetButton(joystick_handle, assignment[i].num) == 1 ? joystick_analog_max : 0; + break; + + case HAT: + temp = SDL_JoystickGetHat(joystick_handle, assignment[i].num); + + if (assignment[i].x_axis) + temp &= SDL_HAT_LEFT | SDL_HAT_RIGHT; + else + temp &= SDL_HAT_UP | SDL_HAT_DOWN; + + if (assignment[i].negative_axis) + temp &= SDL_HAT_LEFT | SDL_HAT_UP; + else + temp &= SDL_HAT_RIGHT | SDL_HAT_DOWN; + + temp = temp ? joystick_analog_max : 0; + break; + } + + if (temp > result) + result = temp; + } + + return result; +} + +// updates joystick state +void poll_joystick( int j ) +{ + assert(j < joysticks); + + if (joystick[j].handle == NULL) + return; + + SDL_JoystickUpdate(); + + // indicates that a direction/action was pressed since last poll + joystick[j].input_pressed = false; + + // indicates that an direction/action has been held long enough to fake a repeat press + bool repeat = joystick[j].joystick_delay < SDL_GetTicks(); + + // update direction state + for (uint d = 0; d < COUNTOF(joystick[j].direction); d++) + { + bool old = joystick[j].direction[d]; + + joystick[j].analog_direction[d] = check_assigned(joystick[j].handle, joystick[j].assignment[d]); + joystick[j].direction[d] = joystick[j].analog_direction[d] > (joystick_analog_max / 2); + joydown |= joystick[j].direction[d]; + + joystick[j].direction_pressed[d] = joystick[j].direction[d] && (!old || repeat); + joystick[j].input_pressed |= joystick[j].direction_pressed[d]; + } + + joystick[j].x = -joystick[j].analog_direction[3] + joystick[j].analog_direction[1]; + joystick[j].y = -joystick[j].analog_direction[0] + joystick[j].analog_direction[2]; + + // update action state + for (uint d = 0; d < COUNTOF(joystick[j].action); d++) + { + bool old = joystick[j].action[d]; + + joystick[j].action[d] = check_assigned(joystick[j].handle, joystick[j].assignment[d + COUNTOF(joystick[j].direction)]) > (joystick_analog_max / 2); + joydown |= joystick[j].action[d]; + + joystick[j].action_pressed[d] = joystick[j].action[d] && (!old || repeat); + joystick[j].input_pressed |= joystick[j].action_pressed[d]; + } + + joystick[j].confirm = joystick[j].action[0] || joystick[j].action[4]; + joystick[j].cancel = joystick[j].action[1] || joystick[j].action[5]; + + // if new input, reset press-repeat delay + if (joystick[j].input_pressed) + joystick[j].joystick_delay = SDL_GetTicks() + joystick_repeat_delay; +} + +// updates all joystick states +void poll_joysticks( void ) +{ + joydown = false; + + for (int j = 0; j < joysticks; j++) + poll_joystick(j); +} + +// sends SDL KEYDOWN and KEYUP events for a key +void push_key( SDLKey key ) +{ + SDL_Event e; + + memset(&e.key.keysym, 0, sizeof(e.key.keysym)); + + e.key.keysym.sym = key; + e.key.keysym.unicode = key; + + e.key.state = SDL_RELEASED; + + e.type = SDL_KEYDOWN; + SDL_PushEvent(&e); + + e.type = SDL_KEYUP; + SDL_PushEvent(&e); +} + +// helps us be lazy by pretending joysticks are a keyboard (useful for menus) +void push_joysticks_as_keyboard( void ) +{ + const SDLKey confirm = SDLK_RETURN, cancel = SDLK_ESCAPE; + const SDLKey direction[4] = { SDLK_UP, SDLK_RIGHT, SDLK_DOWN, SDLK_LEFT }; + + poll_joysticks(); + + for (int j = 0; j < joysticks; j++) + { + if (!joystick[j].input_pressed) + continue; + + if (joystick[j].confirm) + push_key(confirm); + if (joystick[j].cancel) + push_key(cancel); + + for (uint d = 0; d < COUNTOF(joystick[j].direction_pressed); d++) + { + if (joystick[j].direction_pressed[d]) + push_key(direction[d]); + } + } +} + +// initializes SDL joystick system and loads assignments for joysticks found +void init_joysticks( void ) +{ + if (ignore_joystick) + return; + + if (SDL_InitSubSystem(SDL_INIT_JOYSTICK)) + { + fprintf(stderr, "warning: failed to initialize joystick system: %s\n", SDL_GetError()); + ignore_joystick = true; + return; + } + + SDL_JoystickEventState(SDL_IGNORE); + + joysticks = SDL_NumJoysticks(); + joystick = malloc(joysticks * sizeof(*joystick)); + + for (int j = 0; j < joysticks; j++) + { + memset(&joystick[j], 0, sizeof(*joystick)); + + joystick[j].handle = SDL_JoystickOpen(j); + if (joystick[j].handle != NULL) + { + printf("joystick detected: %s ", SDL_JoystickName(j)); + printf("(%d axes, %d buttons, %d hats)\n", + SDL_JoystickNumAxes(joystick[j].handle), + SDL_JoystickNumButtons(joystick[j].handle), + SDL_JoystickNumHats(joystick[j].handle)); + + if (!load_joystick_assignments(&opentyrian_config, j)) + reset_joystick_assignments(j); + } + } + + if (joysticks == 0) + printf("no joysticks detected\n"); +} + +// deinitializes SDL joystick system and saves joystick assignments +void deinit_joysticks( void ) +{ + if (ignore_joystick) + return; + + for (int j = 0; j < joysticks; j++) + { + if (joystick[j].handle != NULL) + { + save_joystick_assignments(&opentyrian_config, j); + SDL_JoystickClose(joystick[j].handle); + } + } + + free(joystick); + + SDL_QuitSubSystem(SDL_INIT_JOYSTICK); +} + +void reset_joystick_assignments( int j ) +{ + assert(j < joysticks); + + // defaults: first 2 axes, first hat, first 6 buttons + for (uint a = 0; a < COUNTOF(joystick[j].assignment); a++) + { + // clear assignments + for (uint i = 0; i < COUNTOF(joystick[j].assignment[a]); i++) + joystick[j].assignment[a][i].type = NONE; + + if (a < 4) + { + if (SDL_JoystickNumAxes(joystick[j].handle) >= 2) + { + joystick[j].assignment[a][0].type = AXIS; + joystick[j].assignment[a][0].num = (a + 1) % 2; + joystick[j].assignment[a][0].negative_axis = (a == 0 || a == 3); + } + + if (SDL_JoystickNumHats(joystick[j].handle) >= 1) + { + joystick[j].assignment[a][1].type = HAT; + joystick[j].assignment[a][1].num = 0; + joystick[j].assignment[a][1].x_axis = (a == 1 || a == 3); + joystick[j].assignment[a][1].negative_axis = (a == 0 || a == 3); + } + } + else + { + if (a - 4 < (unsigned)SDL_JoystickNumButtons(joystick[j].handle)) + { + joystick[j].assignment[a][0].type = BUTTON; + joystick[j].assignment[a][0].num = a - 4; + } + } + } + + joystick[j].analog = false; + joystick[j].sensitivity = 5; + joystick[j].threshold = 5; +} + +static const char* const assignment_names[] = +{ + "up", + "right", + "down", + "left", + "fire", + "change fire", + "left sidekick", + "right sidekick", + "menu", + "pause", +}; + +bool load_joystick_assignments( Config *config, int j ) +{ + ConfigSection *section = config_find_section(config, "joystick", SDL_JoystickName(j)); + if (section == NULL) + return false; + + if (!config_get_bool_option(section, "analog", &joystick[j].analog)) + joystick[j].analog = false; + + joystick[j].sensitivity = config_get_or_set_int_option(section, "sensitivity", 5); + + joystick[j].threshold = config_get_or_set_int_option(section, "threshold", 5); + + for (size_t a = 0; a < COUNTOF(assignment_names); ++a) + { + for (unsigned int i = 0; i < COUNTOF(joystick[j].assignment[a]); ++i) + joystick[j].assignment[a][i].type = NONE; + + ConfigOption *option = config_get_option(section, assignment_names[a]); + if (option == NULL) + continue; + + foreach_option_i_value(i, value, option) + { + if (i >= COUNTOF(joystick[j].assignment[a])) + break; + + code_to_assignment(&joystick[j].assignment[a][i], value); + } + } + + return true; +} + +bool save_joystick_assignments( Config *config, int j ) +{ + ConfigSection *section = config_find_or_add_section(config, "joystick", SDL_JoystickName(j)); + if (section == NULL) + exit(EXIT_FAILURE); // out of memory + + config_set_bool_option(section, "analog", joystick[j].analog, NO_YES); + + config_set_int_option(section, "sensitivity", joystick[j].sensitivity); + + config_set_int_option(section, "threshold", joystick[j].threshold); + + for (size_t a = 0; a < COUNTOF(assignment_names); ++a) + { + ConfigOption *option = config_set_option(section, assignment_names[a], NULL); + if (option == NULL) + exit(EXIT_FAILURE); // out of memory + + option = config_set_value(option, NULL); + if (option == NULL) + exit(EXIT_FAILURE); // out of memory + + for (size_t i = 0; i < COUNTOF(joystick[j].assignment[a]); ++i) + { + if (joystick[j].assignment[a][i].type == NONE) + continue; + + option = config_add_value(option, assignment_to_code(&joystick[j].assignment[a][i])); + if (option == NULL) + exit(EXIT_FAILURE); // out of memory + } + } + + return true; +} + +// fills buffer with comma separated list of assigned joystick functions +void joystick_assignments_to_string( char *buffer, size_t buffer_len, const Joystick_assignment *assignments ) +{ + strncpy(buffer, "", buffer_len); + + bool comma = false; + for (uint i = 0; i < COUNTOF(*joystick->assignment); ++i) + { + if (assignments[i].type == NONE) + continue; + + size_t len = snprintf(buffer, buffer_len, "%s%s", + comma ? ", " : "", + assignment_to_code(&assignments[i])); + buffer += len; + buffer_len -= len; + + comma = true; + } +} + +// reverse of assignment_to_code() +void code_to_assignment( Joystick_assignment *assignment, const char *buffer ) +{ + memset(assignment, 0, sizeof(*assignment)); + + char axis = 0, direction = 0; + + if (sscanf(buffer, " AX %d%c", &assignment->num, &direction) == 2) + assignment->type = AXIS; + else if (sscanf(buffer, " BTN %d", &assignment->num) == 1) + assignment->type = BUTTON; + else if (sscanf(buffer, " H %d%c%c", &assignment->num, &axis, &direction) == 3) + assignment->type = HAT; + + if (assignment->num == 0) + assignment->type = NONE; + else + --assignment->num; + + assignment->x_axis = (toupper(axis) == 'X'); + assignment->negative_axis = (toupper(direction) == '-'); +} + +/* gives the short (6 or less characters) identifier for a joystick assignment + * + * two of these per direction/action is all that can fit on the joystick config screen, + * assuming two digits for the axis/button/hat number + */ +const char *assignment_to_code( const Joystick_assignment *assignment ) +{ + static char name[7]; + + switch (assignment->type) + { + case NONE: + strcpy(name, ""); + break; + + case AXIS: + snprintf(name, sizeof(name), "AX %d%c", + assignment->num + 1, + assignment->negative_axis ? '-' : '+'); + break; + + case BUTTON: + snprintf(name, sizeof(name), "BTN %d", + assignment->num + 1); + break; + + case HAT: + snprintf(name, sizeof(name), "H %d%c%c", + assignment->num + 1, + assignment->x_axis ? 'X' : 'Y', + assignment->negative_axis ? '-' : '+'); + break; + } + + return name; +} + +// captures joystick input for configuring assignments +// returns false if non-joystick input was detected +// TODO: input from joystick other than the one being configured probably should not be ignored +bool detect_joystick_assignment( int j, Joystick_assignment *assignment ) +{ + // get initial joystick state to compare against to see if anything was pressed + + const int axes = SDL_JoystickNumAxes(joystick[j].handle); + Sint16 *axis = malloc(axes * sizeof(*axis)); + for (int i = 0; i < axes; i++) + axis[i] = SDL_JoystickGetAxis(joystick[j].handle, i); + + const int buttons = SDL_JoystickNumButtons(joystick[j].handle); + Uint8 *button = malloc(buttons * sizeof(*button)); + for (int i = 0; i < buttons; i++) + button[i] = SDL_JoystickGetButton(joystick[j].handle, i); + + const int hats = SDL_JoystickNumHats(joystick[j].handle); + Uint8 *hat = malloc(hats * sizeof(*hat)); + for (int i = 0; i < hats; i++) + hat[i] = SDL_JoystickGetHat(joystick[j].handle, i); + + bool detected = false; + + do + { + setjasondelay(1); + + SDL_JoystickUpdate(); + + for (int i = 0; i < axes; ++i) + { + Sint16 temp = SDL_JoystickGetAxis(joystick[j].handle, i); + + if (abs(temp - axis[i]) > joystick_analog_max * 2 / 3) + { + assignment->type = AXIS; + assignment->num = i; + assignment->negative_axis = temp < axis[i]; + detected = true; + break; + } + } + + for (int i = 0; i < buttons; ++i) + { + Uint8 new_button = SDL_JoystickGetButton(joystick[j].handle, i), + changed = button[i] ^ new_button; + + if (!changed) + continue; + + if (new_button == 0) // button was released + { + button[i] = new_button; + } + else // button was pressed + { + assignment->type = BUTTON; + assignment->num = i; + detected = true; + break; + } + } + + for (int i = 0; i < hats; ++i) + { + Uint8 new_hat = SDL_JoystickGetHat(joystick[j].handle, i), + changed = hat[i] ^ new_hat; + + if (!changed) + continue; + + if ((new_hat & changed) == SDL_HAT_CENTERED) // hat was centered + { + hat[i] = new_hat; + } + else + { + assignment->type = HAT; + assignment->num = i; + assignment->x_axis = changed & (SDL_HAT_LEFT | SDL_HAT_RIGHT); + assignment->negative_axis = changed & (SDL_HAT_LEFT | SDL_HAT_UP); + detected = true; + } + } + + service_SDL_events(true); + JE_showVGA(); + + wait_delay(); + } + while (!detected && !newkey && !newmouse); + + free(axis); + free(button); + free(hat); + + return detected; +} + +// compares relevant parts of joystick assignments for equality +bool joystick_assignment_cmp( const Joystick_assignment *a, const Joystick_assignment *b ) +{ + if (a->type == b->type) + { + switch (a->type) + { + case NONE: + return true; + case AXIS: + return (a->num == b->num) && + (a->negative_axis == b->negative_axis); + case BUTTON: + return (a->num == b->num); + case HAT: + return (a->num == b->num) && + (a->x_axis == b->x_axis) && + (a->negative_axis == b->negative_axis); + } + } + + return false; +} + diff --git a/contrib/games/opentyrian/src/joystick.h b/contrib/games/opentyrian/src/joystick.h new file mode 100644 index 000000000..2802bb387 --- /dev/null +++ b/contrib/games/opentyrian/src/joystick.h @@ -0,0 +1,98 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef JOYSTICK_H +#define JOYSTICK_H + +#include "opentyr.h" +#include "config_file.h" + +#include "SDL.h" + +typedef enum +{ + NONE, + AXIS, + BUTTON, + HAT +} +Joystick_assignment_types; + +typedef struct +{ + Joystick_assignment_types type; + int num; + + // if hat + bool x_axis; // else y_axis + + // if hat or axis + bool negative_axis; // else positive +} +Joystick_assignment; + +typedef struct +{ + SDL_Joystick *handle; + + Joystick_assignment assignment[10][2]; // 0-3: directions, 4-9: actions + + bool analog; + int sensitivity, threshold; + + signed int x, y; + int analog_direction[4]; + bool direction[4], direction_pressed[4]; // up, right, down, left (_pressed, for emulating key presses) + + bool confirm, cancel; + bool action[6], action_pressed[6]; // fire, mode swap, left fire, right fire, menu, pause + + Uint32 joystick_delay; + bool input_pressed; +} +Joystick; + +extern int joystick_repeat_delay; +extern bool joydown; +extern bool ignore_joystick; +extern int joysticks; +extern Joystick *joystick; + +int joystick_axis_reduce( int j, int value ); +bool joystick_analog_angle( int j, float *angle ); + +void poll_joystick( int j ); +void poll_joysticks( void ); + +void push_key( SDLKey key ); +void push_joysticks_as_keyboard( void ); + +void init_joysticks( void ); +void deinit_joysticks( void ); + +void reset_joystick_assignments( int j ); +bool load_joystick_assignments( Config* config, int j ); +bool save_joystick_assignments( Config* config, int j ); + +void joystick_assignments_to_string( char *buffer, size_t buffer_len, const Joystick_assignment *assignments ); + +bool detect_joystick_assignment( int j, Joystick_assignment *assignment ); +bool joystick_assignment_cmp( const Joystick_assignment *, const Joystick_assignment * ); + +#endif /* JOYSTICK_H */ + diff --git a/contrib/games/opentyrian/src/jukebox.c b/contrib/games/opentyrian/src/jukebox.c new file mode 100644 index 000000000..9e22f1bf0 --- /dev/null +++ b/contrib/games/opentyrian/src/jukebox.c @@ -0,0 +1,203 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "jukebox.h" + +#include "font.h" +#include "joystick.h" +#include "keyboard.h" +#include "lds_play.h" +#include "loudness.h" +#include "mtrand.h" +#include "nortsong.h" +#include "opentyr.h" +#include "palette.h" +#include "sprite.h" +#include "starlib.h" +#include "vga_palette.h" +#include "video.h" + +#include + +void jukebox( void ) +{ + bool trigger_quit = false, // true when user wants to quit + quitting = false; + + bool hide_text = false; + + bool fade_looped_songs = true, fading_song = false; + bool stopped = false; + + bool fx = false; + int fx_num = 0; + + int palette_fade_steps = 15; + + int diff[256][3]; + init_step_fade_palette(diff, vga_palette, 0, 255); + + JE_starlib_init(); + + int fade_volume = tyrMusicVolume; + + for (; ; ) + { + if (!stopped && !audio_disabled) + { + if (songlooped && fade_looped_songs) + fading_song = true; + + if (fading_song) + { + if (fade_volume > 5) + { + fade_volume -= 2; + } + else + { + fade_volume = tyrMusicVolume; + + fading_song = false; + } + + set_volume(fade_volume, fxVolume); + } + + if (!playing || (songlooped && fade_looped_songs && !fading_song)) + play_song(mt_rand() % MUSIC_NUM); + } + + setdelay(1); + + SDL_FillRect(VGAScreenSeg, NULL, 0); + + // starlib input needs to be rewritten + JE_starlib_main(); + + push_joysticks_as_keyboard(); + service_SDL_events(true); + + if (!hide_text) + { + char buffer[60]; + + if (fx) + snprintf(buffer, sizeof(buffer), "%d %s", fx_num + 1, soundTitle[fx_num]); + else + snprintf(buffer, sizeof(buffer), "%d %s", song_playing + 1, musicTitle[song_playing]); + + const int x = VGAScreen->w / 2; + + draw_font_hv(VGAScreen, x, 170, "Press ESC to quit the jukebox.", small_font, centered, 1, 0); + draw_font_hv(VGAScreen, x, 180, "Arrow keys change the song being played.", small_font, centered, 1, 0); + draw_font_hv(VGAScreen, x, 190, buffer, small_font, centered, 1, 4); + } + + if (palette_fade_steps > 0) + step_fade_palette(diff, palette_fade_steps--, 0, 255); + + JE_showVGA(); + + wait_delay(); + + // quit on mouse click + Uint16 x, y; + if (JE_mousePosition(&x, &y) > 0) + trigger_quit = true; + + if (newkey) + { + switch (lastkey_sym) + { + case SDLK_ESCAPE: // quit jukebox + case SDLK_q: + trigger_quit = true; + break; + + case SDLK_SPACE: + hide_text = !hide_text; + break; + + case SDLK_f: + fading_song = !fading_song; + break; + case SDLK_n: + fade_looped_songs = !fade_looped_songs; + break; + + case SDLK_SLASH: // switch to sfx mode + fx = !fx; + break; + case SDLK_COMMA: + if (fx && --fx_num < 0) + fx_num = SAMPLE_COUNT - 1; + break; + case SDLK_PERIOD: + if (fx && ++fx_num >= SAMPLE_COUNT) + fx_num = 0; + break; + case SDLK_SEMICOLON: + if (fx) + JE_playSampleNum(fx_num + 1); + break; + + case SDLK_LEFT: + case SDLK_UP: + play_song((song_playing > 0 ? song_playing : MUSIC_NUM) - 1); + stopped = false; + break; + case SDLK_RETURN: + case SDLK_RIGHT: + case SDLK_DOWN: + play_song((song_playing + 1) % MUSIC_NUM); + stopped = false; + break; + case SDLK_s: // stop song + stop_song(); + stopped = true; + break; + case SDLK_r: // restart song + restart_song(); + stopped = false; + break; + + default: + break; + } + } + + // user wants to quit, start fade-out + if (trigger_quit && !quitting) + { + palette_fade_steps = 15; + + SDL_Color black = { 0, 0, 0 }; + init_step_fade_solid(diff, black, 0, 255); + + quitting = true; + } + + // if fade-out finished, we can finally quit + if (quitting && palette_fade_steps == 0) + break; + } + + set_volume(tyrMusicVolume, fxVolume); +} + diff --git a/contrib/games/opentyrian/src/jukebox.h b/contrib/games/opentyrian/src/jukebox.h new file mode 100644 index 000000000..6dd9a8e08 --- /dev/null +++ b/contrib/games/opentyrian/src/jukebox.h @@ -0,0 +1,27 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef JUKEBOX_H +#define JUKEBOX_H + +#include "opentyr.h" + +void jukebox( void ); + +#endif /* JUKEBOX_H */ + diff --git a/contrib/games/opentyrian/src/keyboard.c b/contrib/games/opentyrian/src/keyboard.c new file mode 100644 index 000000000..df3346d50 --- /dev/null +++ b/contrib/games/opentyrian/src/keyboard.c @@ -0,0 +1,255 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "keyboard.h" + +#include "joystick.h" +#include "network.h" +#include "opentyr.h" +#include "video.h" +#include "video_scale.h" + +#include "SDL.h" + +#include + + +JE_boolean ESCPressed; + +JE_boolean newkey, newmouse, keydown, mousedown; +SDLKey lastkey_sym; +SDLMod lastkey_mod; +unsigned char lastkey_char; +Uint8 lastmouse_but; +Uint16 lastmouse_x, lastmouse_y; +JE_boolean mouse_pressed[3] = {false, false, false}; +Uint16 mouse_x, mouse_y; + +Uint8 keysactive[SDLK_LAST]; + +#ifdef NDEBUG +bool input_grab_enabled = true; +#else +bool input_grab_enabled = false; +#endif + + +void flush_events_buffer( void ) +{ + SDL_Event ev; + + while (SDL_PollEvent(&ev)); +} + +void wait_input( JE_boolean keyboard, JE_boolean mouse, JE_boolean joystick ) +{ + service_SDL_events(false); + while (!((keyboard && keydown) || (mouse && mousedown) || (joystick && joydown))) + { + uSDL_Delay(SDL_POLL_INTERVAL); + push_joysticks_as_keyboard(); + service_SDL_events(false); + +#ifdef WITH_NETWORK + if (isNetworkGame) + network_check(); +#endif + } +} + +void wait_noinput( JE_boolean keyboard, JE_boolean mouse, JE_boolean joystick ) +{ + service_SDL_events(false); + while ((keyboard && keydown) || (mouse && mousedown) || (joystick && joydown)) + { + uSDL_Delay(SDL_POLL_INTERVAL); + poll_joysticks(); + service_SDL_events(false); + +#ifdef WITH_NETWORK + if (isNetworkGame) + network_check(); +#endif + } +} + +void init_keyboard( void ) +{ + SDL_EnableKeyRepeat(500, 60); + + newkey = newmouse = false; + keydown = mousedown = false; + + SDL_EnableUNICODE(1); +} + +void input_grab( bool enable ) +{ +#if defined(TARGET_GP2X) || defined(TARGET_DINGUX) + enable = true; +#endif + + input_grab_enabled = enable || fullscreen_enabled; + + SDL_ShowCursor(input_grab_enabled ? SDL_DISABLE : SDL_ENABLE); +#ifdef NDEBUG + SDL_WM_GrabInput(input_grab_enabled ? SDL_GRAB_ON : SDL_GRAB_OFF); +#endif +} + +JE_word JE_mousePosition( JE_word *mouseX, JE_word *mouseY ) +{ + service_SDL_events(false); + *mouseX = mouse_x; + *mouseY = mouse_y; + return mousedown ? lastmouse_but : 0; +} + +void set_mouse_position( int x, int y ) +{ + if (input_grab_enabled) + { + SDL_WarpMouse(x * scalers[scaler].width / vga_width, y * scalers[scaler].height / vga_height); + mouse_x = x; + mouse_y = y; + } +} + +void service_SDL_events( JE_boolean clear_new ) +{ + SDL_Event ev; + + if (clear_new) + newkey = newmouse = false; + + while (SDL_PollEvent(&ev)) + { + switch (ev.type) + { + case SDL_ACTIVEEVENT: + if (ev.active.state == SDL_APPINPUTFOCUS && !ev.active.gain) + input_grab(false); + break; + + case SDL_MOUSEMOTION: + mouse_x = ev.motion.x * vga_width / scalers[scaler].width; + mouse_y = ev.motion.y * vga_height / scalers[scaler].height; + break; + case SDL_KEYDOWN: + if (ev.key.keysym.mod & KMOD_CTRL) + { + /* emergency kill */ + if (ev.key.keysym.sym == SDLK_BACKSPACE) + { + puts("\n\n\nCtrl+Backspace pressed. Doing emergency quit.\n"); + SDL_Quit(); + exit(1); + } + + /* toggle input grab */ + if (ev.key.keysym.sym == SDLK_F10) + { + input_grab(!input_grab_enabled); + break; + } + } + + if (ev.key.keysym.mod & KMOD_ALT) + { + /* toggle fullscreen */ + if (ev.key.keysym.sym == SDLK_RETURN) + { + if (!init_scaler(scaler, !fullscreen_enabled) && // try new fullscreen state + !init_any_scaler(!fullscreen_enabled) && // try any scaler in new fullscreen state + !init_scaler(scaler, fullscreen_enabled)) // revert on fail + { + exit(EXIT_FAILURE); + } + break; + } + + /* disable input grab and fullscreen */ + if (ev.key.keysym.sym == SDLK_TAB) + { + if (!init_scaler(scaler, false) && // try windowed + !init_any_scaler(false) && // try any scaler windowed + !init_scaler(scaler, fullscreen_enabled)) // revert on fail + { + exit(EXIT_FAILURE); + } + + input_grab(false); + break; + } + } + + keysactive[ev.key.keysym.sym] = 1; + + newkey = true; + lastkey_sym = ev.key.keysym.sym; + lastkey_mod = ev.key.keysym.mod; + lastkey_char = ev.key.keysym.unicode; + keydown = true; + return; + case SDL_KEYUP: + keysactive[ev.key.keysym.sym] = 0; + keydown = false; + return; + case SDL_MOUSEBUTTONDOWN: + if (!input_grab_enabled) + { + input_grab(true); + break; + } + // fall through + case SDL_MOUSEBUTTONUP: + if (ev.type == SDL_MOUSEBUTTONDOWN) + { + newmouse = true; + lastmouse_but = ev.button.button; + lastmouse_x = ev.button.x * vga_width / scalers[scaler].width; + lastmouse_y = ev.button.y * vga_height / scalers[scaler].height; + mousedown = true; + } + else + { + mousedown = false; + } + switch (ev.button.button) + { + case SDL_BUTTON_LEFT: + mouse_pressed[0] = mousedown; break; + case SDL_BUTTON_RIGHT: + mouse_pressed[1] = mousedown; break; + case SDL_BUTTON_MIDDLE: + mouse_pressed[2] = mousedown; break; + } + break; + case SDL_QUIT: + /* TODO: Call the cleanup code here. */ + exit(0); + break; + } + } +} + +void JE_clearKeyboard( void ) +{ + // /!\ Doesn't seems important. I think. D: +} + diff --git a/contrib/games/opentyrian/src/keyboard.h b/contrib/games/opentyrian/src/keyboard.h new file mode 100644 index 000000000..24ad407ec --- /dev/null +++ b/contrib/games/opentyrian/src/keyboard.h @@ -0,0 +1,57 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include "opentyr.h" + +#include "SDL.h" + + +#define SDL_POLL_INTERVAL 5 + +extern JE_boolean ESCPressed; +extern JE_boolean newkey, newmouse, keydown, mousedown; +extern SDLKey lastkey_sym; +extern SDLMod lastkey_mod; +extern unsigned char lastkey_char; +extern Uint8 lastmouse_but; +extern Uint16 lastmouse_x, lastmouse_y; +extern JE_boolean mouse_pressed[3]; +extern Uint16 mouse_x, mouse_y; +extern Uint8 keysactive[SDLK_LAST]; + +extern bool input_grab_enabled; + +void flush_events_buffer( void ); +void wait_input( JE_boolean keyboard, JE_boolean mouse, JE_boolean joystick ); +void wait_noinput( JE_boolean keyboard, JE_boolean mouse, JE_boolean joystick ); +void init_keyboard( void ); +void input_grab( bool enable ); +JE_word JE_mousePosition( JE_word *mouseX, JE_word *mouseY ); +void set_mouse_position( int x, int y ); + +void service_SDL_events( JE_boolean clear_new ); + +void sleep_game( void ); + +void JE_clearKeyboard( void ); + +#endif /* KEYBOARD_H */ + diff --git a/contrib/games/opentyrian/src/lds_play.c b/contrib/games/opentyrian/src/lds_play.c new file mode 100644 index 000000000..d730f868d --- /dev/null +++ b/contrib/games/opentyrian/src/lds_play.c @@ -0,0 +1,769 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "lds_play.h" + +#include "file.h" +#include "loudness.h" +#include "opentyr.h" + +#include + +const unsigned char op_table[9] = {0x00, 0x01, 0x02, 0x08, 0x09, 0x0a, 0x10, 0x11, 0x12}; + +/* A substantial amount of this code has been copied and adapted from adplug. + Thanks, guys! Adplug is awesome! :D */ + +/* Note frequency table (16 notes / octave) */ +static const Uint16 frequency[(13 * 15) - 3] = { + 343, 344, 345, 347, 348, 349, 350, 352, 353, 354, 356, 357, 358, + 359, 361, 362, 363, 365, 366, 367, 369, 370, 371, 373, 374, 375, + 377, 378, 379, 381, 382, 384, 385, 386, 388, 389, 391, 392, 393, + 395, 396, 398, 399, 401, 402, 403, 405, 406, 408, 409, 411, 412, + 414, 415, 417, 418, 420, 421, 423, 424, 426, 427, 429, 430, 432, + 434, 435, 437, 438, 440, 442, 443, 445, 446, 448, 450, 451, 453, + 454, 456, 458, 459, 461, 463, 464, 466, 468, 469, 471, 473, 475, + 476, 478, 480, 481, 483, 485, 487, 488, 490, 492, 494, 496, 497, + 499, 501, 503, 505, 506, 508, 510, 512, 514, 516, 518, 519, 521, + 523, 525, 527, 529, 531, 533, 535, 537, 538, 540, 542, 544, 546, + 548, 550, 552, 554, 556, 558, 560, 562, 564, 566, 568, 571, 573, + 575, 577, 579, 581, 583, 585, 587, 589, 591, 594, 596, 598, 600, + 602, 604, 607, 609, 611, 613, 615, 618, 620, 622, 624, 627, 629, + 631, 633, 636, 638, 640, 643, 645, 647, 650, 652, 654, 657, 659, + 662, 664, 666, 669, 671, 674, 676, 678, 681, 683 +}; + +/* Vibrato (sine) table */ +static const Uint8 vibtab[25 + (13 * 3)] = { + 0, 13, 25, 37, 50, 62, 74, 86, 98, 109, 120, 131, 142, 152, 162, + 171, 180, 189, 197, 205, 212, 219, 225, 231, 236, 240, 244, 247, + 250, 252, 254, 255, 255, 255, 254, 252, 250, 247, 244, 240, 236, + 231, 225, 219, 212, 205, 197, 189, 180, 171, 162, 152, 142, 131, + 120, 109, 98, 86, 74, 62, 50, 37, 25, 13 +}; + +/* Tremolo (sine * sine) table */ +static const Uint8 tremtab[128] = { + 0, 0, 1, 1, 2, 4, 5, 7, 10, 12, 15, 18, 21, 25, 29, 33, 37, 42, 47, + 52, 57, 62, 67, 73, 79, 85, 90, 97, 103, 109, 115, 121, 128, 134, + 140, 146, 152, 158, 165, 170, 176, 182, 188, 193, 198, 203, 208, + 213, 218, 222, 226, 230, 234, 237, 240, 243, 245, 248, 250, 251, + 253, 254, 254, 255, 255, 255, 254, 254, 253, 251, 250, 248, 245, + 243, 240, 237, 234, 230, 226, 222, 218, 213, 208, 203, 198, 193, + 188, 182, 176, 170, 165, 158, 152, 146, 140, 134, 127, 121, 115, + 109, 103, 97, 90, 85, 79, 73, 67, 62, 57, 52, 47, 42, 37, 33, 29, + 25, 21, 18, 15, 12, 10, 7, 5, 4, 2, 1, 1, 0 +}; + +static const Uint16 maxsound = 0x3f, maxpos = 0xff; + +static SoundBank *soundbank = NULL; +static Channel channel[9]; +static Position *positions = NULL; + +static Uint8 fmchip[0xff], jumping, fadeonoff, allvolume, hardfade, tempo_now, pattplay, tempo, regbd, chandelay[9], mode, pattlen; +static Uint16 posplay, jumppos, speed; +static Uint16 *patterns = NULL; +static Uint16 numpatch, numposi, mainvolume; + +bool playing, songlooped; + +bool lds_load( FILE *f, unsigned int music_offset, unsigned int music_size ) +{ + SoundBank *sb; + + fseek(f, music_offset, SEEK_SET); + + /* load header */ + mode = fgetc(f); + if (mode > 2) + { + fprintf(stderr, "error: failed to load music\n"); + return false; + } + efread(&speed, 2, 1, f); + tempo = fgetc(f); + pattlen = fgetc(f); + for (unsigned int i = 0; i < 9; i++) + chandelay[i] = fgetc(f); + regbd = fgetc(f); + + /* load patches */ + efread(&numpatch, 2, 1, f); + + free(soundbank); + soundbank = malloc(sizeof(SoundBank) * numpatch); + + for (unsigned int i = 0; i < numpatch; i++) + { + sb = &soundbank[i]; + sb->mod_misc = fgetc(f); + sb->mod_vol = fgetc(f); + sb->mod_ad = fgetc(f); + sb->mod_sr = fgetc(f); + sb->mod_wave = fgetc(f); + sb->car_misc = fgetc(f); + sb->car_vol = fgetc(f); + sb->car_ad = fgetc(f); + sb->car_sr = fgetc(f); + sb->car_wave = fgetc(f); + sb->feedback = fgetc(f); + sb->keyoff = fgetc(f); + sb->portamento = fgetc(f); + sb->glide = fgetc(f); + sb->finetune = fgetc(f); + sb->vibrato = fgetc(f); + sb->vibdelay = fgetc(f); + sb->mod_trem = fgetc(f); + sb->car_trem = fgetc(f); + sb->tremwait = fgetc(f); + sb->arpeggio = fgetc(f); + for (unsigned int j = 0; j < 12; j++) + sb->arp_tab[j] = fgetc(f); + efread(&sb->start, 2, 1, f); + efread(&sb->size, 2, 1, f); + sb->fms = fgetc(f); + efread(&sb->transp, 2, 1, f); + sb->midinst = fgetc(f); + sb->midvelo = fgetc(f); + sb->midkey = fgetc(f); + sb->midtrans = fgetc(f); + sb->middum1 = fgetc(f); + sb->middum2 = fgetc(f); + } + + /* load positions */ + efread(&numposi, 2, 1, f); + + free(positions); + positions = malloc(sizeof(Position) * 9 * numposi); + + for (unsigned int i = 0; i < numposi; i++) + { + for (unsigned int j = 0; j < 9; j++) + { + /* + * patnum is a pointer inside the pattern space, but patterns are 16bit + * word fields anyway, so it ought to be an even number (hopefully) and + * we can just divide it by 2 to get our array index of 16bit words. + */ + Uint16 temp; + efread(&temp, 2, 1, f); + positions[i * 9 + j].patnum = temp / 2; + positions[i * 9 + j].transpose = fgetc(f); + } + } + + /* load patterns */ + fseek(f, 2, SEEK_CUR); /* ignore # of digital sounds (dunno what this is for) */ + + unsigned int remaining = music_size - (ftell(f) - music_offset); + + free(patterns); + patterns = malloc(sizeof(Uint16) * (remaining / 2)); + + for (unsigned int i = 0; i < remaining / 2; i++) + efread(&patterns[i], 2, 1, f); + + lds_rewind(); + + return true; +} + +void lds_free( void ) +{ + free(soundbank); + soundbank = NULL; + + free(positions); + positions = NULL; + + free(patterns); + patterns = NULL; +} + +void lds_rewind( void ) +{ + int i; + + /* init all with 0 */ + tempo_now = 3; + playing = true; songlooped = false; + jumping = fadeonoff = allvolume = hardfade = pattplay = posplay = jumppos = mainvolume = 0; + memset(channel, 0, sizeof(channel)); + memset(fmchip, 0, sizeof(fmchip)); + + /* OPL2 init */ + opl_init(); /* Reset OPL chip */ + opl_write(1, 0x20); + opl_write(8, 0); + opl_write(0xbd, regbd); + + for(i = 0; i < 9; i++) { + opl_write(0x20 + op_table[i], 0); + opl_write(0x23 + op_table[i], 0); + opl_write(0x40 + op_table[i], 0x3f); + opl_write(0x43 + op_table[i], 0x3f); + opl_write(0x60 + op_table[i], 0xff); + opl_write(0x63 + op_table[i], 0xff); + opl_write(0x80 + op_table[i], 0xff); + opl_write(0x83 + op_table[i], 0xff); + opl_write(0xe0 + op_table[i], 0); + opl_write(0xe3 + op_table[i], 0); + opl_write(0xa0 + i, 0); + opl_write(0xb0 + i, 0); + opl_write(0xc0 + i, 0); + } +} + +void lds_setregs(Uint8 reg, Uint8 val) +{ + if(fmchip[reg] == val) return; + + fmchip[reg] = val; + opl_write(reg, val); +} + +void lds_setregs_adv(Uint8 reg, Uint8 mask, Uint8 val) +{ + lds_setregs(reg, (fmchip[reg] & mask) | val); +} + +int lds_update( void ) +{ + Uint16 comword, freq, octave, chan, tune, wibc, tremc, arpreg; + int vbreak; + Uint8 level, regnum, comhi, comlo; + int i; + Channel *c; + + if(!playing) return false; + + /* handle fading */ + if(fadeonoff) + { + if(fadeonoff <= 128) { + if(allvolume > fadeonoff || allvolume == 0) + { + allvolume -= fadeonoff; + } else { + allvolume = 1; + fadeonoff = 0; + if(hardfade != 0) { + playing = false; + hardfade = 0; + for(i = 0; i < 9; i++) + { + channel[i].keycount = 1; + } + } + } + + } else { + if( (Uint8) ((allvolume + (0x100 - fadeonoff)) & 0xff) <= mainvolume) + { + allvolume += 0x100 - fadeonoff; + } else { + allvolume = mainvolume; + fadeonoff = 0; + } + } + } + + /* handle channel delay */ + for(chan = 0; chan < 9; chan++) { + c = &channel[chan]; + if(c->chancheat.chandelay) { + if(!(--c->chancheat.chandelay)) { + lds_playsound(c->chancheat.sound, chan, c->chancheat.high); + } + } + } + + /* handle notes */ + if(!tempo_now && positions) + { + vbreak = false; + for(chan = 0; chan < 9; chan++) + { + c = &channel[chan]; + if(!c->packwait) { + Uint16 patnum = positions[posplay * 9 + chan].patnum; + Uint8 transpose = positions[posplay * 9 + chan].transpose; + /*printf("> %p", positions);*/ + + comword = patterns[patnum + c->packpos]; + comhi = comword >> 8; comlo = comword & 0xff; + if(comword) { + if(comhi == 0x80) + { + c->packwait = comlo; + } else { + if(comhi >= 0x80) + { + switch(comhi) + { + case 0xff: + c->volcar = (((c->volcar & 0x3f) * comlo) >> 6) & 0x3f; + if(fmchip[0xc0 + chan] & 1) + c->volmod = (((c->volmod & 0x3f) * comlo) >> 6) & 0x3f; + break; + + case 0xfe: + tempo = comword & 0x3f; + break; + + case 0xfd: + c->nextvol = comlo; + break; + + case 0xfc: + playing = false; + /* in real player there's also full keyoff here, but we don't need it */ + break; + + case 0xfb: + c->keycount = 1; + break; + + case 0xfa: + vbreak = true; + jumppos = (posplay + 1) & maxpos; + break; + + case 0xf9: + vbreak = true; + jumppos = comlo & maxpos; + jumping = 1; + if(jumppos < posplay) + { + songlooped = true; + } + break; + + case 0xf8: + c->lasttune = 0; + break; + + case 0xf7: + c->vibwait = 0; + /* PASCAL: c->vibspeed = ((comlo >> 4) & 15) + 2; */ + c->vibspeed = (comlo >> 4) + 2; + c->vibrate = (comlo & 15) + 1; + break; + + case 0xf6: + c->glideto = comlo; + break; + + case 0xf5: + c->finetune = comlo; + break; + + case 0xf4: + if(!hardfade) + { + allvolume = mainvolume = comlo; + fadeonoff = 0; + } + break; + + case 0xf3: + if(!hardfade) + { + fadeonoff = comlo; + } + break; + + case 0xf2: + c->trmstay = comlo; + break; + + case 0xf1: /* panorama */ + + case 0xf0: /* progch */ + /* MIDI commands (unhandled) */ + /*AdPlug_LogWrite("CldsPlayer(): not handling MIDI command 0x%x, " + "value = 0x%x\n", comhi);*/ + break; + + default: + if(comhi < 0xa0) + { + c->glideto = comhi & 0x1f; + } else { + /*AdPlug_LogWrite("CldsPlayer(): unknown command 0x%x encountered!" + " value = 0x%x\n", comhi, comlo);*/ + } + break; + } + } else { + Uint8 sound; + Uint16 high; + Sint8 transp = transpose & 127; + /* + * Originally, in assembler code, the player first shifted + * logically left the transpose byte by 1 and then shifted + * arithmetically right the same byte to achieve the final, + * signed transpose value. Since we can't do arithmetic shifts + * in C, we just duplicate the 7th bit into the 8th one and + * discard the 8th one completely. + */ + + if(transpose & 64) + { + transp |= 128; + } + + if(transpose & 128) + { + sound = (comlo + transp) & maxsound; + high = comhi << 4; + } else { + sound = comlo & maxsound; + high = (comhi + transp) << 4; + } + + /* + PASCAL: + sound = comlo & maxsound; + high = (comhi + (((transpose + 0x24) & 0xff) - 0x24)) << 4; + */ + + if(!chandelay[chan]) { + lds_playsound(sound, chan, high); + } else { + c->chancheat.chandelay = chandelay[chan]; + c->chancheat.sound = sound; + c->chancheat.high = high; + } + } + } + } + + c->packpos++; + } else { + c->packwait--; + } + } + + tempo_now = tempo; + /* + The continue table is updated here, but this is only used in the + original player, which can be paused in the middle of a song and then + unpaused. Since AdPlug does all this for us automatically, we don't + have a continue table here. The continue table update code is noted + here for reference only. + + if(!pattplay) { + conttab[speed & maxcont].position = posplay & 0xff; + conttab[speed & maxcont].tempo = tempo; + } + */ + pattplay++; + if(vbreak) + { + pattplay = 0; + for(i = 0; i < 9; i++) + { + channel[i].packpos = channel[i].packwait = 0; + } + posplay = jumppos; + } else { + if(pattplay >= pattlen) { + pattplay = 0; + for(i = 0; i < 9; i++) + { + channel[i].packpos = channel[i].packwait = 0; + } + posplay = (posplay + 1) & maxpos; + } + } + } else { + tempo_now--; + } + + /* make effects */ + for(chan = 0; chan < 9; chan++) { + c = &channel[chan]; + regnum = op_table[chan]; + if(c->keycount > 0) { + if(c->keycount == 1) + lds_setregs_adv(0xb0 + chan, 0xdf, 0); + c->keycount--; + } + + /* arpeggio */ + if(c->arp_size == 0) + arpreg = 0; + else { + arpreg = c->arp_tab[c->arp_pos] << 4; + if(arpreg == 0x800) { + if(c->arp_pos > 0) c->arp_tab[0] = c->arp_tab[c->arp_pos - 1]; + c->arp_size = 1; c->arp_pos = 0; + arpreg = c->arp_tab[0] << 4; + } + + if(c->arp_count == c->arp_speed) { + c->arp_pos++; + if(c->arp_pos >= c->arp_size) c->arp_pos = 0; + c->arp_count = 0; + } else + c->arp_count++; + } + + /* glide & portamento */ + if(c->lasttune && (c->lasttune != c->gototune)) { + if(c->lasttune > c->gototune) { + if(c->lasttune - c->gototune < c->portspeed) + c->lasttune = c->gototune; + else + c->lasttune -= c->portspeed; + } else { + if(c->gototune - c->lasttune < c->portspeed) + c->lasttune = c->gototune; + else + c->lasttune += c->portspeed; + } + + if(arpreg >= 0x800) + arpreg = c->lasttune - (arpreg ^ 0xff0) - 16; + else + arpreg += c->lasttune; + + freq = frequency[arpreg % (12 * 16)]; + octave = arpreg / (12 * 16) - 1; + lds_setregs(0xa0 + chan, freq & 0xff); + lds_setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf); + } else { + /* vibrato */ + if(!c->vibwait) { + if(c->vibrate) { + wibc = vibtab[c->vibcount & 0x3f] * c->vibrate; + + if((c->vibcount & 0x40) == 0) + tune = c->lasttune + (wibc >> 8); + else + tune = c->lasttune - (wibc >> 8); + + if(arpreg >= 0x800) + tune = tune - (arpreg ^ 0xff0) - 16; + else + tune += arpreg; + + freq = frequency[tune % (12 * 16)]; + octave = tune / (12 * 16) - 1; + lds_setregs(0xa0 + chan, freq & 0xff); + lds_setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf); + c->vibcount += c->vibspeed; + } else if(c->arp_size != 0) { /* no vibrato, just arpeggio */ + if(arpreg >= 0x800) + tune = c->lasttune - (arpreg ^ 0xff0) - 16; + else + tune = c->lasttune + arpreg; + + freq = frequency[tune % (12 * 16)]; + octave = tune / (12 * 16) - 1; + lds_setregs(0xa0 + chan, freq & 0xff); + lds_setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf); + } + } else { /* no vibrato, just arpeggio */ + c->vibwait--; + + if(c->arp_size != 0) { + if(arpreg >= 0x800) + tune = c->lasttune - (arpreg ^ 0xff0) - 16; + else + tune = c->lasttune + arpreg; + + freq = frequency[tune % (12 * 16)]; + octave = tune / (12 * 16) - 1; + lds_setregs(0xa0 + chan, freq & 0xff); + lds_setregs_adv(0xb0 + chan, 0x20, ((octave << 2) + (freq >> 8)) & 0xdf); + } + } + } + + /* tremolo (modulator) */ + if(!c->trmwait) { + if(c->trmrate) { + tremc = tremtab[c->trmcount & 0x7f] * c->trmrate; + if((tremc >> 8) <= (c->volmod & 0x3f)) + level = (c->volmod & 0x3f) - (tremc >> 8); + else + level = 0; + + if(allvolume != 0 && (fmchip[0xc0 + chan] & 1)) + lds_setregs_adv(0x40 + regnum, 0xc0, ((level * allvolume) >> 8) ^ 0x3f); + else + lds_setregs_adv(0x40 + regnum, 0xc0, level ^ 0x3f); + + c->trmcount += c->trmspeed; + } else if(allvolume != 0 && (fmchip[0xc0 + chan] & 1)) + lds_setregs_adv(0x40 + regnum, 0xc0, ((((c->volmod & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f); + else + lds_setregs_adv(0x40 + regnum, 0xc0, (c->volmod ^ 0x3f) & 0x3f); + } else { + c->trmwait--; + if(allvolume != 0 && (fmchip[0xc0 + chan] & 1)) + lds_setregs_adv(0x40 + regnum, 0xc0, ((((c->volmod & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f); + } + + /* tremolo (carrier) */ + if(!c->trcwait) { + if(c->trcrate) { + tremc = tremtab[c->trccount & 0x7f] * c->trcrate; + if((tremc >> 8) <= (c->volcar & 0x3f)) + level = (c->volcar & 0x3f) - (tremc >> 8); + else + level = 0; + + if(allvolume != 0) + lds_setregs_adv(0x43 + regnum, 0xc0, ((level * allvolume) >> 8) ^ 0x3f); + else + lds_setregs_adv(0x43 + regnum, 0xc0, level ^ 0x3f); + c->trccount += c->trcspeed; + } else if(allvolume != 0) + lds_setregs_adv(0x43 + regnum, 0xc0, ((((c->volcar & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f); + else + lds_setregs_adv(0x43 + regnum, 0xc0, (c->volcar ^ 0x3f) & 0x3f); + } else { + c->trcwait--; + if(allvolume != 0) + lds_setregs_adv(0x43 + regnum, 0xc0, ((((c->volcar & 0x3f) * allvolume) >> 8) ^ 0x3f) & 0x3f); + } + } + + return (!playing || songlooped) ? false : true; +} + +void lds_playsound(int inst_number, int channel_number, int tunehigh) +{ + Channel *c = &channel[channel_number]; /* current channel */ + SoundBank *i = &soundbank[inst_number]; /* current instrument */ + Uint32 regnum = op_table[channel_number]; /* channel's OPL2 register */ + Uint8 volcalc, octave; + Uint16 freq; + + /* set fine tune */ + tunehigh += ((i->finetune + c->finetune + 0x80) & 0xff) - 0x80; + + /* arpeggio handling */ + if(!i->arpeggio) { + Uint16 arpcalc = i->arp_tab[0] << 4; + + if(arpcalc > 0x800) + tunehigh = tunehigh - (arpcalc ^ 0xff0) - 16; + else + tunehigh += arpcalc; + } + + /* glide handling */ + if(c->glideto != 0) { + c->gototune = tunehigh; + c->portspeed = c->glideto; + c->glideto = c->finetune = 0; + return; + } + + /* set modulator registers */ + lds_setregs(0x20 + regnum, i->mod_misc); + volcalc = i->mod_vol; + if(!c->nextvol || !(i->feedback & 1)) + c->volmod = volcalc; + else + c->volmod = (volcalc & 0xc0) | ((((volcalc & 0x3f) * c->nextvol) >> 6)); + + if((i->feedback & 1) == 1 && allvolume != 0) + lds_setregs(0x40 + regnum, ((c->volmod & 0xc0) | (((c->volmod & 0x3f) * allvolume) >> 8)) ^ 0x3f); + else + lds_setregs(0x40 + regnum, c->volmod ^ 0x3f); + lds_setregs(0x60 + regnum, i->mod_ad); + lds_setregs(0x80 + regnum, i->mod_sr); + lds_setregs(0xe0 + regnum, i->mod_wave); + + /* Set carrier registers */ + lds_setregs(0x23 + regnum, i->car_misc); + volcalc = i->car_vol; + if(!c->nextvol) + c->volcar = volcalc; + else + c->volcar = (volcalc & 0xc0) | ((((volcalc & 0x3f) * c->nextvol) >> 6)); + + if(allvolume) + lds_setregs(0x43 + regnum, ((c->volcar & 0xc0) | (((c->volcar & 0x3f) * allvolume) >> 8)) ^ 0x3f); + else + lds_setregs(0x43 + regnum, c->volcar ^ 0x3f); + lds_setregs(0x63 + regnum, i->car_ad); + lds_setregs(0x83 + regnum, i->car_sr); + lds_setregs(0xe3 + regnum, i->car_wave); + lds_setregs(0xc0 + channel_number, i->feedback); + lds_setregs_adv(0xb0 + channel_number, 0xdf, 0); /* key off */ + + freq = frequency[tunehigh % (12 * 16)]; + octave = tunehigh / (12 * 16) - 1; + if(!i->glide) { + if(!i->portamento || !c->lasttune) { + lds_setregs(0xa0 + channel_number, freq & 0xff); + lds_setregs(0xb0 + channel_number, (octave << 2) + 0x20 + (freq >> 8)); + c->lasttune = c->gototune = tunehigh; + } else { + c->gototune = tunehigh; + c->portspeed = i->portamento; + lds_setregs_adv(0xb0 + channel_number, 0xdf, 0x20); /* key on */ + } + } else { + lds_setregs(0xa0 + channel_number, freq & 0xff); + lds_setregs(0xb0 + channel_number, (octave << 2) + 0x20 + (freq >> 8)); + c->lasttune = tunehigh; + c->gototune = tunehigh + ((i->glide + 0x80) & 0xff) - 0x80; /* set destination */ + c->portspeed = i->portamento; + } + + if(!i->vibrato) + c->vibwait = c->vibspeed = c->vibrate = 0; + else { + c->vibwait = i->vibdelay; + /* PASCAL: c->vibspeed = ((i->vibrato >> 4) & 15) + 1; */ + c->vibspeed = (i->vibrato >> 4) + 2; + c->vibrate = (i->vibrato & 15) + 1; + } + + if(!(c->trmstay & 0xf0)) { + c->trmwait = (i->tremwait & 0xf0) >> 3; + /* PASCAL: c->trmspeed = (i->mod_trem >> 4) & 15; */ + c->trmspeed = i->mod_trem >> 4; + c->trmrate = i->mod_trem & 15; + c->trmcount = 0; + } + + if(!(c->trmstay & 0x0f)) { + c->trcwait = (i->tremwait & 15) << 1; + /* PASCAL: c->trcspeed = (i->car_trem >> 4) & 15; */ + c->trcspeed = i->car_trem >> 4; + c->trcrate = i->car_trem & 15; + c->trccount = 0; + } + + c->arp_size = i->arpeggio & 15; + c->arp_speed = i->arpeggio >> 4; + memcpy(c->arp_tab, i->arp_tab, 12); + c->keycount = i->keyoff; + c->nextvol = c->glideto = c->finetune = c->vibcount = c->arp_pos = c->arp_count = 0; +} + diff --git a/contrib/games/opentyrian/src/lds_play.h b/contrib/games/opentyrian/src/lds_play.h new file mode 100644 index 000000000..b7fbe1cb6 --- /dev/null +++ b/contrib/games/opentyrian/src/lds_play.h @@ -0,0 +1,74 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef LDS_PLAY_H +#define LDS_PLAY_H + +#include "opentyr.h" + +#include + +extern bool playing, songlooped; + +int lds_update( void ); +bool lds_load( FILE *f, unsigned int music_offset, unsigned int music_size ); +void lds_free( void ); +void lds_rewind( void ); + +#define REFRESH 70.0f + +/*unsigned int getorders() { return numposi; } +unsigned int getorder() { return posplay; } +unsigned int getrow() { return pattplay; } +unsigned int getspeed() { return speed; } +unsigned int getinstruments() { return numpatch; }*/ + +typedef struct { + unsigned char mod_misc, mod_vol, mod_ad, mod_sr, mod_wave, + car_misc, car_vol, car_ad, car_sr, car_wave, feedback, keyoff, + portamento, glide, finetune, vibrato, vibdelay, mod_trem, car_trem, + tremwait, arpeggio, arp_tab[12]; + unsigned short start, size; + unsigned char fms; + unsigned short transp; + unsigned char midinst, midvelo, midkey, midtrans, middum1, middum2; +} SoundBank; + +typedef struct { + unsigned short gototune, lasttune, packpos; + unsigned char finetune, glideto, portspeed, nextvol, volmod, volcar, + vibwait, vibspeed, vibrate, trmstay, trmwait, trmspeed, trmrate, trmcount, + trcwait, trcspeed, trcrate, trccount, arp_size, arp_speed, keycount, + vibcount, arp_pos, arp_count, packwait, arp_tab[12]; + struct { + unsigned char chandelay, sound; + unsigned short high; + } chancheat; +} Channel; + +typedef struct { + unsigned short patnum; + unsigned char transpose; +} Position; + +void lds_playsound(int inst_number, int channel_number, int tunehigh); +void lds_setregs(unsigned char reg, unsigned char val); +void lds_setregs_adv(unsigned char reg, unsigned char mask, unsigned char val); + +#endif /* LDS_PLAY_H */ + diff --git a/contrib/games/opentyrian/src/loudness.c b/contrib/games/opentyrian/src/loudness.c new file mode 100644 index 000000000..5e590b5d4 --- /dev/null +++ b/contrib/games/opentyrian/src/loudness.c @@ -0,0 +1,294 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "loudness.h" + +#include "file.h" +#include "lds_play.h" +#include "nortsong.h" +#include "opentyr.h" +#include "params.h" + +float music_volume = 0, sample_volume = 0; + +bool music_stopped = true; +unsigned int song_playing = 0; + +bool audio_disabled = false, music_disabled = false, samples_disabled = false; + +/* SYN: These shouldn't be used outside this file. Hands off! */ +FILE *music_file = NULL; +Uint32 *song_offset; +Uint16 song_count = 0; + + +SAMPLE_TYPE *channel_buffer[SFX_CHANNELS] = { NULL }; +SAMPLE_TYPE *channel_pos[SFX_CHANNELS] = { NULL }; +Uint32 channel_len[SFX_CHANNELS] = { 0 }; +Uint8 channel_vol[SFX_CHANNELS]; + +int sound_init_state = false; +int freq = 11025 * OUTPUT_QUALITY; + +static SDL_AudioCVT audio_cvt; // used for format conversion + +void audio_cb( void *userdata, unsigned char *feedme, int howmuch ); + +void load_song( unsigned int song_num ); + +bool init_audio( void ) +{ + if (audio_disabled) + return false; + + SDL_AudioSpec ask, got; + + ask.freq = freq; + ask.format = (BYTES_PER_SAMPLE == 2) ? AUDIO_S16SYS : AUDIO_S8; + ask.channels = 1; + ask.samples = 2048; + ask.callback = audio_cb; + + printf("\trequested %d Hz, %d channels, %d samples\n", ask.freq, ask.channels, ask.samples); + + #ifdef _KOLIBRI + SDL_AudioInit(NULL); + #endif + + if (SDL_OpenAudio(&ask, &got) == -1) + { + fprintf(stderr, "error: failed to initialize SDL audio: %s\n", SDL_GetError()); + audio_disabled = true; + return false; + } + + printf("\tobtained %d Hz, %d channels, %d samples\n", got.freq, got.channels, got.samples); + + SDL_BuildAudioCVT(&audio_cvt, ask.format, ask.channels, ask.freq, got.format, got.channels, got.freq); + + opl_init(); + + SDL_PauseAudio(0); // unpause + + return true; +} + +void audio_cb( void *user_data, unsigned char *sdl_buffer, int howmuch ) +{ + (void)user_data; + + // prepare for conversion + howmuch /= audio_cvt.len_mult; + audio_cvt.buf = sdl_buffer; + audio_cvt.len = howmuch; + + static long ct = 0; + + SAMPLE_TYPE *feedme = (SAMPLE_TYPE *)sdl_buffer; + + if (!music_disabled && !music_stopped) + { + /* SYN: Simulate the fm synth chip */ + SAMPLE_TYPE *music_pos = feedme; + long remaining = howmuch / BYTES_PER_SAMPLE; + while (remaining > 0) + { + while (ct < 0) + { + ct += freq; + lds_update(); /* SYN: Do I need to use the return value for anything here? */ + } + /* SYN: Okay, about the calculations below. I still don't 100% get what's going on, but... + - freq is samples/time as output by SDL. + - REFRESH is how often the play proc would have been called in Tyrian. Standard speed is + 70Hz, which is the default value of 70.0f + - ct represents the margin between play time (representing # of samples) and tick speed of + the songs (70Hz by default). It keeps track of which one is ahead, because they don't + synch perfectly. */ + + /* set i to smaller of data requested by SDL and a value calculated from the refresh rate */ + long i = (long)((ct / REFRESH) + 4) & ~3; + i = (i > remaining) ? remaining : i; /* i should now equal the number of samples we get */ + opl_update((SAMPLE_TYPE *)music_pos, i); + music_pos += i; + remaining -= i; + ct -= (long)(REFRESH * i); + } + + /* Reduce the music volume. */ + int qu = howmuch / BYTES_PER_SAMPLE; + for (int smp = 0; smp < qu; smp++) + { + feedme[smp] *= music_volume; + } + } + + if (!samples_disabled) + { + /* SYN: Mix sound channels and shove into audio buffer */ + for (int ch = 0; ch < SFX_CHANNELS; ch++) + { + float volume = sample_volume * (channel_vol[ch] / (float)SFX_CHANNELS); + + /* SYN: Don't copy more data than is in the channel! */ + unsigned int qu = ((unsigned)howmuch > channel_len[ch] ? channel_len[ch] : (unsigned)howmuch) / BYTES_PER_SAMPLE; + for (unsigned int smp = 0; smp < qu; smp++) + { +#if (BYTES_PER_SAMPLE == 2) + Sint32 clip = (Sint32)feedme[smp] + (Sint32)(channel_pos[ch][smp] * volume); + feedme[smp] = (clip > 0x7fff) ? 0x7fff : (clip <= -0x8000) ? -0x8000 : (Sint16)clip; +#else /* BYTES_PER_SAMPLE */ + Sint16 clip = (Sint16)feedme[smp] + (Sint16)(channel_pos[ch][smp] * volume); + feedme[smp] = (clip > 0x7f) ? 0x7f : (clip <= -0x80) ? -0x80 : (Sint8)clip; +#endif /* BYTES_PER_SAMPLE */ + } + + channel_pos[ch] += qu; + channel_len[ch] -= qu * BYTES_PER_SAMPLE; + + /* SYN: If we've emptied a channel buffer, let's free the memory and clear the channel. */ + if (channel_len[ch] == 0) + { + free(channel_buffer[ch]); + channel_buffer[ch] = channel_pos[ch] = NULL; + } + } + } + + // do conversion + SDL_ConvertAudio(&audio_cvt); +} + +void deinit_audio( void ) +{ + if (audio_disabled) + return; + + SDL_PauseAudio(1); // pause + + SDL_CloseAudio(); + + for (unsigned int i = 0; i < SFX_CHANNELS; i++) + { + free(channel_buffer[i]); + channel_buffer[i] = channel_pos[i] = NULL; + channel_len[i] = 0; + } + + lds_free(); +} + + +void load_music( void ) +{ + if (music_file == NULL) + { + music_file = dir_fopen_die(data_dir(), "music.mus", "rb"); + + efread(&song_count, sizeof(song_count), 1, music_file); + + song_offset = malloc((song_count + 1) * sizeof(*song_offset)); + + efread(song_offset, 4, song_count, music_file); + song_offset[song_count] = ftell_eof(music_file); + } +} + +void load_song( unsigned int song_num ) +{ + if (audio_disabled) + return; + + SDL_LockAudio(); + + if (song_num < song_count) + { + unsigned int song_size = song_offset[song_num + 1] - song_offset[song_num]; + lds_load(music_file, song_offset[song_num], song_size); + } + else + { + fprintf(stderr, "warning: failed to load song %d\n", song_num + 1); + } + + SDL_UnlockAudio(); +} + +void play_song( unsigned int song_num ) +{ + if (song_num != song_playing) + { + load_song(song_num); + song_playing = song_num; + } + + music_stopped = false; +} + +void restart_song( void ) +{ + unsigned int temp = song_playing; + song_playing = -1; + play_song(temp); +} + +void stop_song( void ) +{ + music_stopped = true; +} + +void fade_song( void ) +{ + /* STUB: we have no implementation of this to port */ +} + +void set_volume( unsigned int music, unsigned int sample ) +{ + music_volume = music * (1.5f / 255.0f); + sample_volume = sample * (1.0f / 255.0f); +} + +void JE_multiSamplePlay(JE_byte *buffer, JE_word size, JE_byte chan, JE_byte vol) +{ + if (audio_disabled || samples_disabled) + return; + + SDL_LockAudio(); + + free(channel_buffer[chan]); + + channel_len[chan] = size * BYTES_PER_SAMPLE * SAMPLE_SCALING; + channel_buffer[chan] = malloc(channel_len[chan]); + channel_pos[chan] = channel_buffer[chan]; + channel_vol[chan] = vol + 1; + + for (int i = 0; i < size; i++) + { + for (int ex = 0; ex < SAMPLE_SCALING; ex++) + { +#if (BYTES_PER_SAMPLE == 2) + channel_buffer[chan][(i * SAMPLE_SCALING) + ex] = (Sint8)buffer[i] << 8; +#else /* BYTES_PER_SAMPLE */ + channel_buffer[chan][(i * SAMPLE_SCALING) + ex] = (Sint8)buffer[i]; +#endif /* BYTES_PER_SAMPLE */ + } + } + + SDL_UnlockAudio(); +} + diff --git a/contrib/games/opentyrian/src/loudness.h b/contrib/games/opentyrian/src/loudness.h new file mode 100644 index 000000000..ac6ba1b92 --- /dev/null +++ b/contrib/games/opentyrian/src/loudness.h @@ -0,0 +1,59 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef LOUDNESS_H +#define LOUDNESS_H + +#include "opentyr.h" +#include "opl.h" + +#include "SDL.h" + +#define SFX_CHANNELS 8 + +#if defined(TARGET_GP2X) || defined(TARGET_DINGUX) +#define OUTPUT_QUALITY 2 // 22 kHz +#else +#define OUTPUT_QUALITY 4 // 44 kHz +#endif + +#define SAMPLE_SCALING OUTPUT_QUALITY +#define SAMPLE_TYPE Bit16s +#define BYTES_PER_SAMPLE 2 + +extern float music_volume, sample_volume; + +extern unsigned int song_playing; + +extern bool audio_disabled, music_disabled, samples_disabled; + +bool init_audio( void ); +void deinit_audio( void ); + +void load_music( void ); +void play_song( unsigned int song_num ); +void restart_song( void ); +void stop_song( void ); +void fade_song( void ); + +void set_volume( unsigned int music, unsigned int sample ); + +void JE_multiSamplePlay(JE_byte *buffer, JE_word size, JE_byte chan, JE_byte vol); + +#endif /* LOUDNESS_H */ + diff --git a/contrib/games/opentyrian/src/lvllib.c b/contrib/games/opentyrian/src/lvllib.c new file mode 100644 index 000000000..301fc0c45 --- /dev/null +++ b/contrib/games/opentyrian/src/lvllib.c @@ -0,0 +1,42 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "lvllib.h" + +#include "file.h" +#include "opentyr.h" + +JE_LvlPosType lvlPos; + +char levelFile[13]; /* string [12] */ +JE_word lvlNum; + +void JE_analyzeLevel( void ) +{ + FILE *f = dir_fopen_die(data_dir(), levelFile, "rb"); + + efread(&lvlNum, sizeof(JE_word), 1, f); + + for (int x = 0; x < lvlNum; x++) + efread(&lvlPos[x], sizeof(JE_longint), 1, f); + + lvlPos[lvlNum] = ftell_eof(f); + + fclose(f); +} + diff --git a/contrib/games/opentyrian/src/lvllib.h b/contrib/games/opentyrian/src/lvllib.h new file mode 100644 index 000000000..f22e3f5fd --- /dev/null +++ b/contrib/games/opentyrian/src/lvllib.h @@ -0,0 +1,34 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef LVLLIB_H +#define LVLLIB_H + +#include "opentyr.h" + + +typedef JE_longint JE_LvlPosType[43]; /* [1..42 + 1] */ + +extern JE_LvlPosType lvlPos; +extern char levelFile[13]; /* string [12] */ +extern JE_word lvlNum; + +void JE_analyzeLevel( void ); + +#endif /* LVLLIB_H */ + diff --git a/contrib/games/opentyrian/src/lvlmast.c b/contrib/games/opentyrian/src/lvlmast.c new file mode 100644 index 000000000..30e7a4edd --- /dev/null +++ b/contrib/games/opentyrian/src/lvlmast.c @@ -0,0 +1,173 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "lvlmast.h" + +#include "opentyr.h" + +const JE_char shapeFile[34] = /* [1..34] */ +{ + '2', '4', '7', '8', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', + 'O', 'P', 'Q', 'R', 'S', 'T', 'U', '5', '#', 'V', '0', '@', '3', '^', '5', '9' +}; + +const char lvlFile[LVL_NUM][9] = /* [1..LVLnum] of string [8] */ +{ +/* 1*/ "ASTROID4", "MAINMAPE", +/* 2*/ "ASTROID5", "MAINMAPD", +/* 3*/ "ASTROID7", "MAINMAPC", +/* 4*/ "BUBBLES", "MAINMAPT", +/* 5*/ "DELI", "MAINMAPS", +/* 6*/ "MINES2", "MAINMAPQ", +/* 7*/ "MINES", "MAINMAPI", +/* 8*/ "SAVARA", "MAINMAPY", +/* 9*/ "TYRLEV", "MAINMAPZ", +/*10*/ "BONUS1-1", "MAINMAP0", +/*11*/ "HOLES", "MAINMAP8", +/*12*/ "SAVARA3", "MAINMAP3", +/*13*/ "DESERT", "MAINMAAA", +/*14*/ "WINDY", "MAINMAAB", +/*15*/ "TYRLEV3", "MAINMAPZ", +/*16*/ "UNDERTYR", "MAINMAAU", +/*17*/ "SAVARA5", "MAINMAAW", +/*18*/ "BEER", "MAINMAAX" +}; + +const char lvlFile2[LVL_NUM_2][9] = /* [1..LVLnum2] of string [8] */ +{ + "JUNGLE", "MAINMAPF", + "GIGER", "MAINMAPR", + "BONUS1-2", "MAINMAP0", + "ASTCITY2", "MAINMAP1", + "SPACEATK", "MAINMAPH", + "STATION", "MAINMAPV", + "GEMWAR", "MAINMAPH", + "MARKERS", "MAINMAPH", + "PATHS", "MAINMAPH", + "JUNGLE2", "MAINMAP5", + "JUNGLE3", "MAINMAP7", + "JUNGLE4", "MAINMAAP" +}; + +const char lvlFile3[LVL_NUM_3][9] = /* [1..LVLnum3] of string [8] */ +{ + "ICE", "MAINMAPX", + "ASTCITY", "MAINMAPP", + "MINES3", "MAINMAPO", + "TUNNEL", "MAINMAPW", + "DELI2", "MAINMAPU", /*Bonus 3*/ + "FLEET", "MAINMAPB", + "STARGATE", "MAINMAP2", /*Bubbly*/ + "TYRLEV2", "MAINMAPZ", + "TUNNEL2", "MAINMAPA", /*Tunnel*/ + "SAVARA2", "MAINMAPY", + "DELI3", "MAINMAPS", + "MACES", "MAINMAP9" /*Bonus Maces*/ +}; + +const char lvlFile4[LVL_NUM_4][9] = /* [1..LVLnum4] of string [8] */ +{ + "HARVEST", "MAINMAAC", /*Harvest World ooooo */ + "MAZE", "MAINMAAD", /*Windy 2 ooooo */ + "SAVARA4", "MAINMAAF", /*Savara IV ooooo */ + "DESERT3", "MAINMAAG", /*Desert ooooo */ + "LAVA1", "MAINMAAH", /*Lava Core ooooo */ + "LAVA2", "MAINMAAJ", /*Lava Center ooooo */ + "CORE", "MAINMAAI", /*Tec tiles ooooo */ + "ICE1", "MAINMAAK", /*Ice exit ooooo */ + "LAVA3", "MAINMAAL", /*Lava Exit ooooo */ + "DESERT4", "MAINMAAM", /*Desert Run ooooo */ + "TUNNEL3", "MAINMAAN", /*Secret Tunnel ooooo */ + "LAVA4", "MAINMAAO", /*Lava Run ooooo */ + "EYESPY", "MAINMAAQ", /*Giger Eye ooooo */ + "FLEET2", "MAINMAPH", /*Dread Not ooooo */ + "BRAIN", "MAINMAAR", /*Brainiac ooooo */ + "NOSE", "MAINMAAS", /*Nose ooooo */ + "DESERT5", "MAINMAAT", /*Time War ooooo */ + "GALAGA", "MAINMAAV", /*Galaga ooooo */ +/*19*/ "SAVARA6", "MAINMAAY", /*Savara Approach ooooo */ +/*20*/ "SPACEAT2", "MAINMABB" /*Camanis Go ooooo */ +}; + +const char lvlFile5[LVL_NUM_5][9] = /* [1..lvlnum5] of string [8] */ +{ +/* 1*/ "E5LVL01", "MAINMA51" /*FogWalls ----- */ +}; + +/*! JE_LvlPosType lvlPos;*/ + +/* + Episode 4 uses... + NEWSH(.SHP + NEWSH^.SHP + NEWSH7.SHP + NEWSHP.SHP + NEWSH&.SHP + NEWSHE.SHP + NEWSHV.SHP + NEWSH#.SHP + NEWSHJ.SHP + NEWSH{.SHP + NEWSHK.SHP + + SHAPESZ.DAT + SHAPESW.DAT + SHAPESX.DAT + SHAPES}.DAT +*/ + +/* +TYPE 5: Shape Files + + SHAPES1.DAT o - - - - Items + SHAPES3.DAT o - - - - Shots + SHAPES6.DAT o - - - - Explosions + SHAPES9.DAT o - - - - Player ships/options + + 1 SHAPES2.DAT - o - - - Tyrian ships + 2 SHAPES4.DAT - o - - - TyrianBoss + 3 SHAPES7.DAT - - - - - Iceships + 4 SHAPES8.DAT - - - - - Tunnel World + 5 SHAPESA.DAT o - - - - Mine Stuff + 6 SHAPESB.DAT - - - - - IceBoss + 7 SHAPESC.DAT - o - - - Deliani Stuff + 8 SHAPESD.DAT o - - - - Asteroid Stuff I + 9 SHAPESE.DAT - o - - - Tyrian Bonus Rock + Bubbles + 10 SHAPESF.DAT - o - - - Savara Stuff I + 11 SHAPESG.DAT - - - - - Giger Stuff + 12 SHAPESH.DAT - - - - - Giger Stuff + 13 SHAPESI.DAT - o - - - Savara Stuff II + Savara Boss + 14 SHAPESJ.DAT - - - - - Jungle Stuff + 15 SHAPESK.DAT - - - - - Snowballs + 16 SHAPESL.DAT - - - - o Satellites + 17 SHAPESM.DAT o - - - - Asteroid Stuff IV + 18 SHAPESN.DAT - - - - - Giger Boss + 19 SHAPESO.DAT - o - - - Savara Boss + 20 SHAPESP.DAT o - - - - Asteroid Stuff III + 21 SHAPESQ.DAT o - - - - Coins and Gems + 22 SHAPESR.DAT - - - - - TunnelWorld Boss + 23 SHAPESS.DAT o - - - - Asteroid Stuff II + 24 SHAPEST.DAT - o - - - Deli Boss + 25 SHAPESU.DAT - - - - - Deli Stuff II + 28 SHAPESV.DAT - - o - o Misc Stuff/Cars + 27 SHAPES#.DAT - - - o o Sawblades + 31 SHAPES(.DAT - - - - o Desert/Lava + + M 1 2 3 4 episode +*/ + diff --git a/contrib/games/opentyrian/src/lvlmast.h b/contrib/games/opentyrian/src/lvlmast.h new file mode 100644 index 000000000..b2cd8f2bf --- /dev/null +++ b/contrib/games/opentyrian/src/lvlmast.h @@ -0,0 +1,47 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef LVLMAST_H +#define LVLMAST_H + +#include "opentyr.h" + +#define EVENT_MAXIMUM 2500 + +#define WEAP_NUM 780 +#define PORT_NUM 42 +#define ARMOR_NUM 4 +#define POWER_NUM 6 +#define ENGINE_NUM 6 +#define OPTION_NUM 30 +#define SHIP_NUM 13 +#define SHIELD_NUM 10 +#define SPECIAL_NUM 46 + +#define ENEMY_NUM 850 + +#define LVL_NUM (18 * 2) +#define LVL_NUM_2 (12 * 2) +#define LVL_NUM_3 (12 * 2) +#define LVL_NUM_4 (20 * 2) +#define LVL_NUM_5 (1 * 2) + +extern const JE_char shapeFile[34]; /* [1..34] */ + +#endif /* LVLMAST_H */ + diff --git a/contrib/games/opentyrian/src/mainint.c b/contrib/games/opentyrian/src/mainint.c new file mode 100644 index 000000000..e58f9f37f --- /dev/null +++ b/contrib/games/opentyrian/src/mainint.c @@ -0,0 +1,4538 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "mainint.h" + +#include "backgrnd.h" +#include "config.h" +#include "editship.h" +#include "episodes.h" +#include "file.h" +#include "fonthand.h" +#include "helptext.h" +#include "helptext.h" +#include "joystick.h" +#include "keyboard.h" +#include "lds_play.h" +#include "loudness.h" +#include "menus.h" +#include "mouse.h" +#include "mtrand.h" +#include "network.h" +#include "nortsong.h" +#include "nortvars.h" +#include "opentyr.h" +#include "palette.h" +#include "params.h" +#include "pcxmast.h" +#include "picload.h" +#include "player.h" +#include "setup.h" +#include "shots.h" +#include "sndmast.h" +#include "sprite.h" +#include "varz.h" +#include "vga256d.h" +#include "video.h" + +#include +#include +#include + +bool button[4]; + +#define MAX_PAGE 8 +#define TOPICS 6 +const JE_byte topicStart[TOPICS] = { 0, 1, 2, 3, 7, 255 }; + +JE_shortint constantLastX; +JE_word textErase; +JE_word upgradeCost; +JE_word downgradeCost; +JE_boolean performSave; +JE_boolean jumpSection; +JE_boolean useLastBank; /* See if I want to use the last 16 colors for DisplayText */ + +bool pause_pressed = false, ingamemenu_pressed = false; + +/* Draws a message at the bottom text window on the playing screen */ +void JE_drawTextWindow( const char *text ) +{ + if (textErase > 0) // erase current text + blit_sprite(VGAScreenSeg, 16, 189, OPTION_SHAPES, 36); // in-game text area + + textErase = 100; + JE_outText(VGAScreenSeg, 20, 190, text, 0, 4); +} + +void JE_outCharGlow( JE_word x, JE_word y, const char *s ) +{ + JE_integer maxloc, loc, z; + JE_shortint glowcol[60]; /* [1..60] */ + JE_shortint glowcolc[60]; /* [1..60] */ + JE_word textloc[60]; /* [1..60] */ + JE_byte bank; + + setjasondelay2(1); + + bank = (warningRed) ? 7 : ((useLastBank) ? 15 : 14); + + if (s[0] == '\0') + return; + + if (frameCountMax == 0) + { + JE_textShade(VGAScreen, x, y, s, bank, 0, PART_SHADE); + JE_showVGA(); + } + else + { + maxloc = strlen(s); + for (z = 0; z < 60; z++) + { + glowcol[z] = -8; + glowcolc[z] = 1; + } + + loc = x; + for (z = 0; z < maxloc; z++) + { + textloc[z] = loc; + + int sprite_id = font_ascii[(unsigned char)s[z]]; + + if (s[z] == ' ') + loc += 6; + else if (sprite_id != -1) + loc += sprite(TINY_FONT, sprite_id)->width + 1; + } + + for (loc = 0; (unsigned)loc < strlen(s) + 28; loc++) + { + if (!ESCPressed) + { + setjasondelay(frameCountMax); + + NETWORK_KEEP_ALIVE(); + + int sprite_id = -1; + + for (z = loc - 28; z <= loc; z++) + { + if (z >= 0 && z < maxloc) + { + sprite_id = font_ascii[(unsigned char)s[z]]; + + if (sprite_id != -1) + { + blit_sprite_hv(VGAScreen, textloc[z], y, TINY_FONT, sprite_id, bank, glowcol[z]); + + glowcol[z] += glowcolc[z]; + if (glowcol[z] > 9) + glowcolc[z] = -1; + } + } + } + if (sprite_id != -1 && --z < maxloc) + blit_sprite_dark(VGAScreen, textloc[z] + 1, y + 1, TINY_FONT, sprite_id, true); + + if (JE_anyButton()) + frameCountMax = 0; + + do + { + if (levelWarningDisplay) + JE_updateWarning(VGAScreen); + + uSDL_Delay(16); + } + while (!(delaycount() == 0 || ESCPressed)); + + JE_showVGA(); + } + } + } +} + +void JE_drawPortConfigButtons( void ) // rear weapon pattern indicator +{ + if (twoPlayerMode) + return; + + if (player[0].weapon_mode == 1) + { + blit_sprite(VGAScreenSeg, 285, 44, OPTION_SHAPES, 18); // lit + blit_sprite(VGAScreenSeg, 302, 44, OPTION_SHAPES, 19); // unlit + } + else // == 2 + { + blit_sprite(VGAScreenSeg, 285, 44, OPTION_SHAPES, 19); // unlit + blit_sprite(VGAScreenSeg, 302, 44, OPTION_SHAPES, 18); // lit + } +} + +void JE_helpSystem( JE_byte startTopic ) +{ + JE_integer page, lastPage = 0; + JE_byte menu; + + page = topicStart[startTopic-1]; + + fade_black(10); + JE_loadPic(VGAScreen, 2, false); + + play_song(SONG_MAPVIEW); + + JE_showVGA(); + fade_palette(colors, 10, 0, 255); + + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); + + do + { + memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->pitch * VGAScreen->h); + + temp2 = 0; + + for (temp = 0; temp < TOPICS; temp++) + { + if (topicStart[temp] <= page) + { + temp2 = temp; + } + } + + if (page > 0) + { + JE_char buf[128]; + + sprintf(buf, "%s %d", miscText[24], page-topicStart[temp2]+1); + JE_outText(VGAScreen, 10, 192, buf, 13, 5); + + sprintf(buf, "%s %d of %d", miscText[25], page, MAX_PAGE); + JE_outText(VGAScreen, 220, 192, buf, 13, 5); + + JE_dString(VGAScreen, JE_fontCenter(topicName[temp2], SMALL_FONT_SHAPES), 1, topicName[temp2], SMALL_FONT_SHAPES); + } + + menu = 0; + + helpBoxBrightness = 3; + verticalHeight = 8; + + switch (page) + { + case 0: + menu = 2; + if (lastPage == MAX_PAGE) + { + menu = TOPICS; + } + JE_dString(VGAScreen, JE_fontCenter(topicName[0], FONT_SHAPES), 30, topicName[0], FONT_SHAPES); + + do + { + for (temp = 1; temp <= TOPICS; temp++) + { + char buf[21+1]; + + if (temp == menu-1) + { + strcpy(buf+1, topicName[temp]); + buf[0] = '~'; + } else { + strcpy(buf, topicName[temp]); + } + + JE_dString(VGAScreen, JE_fontCenter(topicName[temp], SMALL_FONT_SHAPES), temp * 20 + 40, buf, SMALL_FONT_SHAPES); + } + + //JE_waitRetrace(); didn't do anything anyway? + JE_showVGA(); + + tempW = 0; + JE_textMenuWait(&tempW, false); + if (newkey) + { + switch (lastkey_sym) + { + case SDLK_UP: + menu--; + if (menu < 2) + { + menu = TOPICS; + } + JE_playSampleNum(S_CURSOR); + break; + case SDLK_DOWN: + menu++; + if (menu > TOPICS) + { + menu = 2; + } + JE_playSampleNum(S_CURSOR); + break; + default: + break; + } + } + } while (!(lastkey_sym == SDLK_ESCAPE || lastkey_sym == SDLK_RETURN)); + + if (lastkey_sym == SDLK_RETURN) + { + page = topicStart[menu-1]; + JE_playSampleNum(S_CLICK); + } + + break; + case 1: /* One-Player Menu */ + JE_HBox(VGAScreen, 10, 20, 2, 60); + JE_HBox(VGAScreen, 10, 50, 5, 60); + JE_HBox(VGAScreen, 10, 80, 21, 60); + JE_HBox(VGAScreen, 10, 110, 1, 60); + JE_HBox(VGAScreen, 10, 140, 28, 60); + break; + case 2: /* Two-Player Menu */ + JE_HBox(VGAScreen, 10, 20, 1, 60); + JE_HBox(VGAScreen, 10, 60, 2, 60); + JE_HBox(VGAScreen, 10, 100, 21, 60); + JE_HBox(VGAScreen, 10, 140, 28, 60); + break; + case 3: /* Upgrade Ship */ + JE_HBox(VGAScreen, 10, 20, 5, 60); + JE_HBox(VGAScreen, 10, 70, 6, 60); + JE_HBox(VGAScreen, 10, 110, 7, 60); + break; + case 4: + JE_HBox(VGAScreen, 10, 20, 8, 60); + JE_HBox(VGAScreen, 10, 55, 9, 60); + JE_HBox(VGAScreen, 10, 87, 10, 60); + JE_HBox(VGAScreen, 10, 120, 11, 60); + JE_HBox(VGAScreen, 10, 170, 13, 60); + break; + case 5: + JE_HBox(VGAScreen, 10, 20, 14, 60); + JE_HBox(VGAScreen, 10, 80, 15, 60); + JE_HBox(VGAScreen, 10, 120, 16, 60); + break; + case 6: + JE_HBox(VGAScreen, 10, 20, 17, 60); + JE_HBox(VGAScreen, 10, 40, 18, 60); + JE_HBox(VGAScreen, 10, 130, 20, 60); + break; + case 7: /* Options */ + JE_HBox(VGAScreen, 10, 20, 21, 60); + JE_HBox(VGAScreen, 10, 70, 22, 60); + JE_HBox(VGAScreen, 10, 110, 23, 60); + JE_HBox(VGAScreen, 10, 140, 24, 60); + break; + case 8: + JE_HBox(VGAScreen, 10, 20, 25, 60); + JE_HBox(VGAScreen, 10, 60, 26, 60); + JE_HBox(VGAScreen, 10, 100, 27, 60); + JE_HBox(VGAScreen, 10, 140, 28, 60); + JE_HBox(VGAScreen, 10, 170, 29, 60); + break; + } + + helpBoxBrightness = 1; + verticalHeight = 7; + + lastPage = page; + + if (menu == 0) + { + do { + setjasondelay(3); + + push_joysticks_as_keyboard(); + service_SDL_events(true); + + JE_showVGA(); + + wait_delay(); + } while (!newkey && !newmouse); + + wait_noinput(false, true, false); + + if (newmouse) + { + switch (lastmouse_but) + { + case SDL_BUTTON_LEFT: + lastkey_sym = SDLK_RIGHT; + break; + case SDL_BUTTON_RIGHT: + lastkey_sym = SDLK_LEFT; + break; + case SDL_BUTTON_MIDDLE: + lastkey_sym = SDLK_ESCAPE; + break; + } + do + { + service_SDL_events(false); + } while (mousedown); + newkey = true; + } + + if (newkey) + { + switch (lastkey_sym) + { + case SDLK_LEFT: + case SDLK_UP: + case SDLK_PAGEUP: + page--; + JE_playSampleNum(S_CURSOR); + break; + case SDLK_RIGHT: + case SDLK_DOWN: + case SDLK_PAGEDOWN: + case SDLK_RETURN: + case SDLK_SPACE: + if (page == MAX_PAGE) + { + page = 0; + } else { + page++; + } + JE_playSampleNum(S_CURSOR); + break; + case SDLK_F1: + page = 0; + JE_playSampleNum(S_CURSOR); + break; + default: + break; + } + } + } + + if (page == 255) + { + lastkey_sym = SDLK_ESCAPE; + } + } while (lastkey_sym != SDLK_ESCAPE); +} + +// cost to upgrade a weapon power from power-1 (where power == 0 indicates an unupgraded weapon) +long weapon_upgrade_cost( long base_cost, unsigned int power ) +{ + assert(power <= 11); + + unsigned int temp = 0; + + // 0 1 3 6 10 15 21 29 ... + for (; power > 0; power--) + temp += power; + + return base_cost * temp; +} + +ulong JE_getCost( JE_byte itemType, JE_word itemNum ) +{ + long cost = 0; + + switch (itemType) + { + case 2: + cost = (itemNum > 90) ? 100 : ships[itemNum].cost; + break; + case 3: + case 4: + cost = weaponPort[itemNum].cost; + + const uint port = itemType - 3, + item_power = player[0].items.weapon[port].power - 1; + + downgradeCost = weapon_upgrade_cost(cost, item_power); + upgradeCost = weapon_upgrade_cost(cost, item_power + 1); + break; + case 5: + cost = shields[itemNum].cost; + break; + case 6: + cost = powerSys[itemNum].cost; + break; + case 7: + case 8: + cost = options[itemNum].cost; + break; + } + + return cost; +} + +void JE_loadScreen( void ) +{ + JE_boolean quit; + JE_byte sel, screen, min = 0, max = 0; + char *tempstr; + char *tempstr2; + JE_boolean mal_str = false; + int len; + + tempstr = NULL; + + free_sprite2s(&shapes6); + JE_loadCompShapes(&shapes6, '1'); // need arrow sprites + + fade_black(10); + JE_loadPic(VGAScreen, 2, false); + JE_showVGA(); + fade_palette(colors, 10, 0, 255); + + screen = 1; + sel = 1; + quit = false; + + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); + + do + { + while (mousedown) + { + service_SDL_events(false); + tempX = mouse_x; + tempY = mouse_y; + } + + memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->pitch * VGAScreen->h); + + JE_dString(VGAScreen, JE_fontCenter(miscText[38 + screen - 1], FONT_SHAPES), 5, miscText[38 + screen - 1], FONT_SHAPES); + + switch (screen) + { + case 1: + min = 1; + max = 12; + break; + case 2: + min = 12; + max = 23; + } + + /* SYN: Go through text line by line */ + for (x = min; x <= max; x++) + { + tempY = 30 + (x - min) * 13; + + if (x == max) + { + /* Last line is return to main menu, not a save game */ + if (mal_str) + { + free(tempstr); + mal_str = false; + } + tempstr = miscText[34 - 1]; + + if (x == sel) /* Highlight if selected */ + { + temp2 = 254; + } else { + temp2 = 250; + } + } else { + if (x == sel) /* Highlight if selected */ + { + temp2 = 254; + } else { + temp2 = 250 - ((saveFiles[x - 1].level == 0) << 1); + } + + if (saveFiles[x - 1].level == 0) /* I think this means the save file is unused */ + { + if (mal_str) + { + free(tempstr); + mal_str = false; + } + tempstr = miscText[3 - 1]; + } else { + if (mal_str) + { + free(tempstr); + mal_str = false; + } + tempstr = saveFiles[x - 1].name; + } + } + + /* Write first column text */ + JE_textShade(VGAScreen, 10, tempY, tempstr, 13, (temp2 % 16) - 8, FULL_SHADE); + + if (x < max) /* Write additional columns for all but the last row */ + { + if (saveFiles[x - 1].level == 0) + { + if (mal_str) + { + free(tempstr); + } + tempstr = malloc(7); + mal_str = true; + strcpy(tempstr, "-----"); /* Unused save slot */ + } else { + tempstr = saveFiles[x - 1].levelName; + tempstr2 = malloc(5 + strlen(miscTextB[2-1])); + sprintf(tempstr2, "%s %d", miscTextB[2-1], saveFiles[x - 1].episode); + JE_textShade(VGAScreen, 250, tempY, tempstr2, 5, (temp2 % 16) - 8, FULL_SHADE); + free(tempstr2); + } + + len = strlen(miscTextB[3-1]) + 2 + strlen(tempstr); + tempstr2 = malloc(len); + sprintf(tempstr2, "%s %s", miscTextB[3 - 1], tempstr); + JE_textShade(VGAScreen, 120, tempY, tempstr2, 5, (temp2 % 16) - 8, FULL_SHADE); + free(tempstr2); + } + + } + + if (screen == 2) + { + blit_sprite2x2(VGAScreen, 90, 180, shapes6, 279); + } + if (screen == 1) + { + blit_sprite2x2(VGAScreen, 220, 180, shapes6, 281); + } + + helpBoxColor = 15; + JE_helpBox(VGAScreen, 110, 182, miscText[56-1], 25); + + JE_showVGA(); + + tempW = 0; + JE_textMenuWait(&tempW, false); + + + if (newkey) + { + switch (lastkey_sym) + { + case SDLK_UP: + sel--; + if (sel < min) + { + sel = max; + } + JE_playSampleNum(S_CURSOR); + break; + case SDLK_DOWN: + sel++; + if (sel > max) + { + sel = min; + } + JE_playSampleNum(S_CURSOR); + break; + case SDLK_LEFT: + case SDLK_RIGHT: + if (screen == 1) + { + screen = 2; + sel += 11; + } else { + screen = 1; + sel -= 11; + } + break; + case SDLK_RETURN: + if (sel < max) + { + if (saveFiles[sel - 1].level > 0) + { + JE_playSampleNum(S_SELECT); + performSave = false; + JE_operation(sel); + quit = true; + } else { + JE_playSampleNum(S_CLINK); + } + } else { + quit = true; + } + + + break; + case SDLK_ESCAPE: + quit = true; + break; + default: + break; + } + + } + } while (!quit); +} + +ulong JE_totalScore( const Player *this_player ) +{ + ulong temp = this_player->cash; + + temp += JE_getValue(2, this_player->items.ship); + temp += JE_getValue(3, this_player->items.weapon[FRONT_WEAPON].id); + temp += JE_getValue(4, this_player->items.weapon[REAR_WEAPON].id); + temp += JE_getValue(5, this_player->items.shield); + temp += JE_getValue(6, this_player->items.generator); + temp += JE_getValue(7, this_player->items.sidekick[LEFT_SIDEKICK]); + temp += JE_getValue(8, this_player->items.sidekick[RIGHT_SIDEKICK]); + + return temp; +} + +JE_longint JE_getValue( JE_byte itemType, JE_word itemNum ) +{ + long value = 0; + + switch (itemType) + { + case 2: + value = ships[itemNum].cost; + break; + case 3: + case 4:; + const long base_value = weaponPort[itemNum].cost; + + // if two-player, use first player's front and second player's rear weapon + const uint port = itemType - 3; + const uint item_power = player[twoPlayerMode ? port : 0].items.weapon[port].power - 1; + + value = base_value; + for (unsigned int i = 1; i <= item_power; ++i) + value += weapon_upgrade_cost(base_value, i); + break; + case 5: + value = shields[itemNum].cost; + break; + case 6: + value = powerSys[itemNum].cost; + break; + case 7: + case 8: + value = options[itemNum].cost; + break; + } + + return value; +} + +void JE_nextEpisode( void ) +{ + strcpy(lastLevelName, "Completed"); + + if (episodeNum == initial_episode_num && !gameHasRepeated && episodeNum != EPISODE_AVAILABLE && + !isNetworkGame && !constantPlay) + { + JE_highScoreCheck(); + } + + unsigned int newEpisode = JE_findNextEpisode(); + + if (jumpBackToEpisode1) + { + // shareware version check + if (episodeNum == 1 && + !isNetworkGame && !constantPlay) + { + // JE_loadOrderingInfo(); + } + + if (episodeNum > 2 && + !constantPlay) + { + JE_playCredits(); + } + + // randomly give player the SuperCarrot + if ((mt_rand() % 6) == 0) + { + player[0].items.ship = 2; // SuperCarrot + player[0].items.weapon[FRONT_WEAPON].id = 23; // Banana Blast + player[0].items.weapon[REAR_WEAPON].id = 24; // Banana Blast Rear + + for (uint i = 0; i < COUNTOF(player[0].items.weapon); ++i) + player[0].items.weapon[i].power = 1; + + player[1].items.weapon[REAR_WEAPON].id = 24; // Banana Blast Rear + + player[0].last_items = player[0].items; + } + } + + if (newEpisode != episodeNum) + JE_initEpisode(newEpisode); + + gameLoaded = true; + mainLevel = FIRST_LEVEL; + saveLevel = FIRST_LEVEL; + + play_song(26); + + JE_clr256(VGAScreen); + memcpy(colors, palettes[6-1], sizeof(colors)); + + JE_dString(VGAScreen, JE_fontCenter(episode_name[episodeNum], SMALL_FONT_SHAPES), 130, episode_name[episodeNum], SMALL_FONT_SHAPES); + JE_dString(VGAScreen, JE_fontCenter(miscText[5-1], SMALL_FONT_SHAPES), 185, miscText[5-1], SMALL_FONT_SHAPES); + + JE_showVGA(); + fade_palette(colors, 15, 0, 255); + + JE_wipeKey(); + if (!constantPlay) + { + do + { + NETWORK_KEEP_ALIVE(); + + uSDL_Delay(16); + } while (!JE_anyButton()); + } + + fade_black(15); +} + +void JE_initPlayerData( void ) +{ + /* JE: New Game Items/Data */ + + player[0].items.ship = 1; // USP Talon + player[0].items.weapon[FRONT_WEAPON].id = 1; // Pulse Cannon + player[0].items.weapon[REAR_WEAPON].id = 0; // None + player[0].items.shield = 4; // Gencore High Energy Shield + player[0].items.generator = 2; // Advanced MR-12 + for (uint i = 0; i < COUNTOF(player[0].items.sidekick); ++i) + player[0].items.sidekick[i] = 0; // None + player[0].items.special = 0; // None + + player[0].last_items = player[0].items; + + player[1].items = player[0].items; + player[1].items.weapon[REAR_WEAPON].id = 15; // Vulcan Cannon + player[1].items.sidekick_level = 101; // 101, 102, 103 + player[1].items.sidekick_series = 0; // None + + gameHasRepeated = false; + onePlayerAction = false; + superArcadeMode = SA_NONE; + superTyrian = false; + twoPlayerMode = false; + + secretHint = (mt_rand() % 3) + 1; + + for (uint p = 0; p < COUNTOF(player); ++p) + { + for (uint i = 0; i < COUNTOF(player->items.weapon); ++i) + { + player[p].items.weapon[i].power = 1; + } + + player[p].weapon_mode = 1; + player[p].armor = ships[player[p].items.ship].dmg; + + player[p].is_dragonwing = (p == 1); + player[p].lives = &player[p].items.weapon[p].power; + + } + + mainLevel = FIRST_LEVEL; + saveLevel = FIRST_LEVEL; + + strcpy(lastLevelName, miscText[19]); +} + +void JE_sortHighScores( void ) +{ + JE_byte x; + + temp = 0; + for (x = 0; x < 6; x++) + { + JE_sort(); + temp += 3; + } +} + +void JE_highScoreScreen( void ) +{ + int min = 1; + int max = 3; + + int x, z; + short int chg; + int quit; + char scoretemp[32]; + + free_sprite2s(&shapes6); + JE_loadCompShapes(&shapes6, '1'); // need arrow sprites + + fade_black(10); + JE_loadPic(VGAScreen, 2, false); + JE_showVGA(); + fade_palette(colors, 10, 0, 255); + + quit = false; + x = 1; + chg = 1; + + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); + + do + { + if (episodeAvail[x-1]) + { + memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->pitch * VGAScreen->h); + + JE_dString(VGAScreen, JE_fontCenter(miscText[51 - 1], FONT_SHAPES), 03, miscText[51 - 1], FONT_SHAPES); + JE_dString(VGAScreen, JE_fontCenter(episode_name[x], SMALL_FONT_SHAPES), 30, episode_name[x], SMALL_FONT_SHAPES); + + /* Player 1 */ + temp = (x * 6) - 6; + + JE_dString(VGAScreen, JE_fontCenter(miscText[47 - 1], SMALL_FONT_SHAPES), 55, miscText[47 - 1], SMALL_FONT_SHAPES); + + for (z = 0; z < 3; z++) + { + int difficulty = saveFiles[temp + z].highScoreDiff; + if (difficulty > 9) + { + saveFiles[temp + z].highScoreDiff = 0; + difficulty = 0; + } + sprintf(scoretemp, "~#%d:~ %d", z + 1, saveFiles[temp+z].highScore1); + JE_textShade(VGAScreen, 250, ((z+1) * 10) + 65 , difficultyNameB[difficulty], 15, difficulty + (difficulty == 0 ? 0 : -1), FULL_SHADE); + JE_textShade(VGAScreen, 20, ((z+1) * 10) + 65 , scoretemp, 15, 0, FULL_SHADE); + JE_textShade(VGAScreen, 110, ((z+1) * 10) + 65 , saveFiles[temp + z].highScoreName, 15, 2, FULL_SHADE); + } + + /* Player 2 */ + temp += 3; + + JE_dString(VGAScreen, JE_fontCenter( miscText[48 - 1], SMALL_FONT_SHAPES), 120, miscText[48 - 1], SMALL_FONT_SHAPES); + + /*{ textshade(20,125,misctext[49],15,3,_FullShade); + textshade(80,125,misctext[50],15,3,_FullShade);}*/ + + for (z = 0; z < 3; z++) + { + int difficulty = saveFiles[temp + z].highScoreDiff; + if (difficulty > 9) + { + saveFiles[temp + z].highScoreDiff = 0; + difficulty = 0; + } + sprintf(scoretemp, "~#%d:~ %d", z + 1, saveFiles[temp+z].highScore1); /* Not .highScore2 for some reason */ + JE_textShade(VGAScreen, 250, ((z+1) * 10) + 125 , difficultyNameB[difficulty], 15, difficulty + (difficulty == 0 ? 0 : -1), FULL_SHADE); + JE_textShade(VGAScreen, 20, ((z+1) * 10) + 125 , scoretemp, 15, 0, FULL_SHADE); + JE_textShade(VGAScreen, 110, ((z+1) * 10) + 125 , saveFiles[temp + z].highScoreName, 15, 2, FULL_SHADE); + } + + if (x > 1) + { + blit_sprite2x2(VGAScreen, 90, 180, shapes6, 279); + } + + if ( ( (x < 2) && episodeAvail[2-1] ) || ( (x < 3) && episodeAvail[3-1] ) ) + { + blit_sprite2x2(VGAScreen, 220, 180, shapes6, 281); + } + + helpBoxColor = 15; + JE_helpBox(VGAScreen, 110, 182, miscText[57 - 1], 25); + + /* {Dstring(fontcenter(misctext[57],_SmallFontShapes),190,misctext[57],_SmallFontShapes);} */ + + JE_showVGA(); + + tempW = 0; + JE_textMenuWait(&tempW, false); + + if (newkey) + { + switch (lastkey_sym) + { + case SDLK_LEFT: + x--; + chg = -1; + break; + case SDLK_RIGHT: + x++; + chg = 1; + break; + default: + break; + } + } + + } else { + x += chg; + } + + x = ( x < min ) ? max : ( x > max ) ? min : x; + + if (newkey) + { + switch (lastkey_sym) + { + case SDLK_RETURN: + case SDLK_ESCAPE: + quit = true; + break; + default: + break; + } + } + + } while (!quit); + +} + +void JE_gammaCorrect_func( JE_byte *col, JE_real r ) +{ + int temp = roundf(*col * r); + if (temp > 255) + { + temp = 255; + } + *col = temp; +} + +void JE_gammaCorrect( Palette *colorBuffer, JE_byte gamma ) +{ + int x; + JE_real r = 1 + (JE_real)gamma / 10; + + for (x = 0; x < 256; x++) + { + JE_gammaCorrect_func(&(*colorBuffer)[x].r, r); + JE_gammaCorrect_func(&(*colorBuffer)[x].g, r); + JE_gammaCorrect_func(&(*colorBuffer)[x].b, r); + } +} + +JE_boolean JE_gammaCheck( void ) +{ + bool temp = keysactive[SDLK_F11] != 0; + if (temp) + { + keysactive[SDLK_F11] = false; + newkey = false; + gammaCorrection = (gammaCorrection + 1) % 4; + memcpy(colors, palettes[pcxpal[3-1]], sizeof(colors)); + JE_gammaCorrect(&colors, gammaCorrection); + set_palette(colors, 0, 255); + } + return temp; +} + +void JE_doInGameSetup( void ) +{ + haltGame = false; + +#ifdef WITH_NETWORK + if (isNetworkGame) + { + network_prepare(PACKET_GAME_MENU); + network_send(4); // PACKET_GAME_MENU + + while (true) + { + service_SDL_events(false); + + if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_GAME_MENU) + { + network_update(); + break; + } + + network_update(); + network_check(); + + uSDL_Delay(16); + } + } +#endif + + if (yourInGameMenuRequest) + { + if (JE_inGameSetup()) + { + reallyEndLevel = true; + playerEndLevel = true; + } + quitRequested = false; + + keysactive[SDLK_ESCAPE] = false; + +#ifdef WITH_NETWORK + if (isNetworkGame) + { + if (!playerEndLevel) + { + network_prepare(PACKET_WAITING); + network_send(4); // PACKET_WAITING + } else { + network_prepare(PACKET_GAME_QUIT); + network_send(4); // PACKET_GAMEQUIT + } + } +#endif + } + +#ifdef WITH_NETWORK + if (isNetworkGame) + { + SDL_Surface *temp_surface = VGAScreen; + VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ + + if (!yourInGameMenuRequest) + { + JE_barShade(VGAScreen, 3, 60, 257, 80); /*Help Box*/ + JE_barShade(VGAScreen, 5, 62, 255, 78); + JE_dString(VGAScreen, 10, 65, "Other player in options menu.", SMALL_FONT_SHAPES); + JE_showVGA(); + + while (true) + { + service_SDL_events(false); + JE_showVGA(); + + if (packet_in[0]) + { + if (SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_WAITING) + { + network_check(); + break; + } else if (SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_GAME_QUIT) { + reallyEndLevel = true; + playerEndLevel = true; + + network_check(); + break; + } + } + + network_update(); + network_check(); + + uSDL_Delay(16); + } + } else { + /* + JE_barShade(3, 160, 257, 180); /-*Help Box*-/ + JE_barShade(5, 162, 255, 178); + tempScreenSeg = VGAScreen; + JE_dString(VGAScreen, 10, 165, "Waiting for other player.", SMALL_FONT_SHAPES); + JE_showVGA(); + */ + } + + while (!network_is_sync()) + { + service_SDL_events(false); + + network_check(); + uSDL_Delay(16); + } + + VGAScreen = temp_surface; /* side-effect of game_screen */ + } +#endif + + yourInGameMenuRequest = false; + + //skipStarShowVGA = true; +} + +JE_boolean JE_inGameSetup( void ) +{ + SDL_Surface *temp_surface = VGAScreen; + VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ + + JE_boolean returnvalue = false; + + const JE_byte help[6] /* [1..6] */ = {15, 15, 28, 29, 26, 27}; + JE_byte sel; + JE_boolean quit; + + bool first = true; + + //tempScreenSeg = VGAScreenSeg; /* ? should work as VGAScreen */ + + quit = false; + sel = 1; + + JE_barShade(VGAScreen, 3, 13, 217, 137); /*Main Box*/ + JE_barShade(VGAScreen, 5, 15, 215, 135); + + JE_barShade(VGAScreen, 3, 143, 257, 157); /*Help Box*/ + JE_barShade(VGAScreen, 5, 145, 255, 155); + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); + + do + { + memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->pitch * VGAScreen->h); + + for (x = 0; x < 6; x++) + { + JE_outTextAdjust(VGAScreen, 10, (x + 1) * 20, inGameText[x], 15, ((sel == x+1) << 1) - 4, SMALL_FONT_SHAPES, true); + } + + JE_outTextAdjust(VGAScreen, 120, 3 * 20, detailLevel[processorType-1], 15, ((sel == 3) << 1) - 4, SMALL_FONT_SHAPES, true); + JE_outTextAdjust(VGAScreen, 120, 4 * 20, gameSpeedText[gameSpeed-1], 15, ((sel == 4) << 1) - 4, SMALL_FONT_SHAPES, true); + + JE_outTextAdjust(VGAScreen, 10, 147, mainMenuHelp[help[sel-1]-1], 14, 6, TINY_FONT, true); + + JE_barDrawShadow(VGAScreen, 120, 20, 1, music_disabled ? 12 : 16, tyrMusicVolume / 12, 3, 13); + JE_barDrawShadow(VGAScreen, 120, 40, 1, samples_disabled ? 12 : 16, fxVolume / 12, 3, 13); + + JE_showVGA(); + + if (first) + { + first = false; + wait_noinput(false, false, true); // TODO: should up the joystick repeat temporarily instead + } + + tempW = 0; + JE_textMenuWait(&tempW, true); + + if (inputDetected) + { + switch (lastkey_sym) + { + case SDLK_RETURN: + JE_playSampleNum(S_SELECT); + switch (sel) + { + case 1: + music_disabled = !music_disabled; + break; + case 2: + samples_disabled = !samples_disabled; + break; + case 3: + case 4: + sel = 5; + break; + case 5: + quit = true; + break; + case 6: + returnvalue = true; + quit = true; + if (constantPlay) + { + JE_tyrianHalt(0); + } + + if (isNetworkGame) + { /*Tell other computer to exit*/ + haltGame = true; + playerEndLevel = true; + } + break; + } + break; + case SDLK_ESCAPE: + quit = true; + JE_playSampleNum(S_SPRING); + break; + case SDLK_UP: + if (--sel < 1) + { + sel = 6; + } + JE_playSampleNum(S_CURSOR); + break; + case SDLK_DOWN: + if (++sel > 6) + { + sel = 1; + } + JE_playSampleNum(S_CURSOR); + break; + case SDLK_LEFT: + switch (sel) + { + case 1: + JE_changeVolume(&tyrMusicVolume, -12, &fxVolume, 0); + if (music_disabled) + { + music_disabled = false; + restart_song(); + } + break; + case 2: + JE_changeVolume(&tyrMusicVolume, 0, &fxVolume, -12); + samples_disabled = false; + break; + case 3: + if (--processorType < 1) + { + processorType = 4; + } + JE_initProcessorType(); + JE_setNewGameSpeed(); + break; + case 4: + if (--gameSpeed < 1) + { + gameSpeed = 5; + } + JE_initProcessorType(); + JE_setNewGameSpeed(); + break; + } + if (sel < 5) + { + JE_playSampleNum(S_CURSOR); + } + break; + case SDLK_RIGHT: + switch (sel) + { + case 1: + JE_changeVolume(&tyrMusicVolume, 12, &fxVolume, 0); + if (music_disabled) + { + music_disabled = false; + restart_song(); + } + break; + case 2: + JE_changeVolume(&tyrMusicVolume, 0, &fxVolume, 12); + samples_disabled = false; + break; + case 3: + if (++processorType > 4) + { + processorType = 1; + } + JE_initProcessorType(); + JE_setNewGameSpeed(); + break; + case 4: + if (++gameSpeed > 5) + { + gameSpeed = 1; + } + JE_initProcessorType(); + JE_setNewGameSpeed(); + break; + } + if (sel < 5) + { + JE_playSampleNum(S_CURSOR); + } + break; + case SDLK_w: + if (sel == 3) + { + processorType = 6; + JE_initProcessorType(); + } + default: + break; + } + } + + } while (!(quit || haltGame)); + + VGAScreen = temp_surface; /* side-effect of game_screen */ + + return returnvalue; +} + +void JE_inGameHelp( void ) +{ + SDL_Surface *temp_surface = VGAScreen; + VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ + + //tempScreenSeg = VGAScreenSeg; + + JE_clearKeyboard(); + JE_wipeKey(); + + JE_barShade(VGAScreen, 1, 1, 262, 182); /*Main Box*/ + JE_barShade(VGAScreen, 3, 3, 260, 180); + JE_barShade(VGAScreen, 5, 5, 258, 178); + JE_barShade(VGAScreen, 7, 7, 256, 176); + fill_rectangle_xy(VGAScreen, 9, 9, 254, 174, 0); + + if (twoPlayerMode) // Two-Player Help + { + helpBoxColor = 3; + helpBoxBrightness = 3; + JE_HBox(VGAScreen, 20, 4, 36, 50); + + // weapon help + blit_sprite(VGAScreenSeg, 2, 21, OPTION_SHAPES, 43); + helpBoxColor = 5; + helpBoxBrightness = 3; + JE_HBox(VGAScreen, 55, 20, 37, 40); + + // sidekick help + blit_sprite(VGAScreenSeg, 5, 36, OPTION_SHAPES, 41); + helpBoxColor = 5; + helpBoxBrightness = 3; + JE_HBox(VGAScreen, 40, 43, 34, 44); + + // sheild/armor help + blit_sprite(VGAScreenSeg, 2, 79, OPTION_SHAPES, 42); + helpBoxColor = 5; + helpBoxBrightness = 3; + JE_HBox(VGAScreen, 54, 84, 35, 40); + + helpBoxColor = 5; + helpBoxBrightness = 3; + JE_HBox(VGAScreen, 5, 126, 38, 55); + helpBoxColor = 5; + helpBoxBrightness = 3; + JE_HBox(VGAScreen, 5, 160, 39, 55); + } + else + { + // power bar help + blit_sprite(VGAScreenSeg, 15, 5, OPTION_SHAPES, 40); + helpBoxColor = 5; + helpBoxBrightness = 3; + JE_HBox(VGAScreen, 40, 10, 31, 45); + + // weapon help + blit_sprite(VGAScreenSeg, 5, 37, OPTION_SHAPES, 39); + helpBoxColor = 5; + helpBoxBrightness = 3; + JE_HBox(VGAScreen, 40, 40, 32, 44); + helpBoxColor = 5; + helpBoxBrightness = 3; + JE_HBox(VGAScreen, 40, 60, 33, 44); + + // sidekick help + blit_sprite(VGAScreenSeg, 5, 98, OPTION_SHAPES, 41); + helpBoxColor = 5; + helpBoxBrightness = 3; + JE_HBox(VGAScreen, 40, 103, 34, 44); + + // shield/armor help + blit_sprite(VGAScreenSeg, 2, 138, OPTION_SHAPES, 42); + helpBoxColor = 5; + helpBoxBrightness = 3; + JE_HBox(VGAScreen, 54, 143, 35, 40); + } + + // "press a key" + blit_sprite(VGAScreenSeg, 16, 189, OPTION_SHAPES, 36); // in-game text area + JE_outText(VGAScreenSeg, 120 - JE_textWidth(miscText[5-1], TINY_FONT) / 2 + 20, 190, miscText[5-1], 0, 4); + + JE_showVGA(); + + do + { + tempW = 0; + JE_textMenuWait(&tempW, true); + } + while (!inputDetected); + + textErase = 1; + + VGAScreen = temp_surface; +} + +void JE_highScoreCheck( void ) +{ + free_sprite2s(&shapes6); + JE_loadCompShapes(&shapes6, '1'); // need mouse cursor sprite + + Sint32 temp_score; + + for (int temp_p = 0; temp_p < (twoPlayerMode ? 2 : 1); ++temp_p) + { + JE_sortHighScores(); + + int p = temp_p; + + if (twoPlayerMode) + { + // ask for the highest scorer first + if (player[0].cash < player[1].cash) + p = (temp_p == 0) ? 1 : 0; + + temp_score = (p == 0) ? player[0].cash : player[1].cash; + } + else + { + // single player highscore includes cost of upgrades + temp_score = JE_totalScore(&player[0]); + } + + int slot; + const int first_slot = (initial_episode_num - 1) * 6 + (twoPlayerMode ? 3 : 0), + slot_limit = first_slot + 3; + + for (slot = first_slot; slot < slot_limit; ++slot) + { + if (temp_score > saveFiles[slot].highScore1) + break; + } + + // did you get a high score? + if (slot < slot_limit) + { + // shift down old scores + for (int i = slot_limit - 1; i > slot; --i) + { + saveFiles[i].highScore1 = saveFiles[i - 1].highScore1; + strcpy(saveFiles[i].highScoreName, saveFiles[i - 1].highScoreName); + } + + wait_noinput(false, true, false); + + JE_clr256(VGAScreen); + JE_showVGA(); + memcpy(colors, palettes[0], sizeof(colors)); + + play_song(33); + + { + /* Enter Thy name */ + + JE_byte flash = 8 * 16 + 10; + JE_boolean fadein = true; + JE_boolean quit = false, cancel = false; + char stemp[30], tempstr[30]; + char buffer[256]; + + strcpy(stemp, " "); + temp = 0; + + JE_barShade(VGAScreen, 65, 55, 255, 155); + + do + { + service_SDL_events(true); + + JE_dString(VGAScreen, JE_fontCenter(miscText[51], FONT_SHAPES), 3, miscText[51], FONT_SHAPES); + + temp3 = twoPlayerMode ? 58 + p : 53; + + JE_dString(VGAScreen, JE_fontCenter(miscText[temp3-1], SMALL_FONT_SHAPES), 30, miscText[temp3-1], SMALL_FONT_SHAPES); + + blit_sprite(VGAScreenSeg, 50, 50, OPTION_SHAPES, 35); // message box + + if (twoPlayerMode) + { + sprintf(buffer, "%s %s", miscText[48 + p], miscText[53]); + JE_textShade(VGAScreen, 60, 55, buffer, 11, 4, FULL_SHADE); + } + else + { + JE_textShade(VGAScreen, 60, 55, miscText[53], 11, 4, FULL_SHADE); + } + + sprintf(buffer, "%s %d", miscText[37], temp_score); + JE_textShade(VGAScreen, 70, 70, buffer, 11, 4, FULL_SHADE); + + do + { + flash = (flash == 8 * 16 + 10) ? 8 * 16 + 2 : 8 * 16 + 10; + temp3 = (temp3 == 6) ? 2 : 6; + + strncpy(tempstr, stemp, temp); + tempstr[temp] = '\0'; + JE_outText(VGAScreen, 65, 89, tempstr, 8, 3); + tempW = 65 + JE_textWidth(tempstr, TINY_FONT); + JE_barShade(VGAScreen, tempW + 2, 90, tempW + 6, 95); + fill_rectangle_xy(VGAScreen, tempW + 1, 89, tempW + 5, 94, flash); + + for (int i = 0; i < 14; i++) + { + setjasondelay(1); + + JE_mouseStart(); + JE_showVGA(); + if (fadein) + { + fade_palette(colors, 15, 0, 255); + fadein = false; + } + JE_mouseReplace(); + + push_joysticks_as_keyboard(); + service_wait_delay(); + + if (newkey || newmouse) + break; + } + + } while (!newkey && !newmouse); + + if (!playing) + play_song(31); + + if (mouseButton > 0) + { + if (mouseX > 56 && mouseX < 142 && mouseY > 123 && mouseY < 149) + { + quit = true; + } + else if (mouseX > 151 && mouseX < 237 && mouseY > 123 && mouseY < 149) + { + quit = true; + cancel = true; + } + } + else if (newkey) + { + bool validkey = false; + lastkey_char = toupper(lastkey_char); + switch(lastkey_char) + { + case ' ': + case '-': + case '.': + case ',': + case ':': + case '!': + case '?': + case '#': + case '@': + case '$': + case '%': + case '*': + case '(': + case ')': + case '/': + case '=': + case '+': + case '<': + case '>': + case ';': + case '"': + case '\'': + validkey = true; + // fall through + default: + if (temp < 28 && (validkey || (lastkey_char >= 'A' && lastkey_char <= 'Z') || (lastkey_char >= '0' && lastkey_char <= '9'))) + { + stemp[temp] = lastkey_char; + temp++; + } + break; + case SDLK_BACKSPACE: + case SDLK_DELETE: + if (temp) + { + temp--; + stemp[temp] = ' '; + } + break; + case SDLK_ESCAPE: + quit = true; + cancel = true; + break; + case SDLK_RETURN: + quit = true; + break; + } + } + } + while (!quit); + + if (!cancel) + { + saveFiles[slot].highScore1 = temp_score; + strcpy(saveFiles[slot].highScoreName, stemp); + saveFiles[slot].highScoreDiff = difficultyLevel; + } + + fade_black(15); + JE_loadPic(VGAScreen, 2, false); + + JE_dString(VGAScreen, JE_fontCenter(miscText[50], FONT_SHAPES), 10, miscText[50], FONT_SHAPES); + JE_dString(VGAScreen, JE_fontCenter(episode_name[episodeNum], SMALL_FONT_SHAPES), 35, episode_name[episodeNum], SMALL_FONT_SHAPES); + + for (int i = first_slot; i < slot_limit; ++i) + { + if (i != slot) + { + sprintf(buffer, "~#%d:~ %d", (i - first_slot + 1), saveFiles[i].highScore1); + JE_textShade(VGAScreen, 20, ((i - first_slot + 1) * 12) + 65, buffer, 15, 0, FULL_SHADE); + JE_textShade(VGAScreen, 150, ((i - first_slot + 1) * 12) + 65, saveFiles[i].highScoreName, 15, 2, FULL_SHADE); + } + } + + JE_showVGA(); + + fade_palette(colors, 15, 0, 255); + + sprintf(buffer, "~#%d:~ %d", (slot - first_slot + 1), saveFiles[slot].highScore1); + + frameCountMax = 6; + textGlowFont = TINY_FONT; + + textGlowBrightness = 10; + JE_outTextGlow(VGAScreenSeg, 20, (slot - first_slot + 1) * 12 + 65, buffer); + textGlowBrightness = 10; + JE_outTextGlow(VGAScreenSeg, 150, (slot - first_slot + 1) * 12 + 65, saveFiles[slot].highScoreName); + textGlowBrightness = 10; + JE_outTextGlow(VGAScreenSeg, JE_fontCenter(miscText[4], TINY_FONT), 180, miscText[4]); + + JE_showVGA(); + + if (frameCountMax != 0) + wait_input(true, true, true); + + fade_black(15); + } + + } + } +} + +// increases game difficulty based on player's total score / total of players' scores +void adjust_difficulty( void ) +{ + const float score_multiplier[10] = + { + 0, // Wimp (doesn't exist) + 0.4f, // Easy + 0.8f, // Normal + 1.3f, // Hard + 1.6f, // Impossible + 2, // Insanity + 2, // Suicide + 3, // Maniacal + 3, // Zinglon + 3, // Nortaneous + }; + + assert(initialDifficulty > 0 && initialDifficulty < 10); + + const ulong score = twoPlayerMode ? (player[0].cash + player[1].cash) : JE_totalScore(&player[0]), + adjusted_score = roundf(score * score_multiplier[initialDifficulty]); + + uint new_difficulty = 0; + + if (twoPlayerMode) + { + if (adjusted_score < 10000) + new_difficulty = 1; // Easy + else if (adjusted_score < 20000) + new_difficulty = 2; // Normal + else if (adjusted_score < 50000) + new_difficulty = 3; // Hard + else if (adjusted_score < 80000) + new_difficulty = 4; // Impossible + else if (adjusted_score < 125000) + new_difficulty = 5; // Insanity + else if (adjusted_score < 200000) + new_difficulty = 6; // Suicide + else if (adjusted_score < 400000) + new_difficulty = 7; // Maniacal + else if (adjusted_score < 600000) + new_difficulty = 8; // Zinglon + else + new_difficulty = 9; // Nortaneous + } + else + { + if (adjusted_score < 40000) + new_difficulty = 1; // Easy + else if (adjusted_score < 70000) + new_difficulty = 2; // Normal + else if (adjusted_score < 150000) + new_difficulty = 3; // Hard + else if (adjusted_score < 300000) + new_difficulty = 4; // Impossible + else if (adjusted_score < 600000) + new_difficulty = 5; // Insanity + else if (adjusted_score < 1000000) + new_difficulty = 6; // Suicide + else if (adjusted_score < 2000000) + new_difficulty = 7; // Maniacal + else if (adjusted_score < 3000000) + new_difficulty = 8; // Zinglon + else + new_difficulty = 9; // Nortaneous + } + + difficultyLevel = MAX((unsigned)difficultyLevel, new_difficulty); +} + +bool load_next_demo( void ) +{ + if (++demo_num > 5) + demo_num = 1; + + char demo_filename[9]; + snprintf(demo_filename, sizeof(demo_filename), "demo.%d", demo_num); + demo_file = dir_fopen_die(data_dir(), demo_filename, "rb"); // TODO: only play demos from existing file (instead of dying) + + difficultyLevel = 2; + bonusLevelCurrent = false; + + Uint8 temp = fgetc(demo_file); + JE_initEpisode(temp); + efread(levelName, 1, 10, demo_file); levelName[10] = '\0'; + lvlFileNum = fgetc(demo_file); + + player[0].items.weapon[FRONT_WEAPON].id = fgetc(demo_file); + player[0].items.weapon[REAR_WEAPON].id = fgetc(demo_file); + player[0].items.super_arcade_mode = fgetc(demo_file); + player[0].items.sidekick[LEFT_SIDEKICK] = fgetc(demo_file); + player[0].items.sidekick[RIGHT_SIDEKICK] = fgetc(demo_file); + player[0].items.generator = fgetc(demo_file); + + player[0].items.sidekick_level = fgetc(demo_file); // could probably ignore + player[0].items.sidekick_series = fgetc(demo_file); // could probably ignore + + initial_episode_num = fgetc(demo_file); // could probably ignore + + player[0].items.shield = fgetc(demo_file); + player[0].items.special = fgetc(demo_file); + player[0].items.ship = fgetc(demo_file); + + for (uint i = 0; i < 2; ++i) + player[0].items.weapon[i].power = fgetc(demo_file); + + fseek(demo_file, 3, SEEK_CUR); + + levelSong = fgetc(demo_file); + + demo_keys_wait = 0; + demo_keys = next_demo_keys = 0; + + printf("loaded demo '%s'\n", demo_filename); + + return true; +} + +bool replay_demo_keys( void ) +{ + if (demo_keys_wait == 0) + if (read_demo_keys() == false) + return false; // no more keys + + if (demo_keys_wait > 0) + demo_keys_wait--; + + if (demo_keys & (1 << 0)) + player[0].y -= CURRENT_KEY_SPEED; + if (demo_keys & (1 << 1)) + player[0].y += CURRENT_KEY_SPEED; + + if (demo_keys & (1 << 2)) + player[0].x -= CURRENT_KEY_SPEED; + if (demo_keys & (1 << 3)) + player[0].x += CURRENT_KEY_SPEED; + + button[0] = (bool)(demo_keys & (1 << 4)); + button[3] = (bool)(demo_keys & (1 << 5)); + button[1] = (bool)(demo_keys & (1 << 6)); + button[2] = (bool)(demo_keys & (1 << 7)); + + return true; +} + +bool read_demo_keys( void ) +{ + demo_keys = next_demo_keys; + + efread(&demo_keys_wait, sizeof(Uint16), 1, demo_file); + demo_keys_wait = SDL_Swap16(demo_keys_wait); + + next_demo_keys = getc(demo_file); + + return !feof(demo_file); +} + +/*Street Fighter codes*/ +void JE_SFCodes( JE_byte playerNum_, JE_integer PX_, JE_integer PY_, JE_integer mouseX_, JE_integer mouseY_ ) +{ + JE_byte temp, temp2, temp3, temp4, temp5; + + uint ship = player[playerNum_-1].items.ship; + + /*Get direction*/ + if (playerNum_ == 2 && ship < 15) + { + ship = 0; + } + + if (ship < 15) + { + + temp2 = (mouseY_ > PY_) + /*UP*/ + (mouseY_ < PY_) + /*DOWN*/ + (PX_ < mouseX_) + /*LEFT*/ + (PX_ > mouseX_); /*RIGHT*/ + temp = (mouseY_ > PY_) * 1 + /*UP*/ + (mouseY_ < PY_) * 2 + /*DOWN*/ + (PX_ < mouseX_) * 3 + /*LEFT*/ + (PX_ > mouseX_) * 4; /*RIGHT*/ + + if (temp == 0) // no direction being pressed + { + if (!button[0]) // if fire button is released + { + temp = 9; + temp2 = 1; + } else { + temp2 = 0; + temp = 99; + } + } + + if (temp2 == 1) // if exactly one direction pressed or firebutton is released + { + temp += button[0] * 4; + + temp3 = superTyrian ? 21 : 3; + for (temp2 = 0; temp2 < temp3; temp2++) + { + + /*Use SuperTyrian ShipCombos or not?*/ + temp5 = superTyrian ? shipCombosB[temp2] : shipCombos[ship][temp2]; + + // temp5 == selected combo in ship + if (temp5 == 0) /* combo doesn't exists */ + { + // mark twiddles as cancelled/finished + SFCurrentCode[playerNum_-1][temp2] = 0; + } else { + // get next combo key + temp4 = keyboardCombos[temp5-1][SFCurrentCode[playerNum_-1][temp2]]; + + // correct key + if (temp4 == temp) + { + SFCurrentCode[playerNum_-1][temp2]++; + + temp4 = keyboardCombos[temp5-1][SFCurrentCode[playerNum_-1][temp2]]; + if (temp4 > 100 && temp4 <= 100 + SPECIAL_NUM) + { + SFCurrentCode[playerNum_-1][temp2] = 0; + SFExecuted[playerNum_-1] = temp4 - 100; + } + } else { + if ((temp != 9) && + (temp4 - 1) % 4 != (temp - 1) % 4 && + (SFCurrentCode[playerNum_-1][temp2] == 0 || + keyboardCombos[temp5-1][SFCurrentCode[playerNum_-1][temp2]-1] != temp)) + { + SFCurrentCode[playerNum_-1][temp2] = 0; + } + } + } + } + } + + } +} + +void JE_sort( void ) +{ + JE_byte a, b; + + for (a = 0; a < 2; a++) + { + for (b = a + 1; b < 3; b++) + { + if (saveFiles[temp + a].highScore1 < saveFiles[temp + b].highScore1) + { + JE_longint tempLI; + char tempStr[30]; + JE_byte tempByte; + + tempLI = saveFiles[temp + a].highScore1; + saveFiles[temp + a].highScore1 = saveFiles[temp + b].highScore1; + saveFiles[temp + b].highScore1 = tempLI; + + strcpy(tempStr, saveFiles[temp + a].highScoreName); + strcpy(saveFiles[temp + a].highScoreName, saveFiles[temp + b].highScoreName); + strcpy(saveFiles[temp + b].highScoreName, tempStr); + + tempByte = saveFiles[temp + a].highScoreDiff; + saveFiles[temp + a].highScoreDiff = saveFiles[temp + b].highScoreDiff; + saveFiles[temp + b].highScoreDiff = tempByte; + } + } + } +} + +void JE_playCredits( void ) +{ + enum { lines_max = 132 }; + enum { line_max_length = 65 }; + + char credstr[lines_max][line_max_length + 1]; + + int lines = 0; + + JE_byte currentpic = 0, fade = 0; + JE_shortint fadechg = 1; + JE_byte currentship = 0; + JE_integer shipx = 0, shipxwait = 0; + JE_shortint shipxc = 0, shipxca = 0; + + load_sprites_file(EXTRA_SHAPES, "estsc.shp"); + + setjasondelay2(1000); + + play_song(8); + + // load credits text + FILE *f = dir_fopen_die(data_dir(), "tyrian.cdt", "rb"); + for (lines = 0; !feof(f) && lines < lines_max; ++lines) + { + read_encrypted_pascal_string(credstr[lines], sizeof(credstr[lines]), f); + } + if (lines == lines_max) + --lines; + fclose(f); + + memcpy(colors, palettes[6-1], sizeof(colors)); + JE_clr256(VGAScreen); + JE_showVGA(); + fade_palette(colors, 2, 0, 255); + + //tempScreenSeg = VGAScreenSeg; + + const int ticks_max = lines * 20 * 3; + for (int ticks = 0; ticks < ticks_max; ++ticks) + { + setjasondelay(1); + JE_clr256(VGAScreen); + + blit_sprite_hv(VGAScreenSeg, 319 - sprite(EXTRA_SHAPES, currentpic)->width, 100 - (sprite(EXTRA_SHAPES, currentpic)->height / 2), EXTRA_SHAPES, currentpic, 0x0, fade - 15); + + fade += fadechg; + if (fade == 0 && fadechg == -1) + { + fadechg = 1; + ++currentpic; + if (currentpic >= sprite_table[EXTRA_SHAPES].count) + currentpic = 0; + } + if (fade == 15) + fadechg = 0; + + if (delaycount2() == 0) + { + fadechg = -1; + setjasondelay2(900); + } + + if (ticks % 200 == 0) + { + currentship = (mt_rand() % 11) + 1; + shipxwait = (mt_rand() % 80) + 10; + if ((mt_rand() % 2) == 1) + { + shipx = 1; + shipxc = 0; + shipxca = 1; + } + else + { + shipx = 900; + shipxc = 0; + shipxca = -1; + } + } + + shipxwait--; + if (shipxwait == 0) + { + if (shipx == 1 || shipx == 900) + shipxc = 0; + shipxca = -shipxca; + shipxwait = (mt_rand() % 40) + 15; + } + shipxc += shipxca; + shipx += shipxc; + if (shipx < 1) + { + shipx = 1; + shipxwait = 1; + } + if (shipx > 900) + { + shipx = 900; + shipxwait = 1; + } + int tmp_unknown = shipxc * shipxc; + if (450 + tmp_unknown < 0 || 450 + tmp_unknown > 900) + { + if (shipxca < 0 && shipxc < 0) + shipxwait = 1; + if (shipxca > 0 && shipxc > 0) + shipxwait = 1; + } + + uint ship_sprite = ships[currentship].shipgraphic; + if (shipxc < -10) + ship_sprite -= (shipxc < -20) ? 4 : 2; + else if (shipxc > 10) + ship_sprite += (shipxc > 20) ? 4 : 2; + + blit_sprite2x2(VGAScreen, shipx / 40, 184 - (ticks % 200), shapes9, ship_sprite); + + const int bottom_line = (ticks / 3) / 20; + int y = 20 - ((ticks / 3) % 20); + + for (int line = bottom_line - 10; line < bottom_line; ++line) + { + if (line >= 0 && line < lines_max) + { + if (strcmp(&credstr[line][0], ".") != 0 && strlen(credstr[line])) + { + const Uint8 color = credstr[line][0] - 65; + const char *text = &credstr[line][1]; + + const int x = 110 - JE_textWidth(text, SMALL_FONT_SHAPES) / 2; + + JE_outTextAdjust(VGAScreen, x + abs((y / 18) % 4 - 2) - 1, y - 1, text, color, -8, SMALL_FONT_SHAPES, false); + JE_outTextAdjust(VGAScreen, x, y, text, color, -2, SMALL_FONT_SHAPES, false); + } + } + + y += 20; + } + + fill_rectangle_xy(VGAScreen, 0, 0, 319, 10, 0); + fill_rectangle_xy(VGAScreen, 0, 190, 319, 199, 0); + + if (currentpic == sprite_table[EXTRA_SHAPES].count - 1) + JE_outTextAdjust(VGAScreen, 5, 180, miscText[54], 2, -2, SMALL_FONT_SHAPES, false); // levels-in-episode + + if (bottom_line == lines_max - 8) + fade_song(); + + if (ticks == ticks_max - 1) + { + --ticks; + play_song(9); + } + + NETWORK_KEEP_ALIVE(); + + JE_showVGA(); + + wait_delay(); + + if (JE_anyButton()) + break; + } + + fade_black(10); + + free_sprites(EXTRA_SHAPES); +} + +void JE_endLevelAni( void ) +{ + JE_word x, y; + JE_byte temp; + char tempStr[256]; + + Sint8 i; + + if (!constantPlay) + { + // grant shipedit privileges + + // special + if (player[0].items.special < 21) + saveTemp[SAVE_FILES_SIZE + 81 + player[0].items.special] = 1; + + for (uint p = 0; p < COUNTOF(player); ++p) + { + // front, rear + for (uint i = 0; i < COUNTOF(player[p].items.weapon); ++i) + saveTemp[SAVE_FILES_SIZE + player[p].items.weapon[i].id] = 1; + + // options + for (uint i = 0; i < COUNTOF(player[p].items.sidekick); ++i) + saveTemp[SAVE_FILES_SIZE + 51 + player[p].items.sidekick[i]] = 1; + } + } + + adjust_difficulty(); + + player[0].last_items = player[0].items; + strcpy(lastLevelName, levelName); + + JE_wipeKey(); + frameCountMax = 4; + textGlowFont = SMALL_FONT_SHAPES; + + SDL_Color white = { 255, 255, 255 }; + set_colors(white, 254, 254); + + if (!levelTimer || levelTimerCountdown > 0 || !(episodeNum == 4)) + JE_playSampleNum(V_LEVEL_END); + else + play_song(21); + + if (bonusLevel) + { + JE_outTextGlow(VGAScreenSeg, 20, 20, miscText[17-1]); + } + else if (all_players_alive()) + { + sprintf(tempStr, "%s %s", miscText[27-1], levelName); // "Completed" + JE_outTextGlow(VGAScreenSeg, 20, 20, tempStr); + } + else + { + sprintf(tempStr, "%s %s", miscText[62-1], levelName); // "Exiting" + JE_outTextGlow(VGAScreenSeg, 20, 20, tempStr); + } + + if (twoPlayerMode) + { + for (uint i = 0; i < 2; ++i) + { + snprintf(tempStr, sizeof(tempStr), "%s %lu", miscText[40 + i], player[i].cash); + JE_outTextGlow(VGAScreenSeg, 30, 50 + 20 * i, tempStr); + } + } + else + { + sprintf(tempStr, "%s %lu", miscText[28-1], player[0].cash); + JE_outTextGlow(VGAScreenSeg, 30, 50, tempStr); + } + + temp = (totalEnemy == 0) ? 0 : roundf(enemyKilled * 100 / totalEnemy); + sprintf(tempStr, "%s %d%%", miscText[63-1], temp); + JE_outTextGlow(VGAScreenSeg, 40, 90, tempStr); + + if (!constantPlay) + editorLevel += temp / 5; + + if (!onePlayerAction && !twoPlayerMode) + { + JE_outTextGlow(VGAScreenSeg, 30, 120, miscText[4-1]); /*Cubes*/ + + if (cubeMax > 0) + { + if (cubeMax > 4) + cubeMax = 4; + + if (frameCountMax != 0) + frameCountMax = 1; + + for (temp = 1; temp <= cubeMax; temp++) + { + NETWORK_KEEP_ALIVE(); + + JE_playSampleNum(S_ITEM); + x = 20 + 30 * temp; + y = 135; + JE_drawCube(VGAScreenSeg, x, y, 9, 0); + JE_showVGA(); + + for (i = -15; i <= 10; i++) + { + setjasondelay(frameCountMax); + + blit_sprite_hv(VGAScreenSeg, x, y, OPTION_SHAPES, 25, 0x9, i); + + if (JE_anyButton()) + frameCountMax = 0; + + JE_showVGA(); + + wait_delay(); + } + for (i = 10; i >= 0; i--) + { + setjasondelay(frameCountMax); + + blit_sprite_hv(VGAScreenSeg, x, y, OPTION_SHAPES, 25, 0x9, i); + + if (JE_anyButton()) + frameCountMax = 0; + + JE_showVGA(); + + wait_delay(); + } + } + } + else + { + JE_outTextGlow(VGAScreenSeg, 50, 135, miscText[15-1]); + } + + } + + if (frameCountMax != 0) + { + frameCountMax = 6; + temp = 1; + } else { + temp = 0; + } + temp2 = twoPlayerMode ? 150 : 160; + JE_outTextGlow(VGAScreenSeg, 90, temp2, miscText[5-1]); + + if (!constantPlay) + { + do + { + setjasondelay(1); + + NETWORK_KEEP_ALIVE(); + + wait_delay(); + } while (!(JE_anyButton() || (frameCountMax == 0 && temp == 1))); + } + + wait_noinput(false, false, true); // TODO: should up the joystick repeat temporarily instead + + fade_black(15); + JE_clr256(VGAScreen); +} + +void JE_drawCube( SDL_Surface * screen, JE_word x, JE_word y, JE_byte filter, JE_byte brightness ) +{ + blit_sprite_dark(screen, x + 4, y + 4, OPTION_SHAPES, 25, false); + blit_sprite_dark(screen, x + 3, y + 3, OPTION_SHAPES, 25, false); + blit_sprite_hv(screen, x, y, OPTION_SHAPES, 25, filter, brightness); +} + +void JE_handleChat( void ) +{ + // STUB(); Annoying piece of crap =P +} + +bool str_pop_int( char *str, int *val ) +{ + bool success = false; + + char buf[256]; + assert(strlen(str) < sizeof(buf)); + + // grab the value from str + char *end; + *val = strtol(str, &end, 10); + + if (end != str) + { + success = true; + + // shift the rest to the beginning + strcpy(buf, end); + strcpy(str, buf); + } + + return success; +} + +void JE_operation( JE_byte slot ) +{ + JE_byte flash; + char stemp[21]; + char tempStr[51]; + + if (!performSave) + { + if (saveFiles[slot-1].level > 0) + { + gameJustLoaded = true; + JE_loadGame(slot); + gameLoaded = true; + } + } + else if (slot % 11 != 0) + { + strcpy(stemp, " "); + memcpy(stemp, saveFiles[slot-1].name, strlen(saveFiles[slot-1].name)); + temp = strlen(stemp); + while (stemp[temp-1] == ' ' && --temp); + + flash = 8 * 16 + 10; + + wait_noinput(false, true, false); + + JE_barShade(VGAScreen, 65, 55, 255, 155); + + bool quit = false; + while (!quit) + { + service_SDL_events(true); + + blit_sprite(VGAScreen, 50, 50, OPTION_SHAPES, 35); // message box + + JE_textShade(VGAScreen, 60, 55, miscText[1-1], 11, 4, DARKEN); + JE_textShade(VGAScreen, 70, 70, levelName, 11, 4, DARKEN); + + do + { + flash = (flash == 8 * 16 + 10) ? 8 * 16 + 2 : 8 * 16 + 10; + temp3 = (temp3 == 6) ? 2 : 6; + + strcpy(tempStr, miscText[2-1]); + strncat(tempStr, stemp, temp); + JE_outText(VGAScreen, 65, 89, tempStr, 8, 3); + tempW = 65 + JE_textWidth(tempStr, TINY_FONT); + JE_barShade(VGAScreen, tempW + 2, 90, tempW + 6, 95); + fill_rectangle_xy(VGAScreen, tempW + 1, 89, tempW + 5, 94, flash); + + for (int i = 0; i < 14; i++) + { + setjasondelay(1); + + JE_mouseStart(); + JE_showVGA(); + JE_mouseReplace(); + + push_joysticks_as_keyboard(); + service_wait_delay(); + + if (newkey || newmouse) + break; + } + + } + while (!newkey && !newmouse); + + if (mouseButton > 0) + { + if (mouseX > 56 && mouseX < 142 && mouseY > 123 && mouseY < 149) + { + quit = true; + JE_saveGame(slot, stemp); + JE_playSampleNum(S_SELECT); + } + else if (mouseX > 151 && mouseX < 237 && mouseY > 123 && mouseY < 149) + { + quit = true; + JE_playSampleNum(S_SPRING); + } + } + else if (newkey) + { + bool validkey = false; + lastkey_char = toupper(lastkey_char); + switch (lastkey_char) + { + case ' ': + case '-': + case '.': + case ',': + case ':': + case '!': + case '?': + case '#': + case '@': + case '$': + case '%': + case '*': + case '(': + case ')': + case '/': + case '=': + case '+': + case '<': + case '>': + case ';': + case '"': + case '\'': + validkey = true; + // fall through + default: + if (temp < 14 && (validkey || (lastkey_char >= 'A' && lastkey_char <= 'Z') || (lastkey_char >= '0' && lastkey_char <= '9'))) + { + JE_playSampleNum(S_CURSOR); + stemp[temp] = lastkey_char; + temp++; + } + break; + case SDLK_BACKSPACE: + case SDLK_DELETE: + if (temp) + { + temp--; + stemp[temp] = ' '; + JE_playSampleNum(S_CLICK); + } + break; + case SDLK_ESCAPE: + quit = true; + JE_playSampleNum(S_SPRING); + break; + case SDLK_RETURN: + quit = true; + JE_saveGame(slot, stemp); + JE_playSampleNum(S_SELECT); + break; + } + + } + } + } + + wait_noinput(false, true, false); +} + +void JE_inGameDisplays( void ) +{ + char stemp[21]; + char tempstr[256]; + + for (uint i = 0; i < ((twoPlayerMode && !galagaMode) ? 2 : 1); ++i) + { + snprintf(tempstr, sizeof(tempstr), "%lu", player[i].cash); + JE_textShade(VGAScreen, 30 + 200 * i, 175, tempstr, 2, 4, FULL_SHADE); + } + + /*Special Weapon?*/ + if (player[0].items.special > 0) + blit_sprite2x2(VGAScreen, 25, 1, eShapes[5], special[player[0].items.special].itemgraphic); + + /*Lives Left*/ + if (onePlayerAction || twoPlayerMode) + { + for (int temp = 0; temp < (onePlayerAction ? 1 : 2); temp++) + { + const uint extra_lives = *player[temp].lives - 1; + + int y = (temp == 0 && player[0].items.special > 0) ? 35 : 15; + tempW = (temp == 0) ? 30: 270; + + if (extra_lives >= 5) + { + blit_sprite2(VGAScreen, tempW, y, shapes9, 285); + tempW = (temp == 0) ? 45 : 250; + sprintf(tempstr, "%d", extra_lives); + JE_textShade(VGAScreen, tempW, y + 3, tempstr, 15, 1, FULL_SHADE); + } + else if (extra_lives >= 1) + { + for (uint i = 0; i < extra_lives; ++i) + { + blit_sprite2(VGAScreen, tempW, y, shapes9, 285); + + tempW += (temp == 0) ? 12 : -12; + } + } + + strcpy(stemp, (temp == 0) ? miscText[49-1] : miscText[50-1]); + if (isNetworkGame) + { + strcpy(stemp, JE_getName(temp+1)); + } + + tempW = (temp == 0) ? 28 : (285 - JE_textWidth(stemp, TINY_FONT)); + JE_textShade(VGAScreen, tempW, y - 7, stemp, 2, 6, FULL_SHADE); + } + } + + /*Super Bombs!!*/ + for (uint i = 0; i < COUNTOF(player); ++i) + { + int x = (i == 0) ? 30 : 270; + + for (uint j = player[i].superbombs; j > 0; --j) + { + blit_sprite2(VGAScreen, x, 160, shapes9, 304); + x += (i == 0) ? 12 : -12; + } + } + + if (youAreCheating) + { + JE_outText(VGAScreen, 90, 170, "Cheaters always prosper.", 3, 4); + } +} + +void JE_mainKeyboardInput( void ) +{ + JE_gammaCheck(); + + /* { Network Request Commands } */ + + if (!isNetworkGame) + { + /* { Edited Ships } for Player 1 */ + if (extraAvail && keysactive[SDLK_TAB] && !isNetworkGame && !superTyrian) + { + for (x = SDLK_0; x <= SDLK_9; x++) + { + if (keysactive[x]) + { + int z = x == SDLK_0 ? 10 : x - SDLK_0; + player[0].items.ship = 90 + z; /*Ships*/ + z = (z - 1) * 15; + player[0].items.weapon[FRONT_WEAPON].id = extraShips[z + 1]; + player[0].items.weapon[REAR_WEAPON].id = extraShips[z + 2]; + player[0].items.special = extraShips[z + 3]; + player[0].items.sidekick[LEFT_SIDEKICK] = extraShips[z + 4]; + player[0].items.sidekick[RIGHT_SIDEKICK] = extraShips[z + 5]; + player[0].items.generator = extraShips[z + 6]; + /*Armor*/ + player[0].items.shield = extraShips[z + 8]; + memset(shotMultiPos, 0, sizeof(shotMultiPos)); + + if (player[0].weapon_mode > JE_portConfigs()) + player[0].weapon_mode = 1; + + tempW = player[0].armor; + JE_getShipInfo(); + if (player[0].armor > tempW && editShip1) + player[0].armor = tempW; + else + editShip1 = true; + + SDL_Surface *temp_surface = VGAScreen; + VGAScreen = VGAScreenSeg; + JE_wipeShieldArmorBars(); + JE_drawArmor(); + JE_drawShield(); + VGAScreen = temp_surface; + JE_drawOptions(); + + keysactive[x] = false; + } + } + } + + /* for Player 2 */ + if (extraAvail && keysactive[SDLK_CAPSLOCK] && !isNetworkGame && !superTyrian) + { + for (x = SDLK_0; x <= SDLK_9; x++) + { + if (keysactive[x]) + { + int z = x == SDLK_0 ? 10 : x - SDLK_0; + player[1].items.ship = 90 + z; + z = (z - 1) * 15; + player[1].items.weapon[FRONT_WEAPON].id = extraShips[z + 1]; + player[1].items.weapon[REAR_WEAPON].id = extraShips[z + 2]; + player[1].items.special = extraShips[z + 3]; + player[1].items.sidekick[LEFT_SIDEKICK] = extraShips[z + 4]; + player[1].items.sidekick[RIGHT_SIDEKICK] = extraShips[z + 5]; + player[1].items.generator = extraShips[z + 6]; + /*Armor*/ + player[1].items.shield = extraShips[z + 8]; + memset(shotMultiPos, 0, sizeof(shotMultiPos)); + + if (player[1].weapon_mode > JE_portConfigs()) + player[1].weapon_mode = 1; + + tempW = player[1].armor; + JE_getShipInfo(); + if (player[1].armor > tempW && editShip2) + player[1].armor = tempW; + else + editShip2 = true; + + SDL_Surface *temp_surface = VGAScreen; + VGAScreen = VGAScreenSeg; + JE_wipeShieldArmorBars(); + JE_drawArmor(); + JE_drawShield(); + VGAScreen = temp_surface; + JE_drawOptions(); + + keysactive[x] = false; + } + } + } + } + + /* { In-Game Help } */ + if (keysactive[SDLK_F1]) + { + if (isNetworkGame) + { + helpRequest = true; + } else { + JE_inGameHelp(); + skipStarShowVGA = true; + } + } + + /* {!Activate Nort Ship!} */ + if (keysactive[SDLK_F2] && keysactive[SDLK_F4] && keysactive[SDLK_F6] && keysactive[SDLK_F7] && + keysactive[SDLK_F9] && keysactive[SDLK_BACKSLASH] && keysactive[SDLK_SLASH]) + { + if (isNetworkGame) + { + nortShipRequest = true; + } + else + { + player[0].items.ship = 12; // Nort Ship + player[0].items.special = 13; // Astral Zone + player[0].items.weapon[FRONT_WEAPON].id = 36; // NortShip Super Pulse + player[0].items.weapon[REAR_WEAPON].id = 37; // NortShip Spreader + shipGr = 1; + } + } + + /* {Cheating} */ + if (!isNetworkGame && !twoPlayerMode && !superTyrian && superArcadeMode == SA_NONE) + { + if (keysactive[SDLK_F2] && keysactive[SDLK_F3] && keysactive[SDLK_F6]) + { + youAreCheating = !youAreCheating; + keysactive[SDLK_F2] = false; + } + + if (keysactive[SDLK_F2] && keysactive[SDLK_F3] && (keysactive[SDLK_F4] || keysactive[SDLK_F5])) + { + for (uint i = 0; i < COUNTOF(player); ++i) + player[i].armor = 0; + + youAreCheating = !youAreCheating; + JE_drawTextWindow(miscText[63-1]); + } + + if (constantPlay && keysactive[SDLK_c]) + { + youAreCheating = !youAreCheating; + keysactive[SDLK_c] = false; + } + } + + if (superTyrian) + { + youAreCheating = false; + } + + /* {Personal Commands} */ + + /* {DEBUG} */ + if (keysactive[SDLK_F10] && keysactive[SDLK_BACKSPACE]) + { + keysactive[SDLK_F10] = false; + debug = !debug; + + debugHist = 1; + debugHistCount = 1; + + /* YKS: clock ticks since midnight replaced by SDL_GetTicks */ + lastDebugTime = SDL_GetTicks(); + } + + /* {CHEAT-SKIP LEVEL} */ + if (keysactive[SDLK_F2] && keysactive[SDLK_F6] && (keysactive[SDLK_F7] || keysactive[SDLK_F8]) && !keysactive[SDLK_F9] + && !superTyrian && superArcadeMode == SA_NONE) + { + if (isNetworkGame) + { + skipLevelRequest = true; + } else { + levelTimer = true; + levelTimerCountdown = 0; + endLevel = true; + levelEnd = 40; + } + } + + /* pause game */ + pause_pressed = pause_pressed || keysactive[SDLK_p]; + + /* in-game setup */ + ingamemenu_pressed = ingamemenu_pressed || keysactive[SDLK_ESCAPE]; + + if (keysactive[SDLK_BACKSPACE]) + { + /* toggle screenshot pause */ + if (keysactive[SDLK_NUMLOCK]) + { + superPause = !superPause; + } + + /* {SMOOTHIES} */ + if (keysactive[SDLK_F12] && keysactive[SDLK_SCROLLOCK]) + { + for (temp = SDLK_2; temp <= SDLK_9; temp++) + { + if (keysactive[temp]) + { + smoothies[temp-SDLK_2] = !smoothies[temp-SDLK_2]; + } + } + if (keysactive[SDLK_0]) + { + smoothies[8] = !smoothies[8]; + } + } else + + /* {CYCLE THROUGH FILTER COLORS} */ + if (keysactive[SDLK_MINUS]) + { + if (levelFilter == -99) + { + levelFilter = 0; + } else { + levelFilter++; + if (levelFilter == 16) + { + levelFilter = -99; + } + } + } else + + /* {HYPER-SPEED} */ + if (keysactive[SDLK_1]) + { + fastPlay++; + if (fastPlay > 2) + { + fastPlay = 0; + } + keysactive[SDLK_1] = false; + JE_setNewGameSpeed(); + } + + /* {IN-GAME RANDOM MUSIC SELECTION} */ + if (keysactive[SDLK_SCROLLOCK]) + { + play_song(mt_rand() % MUSIC_NUM); + } + } +} + +void JE_pauseGame( void ) +{ + JE_boolean done = false; + JE_word mouseX, mouseY; + + //tempScreenSeg = VGAScreenSeg; // sega000 + if (!superPause) + { + JE_dString(VGAScreenSeg, 120, 90, miscText[22], FONT_SHAPES); + + VGAScreen = VGAScreenSeg; + JE_showVGA(); + } + + set_volume(tyrMusicVolume / 2, fxVolume); + +#ifdef WITH_NETWORK + if (isNetworkGame) + { + network_prepare(PACKET_GAME_PAUSE); + network_send(4); // PACKET_GAME_PAUSE + + while (true) + { + service_SDL_events(false); + + if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_GAME_PAUSE) + { + network_update(); + break; + } + + network_update(); + network_check(); + + uSDL_Delay(16); + } + } +#endif + + wait_noinput(false, false, true); // TODO: should up the joystick repeat temporarily instead + + do + { + setjasondelay(2); + + push_joysticks_as_keyboard(); + service_SDL_events(true); + + if ((newkey && lastkey_sym != SDLK_LCTRL && lastkey_sym != SDLK_RCTRL && lastkey_sym != SDLK_LALT && lastkey_sym != SDLK_RALT) + || JE_mousePosition(&mouseX, &mouseY) > 0) + { +#ifdef WITH_NETWORK + if (isNetworkGame) + { + network_prepare(PACKET_WAITING); + network_send(4); // PACKET_WAITING + } +#endif + done = true; + } + +#ifdef WITH_NETWORK + if (isNetworkGame) + { + network_check(); + + if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_WAITING) + { + network_check(); + + done = true; + } + } +#endif + + wait_delay(); + } while (!done); + +#ifdef WITH_NETWORK + if (isNetworkGame) + { + while (!network_is_sync()) + { + service_SDL_events(false); + + network_check(); + uSDL_Delay(16); + } + } +#endif + + set_volume(tyrMusicVolume, fxVolume); + + //skipStarShowVGA = true; +} + +void JE_playerMovement( Player *this_player, + JE_byte inputDevice, + JE_byte playerNum_, + JE_word shipGr_, + Sprite2_array *shapes9ptr_, + JE_word *mouseX_, JE_word *mouseY_ ) +{ + JE_integer mouseXC, mouseYC; + JE_integer accelXC, accelYC; + + if (playerNum_ == 2 || !twoPlayerMode) + { + tempW = weaponPort[this_player->items.weapon[REAR_WEAPON].id].opnum; + + if (this_player->weapon_mode > tempW) + this_player->weapon_mode = 1; + } + +#ifdef WITH_NETWORK + if (isNetworkGame && thisPlayerNum == playerNum_) + { + network_state_prepare(); + memset(&packet_state_out[0]->data[4], 0, 10); + } +#endif + +redo: + + if (isNetworkGame) + { + inputDevice = 0; + } + + mouseXC = 0; + mouseYC = 0; + accelXC = 0; + accelYC = 0; + + bool link_gun_analog = false; + float link_gun_angle = 0; + + /* Draw Player */ + if (!this_player->is_alive) + { + if (this_player->exploding_ticks > 0) + { + --this_player->exploding_ticks; + + if (levelEndFxWait > 0) + { + levelEndFxWait--; + } + else + { + levelEndFxWait = (mt_rand() % 6) + 3; + if ((mt_rand() % 3) == 1) + soundQueue[6] = S_EXPLOSION_9; + else + soundQueue[5] = S_EXPLOSION_11; + } + + int explosion_x = this_player->x + (mt_rand() % 32) - 16; + int explosion_y = this_player->y + (mt_rand() % 32) - 16; + JE_setupExplosionLarge(false, 0, explosion_x, explosion_y + 7); + JE_setupExplosionLarge(false, 0, this_player->x, this_player->y + 7); + + if (levelEnd > 0) + levelEnd--; + } + else + { + if (twoPlayerMode || onePlayerAction) // if arcade mode + { + if (*this_player->lives > 1) // respawn if any extra lives + { + --(*this_player->lives); + + reallyEndLevel = false; + shotMultiPos[playerNum_-1] = 0; + calc_purple_balls_needed(this_player); + twoPlayerLinked = false; + if (galagaMode) + twoPlayerMode = false; + this_player->y = 160; + this_player->invulnerable_ticks = 100; + this_player->is_alive = true; + endLevel = false; + + if (galagaMode || episodeNum == 4) + this_player->armor = this_player->initial_armor; + else + this_player->armor = this_player->initial_armor / 2; + + if (galagaMode) + this_player->shield = 0; + else + this_player->shield = this_player->shield_max / 2; + + VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ + JE_drawArmor(); + JE_drawShield(); + VGAScreen = game_screen; /* side-effect of game_screen */ + goto redo; + } + else + { + if (galagaMode) + twoPlayerMode = false; + if (allPlayersGone && isNetworkGame) + reallyEndLevel = true; + } + + } + } + } + else if (constantDie) + { + // finished exploding? start dying again + if (this_player->exploding_ticks == 0) + { + this_player->shield = 0; + + if (this_player->armor > 0) + { + --this_player->armor; + } + else + { + this_player->is_alive = false; + this_player->exploding_ticks = 60; + levelEnd = 40; + } + + JE_wipeShieldArmorBars(); + VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ + JE_drawArmor(); + VGAScreen = game_screen; /* side-effect of game_screen */ + + // as if instant death weren't enough, player also gets infinite lives in order to enjoy an infinite number of deaths -_- + if (*player[0].lives < 11) + ++(*player[0].lives); + } + } + + + if (!this_player->is_alive) + { + explosionFollowAmountX = explosionFollowAmountY = 0; + return; + } + + if (!endLevel) + { + *mouseX_ = this_player->x; + *mouseY_ = this_player->y; + button[1-1] = false; + button[2-1] = false; + button[3-1] = false; + button[4-1] = false; + + /* --- Movement Routine Beginning --- */ + + if (!isNetworkGame || playerNum_ == thisPlayerNum) + { + if (endLevel) + { + this_player->y -= 2; + } + else + { + if (record_demo || play_demo) + inputDevice = 1; // keyboard is required device for demo recording + + // demo playback input + if (play_demo) + { + if (!replay_demo_keys()) + { + endLevel = true; + levelEnd = 40; + } + } + + /* joystick input */ + if ((inputDevice == 0 || inputDevice >= 3) && joysticks > 0) + { + int j = inputDevice == 0 ? 0 : inputDevice - 3; + int j_max = inputDevice == 0 ? joysticks : inputDevice - 3 + 1; + for (; j < j_max; j++) + { + poll_joystick(j); + + if (joystick[j].analog) + { + mouseXC += joystick_axis_reduce(j, joystick[j].x); + mouseYC += joystick_axis_reduce(j, joystick[j].y); + + link_gun_analog = joystick_analog_angle(j, &link_gun_angle); + } + else + { + this_player->x += (joystick[j].direction[3] ? -CURRENT_KEY_SPEED : 0) + (joystick[j].direction[1] ? CURRENT_KEY_SPEED : 0); + this_player->y += (joystick[j].direction[0] ? -CURRENT_KEY_SPEED : 0) + (joystick[j].direction[2] ? CURRENT_KEY_SPEED : 0); + } + + button[0] |= joystick[j].action[0]; + button[1] |= joystick[j].action[2]; + button[2] |= joystick[j].action[3]; + button[3] |= joystick[j].action_pressed[1]; + + ingamemenu_pressed |= joystick[j].action_pressed[4]; + pause_pressed |= joystick[j].action_pressed[5]; + } + } + + service_SDL_events(false); + + /* mouse input */ + if ((inputDevice == 0 || inputDevice == 2) && has_mouse) + { + button[0] |= mouse_pressed[0]; + button[1] |= mouse_pressed[1]; + button[2] |= mouse_has_three_buttons ? mouse_pressed[2] : mouse_pressed[1]; + + if (input_grab_enabled) + { + mouseXC += mouse_x - 159; + mouseYC += mouse_y - 100; + } + + if ((!isNetworkGame || playerNum_ == thisPlayerNum) + && (!galagaMode || (playerNum_ == 2 || !twoPlayerMode || player[1].exploding_ticks > 0))) + { + set_mouse_position(159, 100); + } + } + + /* keyboard input */ + if ((inputDevice == 0 || inputDevice == 1) && !play_demo) + { + if (keysactive[keySettings[0]]) + this_player->y -= CURRENT_KEY_SPEED; + if (keysactive[keySettings[1]]) + this_player->y += CURRENT_KEY_SPEED; + + if (keysactive[keySettings[2]]) + this_player->x -= CURRENT_KEY_SPEED; + if (keysactive[keySettings[3]]) + this_player->x += CURRENT_KEY_SPEED; + + button[0] = button[0] || keysactive[keySettings[4]]; + button[3] = button[3] || keysactive[keySettings[5]]; + button[1] = button[1] || keysactive[keySettings[6]]; + button[2] = button[2] || keysactive[keySettings[7]]; + + if (constantPlay) + { + for (unsigned int i = 0; i < 4; i++) + button[i] = true; + + ++this_player->y; + this_player->x += constantLastX; + } + + // TODO: check if demo recording still works + if (record_demo) + { + bool new_input = false; + + for (unsigned int i = 0; i < 8; i++) + { + bool temp = demo_keys & (1 << i); + if (temp != keysactive[keySettings[i]]) + new_input = true; + } + + demo_keys_wait++; + + if (new_input) + { + demo_keys_wait = SDL_Swap16(demo_keys_wait); + efwrite(&demo_keys_wait, sizeof(Uint16), 1, demo_file); + + demo_keys = 0; + for (unsigned int i = 0; i < 8; i++) + demo_keys |= keysactive[keySettings[i]] ? (1 << i) : 0; + + fputc(demo_keys, demo_file); + + demo_keys_wait = 0; + } + } + } + + if (smoothies[9-1]) + { + *mouseY_ = this_player->y - (*mouseY_ - this_player->y); + mouseYC = -mouseYC; + } + + accelXC += this_player->x - *mouseX_; + accelYC += this_player->y - *mouseY_; + + if (mouseXC > 30) + mouseXC = 30; + else if (mouseXC < -30) + mouseXC = -30; + if (mouseYC > 30) + mouseYC = 30; + else if (mouseYC < -30) + mouseYC = -30; + + if (mouseXC > 0) + this_player->x += (mouseXC + 3) / 4; + else if (mouseXC < 0) + this_player->x += (mouseXC - 3) / 4; + if (mouseYC > 0) + this_player->y += (mouseYC + 3) / 4; + else if (mouseYC < 0) + this_player->y += (mouseYC - 3) / 4; + + if (mouseXC > 3) + accelXC++; + else if (mouseXC < -2) + accelXC--; + if (mouseYC > 2) + accelYC++; + else if (mouseYC < -2) + accelYC--; + + } /*endLevel*/ + +#ifdef WITH_NETWORK + if (isNetworkGame && playerNum_ == thisPlayerNum) + { + Uint16 buttons = 0; + for (int i = 4 - 1; i >= 0; i--) + { + buttons <<= 1; + buttons |= button[i]; + } + + SDLNet_Write16(this_player->x - *mouseX_, &packet_state_out[0]->data[4]); + SDLNet_Write16(this_player->y - *mouseY_, &packet_state_out[0]->data[6]); + SDLNet_Write16(accelXC, &packet_state_out[0]->data[8]); + SDLNet_Write16(accelYC, &packet_state_out[0]->data[10]); + SDLNet_Write16(buttons, &packet_state_out[0]->data[12]); + + this_player->x = *mouseX_; + this_player->y = *mouseY_; + + button[0] = false; + button[1] = false; + button[2] = false; + button[3] = false; + + accelXC = 0; + accelYC = 0; + } +#endif + } /*isNetworkGame*/ + + /* --- Movement Routine Ending --- */ + + moveOk = true; + +#ifdef WITH_NETWORK + if (isNetworkGame && !network_state_is_reset()) + { + if (playerNum_ != thisPlayerNum) + { + if (thisPlayerNum == 2) + difficultyLevel = SDLNet_Read16(&packet_state_in[0]->data[16]); + + Uint16 buttons = SDLNet_Read16(&packet_state_in[0]->data[12]); + for (int i = 0; i < 4; i++) + { + button[i] = buttons & 1; + buttons >>= 1; + } + + this_player->x += (Sint16)SDLNet_Read16(&packet_state_in[0]->data[4]); + this_player->y += (Sint16)SDLNet_Read16(&packet_state_in[0]->data[6]); + accelXC = (Sint16)SDLNet_Read16(&packet_state_in[0]->data[8]); + accelYC = (Sint16)SDLNet_Read16(&packet_state_in[0]->data[10]); + } + else + { + Uint16 buttons = SDLNet_Read16(&packet_state_out[network_delay]->data[12]); + for (int i = 0; i < 4; i++) + { + button[i] = buttons & 1; + buttons >>= 1; + } + + this_player->x += (Sint16)SDLNet_Read16(&packet_state_out[network_delay]->data[4]); + this_player->y += (Sint16)SDLNet_Read16(&packet_state_out[network_delay]->data[6]); + accelXC = (Sint16)SDLNet_Read16(&packet_state_out[network_delay]->data[8]); + accelYC = (Sint16)SDLNet_Read16(&packet_state_out[network_delay]->data[10]); + } + } +#endif + + /*Street-Fighter codes*/ + JE_SFCodes(playerNum_, this_player->x, this_player->y, *mouseX_, *mouseY_); + + if (moveOk) + { + /* END OF MOVEMENT ROUTINES */ + + /*Linking Routines*/ + + if (twoPlayerMode && !twoPlayerLinked && this_player->x == *mouseX_ && this_player->y == *mouseY_ + && abs(player[0].x - player[1].x) < 8 && abs(player[0].y - player[1].y) < 8 + && player[0].is_alive && player[1].is_alive && !galagaMode) + { + twoPlayerLinked = true; + } + + if (playerNum_ == 1 && (button[3-1] || button[2-1]) && !galagaMode) + twoPlayerLinked = false; + + if (twoPlayerMode && twoPlayerLinked && playerNum_ == 2 + && (this_player->x != *mouseX_ || this_player->y != *mouseY_)) + { + if (button[0]) + { + if (link_gun_analog) + { + linkGunDirec = link_gun_angle; + } + else + { + JE_real tempR; + + if (abs(this_player->x - *mouseX_) > abs(this_player->y - *mouseY_)) + tempR = (this_player->x - *mouseX_ > 0) ? M_PI_2 : (M_PI + M_PI_2); + else + tempR = (this_player->y - *mouseY_ > 0) ? 0 : M_PI; + + if (fabsf(linkGunDirec - tempR) < 0.3f) + linkGunDirec = tempR; + else if (linkGunDirec < tempR && linkGunDirec - tempR > -3.24f) + linkGunDirec += 0.2f; + else if (linkGunDirec - tempR < M_PI) + linkGunDirec -= 0.2f; + else + linkGunDirec += 0.2f; + } + + if (linkGunDirec >= (2 * M_PI)) + linkGunDirec -= (2 * M_PI); + else if (linkGunDirec < 0) + linkGunDirec += (2 * M_PI); + } + else if (!galagaMode) + { + twoPlayerLinked = false; + } + } + } + } + + if (levelEnd > 0 && all_players_dead()) + reallyEndLevel = true; + + /* End Level Fade-Out */ + if (this_player->is_alive && endLevel) + { + if (levelEnd == 0) + { + reallyEndLevel = true; + } + else + { + this_player->y -= levelEndWarp; + if (this_player->y < -200) + reallyEndLevel = true; + + int trail_spacing = 1; + int trail_y = this_player->y; + int num_trails = abs(41 - levelEnd); + if (num_trails > 20) + num_trails = 20; + + for (int i = 0; i < num_trails; i++) + { + trail_y += trail_spacing; + trail_spacing++; + } + + for (int i = 1; i < num_trails; i++) + { + trail_y -= trail_spacing; + trail_spacing--; + + if (trail_y > 0 && trail_y < 170) + { + if (shipGr_ == 0) + { + blit_sprite2x2(VGAScreen, this_player->x - 17, trail_y - 7, *shapes9ptr_, 13); + blit_sprite2x2(VGAScreen, this_player->x + 7 , trail_y - 7, *shapes9ptr_, 51); + } + else if (shipGr_ == 1) + { + blit_sprite2x2(VGAScreen, this_player->x - 17, trail_y - 7, *shapes9ptr_, 220); + blit_sprite2x2(VGAScreen, this_player->x + 7 , trail_y - 7, *shapes9ptr_, 222); + } + else + { + blit_sprite2x2(VGAScreen, this_player->x - 5, trail_y - 7, *shapes9ptr_, shipGr_); + } + } + } + } + } + + if (play_demo) + JE_dString(VGAScreen, 115, 10, miscText[7], SMALL_FONT_SHAPES); // insert coin + + if (this_player->is_alive && !endLevel) + { + if (!twoPlayerLinked || playerNum_ < 2) + { + if (!twoPlayerMode || shipGr2 != 0) // if not dragonwing + { + if (this_player->sidekick[LEFT_SIDEKICK].style == 0) + { + this_player->sidekick[LEFT_SIDEKICK].x = *mouseX_ - 14; + this_player->sidekick[LEFT_SIDEKICK].y = *mouseY_; + } + + if (this_player->sidekick[RIGHT_SIDEKICK].style == 0) + { + this_player->sidekick[RIGHT_SIDEKICK].x = *mouseX_ + 16; + this_player->sidekick[RIGHT_SIDEKICK].y = *mouseY_; + } + } + + if (this_player->x_friction_ticks > 0) + { + --this_player->x_friction_ticks; + } + else + { + this_player->x_friction_ticks = 1; + + if (this_player->x_velocity < 0) + ++this_player->x_velocity; + else if (this_player->x_velocity > 0) + --this_player->x_velocity; + } + + if (this_player->y_friction_ticks > 0) + { + --this_player->y_friction_ticks; + } + else + { + this_player->y_friction_ticks = 2; + + if (this_player->y_velocity < 0) + ++this_player->y_velocity; + else if (this_player->y_velocity > 0) + --this_player->y_velocity; + } + + this_player->x_velocity += accelXC; + this_player->y_velocity += accelYC; + + this_player->x_velocity = MIN(MAX(-4, this_player->x_velocity), 4); + this_player->y_velocity = MIN(MAX(-4, this_player->y_velocity), 4); + + this_player->x += this_player->x_velocity; + this_player->y += this_player->y_velocity; + + // if player moved, add new ship x, y history entry + if (this_player->x - *mouseX_ != 0 || this_player->y - *mouseY_ != 0) + { + for (uint i = 1; i < COUNTOF(player->old_x); ++i) + { + this_player->old_x[i - 1] = this_player->old_x[i]; + this_player->old_y[i - 1] = this_player->old_y[i]; + } + this_player->old_x[COUNTOF(player->old_x) - 1] = this_player->x; + this_player->old_y[COUNTOF(player->old_x) - 1] = this_player->y; + } + } + else /*twoPlayerLinked*/ + { + if (shipGr_ == 0) + this_player->x = player[0].x - 1; + else + this_player->x = player[0].x; + this_player->y = player[0].y + 8; + + this_player->x_velocity = player[0].x_velocity; + this_player->y_velocity = 4; + + // turret direction marker/shield + shotMultiPos[SHOT_MISC] = 0; + b = player_shot_create(0, SHOT_MISC, this_player->x + 1 + roundf(sinf(linkGunDirec + 0.2f) * 26), this_player->y + roundf(cosf(linkGunDirec + 0.2f) * 26), *mouseX_, *mouseY_, 148, playerNum_); + shotMultiPos[SHOT_MISC] = 0; + b = player_shot_create(0, SHOT_MISC, this_player->x + 1 + roundf(sinf(linkGunDirec - 0.2f) * 26), this_player->y + roundf(cosf(linkGunDirec - 0.2f) * 26), *mouseX_, *mouseY_, 148, playerNum_); + shotMultiPos[SHOT_MISC] = 0; + b = player_shot_create(0, SHOT_MISC, this_player->x + 1 + roundf(sinf(linkGunDirec) * 26), this_player->y + roundf(cosf(linkGunDirec) * 26), *mouseX_, *mouseY_, 147, playerNum_); + + if (shotRepeat[SHOT_REAR] > 0) + { + --shotRepeat[SHOT_REAR]; + } + else if (button[1-1]) + { + shotMultiPos[SHOT_REAR] = 0; + b = player_shot_create(0, SHOT_REAR, this_player->x + 1 + roundf(sinf(linkGunDirec) * 20), this_player->y + roundf(cosf(linkGunDirec) * 20), *mouseX_, *mouseY_, linkGunWeapons[this_player->items.weapon[REAR_WEAPON].id-1], playerNum_); + player_shot_set_direction(b, this_player->items.weapon[REAR_WEAPON].id, linkGunDirec); + } + } + } + + if (!endLevel) + { + if (this_player->x > 256) + { + this_player->x = 256; + constantLastX = -constantLastX; + } + if (this_player->x < 40) + { + this_player->x = 40; + constantLastX = -constantLastX; + } + + if (isNetworkGame && playerNum_ == 1) + { + if (this_player->y > 154) + this_player->y = 154; + } + else + { + if (this_player->y > 160) + this_player->y = 160; + } + + if (this_player->y < 10) + this_player->y = 10; + + // Determines the ship banking sprite to display, depending on horizontal velocity and acceleration + int ship_banking = this_player->x_velocity / 2 + (this_player->x - *mouseX_) / 6; + ship_banking = MAX(-2, MIN(ship_banking, 2)); + + int ship_sprite = ship_banking * 2 + shipGr_; + + explosionFollowAmountX = this_player->x - this_player->last_x_explosion_follow; + explosionFollowAmountY = this_player->y - this_player->last_y_explosion_follow; + + if (explosionFollowAmountY < 0) + explosionFollowAmountY = 0; + + this_player->last_x_explosion_follow = this_player->x; + this_player->last_y_explosion_follow = this_player->y; + + if (shipGr_ == 0) + { + if (background2) + { + blit_sprite2x2_darken(VGAScreen, this_player->x - 17 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shapes9ptr_, ship_sprite + 13); + blit_sprite2x2_darken(VGAScreen, this_player->x + 7 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shapes9ptr_, ship_sprite + 51); + if (superWild) + { + blit_sprite2x2_darken(VGAScreen, this_player->x - 16 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shapes9ptr_, ship_sprite + 13); + blit_sprite2x2_darken(VGAScreen, this_player->x + 6 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shapes9ptr_, ship_sprite + 51); + } + } + } + else if (shipGr_ == 1) + { + if (background2) + { + blit_sprite2x2_darken(VGAScreen, this_player->x - 17 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shapes9ptr_, 220); + blit_sprite2x2_darken(VGAScreen, this_player->x + 7 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shapes9ptr_, 222); + } + } + else + { + if (background2) + { + blit_sprite2x2_darken(VGAScreen, this_player->x - 5 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shapes9ptr_, ship_sprite); + if (superWild) + { + blit_sprite2x2_darken(VGAScreen, this_player->x - 4 - mapX2Ofs + 30, this_player->y - 7 + shadowYDist, *shapes9ptr_, ship_sprite); + } + } + } + + if (this_player->invulnerable_ticks > 0) + { + --this_player->invulnerable_ticks; + + if (shipGr_ == 0) + { + blit_sprite2x2_blend(VGAScreen, this_player->x - 17, this_player->y - 7, *shapes9ptr_, ship_sprite + 13); + blit_sprite2x2_blend(VGAScreen, this_player->x + 7 , this_player->y - 7, *shapes9ptr_, ship_sprite + 51); + } + else if (shipGr_ == 1) + { + blit_sprite2x2_blend(VGAScreen, this_player->x - 17, this_player->y - 7, *shapes9ptr_, 220); + blit_sprite2x2_blend(VGAScreen, this_player->x + 7 , this_player->y - 7, *shapes9ptr_, 222); + } + else + blit_sprite2x2_blend(VGAScreen, this_player->x - 5, this_player->y - 7, *shapes9ptr_, ship_sprite); + } + else + { + if (shipGr_ == 0) + { + blit_sprite2x2(VGAScreen, this_player->x - 17, this_player->y - 7, *shapes9ptr_, ship_sprite + 13); + blit_sprite2x2(VGAScreen, this_player->x + 7, this_player->y - 7, *shapes9ptr_, ship_sprite + 51); + } + else if (shipGr_ == 1) + { + blit_sprite2x2(VGAScreen, this_player->x - 17, this_player->y - 7, *shapes9ptr_, 220); + blit_sprite2x2(VGAScreen, this_player->x + 7, this_player->y - 7, *shapes9ptr_, 222); + + int ship_banking = 0; + switch (ship_sprite) + { + case 5: + blit_sprite2(VGAScreen, this_player->x - 17, this_player->y + 7, *shapes9ptr_, 40); + tempW = this_player->x - 7; + ship_banking = -2; + break; + case 3: + blit_sprite2(VGAScreen, this_player->x - 17, this_player->y + 7, *shapes9ptr_, 39); + tempW = this_player->x - 7; + ship_banking = -1; + break; + case 1: + ship_banking = 0; + break; + case -1: + blit_sprite2(VGAScreen, this_player->x + 19, this_player->y + 7, *shapes9ptr_, 58); + tempW = this_player->x + 9; + ship_banking = 1; + break; + case -3: + blit_sprite2(VGAScreen, this_player->x + 19, this_player->y + 7, *shapes9ptr_, 59); + tempW = this_player->x + 9; + ship_banking = 2; + break; + } + if (ship_banking != 0) // NortSparks + { + if (shotRepeat[SHOT_NORTSPARKS] > 0) + { + --shotRepeat[SHOT_NORTSPARKS]; + } + else + { + b = player_shot_create(0, SHOT_NORTSPARKS, tempW + (mt_rand() % 8) - 4, this_player->y + (mt_rand() % 8) - 4, *mouseX_, *mouseY_, 671, 1); + shotRepeat[SHOT_NORTSPARKS] = abs(ship_banking) - 1; + } + } + } + else + { + blit_sprite2x2(VGAScreen, this_player->x - 5, this_player->y - 7, *shapes9ptr_, ship_sprite); + } + } + + /*Options Location*/ + if (playerNum_ == 2 && shipGr_ == 0) // if dragonwing + { + if (this_player->sidekick[LEFT_SIDEKICK].style == 0) + { + this_player->sidekick[LEFT_SIDEKICK].x = this_player->x - 14 + ship_banking * 2; + this_player->sidekick[LEFT_SIDEKICK].y = this_player->y; + } + + if (this_player->sidekick[RIGHT_SIDEKICK].style == 0) + { + this_player->sidekick[RIGHT_SIDEKICK].x = this_player->x + 17 + ship_banking * 2; + this_player->sidekick[RIGHT_SIDEKICK].y = this_player->y; + } + } + } // !endLevel + + if (moveOk) + { + if (this_player->is_alive) + { + if (!endLevel) + { + this_player->delta_x_shot_move = this_player->x - this_player->last_x_shot_move; + this_player->delta_y_shot_move = this_player->y - this_player->last_y_shot_move; + + /* PLAYER SHOT Change */ + if (button[4-1]) + { + portConfigChange = true; + if (portConfigDone) + { + shotMultiPos[SHOT_REAR] = 0; + + if (superArcadeMode != SA_NONE && superArcadeMode <= SA_NORTSHIPZ) + { + shotMultiPos[SHOT_SPECIAL] = 0; + shotMultiPos[SHOT_SPECIAL2] = 0; + if (player[0].items.special == SASpecialWeapon[superArcadeMode-1]) + { + player[0].items.special = SASpecialWeaponB[superArcadeMode-1]; + this_player->weapon_mode = 2; + } + else + { + player[0].items.special = SASpecialWeapon[superArcadeMode-1]; + this_player->weapon_mode = 1; + } + } + else if (++this_player->weapon_mode > JE_portConfigs()) + this_player->weapon_mode = 1; + + JE_drawPortConfigButtons(); + portConfigDone = false; + } + } + + /* PLAYER SHOT Creation */ + + /*SpecialShot*/ + if (!galagaMode) + JE_doSpecialShot(playerNum_, &this_player->armor, &this_player->shield); + + /*Normal Main Weapons*/ + if (!(twoPlayerLinked && playerNum_ == 2)) + { + int min, max; + + if (!twoPlayerMode) + min = 1, max = 2; + else + min = max = playerNum_; + + for (temp = min - 1; temp < max; temp++) + { + const uint item = this_player->items.weapon[temp].id; + + if (item > 0) + { + if (shotRepeat[temp] > 0) + { + --shotRepeat[temp]; + } + else if (button[1-1]) + { + const uint item_power = galagaMode ? 0 : this_player->items.weapon[temp].power - 1, + item_mode = (temp == REAR_WEAPON) ? this_player->weapon_mode - 1 : 0; + + b = player_shot_create(item, temp, this_player->x, this_player->y, *mouseX_, *mouseY_, weaponPort[item].op[item_mode][item_power], playerNum_); + } + } + } + } + + /*Super Charge Weapons*/ + if (playerNum_ == 2) + { + + if (!twoPlayerLinked) + blit_sprite2(VGAScreen, this_player->x + (shipGr_ == 0) + 1, this_player->y - 13, eShapes[5], 77 + chargeLevel + chargeGr * 19); + + if (chargeGrWait > 0) + { + chargeGrWait--; + } + else + { + chargeGr++; + if (chargeGr == 4) + chargeGr = 0; + chargeGrWait = 3; + } + + if (chargeLevel > 0) + { + fill_rectangle_xy(VGAScreenSeg, 269, 107 + (chargeLevel - 1) * 3, 275, 108 + (chargeLevel - 1) * 3, 193); + } + + if (chargeWait > 0) + { + chargeWait--; + } + else + { + if (chargeLevel < chargeMax) + chargeLevel++; + + chargeWait = 28 - this_player->items.weapon[REAR_WEAPON].power * 2; + if (difficultyLevel > 3) + chargeWait -= 5; + } + + if (chargeLevel > 0) + fill_rectangle_xy(VGAScreenSeg, 269, 107 + (chargeLevel - 1) * 3, 275, 108 + (chargeLevel - 1) * 3, 204); + + if (shotRepeat[SHOT_P2_CHARGE] > 0) + { + --shotRepeat[SHOT_P2_CHARGE]; + } + else if (button[1-1] && (!twoPlayerLinked || chargeLevel > 0)) + { + shotMultiPos[SHOT_P2_CHARGE] = 0; + b = player_shot_create(16, SHOT_P2_CHARGE, this_player->x, this_player->y, *mouseX_, *mouseY_, chargeGunWeapons[player[1].items.weapon[REAR_WEAPON].id-1] + chargeLevel, playerNum_); + + if (chargeLevel > 0) + fill_rectangle_xy(VGAScreenSeg, 269, 107 + (chargeLevel - 1) * 3, 275, 108 + (chargeLevel - 1) * 3, 193); + + chargeLevel = 0; + chargeWait = 30 - this_player->items.weapon[REAR_WEAPON].power * 2; + } + } + + /*SUPER BOMB*/ + temp = playerNum_; + if (temp == 0) + temp = 1; /*Get whether player 1 or 2*/ + + if (player[temp-1].superbombs > 0) + { + if (shotRepeat[SHOT_P1_SUPERBOMB + temp-1] > 0) + { + --shotRepeat[SHOT_P1_SUPERBOMB + temp-1]; + } + else if (button[3-1] || button[2-1]) + { + --player[temp-1].superbombs; + shotMultiPos[SHOT_P1_SUPERBOMB + temp-1] = 0; + b = player_shot_create(16, SHOT_P1_SUPERBOMB + temp-1, this_player->x, this_player->y, *mouseX_, *mouseY_, 535, playerNum_); + } + } + + // sidekicks + + if (this_player->sidekick[LEFT_SIDEKICK].style == 4 && this_player->sidekick[RIGHT_SIDEKICK].style == 4) + optionSatelliteRotate += 0.2f; + else if (this_player->sidekick[LEFT_SIDEKICK].style == 4 || this_player->sidekick[RIGHT_SIDEKICK].style == 4) + optionSatelliteRotate += 0.15f; + + switch (this_player->sidekick[LEFT_SIDEKICK].style) + { + case 1: // trailing + case 3: + this_player->sidekick[LEFT_SIDEKICK].x = this_player->old_x[COUNTOF(player->old_x) / 2 - 1]; + this_player->sidekick[LEFT_SIDEKICK].y = this_player->old_y[COUNTOF(player->old_x) / 2 - 1]; + break; + case 2: // front-mounted + this_player->sidekick[LEFT_SIDEKICK].x = this_player->x; + this_player->sidekick[LEFT_SIDEKICK].y = MAX(10, this_player->y - 20); + break; + case 4: // orbitting + this_player->sidekick[LEFT_SIDEKICK].x = this_player->x + roundf(sinf(optionSatelliteRotate) * 20); + this_player->sidekick[LEFT_SIDEKICK].y = this_player->y + roundf(cosf(optionSatelliteRotate) * 20); + break; + } + + switch (this_player->sidekick[RIGHT_SIDEKICK].style) + { + case 4: // orbitting + this_player->sidekick[RIGHT_SIDEKICK].x = this_player->x - roundf(sinf(optionSatelliteRotate) * 20); + this_player->sidekick[RIGHT_SIDEKICK].y = this_player->y - roundf(cosf(optionSatelliteRotate) * 20); + break; + case 1: // trailing + case 3: + this_player->sidekick[RIGHT_SIDEKICK].x = this_player->old_x[0]; + this_player->sidekick[RIGHT_SIDEKICK].y = this_player->old_y[0]; + break; + case 2: // front-mounted + if (!optionAttachmentLinked) + { + this_player->sidekick[RIGHT_SIDEKICK].y += optionAttachmentMove / 2; + if (optionAttachmentMove >= -2) + { + if (optionAttachmentReturn) + temp = 2; + else + temp = 0; + + if (this_player->sidekick[RIGHT_SIDEKICK].y > (this_player->y - 20) + 5) + { + temp = 2; + optionAttachmentMove -= 1 + optionAttachmentReturn; + } + else if (this_player->sidekick[RIGHT_SIDEKICK].y > (this_player->y - 20) - 0) + { + temp = 3; + if (optionAttachmentMove > 0) + optionAttachmentMove--; + else + optionAttachmentMove++; + } + else if (this_player->sidekick[RIGHT_SIDEKICK].y > (this_player->y - 20) - 5) + { + temp = 2; + optionAttachmentMove++; + } + else if (optionAttachmentMove < 2 + optionAttachmentReturn * 4) + { + optionAttachmentMove += 1 + optionAttachmentReturn; + } + + if (optionAttachmentReturn) + temp = temp * 2; + if (abs(this_player->sidekick[RIGHT_SIDEKICK].x - this_player->x) < temp) + temp = 1; + + if (this_player->sidekick[RIGHT_SIDEKICK].x > this_player->x) + this_player->sidekick[RIGHT_SIDEKICK].x -= temp; + else if (this_player->sidekick[RIGHT_SIDEKICK].x < this_player->x) + this_player->sidekick[RIGHT_SIDEKICK].x += temp; + + if (abs(this_player->sidekick[RIGHT_SIDEKICK].y - (this_player->y - 20)) + abs(this_player->sidekick[RIGHT_SIDEKICK].x - this_player->x) < 8) + { + optionAttachmentLinked = true; + soundQueue[2] = S_CLINK; + } + + if (button[3-1]) + optionAttachmentReturn = true; + } + else // sidekick needs to catch up to player + { + optionAttachmentMove += 1 + optionAttachmentReturn; + JE_setupExplosion(this_player->sidekick[RIGHT_SIDEKICK].x + 1, this_player->sidekick[RIGHT_SIDEKICK].y + 10, 0, 0, false, false); + } + } + else + { + this_player->sidekick[RIGHT_SIDEKICK].x = this_player->x; + this_player->sidekick[RIGHT_SIDEKICK].y = this_player->y - 20; + if (button[3-1]) + { + optionAttachmentLinked = false; + optionAttachmentReturn = false; + optionAttachmentMove = -20; + soundQueue[3] = S_WEAPON_26; + } + } + + if (this_player->sidekick[RIGHT_SIDEKICK].y < 10) + this_player->sidekick[RIGHT_SIDEKICK].y = 10; + break; + } + + if (playerNum_ == 2 || !twoPlayerMode) // if player has sidekicks + { + for (uint i = 0; i < COUNTOF(player->items.sidekick); ++i) + { + uint shot_i = (i == 0) ? SHOT_LEFT_SIDEKICK : SHOT_RIGHT_SIDEKICK; + + JE_OptionType *this_option = &options[this_player->items.sidekick[i]]; + + // fire/refill sidekick + if (this_option->wport > 0) + { + if (shotRepeat[shot_i] > 0) + { + --shotRepeat[shot_i]; + } + else + { + const int ammo_max = this_player->sidekick[i].ammo_max; + + if (ammo_max > 0) // sidekick has limited ammo + { + if (this_player->sidekick[i].ammo_refill_ticks > 0) + { + --this_player->sidekick[i].ammo_refill_ticks; + } + else // refill one ammo + { + this_player->sidekick[i].ammo_refill_ticks = this_player->sidekick[i].ammo_refill_ticks_max; + + if (this_player->sidekick[i].ammo < ammo_max) + ++this_player->sidekick[i].ammo; + + // draw sidekick refill ammo gauge + const int y = hud_sidekick_y[twoPlayerMode ? 1 : 0][i] + 13; + draw_segmented_gauge(VGAScreenSeg, 284, y, 112, 2, 2, MAX(1, ammo_max / 10), this_player->sidekick[i].ammo); + } + + if (button[1 + i] && this_player->sidekick[i].ammo > 0) + { + b = player_shot_create(this_option->wport, shot_i, this_player->sidekick[i].x, this_player->sidekick[i].y, *mouseX_, *mouseY_, this_option->wpnum + this_player->sidekick[i].charge, playerNum_); + + --this_player->sidekick[i].ammo; + if (this_player->sidekick[i].charge > 0) + { + shotMultiPos[shot_i] = 0; + this_player->sidekick[i].charge = 0; + } + this_player->sidekick[i].charge_ticks = 20; + this_player->sidekick[i].animation_enabled = true; + + // draw sidekick discharge ammo gauge + const int y = hud_sidekick_y[twoPlayerMode ? 1 : 0][i] + 13; + fill_rectangle_xy(VGAScreenSeg, 284, y, 312, y + 2, 0); + draw_segmented_gauge(VGAScreenSeg, 284, y, 112, 2, 2, MAX(1, ammo_max / 10), this_player->sidekick[i].ammo); + } + } + else // has infinite ammo + { + if (button[0] || button[1 + i]) + { + b = player_shot_create(this_option->wport, shot_i, this_player->sidekick[i].x, this_player->sidekick[i].y, *mouseX_, *mouseY_, this_option->wpnum + this_player->sidekick[i].charge, playerNum_); + + if (this_player->sidekick[i].charge > 0) + { + shotMultiPos[shot_i] = 0; + this_player->sidekick[i].charge = 0; + } + this_player->sidekick[i].charge_ticks = 20; + this_player->sidekick[i].animation_enabled = true; + } + } + } + } + } + } // end of if player has sidekicks + } // !endLevel + } // this_player->is_alive + } // moveOK + + // draw sidekicks + if ((playerNum_ == 2 || !twoPlayerMode) && !endLevel) + { + for (uint i = 0; i < COUNTOF(this_player->sidekick); ++i) + { + JE_OptionType *this_option = &options[this_player->items.sidekick[i]]; + + if (this_option->option > 0) + { + if (this_player->sidekick[i].animation_enabled) + { + if (++this_player->sidekick[i].animation_frame >= this_option->ani) + { + this_player->sidekick[i].animation_frame = 0; + this_player->sidekick[i].animation_enabled = (this_option->option == 1); + } + } + + const int x = this_player->sidekick[i].x, + y = this_player->sidekick[i].y; + const uint sprite = this_option->gr[this_player->sidekick[i].animation_frame] + this_player->sidekick[i].charge; + + if (this_player->sidekick[i].style == 1 || this_player->sidekick[i].style == 2) + blit_sprite2x2(VGAScreen, x - 6, y, eShapes[5], sprite); + else + blit_sprite2(VGAScreen, x, y, shapes9, sprite); + } + + if (--this_player->sidekick[i].charge_ticks == 0) + { + if (this_player->sidekick[i].charge < this_option->pwr) + ++this_player->sidekick[i].charge; + this_player->sidekick[i].charge_ticks = 20; + } + } + } +} + +void JE_mainGamePlayerFunctions( void ) +{ + /*PLAYER MOVEMENT/MOUSE ROUTINES*/ + + if (endLevel && levelEnd > 0) + { + levelEnd--; + levelEndWarp++; + } + + /*Reset Street-Fighter commands*/ + memset(SFExecuted, 0, sizeof(SFExecuted)); + + portConfigChange = false; + + if (twoPlayerMode) + { + JE_playerMovement(&player[0], + !galagaMode ? inputDevice[0] : 0, 1, shipGr, shipGrPtr, + &mouseX, &mouseY); + JE_playerMovement(&player[1], + !galagaMode ? inputDevice[1] : 0, 2, shipGr2, shipGr2ptr, + &mouseXB, &mouseYB); + } + else + { + JE_playerMovement(&player[0], + 0, 1, shipGr, shipGrPtr, + &mouseX, &mouseY); + } + + /* == Parallax Map Scrolling == */ + if (twoPlayerMode) + { + tempX = (player[0].x + player[1].x) / 2; + } else { + tempX = player[0].x; + } + + tempW = floorf((260.0f - (tempX - 36.0f)) / (260.0f - 36.0f) * (24.0f * 3.0f) - 1.0f); + mapX3Ofs = tempW; + mapX3Pos = mapX3Ofs % 24; + mapX3bpPos = 1 - (mapX3Ofs / 24); + + mapX2Ofs = (tempW * 2) / 3; + mapX2Pos = mapX2Ofs % 24; + mapX2bpPos = 1 - (mapX2Ofs / 24); + + oldMapXOfs = mapXOfs; + mapXOfs = mapX2Ofs / 2; + mapXPos = mapXOfs % 24; + mapXbpPos = 1 - (mapXOfs / 24); + + if (background3x1) + { + mapX3Ofs = mapXOfs; + mapX3Pos = mapXPos; + mapX3bpPos = mapXbpPos - 1; + } +} + +const char *JE_getName( JE_byte pnum ) +{ + if (pnum == thisPlayerNum && network_player_name[0] != '\0') + return network_player_name; + else if (network_opponent_name[0] != '\0') + return network_opponent_name; + + return miscText[47 + pnum]; +} + +void JE_playerCollide( Player *this_player, JE_byte playerNum_ ) +{ + char tempStr[256]; + + for (int z = 0; z < 100; z++) + { + if (enemyAvail[z] != 1) + { + int enemy_screen_x = enemy[z].ex + enemy[z].mapoffset; + + if (abs(this_player->x - enemy_screen_x) < 12 && abs(this_player->y - enemy[z].ey) < 14) + { /*Collide*/ + int evalue = enemy[z].evalue; + if (evalue > 29999) + { + if (evalue == 30000) // spawn dragonwing in galaga mode, otherwise just a purple ball + { + this_player->cash += 100; + + if (!galagaMode) + { + handle_got_purple_ball(this_player); + } + else + { + // spawn the dragonwing? + if (twoPlayerMode) + this_player->cash += 2400; + twoPlayerMode = true; + twoPlayerLinked = true; + player[1].items.weapon[REAR_WEAPON].power = 1; + player[1].armor = 10; + player[1].is_alive = true; + } + enemyAvail[z] = 1; + soundQueue[7] = S_POWERUP; + } + else if (superArcadeMode != SA_NONE && evalue > 30000) + { + shotMultiPos[SHOT_FRONT] = 0; + shotRepeat[SHOT_FRONT] = 10; + + tempW = SAWeapon[superArcadeMode-1][evalue - 30000-1]; + + // if picked up already-owned weapon, power weapon up + if (tempW == player[0].items.weapon[FRONT_WEAPON].id) + { + this_player->cash += 1000; + power_up_weapon(this_player, FRONT_WEAPON); + } + // else weapon also gives purple ball + else + { + handle_got_purple_ball(this_player); + } + + player[0].items.weapon[FRONT_WEAPON].id = tempW; + this_player->cash += 200; + soundQueue[7] = S_POWERUP; + enemyAvail[z] = 1; + } + else if (evalue > 32100) + { + if (playerNum_ == 1) + { + this_player->cash += 250; + player[0].items.special = evalue - 32100; + shotMultiPos[SHOT_SPECIAL] = 0; + shotRepeat[SHOT_SPECIAL] = 10; + shotMultiPos[SHOT_SPECIAL2] = 0; + shotRepeat[SHOT_SPECIAL2] = 0; + + if (isNetworkGame) + sprintf(tempStr, "%s %s %s", JE_getName(1), miscTextB[4-1], special[evalue - 32100].name); + else if (twoPlayerMode) + sprintf(tempStr, "%s %s", miscText[43-1], special[evalue - 32100].name); + else + sprintf(tempStr, "%s %s", miscText[64-1], special[evalue - 32100].name); + JE_drawTextWindow(tempStr); + soundQueue[7] = S_POWERUP; + enemyAvail[z] = 1; + } + } + else if (evalue > 32000) + { + if (playerNum_ == 2) + { + enemyAvail[z] = 1; + if (isNetworkGame) + sprintf(tempStr, "%s %s %s", JE_getName(2), miscTextB[4-1], options[evalue - 32000].name); + else + sprintf(tempStr, "%s %s", miscText[44-1], options[evalue - 32000].name); + JE_drawTextWindow(tempStr); + + // if picked up a different sidekick than player already has, then reset sidekicks to least powerful, else power them up + if (evalue - 32000u != player[1].items.sidekick_series) + { + player[1].items.sidekick_series = evalue - 32000; + player[1].items.sidekick_level = 101; + } + else if (player[1].items.sidekick_level < 103) + { + ++player[1].items.sidekick_level; + } + + uint temp = player[1].items.sidekick_level - 100 - 1; + for (uint i = 0; i < COUNTOF(player[1].items.sidekick); ++i) + player[1].items.sidekick[i] = optionSelect[player[1].items.sidekick_series][temp][i]; + + + shotMultiPos[SHOT_LEFT_SIDEKICK] = 0; + shotMultiPos[SHOT_RIGHT_SIDEKICK] = 0; + JE_drawOptions(); + soundQueue[7] = S_POWERUP; + } + else if (onePlayerAction) + { + enemyAvail[z] = 1; + sprintf(tempStr, "%s %s", miscText[64-1], options[evalue - 32000].name); + JE_drawTextWindow(tempStr); + + for (uint i = 0; i < COUNTOF(player[0].items.sidekick); ++i) + player[0].items.sidekick[i] = evalue - 32000; + shotMultiPos[SHOT_LEFT_SIDEKICK] = 0; + shotMultiPos[SHOT_RIGHT_SIDEKICK] = 0; + + JE_drawOptions(); + soundQueue[7] = S_POWERUP; + } + if (enemyAvail[z] == 1) + this_player->cash += 250; + } + else if (evalue > 31000) + { + this_player->cash += 250; + if (playerNum_ == 2) + { + if (isNetworkGame) + sprintf(tempStr, "%s %s %s", JE_getName(2), miscTextB[4-1], weaponPort[evalue - 31000].name); + else + sprintf(tempStr, "%s %s", miscText[44-1], weaponPort[evalue - 31000].name); + JE_drawTextWindow(tempStr); + player[1].items.weapon[REAR_WEAPON].id = evalue - 31000; + shotMultiPos[SHOT_REAR] = 0; + enemyAvail[z] = 1; + soundQueue[7] = S_POWERUP; + } + else if (onePlayerAction) + { + sprintf(tempStr, "%s %s", miscText[64-1], weaponPort[evalue - 31000].name); + JE_drawTextWindow(tempStr); + player[0].items.weapon[REAR_WEAPON].id = evalue - 31000; + shotMultiPos[SHOT_REAR] = 0; + enemyAvail[z] = 1; + soundQueue[7] = S_POWERUP; + + if (player[0].items.weapon[REAR_WEAPON].power == 0) // does this ever happen? + player[0].items.weapon[REAR_WEAPON].power = 1; + } + } + else if (evalue > 30000) + { + if (playerNum_ == 1 && twoPlayerMode) + { + if (isNetworkGame) + sprintf(tempStr, "%s %s %s", JE_getName(1), miscTextB[4-1], weaponPort[evalue - 30000].name); + else + sprintf(tempStr, "%s %s", miscText[43-1], weaponPort[evalue - 30000].name); + JE_drawTextWindow(tempStr); + player[0].items.weapon[FRONT_WEAPON].id = evalue - 30000; + shotMultiPos[SHOT_FRONT] = 0; + enemyAvail[z] = 1; + soundQueue[7] = S_POWERUP; + } + else if (onePlayerAction) + { + sprintf(tempStr, "%s %s", miscText[64-1], weaponPort[evalue - 30000].name); + JE_drawTextWindow(tempStr); + player[0].items.weapon[FRONT_WEAPON].id = evalue - 30000; + shotMultiPos[SHOT_FRONT] = 0; + enemyAvail[z] = 1; + soundQueue[7] = S_POWERUP; + } + + if (enemyAvail[z] == 1) + { + player[0].items.special = specialArcadeWeapon[evalue - 30000-1]; + if (player[0].items.special > 0) + { + shotMultiPos[SHOT_SPECIAL] = 0; + shotRepeat[SHOT_SPECIAL] = 0; + shotMultiPos[SHOT_SPECIAL2] = 0; + shotRepeat[SHOT_SPECIAL2] = 0; + } + this_player->cash += 250; + } + + } + } + else if (evalue > 20000) + { + if (twoPlayerLinked) + { + // share the armor evenly between linked players + for (uint i = 0; i < COUNTOF(player); ++i) + { + player[i].armor += (evalue - 20000) / COUNTOF(player); + if (player[i].armor > 28) + player[i].armor = 28; + } + } + else + { + this_player->armor += evalue - 20000; + if (this_player->armor > 28) + this_player->armor = 28; + } + enemyAvail[z] = 1; + VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ + JE_drawArmor(); + VGAScreen = game_screen; /* side-effect of game_screen */ + soundQueue[7] = S_POWERUP; + } + else if (evalue > 10000 && enemyAvail[z] == 2) + { + if (!bonusLevel) + { + play_song(30); /*Zanac*/ + bonusLevel = true; + nextLevel = evalue - 10000; + enemyAvail[z] = 1; + displayTime = 150; + } + } + else if (enemy[z].scoreitem) + { + enemyAvail[z] = 1; + soundQueue[7] = S_ITEM; + if (evalue == 1) + { + cubeMax++; + soundQueue[3] = V_DATA_CUBE; + } + else if (evalue == -1) // got front weapon powerup + { + if (isNetworkGame) + sprintf(tempStr, "%s %s %s", JE_getName(1), miscTextB[4-1], miscText[45-1]); + else if (twoPlayerMode) + sprintf(tempStr, "%s %s", miscText[43-1], miscText[45-1]); + else + strcpy(tempStr, miscText[45-1]); + JE_drawTextWindow(tempStr); + + power_up_weapon(&player[0], FRONT_WEAPON); + soundQueue[7] = S_POWERUP; + } + else if (evalue == -2) // got rear weapon powerup + { + if (isNetworkGame) + sprintf(tempStr, "%s %s %s", JE_getName(2), miscTextB[4-1], miscText[46-1]); + else if (twoPlayerMode) + sprintf(tempStr, "%s %s", miscText[44-1], miscText[46-1]); + else + strcpy(tempStr, miscText[46-1]); + JE_drawTextWindow(tempStr); + + power_up_weapon(twoPlayerMode ? &player[1] : &player[0], REAR_WEAPON); + soundQueue[7] = S_POWERUP; + } + else if (evalue == -3) + { + // picked up orbiting asteroid killer + shotMultiPos[SHOT_MISC] = 0; + b = player_shot_create(0, SHOT_MISC, this_player->x, this_player->y, mouseX, mouseY, 104, playerNum_); + shotAvail[z] = 0; + } + else if (evalue == -4) + { + if (player[playerNum_-1].superbombs < 10) + ++player[playerNum_-1].superbombs; + } + else if (evalue == -5) + { + player[0].items.weapon[FRONT_WEAPON].id = 25; // HOT DOG! + player[0].items.weapon[REAR_WEAPON].id = 26; + player[1].items.weapon[REAR_WEAPON].id = 26; + + player[0].last_items = player[0].items; + + for (uint i = 0; i < COUNTOF(player); ++i) + player[i].weapon_mode = 1; + + memset(shotMultiPos, 0, sizeof(shotMultiPos)); + } + else if (twoPlayerLinked) + { + // players get equal share of pick-up cash when linked + for (uint i = 0; i < COUNTOF(player); ++i) + player[i].cash += evalue / COUNTOF(player); + } + else + { + this_player->cash += evalue; + } + JE_setupExplosion(enemy_screen_x, enemy[z].ey, 0, enemyDat[enemy[z].enemytype].explosiontype, true, false); + } + else if (this_player->invulnerable_ticks == 0 && enemyAvail[z] == 0 && + (enemyDat[enemy[z].enemytype].explosiontype & 1) == 0) // explosiontype & 1 == 0: not ground enemy + { + int armorleft = enemy[z].armorleft; + if (armorleft > damageRate) + armorleft = damageRate; + + JE_playerDamage(armorleft, this_player); + + // player ship gets push-back from collision + if (enemy[z].armorleft > 0) + { + this_player->x_velocity += (enemy[z].exc * enemy[z].armorleft) / 2; + this_player->y_velocity += (enemy[z].eyc * enemy[z].armorleft) / 2; + } + + int armorleft2 = enemy[z].armorleft; + if (armorleft2 == 255) + armorleft2 = 30000; + + temp = enemy[z].linknum; + if (temp == 0) + temp = 255; + + b = z; + + if (armorleft2 > armorleft) + { + // damage enemy + if (enemy[z].armorleft != 255) + enemy[z].armorleft -= armorleft; + soundQueue[5] = S_ENEMY_HIT; + } + else + { + // kill enemy + for (temp2 = 0; temp2 < 100; temp2++) + { + if (enemyAvail[temp2] != 1) + { + temp3 = enemy[temp2].linknum; + if (temp2 == b || + (temp != 255 && + (temp == temp3 || temp - 100 == temp3 + || (temp3 > 40 && temp3 / 20 == temp / 20 && temp3 <= temp)))) + { + int enemy_screen_x = enemy[temp2].ex + enemy[temp2].mapoffset; + + enemy[temp2].linknum = 0; + + enemyAvail[temp2] = 1; + + if (enemyDat[enemy[temp2].enemytype].esize == 1) + { + JE_setupExplosionLarge(enemy[temp2].enemyground, enemy[temp2].explonum, enemy_screen_x, enemy[temp2].ey); + soundQueue[6] = S_EXPLOSION_9; + } + else + { + JE_setupExplosion(enemy_screen_x, enemy[temp2].ey, 0, 1, false, false); + soundQueue[5] = S_EXPLOSION_4; + } + } + } + } + enemyAvail[z] = 1; + } + } + } + + } + } +} + diff --git a/contrib/games/opentyrian/src/mainint.h b/contrib/games/opentyrian/src/mainint.h new file mode 100644 index 000000000..d3714b087 --- /dev/null +++ b/contrib/games/opentyrian/src/mainint.h @@ -0,0 +1,93 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef MAININT_H +#define MAININT_H + +#include "config.h" +#include "opentyr.h" +#include "palette.h" +#include "player.h" +#include "sprite.h" + +extern bool button[4]; // fire, left fire, right fire, mode swap + +extern JE_shortint constantLastX; +extern JE_word textErase; +extern JE_word upgradeCost; +extern JE_word downgradeCost; +extern JE_boolean performSave; +extern JE_boolean jumpSection; +extern JE_boolean useLastBank; + +extern bool pause_pressed, ingamemenu_pressed; + +/*void JE_textMenuWait ( JE_word waittime, JE_boolean dogamma );*/ + +void JE_drawTextWindow( const char *text ); +void JE_initPlayerData( void ); +void JE_highScoreScreen( void ); +void JE_gammaCorrect_func( JE_byte *col, JE_real r ); +void JE_gammaCorrect( Palette *colorBuffer, JE_byte gamma ); +JE_boolean JE_gammaCheck( void ); +/* void JE_textMenuWait( JE_word *waitTime, JE_boolean doGamma ); /!\ In setup.h */ +void JE_loadOrderingInfo( void ); +void JE_nextEpisode( void ); +void JE_helpSystem( JE_byte startTopic ); +void JE_doInGameSetup( void ); +JE_boolean JE_inGameSetup( void ); +void JE_inGameHelp( void ); +void JE_sortHighScores( void ); +void JE_highScoreCheck( void ); +void adjust_difficulty( void ); + +bool load_next_demo( void ); +bool replay_demo_keys( void ); +bool read_demo_keys( void ); + +void JE_SFCodes( JE_byte playerNum_, JE_integer PX_, JE_integer PY_, JE_integer mouseX_, JE_integer mouseY_ ); +void JE_sort( void ); + +long weapon_upgrade_cost( long base_cost, unsigned int power ); +ulong JE_getCost( JE_byte itemType, JE_word itemNum ); +JE_longint JE_getValue( JE_byte itemType, JE_word itemNum ); +ulong JE_totalScore( const Player * ); + +void JE_drawPortConfigButtons( void ); +void JE_outCharGlow( JE_word x, JE_word y, const char *s ); + +void JE_playCredits( void ); +void JE_endLevelAni( void ); +void JE_drawCube( SDL_Surface * screen, JE_word x, JE_word y, JE_byte filter, JE_byte brightness ); +void JE_handleChat( void ); +bool str_pop_int( char *str, int *val ); +void JE_loadScreen( void ); +void JE_operation( JE_byte slot ); +void JE_inGameDisplays( void ); +void JE_mainKeyboardInput( void ); +void JE_pauseGame( void ); + +void JE_playerMovement( Player *this_player, JE_byte inputDevice, JE_byte playerNum, JE_word shipGr, Sprite2_array *shapes9ptr_, JE_word *mouseX, JE_word *mouseY ); +void JE_mainGamePlayerFunctions( void ); +const char *JE_getName( JE_byte pnum ); + +void JE_playerCollide( Player *this_player, JE_byte playerNum ); + + +#endif /* MAININT_H */ + diff --git a/contrib/games/opentyrian/src/menus.c b/contrib/games/opentyrian/src/menus.c new file mode 100644 index 000000000..7a6b73ba6 --- /dev/null +++ b/contrib/games/opentyrian/src/menus.c @@ -0,0 +1,262 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "menus.h" + +#include "config.h" +#include "episodes.h" +#include "fonthand.h" +#include "keyboard.h" +#include "nortsong.h" +#include "opentyr.h" +#include "palette.h" +#include "picload.h" +#include "setup.h" +#include "sprite.h" +#include "video.h" + +char episode_name[6][31], difficulty_name[7][21], gameplay_name[GAMEPLAY_NAME_COUNT][26]; + +bool select_gameplay( void ) +{ + JE_loadPic(VGAScreen, 2, false); + JE_dString(VGAScreen, JE_fontCenter(gameplay_name[0], FONT_SHAPES), 20, gameplay_name[0], FONT_SHAPES); + + int gameplay = 1, + gameplay_max = GAMEPLAY_NAME_COUNT - 1; + + bool fade_in = true; + for (; ; ) + { + for (int i = 1; i <= gameplay_max; i++) + { + JE_outTextAdjust(VGAScreen, JE_fontCenter(gameplay_name[i], SMALL_FONT_SHAPES), i * 24 + 30, gameplay_name[i], 15, -4 + (i == gameplay ? 2 : 0) - (i == (GAMEPLAY_NAME_COUNT - 1) ? 4 : 0), SMALL_FONT_SHAPES, true); + } + JE_showVGA(); + + if (fade_in) + { + fade_palette(colors, 10, 0, 255); + fade_in = false; + } + + JE_word temp = 0; + JE_textMenuWait(&temp, false); + + if (newkey) + { + switch (lastkey_sym) + { + case SDLK_UP: + if (--gameplay < 1) + gameplay = gameplay_max; + JE_playSampleNum(S_CURSOR); + break; + case SDLK_DOWN: + if (++gameplay > gameplay_max) + gameplay = 1; + JE_playSampleNum(S_CURSOR); + break; + + case SDLK_RETURN: + if (gameplay == GAMEPLAY_NAME_COUNT - 1) + { + JE_playSampleNum(S_SPRING); + /* TODO: NETWORK */ + fprintf(stderr, "error: networking via menu not implemented\n"); + break; + } + JE_playSampleNum(S_SELECT); + fade_black(10); + + onePlayerAction = (gameplay == 2); + twoPlayerMode = (gameplay == GAMEPLAY_NAME_COUNT - 2); + return true; + + case SDLK_ESCAPE: + JE_playSampleNum(S_SPRING); + /* fading handled elsewhere + fade_black(10); */ + + return false; + + default: + break; + } + } + } +} + +bool select_episode( void ) +{ + JE_loadPic(VGAScreen, 2, false); + JE_dString(VGAScreen, JE_fontCenter(episode_name[0], FONT_SHAPES), 20, episode_name[0], FONT_SHAPES); + + int episode = 1, episode_max = EPISODE_AVAILABLE; + + bool fade_in = true; + for (; ; ) + { + for (int i = 1; i <= episode_max; i++) + { + JE_outTextAdjust(VGAScreen, 20, i * 30 + 20, episode_name[i], 15, -4 + (i == episode ? 2 : 0) - (!episodeAvail[i - 1] ? 4 : 0), SMALL_FONT_SHAPES, true); + } + JE_showVGA(); + + if (fade_in) + { + fade_palette(colors, 10, 0, 255); + fade_in = false; + } + + JE_word temp = 0; + JE_textMenuWait(&temp, false); + + if (newkey) + { + switch (lastkey_sym) + { + case SDLK_UP: + episode--; + if (episode < 1) + { + episode = episode_max; + } + JE_playSampleNum(S_CURSOR); + break; + case SDLK_DOWN: + episode++; + if (episode > episode_max) + { + episode = 1; + } + JE_playSampleNum(S_CURSOR); + break; + + case SDLK_RETURN: + if (!episodeAvail[episode - 1]) + { + JE_playSampleNum(S_SPRING); + break; + } + JE_playSampleNum(S_SELECT); + fade_black(10); + + JE_initEpisode(episode); + initial_episode_num = episodeNum; + return true; + + case SDLK_ESCAPE: + JE_playSampleNum(S_SPRING); + /* fading handled elsewhere + fade_black(10); */ + + return false; + + default: + break; + } + } + } +} + +bool select_difficulty( void ) +{ + JE_loadPic(VGAScreen, 2, false); + JE_dString(VGAScreen, JE_fontCenter(difficulty_name[0], FONT_SHAPES), 20, difficulty_name[0], FONT_SHAPES); + + difficultyLevel = 2; + int difficulty_max = 3; + + bool fade_in = true; + for (; ; ) + { + for (int i = 1; i <= difficulty_max; i++) + { + JE_outTextAdjust(VGAScreen, JE_fontCenter(difficulty_name[i], SMALL_FONT_SHAPES), i * 24 + 30, difficulty_name[i], 15, -4 + (i == difficultyLevel ? 2 : 0), SMALL_FONT_SHAPES, true); + } + JE_showVGA(); + + if (fade_in) + { + fade_palette(colors, 10, 0, 255); + fade_in = false; + } + + JE_word temp = 0; + JE_textMenuWait(&temp, false); + + if (SDL_GetModState() & KMOD_SHIFT) + { + if ((difficulty_max < 4 && keysactive[SDLK_g]) || + (difficulty_max == 4 && keysactive[SDLK_RIGHTBRACKET])) + { + difficulty_max++; + } + } else if (difficulty_max == 5 && keysactive[SDLK_l] && keysactive[SDLK_o] && keysactive[SDLK_r] && keysactive[SDLK_d]) { + difficulty_max++; + } + + if (newkey) + { + switch (lastkey_sym) + { + case SDLK_UP: + difficultyLevel--; + if (difficultyLevel < 1) + { + difficultyLevel = difficulty_max; + } + JE_playSampleNum(S_CURSOR); + break; + case SDLK_DOWN: + difficultyLevel++; + if (difficultyLevel > difficulty_max) + { + difficultyLevel = 1; + } + JE_playSampleNum(S_CURSOR); + break; + + case SDLK_RETURN: + JE_playSampleNum(S_SELECT); + /* fading handled elsewhere + fade_black(10); */ + + if (difficultyLevel == 6) + { + difficultyLevel = 8; + } else if (difficultyLevel == 5) { + difficultyLevel = 6; + } + return true; + + case SDLK_ESCAPE: + JE_playSampleNum(S_SPRING); + /* fading handled elsewhere + fade_black(10); */ + + return false; + + default: + break; + } + } + } +} + diff --git a/contrib/games/opentyrian/src/menus.h b/contrib/games/opentyrian/src/menus.h new file mode 100644 index 000000000..1b2ec7064 --- /dev/null +++ b/contrib/games/opentyrian/src/menus.h @@ -0,0 +1,32 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef MENUS_H +#define MENUS_H + +#include "opentyr.h" + +#define GAMEPLAY_NAME_COUNT 5 +extern char episode_name[6][31], difficulty_name[7][21], gameplay_name[GAMEPLAY_NAME_COUNT][26]; + +bool select_gameplay( void ); +bool select_episode( void ); +bool select_difficulty( void ); + +#endif /* MENUS_H */ + diff --git a/contrib/games/opentyrian/src/mouse.c b/contrib/games/opentyrian/src/mouse.c new file mode 100644 index 000000000..d73a6b4ef --- /dev/null +++ b/contrib/games/opentyrian/src/mouse.c @@ -0,0 +1,115 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "mouse.h" + +#include "keyboard.h" +#include "nortvars.h" +#include "sprite.h" +#include "video.h" +#include "vga256d.h" + +#if defined(TARGET_GP2X) || defined(TARGET_DINGUX) +bool has_mouse = false; +#else +bool has_mouse = true; +#endif +bool mouse_has_three_buttons = true; + +JE_word lastMouseX, lastMouseY; +JE_byte mouseCursor; +JE_word mouseX, mouseY, mouseButton; +JE_word mouseXB, mouseYB; + +JE_byte mouseGrabShape[24 * 28]; /* [1..24*28] */ + +void JE_drawShapeTypeOne( JE_word x, JE_word y, JE_byte *shape ) +{ + JE_word xloop = 0, yloop = 0; + JE_byte *p = shape; /* shape pointer */ + Uint8 *s; /* screen pointer, 8-bit specific */ + Uint8 *s_limit; /* buffer boundary */ + + s = (Uint8 *)VGAScreen->pixels; + s += y * VGAScreen->pitch + x; + + s_limit = (Uint8 *)VGAScreen->pixels; + s_limit += VGAScreen->h * VGAScreen->pitch; + + for (yloop = 0; yloop < 28; yloop++) + { + for (xloop = 0; xloop < 24; xloop++) + { + if (s >= s_limit) return; + *s = *p; + s++; p++; + } + s -= 24; + s += VGAScreen->pitch; + } +} + +void JE_grabShapeTypeOne( JE_word x, JE_word y, JE_byte *shape ) +{ + JE_word xloop = 0, yloop = 0; + JE_byte *p = shape; /* shape pointer */ + Uint8 *s; /* screen pointer, 8-bit specific */ + Uint8 *s_limit; /* buffer boundary */ + + s = (Uint8 *)VGAScreen->pixels; + s += y * VGAScreen->pitch + x; + + s_limit = (Uint8 *)VGAScreen->pixels; + s_limit += VGAScreen->h * VGAScreen->pitch; + + for (yloop = 0; yloop < 28; yloop++) + { + for (xloop = 0; xloop < 24; xloop++) + { + if (s >= s_limit) return; + *p = *s; + s++; p++; + } + s -= 24; + s += VGAScreen->pitch; + } +} + +void JE_mouseStart( void ) +{ + const JE_word mouseCursorGr[3] /* [1..3] */ = {273, 275, 277}; + + if (has_mouse) + { + service_SDL_events(false); + mouseButton = mousedown ? lastmouse_but : 0; /* incorrect, possibly unimportant */ + lastMouseX = MIN(mouse_x, 320 - 13); + lastMouseY = MIN(mouse_y, 200 - 16); + + JE_grabShapeTypeOne(lastMouseX, lastMouseY, mouseGrabShape); + + blit_sprite2x2(VGAScreen, lastMouseX, lastMouseY, shapes6, mouseCursorGr[mouseCursor]); + } +} + +void JE_mouseReplace( void ) +{ + if (has_mouse) + JE_drawShapeTypeOne(lastMouseX, lastMouseY, mouseGrabShape); +} + diff --git a/contrib/games/opentyrian/src/mouse.h b/contrib/games/opentyrian/src/mouse.h new file mode 100644 index 000000000..12575c525 --- /dev/null +++ b/contrib/games/opentyrian/src/mouse.h @@ -0,0 +1,43 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef MOUSE_H +#define MOUSE_H + +#include "opentyr.h" + +#include "SDL.h" + +extern bool has_mouse; +extern bool mouse_has_three_buttons; + +extern JE_word lastMouseX, lastMouseY; +extern JE_byte mouseCursor; +extern JE_word mouseX, mouseY, mouseButton; +extern JE_word mouseXB, mouseYB; + +extern JE_byte mouseGrabShape[24 * 28]; + +void JE_drawShapeTypeOne( JE_word x, JE_word y, JE_byte *shape ); +void JE_grabShapeTypeOne( JE_word x, JE_word y, JE_byte *shape ); + +void JE_mouseStart( void ); +void JE_mouseReplace( void ); + +#endif /* MOUSE_H */ + diff --git a/contrib/games/opentyrian/src/mtrand.c b/contrib/games/opentyrian/src/mtrand.c new file mode 100644 index 000000000..403ebeb5f --- /dev/null +++ b/contrib/games/opentyrian/src/mtrand.c @@ -0,0 +1,107 @@ +/* + Copyright (C) 1997--2004, Makoto Matsumoto, Takuji Nishimura, and + Eric Landry; All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + 3. The names of its contributors may not be used to endorse or + promote products derived from this software without specific + prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + Any feedback is very welcome. + http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html + email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space) + + Reference: M. Matsumoto and T. Nishimura, "Mersenne Twister: + A 623-Dimensionally Equidistributed Uniform Pseudo-Random Number + Generator", ACM Transactions on Modeling and Computer Simulation, + Vol. 8, No. 1, January 1998, pp 3--30. +*/ + +#include "mtrand.h" + +/* Period parameters */ +#define N 624 +#define M 397 +#define MATRIX_A 0x9908b0dfUL /* constant vector a */ +#define UPPER_MASK 0x80000000UL /* most significant w-r bits */ +#define LOWER_MASK 0x7fffffffUL /* least significant r bits */ + +static unsigned long x[N]; /* the array for the state vector */ +static unsigned long *p0, *p1, *pm; + +void mt_srand( unsigned long s ) +{ + int i; + + x[0] = s & 0xffffffffUL; + for (i = 1; i < N; ++i) { + x[i] = (1812433253UL * (x[i - 1] ^ (x[i - 1] >> 30)) + i) + & 0xffffffffUL; /* for >32 bit machines */ + } + p0 = x; + p1 = x + 1; + pm = x + M; +} + +/* generates a random number on the interval [0,0xffffffff] */ +unsigned long mt_rand( void ) +{ + unsigned long y; + + if (!p0) { + /* Default seed */ + mt_srand(5489UL); + } + /* Twisted feedback */ + y = *p0 = *pm++ ^ (((*p0 & UPPER_MASK) | (*p1 & LOWER_MASK)) >> 1) ^ ((~(*p1 & 1)+1) & MATRIX_A); + p0 = p1++; + if (pm == x + N) { + pm = x; + } + if (p1 == x + N) { + p1 = x; + } + /* Temper */ + y ^= y >> 11; + y ^= y << 7 & 0x9d2c5680UL; + y ^= y << 15 & 0xefc60000UL; + y ^= y >> 18; + return y; +} + +/* generates a random number on the interval [0,1]. */ +float mt_rand_1( void ) +{ + return ((float)mt_rand() / (float)MT_RAND_MAX); +} + +/* generates a random number on the interval [0,1). */ +float mt_rand_lt1( void ) +{ + /* MT_RAND_MAX must be a float before adding one to it! */ + return ((float)mt_rand() / ((float)MT_RAND_MAX + 1.0f)); +} + diff --git a/contrib/games/opentyrian/src/mtrand.h b/contrib/games/opentyrian/src/mtrand.h new file mode 100644 index 000000000..fcb2badfa --- /dev/null +++ b/contrib/games/opentyrian/src/mtrand.h @@ -0,0 +1,30 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef MTRAND_H +#define MTRAND_H + +#define MT_RAND_MAX 0xffffffffUL + +void mt_srand( unsigned long s ); +unsigned long mt_rand( void ); +float mt_rand_1( void ); +float mt_rand_lt1( void ); + +#endif /* MTRAND_H */ + diff --git a/contrib/games/opentyrian/src/musmast.c b/contrib/games/opentyrian/src/musmast.c new file mode 100644 index 000000000..9c3263cfc --- /dev/null +++ b/contrib/games/opentyrian/src/musmast.c @@ -0,0 +1,116 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "musmast.h" + +#include "opentyr.h" + +JE_byte songBuy; + +const char musicFile[MUSIC_NUM][13] = +{ + /* 1 */ "ASTEROI2.DAT", + /* 2 */ "ASTEROID.DAT", + /* 3 */ "BUY.DAT", + /* 4 */ "CAMANIS.DAT", + /* 5 */ "CAMANISE.DAT", + /* 6 */ "DELIANI.DAT", + /* 7 */ "DELIANI2.DAT", + /* 8 */ "ENDING1.DAT", + /* 9 */ "ENDING2.DAT", + /* 10 */ "ENDLEVEL.DAT", + /* 11 */ "GAMEOVER.DAT", + /* 12 */ "GRYPHON.DAT", + /* 13 */ "GRYPHONE.DAT", + /* 14 */ "GYGES.DAT", + /* 15 */ "GYGESE.DAT", + /* 16 */ "HALLOWS.DAT", + /* 17 */ "ZICA.DAT", + /* 18 */ "TYRSONG2.DAT", + /* 19 */ "LOUDNESS.DAT", + /* 20 */ "NAVC.DAT", + /* 21 */ "SAVARA.DAT", + /* 22 */ "SAVARAE.DAT", + /* 23 */ "SPACE1.DAT", + /* 24 */ "SPACE2.DAT", + /* 25 */ "STARENDB.DAT", + /* 26 */ "START5.DAT", + /* 27 */ "TALK.DAT", + /* 28 */ "TORM.DAT", + /* 29 */ "TRANSON.DAT", + /* 30 */ "TYRSONG.DAT", + /* 31 */ "ZANAC3.DAT", + /* 32 */ "ZANACS.DAT", + /* 33 */ "SAVARA2.DAT", + /* 34 */ "HISCORE.DAT", + /* 35 */ "TYR4-1.DAT", /* OMF */ + /* 36 */ "TYR4-3.DAT", /* SARAH */ + /* 37 */ "TYR4-2.DAT", /* MAGFIELD */ + /* 38 */ "TYR4-0.DAT", /* ROCKME */ + /* 39 */ "TYR4-4.DAT", /* quiet music */ + /* 40 */ "TYR4-5.DAT", /* piano */ + /* 41 */ "TYR-BEER.DAT" /* BEER */ +}; + +const char musicTitle[MUSIC_NUM][48] = +{ + "Asteroid Dance Part 2", + "Asteroid Dance Part 1", + "Buy/Sell Music", + "CAMANIS", + "CAMANISE", + "Deli Shop Quartet", + "Deli Shop Quartet No. 2", + "Ending Number 1", + "Ending Number 2", + "End of Level", + "Game Over Solo", + "Gryphons of the West", + "Somebody pick up the Gryphone", + "Gyges, Will You Please Help Me?", + "I speak Gygese", + "Halloween Ramble", + "Tunneling Trolls", + "Tyrian, The Level", + "The MusicMan", + "The Navigator", + "Come Back to Me, Savara", + "Come Back again to Savara", + "Space Journey 1", + "Space Journey 2", + "The final edge", + "START5", + "Parlance", + "Torm - The Gathering", + "TRANSON", + "Tyrian: The Song", + "ZANAC3", + "ZANACS", + "Return me to Savara", + "High Score Table", + "One Mustn't Fall", + "Sarah's Song", + "A Field for Mag", + "Rock Garden", + "Quest for Peace", + "Composition in Q", + "BEER" +}; + +JE_boolean musicFade; + diff --git a/contrib/games/opentyrian/src/musmast.h b/contrib/games/opentyrian/src/musmast.h new file mode 100644 index 000000000..536f589f2 --- /dev/null +++ b/contrib/games/opentyrian/src/musmast.h @@ -0,0 +1,41 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef MUSMAST_H +#define MUSMAST_H + +#include "opentyr.h" + + +#define DEFAULT_SONG_BUY 2 +#define SONG_LEVELEND 9 +#define SONG_GAMEOVER 10 +#define SONG_MAPVIEW 19 +#define SONG_ENDGAME1 7 +#define SONG_ZANAC 31 +#define SONG_TITLE 29 + +#define MUSIC_NUM 41 + +extern JE_byte songBuy; +extern const char musicFile[MUSIC_NUM][13]; +extern const char musicTitle[MUSIC_NUM][48]; +extern JE_boolean musicFade; + +#endif /* MUSMAST_H */ + diff --git a/contrib/games/opentyrian/src/network.c b/contrib/games/opentyrian/src/network.c new file mode 100644 index 000000000..a2cb57bca --- /dev/null +++ b/contrib/games/opentyrian/src/network.c @@ -0,0 +1,791 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "network.h" + +#include "episodes.h" +#include "fonthand.h" +#include "helptext.h" +#include "joystick.h" +#include "keyboard.h" +#include "mainint.h" +#include "nortvars.h" +#include "opentyr.h" +#include "picload.h" +#include "sprite.h" +#include "varz.h" +#include "video.h" + +#include + +/* HERE BE DRAGONS! + * + * When I wrote this code I thought it was wonderful... that thought was very + * wrong. It works, but good luck understanding how... I don't anymore. + * + * Hopefully it'll be rewritten some day. + */ + +#define NET_VERSION 2 // increment whenever networking changes might create incompatability +#define NET_PORT 1333 // UDP + +#define NET_PACKET_SIZE 256 +#define NET_PACKET_QUEUE 16 + +#define NET_RETRY 640 // ticks to wait for packet acknowledgement before resending +#define NET_RESEND 320 // ticks to wait before requesting unreceived game packet +#define NET_KEEP_ALIVE 1600 // ticks to wait between keep-alive packets +#define NET_TIME_OUT 16000 // ticks to wait before considering connection dead + +bool isNetworkGame = false; +int network_delay = 1 + 1; // minimum is 1 + 0 + +char *network_opponent_host = NULL; + +Uint16 network_player_port = NET_PORT, + network_opponent_port = NET_PORT; + +static char empty_string[] = ""; +char *network_player_name = empty_string, + *network_opponent_name = empty_string; + +#ifdef WITH_NETWORK +static UDPsocket socket; +static IPaddress ip; + +UDPpacket *packet_out_temp; +static UDPpacket *packet_temp; + +UDPpacket *packet_in[NET_PACKET_QUEUE] = { NULL }, + *packet_out[NET_PACKET_QUEUE] = { NULL }; + +static Uint16 last_out_sync = 0, queue_in_sync = 0, queue_out_sync = 0, last_ack_sync = 0; +static Uint32 last_in_tick = 0, last_out_tick = 0; + +UDPpacket *packet_state_in[NET_PACKET_QUEUE] = { NULL }; +static UDPpacket *packet_state_in_xor[NET_PACKET_QUEUE] = { NULL }; +UDPpacket *packet_state_out[NET_PACKET_QUEUE] = { NULL }; + +static Uint16 last_state_in_sync = 0, last_state_out_sync = 0; +static Uint32 last_state_in_tick = 0; + +static bool net_initialized = false; +static bool connected = false, quit = false; +#endif + +uint thisPlayerNum = 0; /* Player number on this PC (1 or 2) */ + +JE_boolean haltGame = false; + +JE_boolean moveOk; + +/* Special Requests */ +JE_boolean pauseRequest, skipLevelRequest, helpRequest, nortShipRequest; +JE_boolean yourInGameMenuRequest, inGameMenuRequest; + +#ifdef WITH_NETWORK +static void packet_copy( UDPpacket *dst, UDPpacket *src ) +{ + void *temp = dst->data; + memcpy(dst, src, sizeof(*dst)); + dst->data = temp; + memcpy(dst->data, src->data, src->len); +} + +static void packets_shift_up( UDPpacket **packet, int max_packets ) +{ + if (packet[0]) + { + SDLNet_FreePacket(packet[0]); + } + for (int i = 0; i < max_packets - 1; i++) + { + packet[i] = packet[i + 1]; + } + packet[max_packets - 1] = NULL; +} + +static void packets_shift_down( UDPpacket **packet, int max_packets ) +{ + if (packet[max_packets - 1]) + { + SDLNet_FreePacket(packet[max_packets - 1]); + } + for (int i = max_packets - 1; i > 0; i--) + { + packet[i] = packet[i - 1]; + } + packet[0] = NULL; +} + +// prepare new packet for sending +void network_prepare( Uint16 type ) +{ + SDLNet_Write16(type, &packet_out_temp->data[0]); + SDLNet_Write16(last_out_sync, &packet_out_temp->data[2]); +} + +// send packet but don't expect acknoledgment of delivery +static bool network_send_no_ack( int len ) +{ + packet_out_temp->len = len; + + if (!SDLNet_UDP_Send(socket, 0, packet_out_temp)) + { + printf("SDLNet_UDP_Send: %s\n", SDL_GetError()); + return false; + } + + return true; +} + +// send packet and place it in queue to be acknowledged +bool network_send( int len ) +{ + bool temp = network_send_no_ack(len); + + Uint16 i = last_out_sync - queue_out_sync; + if (i < NET_PACKET_QUEUE) + { + packet_out[i] = SDLNet_AllocPacket(NET_PACKET_SIZE); + packet_copy(packet_out[i], packet_out_temp); + } else { + // connection is probably bad now + fprintf(stderr, "warning: outbound packet queue overflow\n"); + return false; + } + + last_out_sync++; + + if (network_is_sync()) + last_out_tick = SDL_GetTicks(); + + return temp; +} + +// send acknowledgement packet +static int network_acknowledge( Uint16 sync ) +{ + SDLNet_Write16(PACKET_ACKNOWLEDGE, &packet_out_temp->data[0]); + SDLNet_Write16(sync, &packet_out_temp->data[2]); + network_send_no_ack(4); + + return 0; +} + +// activity lately? +static bool network_is_alive( void ) +{ + return (SDL_GetTicks() - last_in_tick < NET_TIME_OUT || SDL_GetTicks() - last_state_in_tick < NET_TIME_OUT); +} + +// poll for new packets received, check that connection is alive, resend queued packets if necessary +int network_check( void ) +{ + if (!net_initialized) + return -1; + + if (connected) + { + // timeout + if (!network_is_alive()) + { + if (!quit) + network_tyrian_halt(2, false); + } + + // keep-alive + static Uint32 keep_alive_tick = 0; + if (SDL_GetTicks() - keep_alive_tick > NET_KEEP_ALIVE) + { + network_prepare(PACKET_KEEP_ALIVE); + network_send_no_ack(4); + + keep_alive_tick = SDL_GetTicks(); + } + } + + // retry + if (packet_out[0] && SDL_GetTicks() - last_out_tick > NET_RETRY) + { + if (!SDLNet_UDP_Send(socket, 0, packet_out[0])) + { + printf("SDLNet_UDP_Send: %s\n", SDL_GetError()); + return -1; + } + + last_out_tick = SDL_GetTicks(); + } + + switch (SDLNet_UDP_Recv(socket, packet_temp)) + { + case -1: + printf("SDLNet_UDP_Recv: %s\n", SDL_GetError()); + return -1; + break; + case 0: + break; + default: + if (packet_temp->channel == 0 && packet_temp->len >= 4) + { + switch (SDLNet_Read16(&packet_temp->data[0])) + { + case PACKET_ACKNOWLEDGE: + if ((Uint16)(SDLNet_Read16(&packet_temp->data[2]) - last_ack_sync) < NET_PACKET_QUEUE) + { + last_ack_sync = SDLNet_Read16(&packet_temp->data[2]); + } + + { + Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - queue_out_sync; + if (i < NET_PACKET_QUEUE) + { + if (packet_out[i]) + { + SDLNet_FreePacket(packet_out[i]); + packet_out[i] = NULL; + } + } + } + + // remove acknowledged packets from queue + while (packet_out[0] == NULL && (Uint16)(last_ack_sync - queue_out_sync) < NET_PACKET_QUEUE) + { + packets_shift_up(packet_out, NET_PACKET_QUEUE); + + queue_out_sync++; + } + + last_in_tick = SDL_GetTicks(); + break; + + case PACKET_CONNECT: + queue_in_sync = SDLNet_Read16(&packet_temp->data[2]); + + for (int i = 0; i < NET_PACKET_QUEUE; i++) + { + if (packet_in[i]) + { + SDLNet_FreePacket(packet_in[i]); + packet_in[i] = NULL; + } + } + // fall through + + case PACKET_DETAILS: + case PACKET_WAITING: + case PACKET_BUSY: + case PACKET_GAME_QUIT: + case PACKET_GAME_PAUSE: + case PACKET_GAME_MENU: + { + Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - queue_in_sync; + if (i < NET_PACKET_QUEUE) + { + if (packet_in[i] == NULL) + packet_in[i] = SDLNet_AllocPacket(NET_PACKET_SIZE); + packet_copy(packet_in[i], packet_temp); + } else { + // inbound packet queue overflow/underflow + // under normal circumstances, this is okay + } + } + + network_acknowledge(SDLNet_Read16(&packet_temp->data[2])); + // fall through + + case PACKET_KEEP_ALIVE: + last_in_tick = SDL_GetTicks(); + break; + + case PACKET_QUIT: + if (!quit) + { + network_prepare(PACKET_QUIT); + network_send(4); // PACKET_QUIT + } + + network_acknowledge(SDLNet_Read16(&packet_temp->data[2])); + + if (!quit) + network_tyrian_halt(1, true); + break; + + case PACKET_STATE: + // place packet in queue if within limits + { + Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - last_state_in_sync + 1; + if (i < NET_PACKET_QUEUE) + { + if (packet_state_in[i] == NULL) + packet_state_in[i] = SDLNet_AllocPacket(NET_PACKET_SIZE); + packet_copy(packet_state_in[i], packet_temp); + } + } + break; + + case PACKET_STATE_XOR: + // place packet in queue if within limits + { + Uint16 i = SDLNet_Read16(&packet_temp->data[2]) - last_state_in_sync + 1; + if (i < NET_PACKET_QUEUE) + { + if (packet_state_in_xor[i] == NULL) + { + packet_state_in_xor[i] = SDLNet_AllocPacket(NET_PACKET_SIZE); + packet_copy(packet_state_in_xor[i], packet_temp); + } else if (SDLNet_Read16(&packet_state_in_xor[i]->data[0]) != PACKET_STATE_XOR) { + for (int j = 4; j < packet_state_in_xor[i]->len; j++) + packet_state_in_xor[i]->data[j] ^= packet_temp->data[j]; + SDLNet_Write16(PACKET_STATE_XOR, &packet_state_in_xor[i]->data[0]); + } + } + } + break; + + case PACKET_STATE_RESEND: + // resend requested state packet if still available + { + Uint16 i = last_state_out_sync - SDLNet_Read16(&packet_temp->data[2]); + if (i > 0 && i < NET_PACKET_QUEUE) + { + if (packet_state_out[i]) + { + if (!SDLNet_UDP_Send(socket, 0, packet_state_out[i])) + { + printf("SDLNet_UDP_Send: %s\n", SDL_GetError()); + return -1; + } + } + } + } + break; + + default: + fprintf(stderr, "warning: bad packet %d received\n", SDLNet_Read16(&packet_temp->data[0])); + return 0; + break; + } + + return 1; + } + break; + } + + return 0; +} + +// discard working packet, now processing next packet in queue +bool network_update( void ) +{ + if (packet_in[0]) + { + packets_shift_up(packet_in, NET_PACKET_QUEUE); + + queue_in_sync++; + + return true; + } + + return false; +} + +// has opponent gotten all the packets we've sent? +bool network_is_sync( void ) +{ + return (queue_out_sync - last_ack_sync == 1); +} + + +// prepare new state for sending +void network_state_prepare( void ) +{ + if (packet_state_out[0]) + { + fprintf(stderr, "warning: state packet overwritten (previous packet remains unsent)\n"); + } else { + packet_state_out[0] = SDLNet_AllocPacket(NET_PACKET_SIZE); + packet_state_out[0]->len = 28; + } + + SDLNet_Write16(PACKET_STATE, &packet_state_out[0]->data[0]); + SDLNet_Write16(last_state_out_sync, &packet_state_out[0]->data[2]); + memset(&packet_state_out[0]->data[4], 0, 28 - 4); +} + +// send state packet, xor packet if applicable +int network_state_send( void ) +{ + if (!SDLNet_UDP_Send(socket, 0, packet_state_out[0])) + { + printf("SDLNet_UDP_Send: %s\n", SDL_GetError()); + return -1; + } + + // send xor of last network_delay packets + if (network_delay > 1 && (last_state_out_sync + 1) % network_delay == 0 && packet_state_out[network_delay - 1] != NULL) + { + packet_copy(packet_temp, packet_state_out[0]); + SDLNet_Write16(PACKET_STATE_XOR, &packet_temp->data[0]); + for (int i = 1; i < network_delay; i++) + for (int j = 4; j < packet_temp->len; j++) + packet_temp->data[j] ^= packet_state_out[i]->data[j]; + + if (!SDLNet_UDP_Send(socket, 0, packet_temp)) + { + printf("SDLNet_UDP_Send: %s\n", SDL_GetError()); + return -1; + } + } + + packets_shift_down(packet_state_out, NET_PACKET_QUEUE); + + last_state_out_sync++; + + return 0; +} + +// receive state packet, wait until received +bool network_state_update( void ) +{ + if (network_state_is_reset()) + { + return 0; + } else { + packets_shift_up(packet_state_in, NET_PACKET_QUEUE); + + packets_shift_up(packet_state_in_xor, NET_PACKET_QUEUE); + + last_state_in_sync++; + + // current xor packet index + int x = network_delay - (last_state_in_sync - 1) % network_delay - 1; + + // loop until needed packet is available + while (!packet_state_in[0]) + { + // xor the packet from thin air, if possible + if (packet_state_in_xor[x] && SDLNet_Read16(&packet_state_in_xor[x]->data[0]) == PACKET_STATE_XOR) + { + // check for all other required packets + bool okay = true; + for (int i = 1; i <= x; i++) + { + if (packet_state_in[i] == NULL) + { + okay = false; + break; + } + } + if (okay) + { + packet_state_in[0] = SDLNet_AllocPacket(NET_PACKET_SIZE); + packet_copy(packet_state_in[0], packet_state_in_xor[x]); + for (int i = 1; i <= x; i++) + for (int j = 4; j < packet_state_in[0]->len; j++) + packet_state_in[0]->data[j] ^= packet_state_in[i]->data[j]; + break; + } + } + + static Uint32 resend_tick = 0; + if (SDL_GetTicks() - last_state_in_tick > NET_RESEND && SDL_GetTicks() - resend_tick > NET_RESEND) + { + SDLNet_Write16(PACKET_STATE_RESEND, &packet_out_temp->data[0]); + SDLNet_Write16(last_state_in_sync - 1, &packet_out_temp->data[2]); + network_send_no_ack(4); // PACKET_RESEND + + resend_tick = SDL_GetTicks(); + } + + if (network_check() == 0) + uSDL_Delay(1); + } + + if (network_delay > 1) + { + // process the current in packet against the xor queue + if (packet_state_in_xor[x] == NULL) + { + packet_state_in_xor[x] = SDLNet_AllocPacket(NET_PACKET_SIZE); + packet_copy(packet_state_in_xor[x], packet_state_in[0]); + packet_state_in_xor[x]->status = 0; + } else { + for (int j = 4; j < packet_state_in_xor[x]->len; j++) + packet_state_in_xor[x]->data[j] ^= packet_state_in[0]->data[j]; + } + } + + last_state_in_tick = SDL_GetTicks(); + } + + return 1; +} + +// ignore first network_delay states of level +bool network_state_is_reset( void ) +{ + return (last_state_out_sync < network_delay); +} + +// reset queues for new level +void network_state_reset( void ) +{ + last_state_in_sync = last_state_out_sync = 0; + + for (int i = 0; i < NET_PACKET_QUEUE; i++) + { + if (packet_state_in[i]) + { + SDLNet_FreePacket(packet_state_in[i]); + packet_state_in[i] = NULL; + } + } + for (int i = 0; i < NET_PACKET_QUEUE; i++) + { + if (packet_state_in_xor[i]) + { + SDLNet_FreePacket(packet_state_in_xor[i]); + packet_state_in_xor[i] = NULL; + } + } + for (int i = 0; i < NET_PACKET_QUEUE; i++) + { + if (packet_state_out[i]) + { + SDLNet_FreePacket(packet_state_out[i]); + packet_state_out[i] = NULL; + } + } + + last_state_in_tick = SDL_GetTicks(); +} + + +// attempt to punch through firewall by firing off UDP packets at the opponent +// exchange game information +int network_connect( void ) +{ + SDLNet_ResolveHost(&ip, network_opponent_host, network_opponent_port); + + SDLNet_UDP_Bind(socket, 0, &ip); + + Uint16 episodes = 0, episodes_local = 0; + assert(EPISODE_MAX <= 16); + for (int i = EPISODE_MAX - 1; i >= 0; i--) + { + episodes <<= 1; + episodes |= (episodeAvail[i] != 0); + } + episodes_local = episodes; + + assert(NET_PACKET_SIZE - 12 >= 20 + 1); + if (strlen(network_player_name) > 20) + network_player_name[20] = '\0'; + +connect_reset: + network_prepare(PACKET_CONNECT); + SDLNet_Write16(NET_VERSION, &packet_out_temp->data[4]); + SDLNet_Write16(network_delay, &packet_out_temp->data[6]); + SDLNet_Write16(episodes_local, &packet_out_temp->data[8]); + SDLNet_Write16(thisPlayerNum, &packet_out_temp->data[10]); + strcpy((char *)&packet_out_temp->data[12], network_player_name); + network_send(12 + strlen(network_player_name) + 1); // PACKET_CONNECT + + // until opponent sends connect packet + while (true) + { + push_joysticks_as_keyboard(); + service_SDL_events(false); + + if (newkey && lastkey_sym == SDLK_ESCAPE) + network_tyrian_halt(0, false); + + // never timeout + last_in_tick = SDL_GetTicks(); + + if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_CONNECT) + break; + + network_update(); + network_check(); + + uSDL_Delay(16); + } + +connect_again: + if (SDLNet_Read16(&packet_in[0]->data[4]) != NET_VERSION) + { + fprintf(stderr, "error: network version did not match opponent's\n"); + network_tyrian_halt(4, true); + } + if (SDLNet_Read16(&packet_in[0]->data[6]) != network_delay) + { + fprintf(stderr, "error: network delay did not match opponent's\n"); + network_tyrian_halt(5, true); + } + if (SDLNet_Read16(&packet_in[0]->data[10]) == thisPlayerNum) + { + fprintf(stderr, "error: player number conflicts with opponent's\n"); + network_tyrian_halt(6, true); + } + + episodes = SDLNet_Read16(&packet_in[0]->data[8]); + for (int i = 0; i < EPISODE_MAX; i++) { + episodeAvail[i] &= (episodes & 1); + episodes >>= 1; + } + + network_opponent_name = malloc(packet_in[0]->len - 12 + 1); + strcpy(network_opponent_name, (char *)&packet_in[0]->data[12]); + + network_update(); + + // until opponent has acknowledged + while (!network_is_sync()) + { + service_SDL_events(false); + + // got a duplicate packet; process it again (but why?) + if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_CONNECT) + goto connect_again; + + network_check(); + + // maybe opponent didn't get our packet + if (SDL_GetTicks() - last_out_tick > NET_RETRY) + goto connect_reset; + + uSDL_Delay(16); + } + + // send another packet since sometimes the network syncs without both connect packets exchanged + // there should be a better way to handle this + network_prepare(PACKET_CONNECT); + SDLNet_Write16(NET_VERSION, &packet_out_temp->data[4]); + SDLNet_Write16(network_delay, &packet_out_temp->data[6]); + SDLNet_Write16(episodes_local, &packet_out_temp->data[8]); + SDLNet_Write16(thisPlayerNum, &packet_out_temp->data[10]); + strcpy((char *)&packet_out_temp->data[12], network_player_name); + network_send(12 + strlen(network_player_name) + 1); // PACKET_CONNECT + + connected = true; + + return 0; +} + +// something has gone wrong :( +void network_tyrian_halt( unsigned int err, bool attempt_sync ) +{ + const char *err_msg[] = { + "Quitting...", + "Other player quit the game.", + "Network connection was lost.", + "Network connection failed.", + "Network version mismatch.", + "Network delay mismatch.", + "Network player number conflict.", + }; + + quit = true; + + if (err >= COUNTOF(err_msg)) + err = 0; + + fade_black(10); + + VGAScreen = VGAScreenSeg; + + JE_loadPic(VGAScreen, 2, false); + JE_dString(VGAScreen, JE_fontCenter(err_msg[err], SMALL_FONT_SHAPES), 140, err_msg[err], SMALL_FONT_SHAPES); + + JE_showVGA(); + fade_palette(colors, 10, 0, 255); + + if (attempt_sync) + { + while (!network_is_sync() && network_is_alive()) + { + service_SDL_events(false); + + network_check(); + uSDL_Delay(16); + } + } + + if (err) + { + while (!JE_anyButton()) + uSDL_Delay(16); + } + + fade_black(10); + + SDLNet_Quit(); + + JE_tyrianHalt(5); +} + +int network_init( void ) +{ + printf("Initializing network...\n"); + + if (network_delay * 2 > NET_PACKET_QUEUE - 2) + { + fprintf(stderr, "error: network delay would overflow packet queue\n"); + return -4; + } + + if (SDLNet_Init() == -1) + { + fprintf(stderr, "error: SDLNet_Init: %s\n", SDLNet_GetError()); + return -1; + } + + socket = SDLNet_UDP_Open(network_player_port); + if (!socket) + { + fprintf(stderr, "error: SDLNet_UDP_Open: %s\n", SDLNet_GetError()); + return -2; + } + + packet_temp = SDLNet_AllocPacket(NET_PACKET_SIZE); + packet_out_temp = SDLNet_AllocPacket(NET_PACKET_SIZE); + + if (!packet_temp || !packet_out_temp) + { + printf("SDLNet_AllocPacket: %s\n", SDLNet_GetError()); + return -3; + } + + net_initialized = true; + + return 0; +} + +#endif + +void JE_clearSpecialRequests( void ) +{ + pauseRequest = false; + inGameMenuRequest = false; + skipLevelRequest = false; + helpRequest = false; + nortShipRequest = false; +} + diff --git a/contrib/games/opentyrian/src/network.h b/contrib/games/opentyrian/src/network.h new file mode 100644 index 000000000..f9c32b0c7 --- /dev/null +++ b/contrib/games/opentyrian/src/network.h @@ -0,0 +1,101 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef NETWORK_H +#define NETWORK_H + +#include "opentyr.h" + +#include "SDL.h" +#ifdef WITH_NETWORK +# include "SDL_net.h" +#endif + + +#define PACKET_ACKNOWLEDGE 0x00 // +#define PACKET_KEEP_ALIVE 0x01 // + +#define PACKET_CONNECT 0x10 // version, delay, episodes, player_number, name +#define PACKET_DETAILS 0x11 // episode, difficulty + +#define PACKET_QUIT 0x20 // +#define PACKET_WAITING 0x21 // +#define PACKET_BUSY 0x22 // + +#define PACKET_GAME_QUIT 0x30 // +#define PACKET_GAME_PAUSE 0x31 // +#define PACKET_GAME_MENU 0x32 // + +#define PACKET_STATE_RESEND 0x40 // state_id +#define PACKET_STATE 0x41 // (not acknowledged) +#define PACKET_STATE_XOR 0x42 // (not acknowledged) + +extern bool isNetworkGame; +extern int network_delay; + +extern char *network_opponent_host; +extern Uint16 network_player_port, network_opponent_port; +extern char *network_player_name, *network_opponent_name; + +#ifdef WITH_NETWORK +extern UDPpacket *packet_out_temp; +extern UDPpacket *packet_in[], *packet_out[], + *packet_state_in[], *packet_state_out[]; +#endif + +extern uint thisPlayerNum; +extern JE_boolean haltGame; +extern JE_boolean moveOk; +extern JE_boolean pauseRequest, skipLevelRequest, helpRequest, nortShipRequest; +extern JE_boolean yourInGameMenuRequest, inGameMenuRequest; + +#ifdef WITH_NETWORK +void network_prepare( Uint16 type ); +bool network_send( int len ); + +int network_check( void ); +bool network_update( void ); + +bool network_is_sync( void ); + +void network_state_prepare( void ); +int network_state_send( void ); +bool network_state_update( void ); +bool network_state_is_reset( void ); +void network_state_reset( void ); + +int network_connect( void ); +void network_tyrian_halt( unsigned int err, bool attempt_sync ); + +int network_init( void ); + +void JE_clearSpecialRequests( void ); + +#define NETWORK_KEEP_ALIVE() \ + if (isNetworkGame) \ + network_check(); + +#else + +#define NETWORK_KEEP_ALIVE() + +#endif + + +#endif /* NETWORK_H */ + diff --git a/contrib/games/opentyrian/src/nortsong.c b/contrib/games/opentyrian/src/nortsong.c new file mode 100644 index 000000000..133910ddd --- /dev/null +++ b/contrib/games/opentyrian/src/nortsong.c @@ -0,0 +1,225 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "nortsong.h" + +#include "file.h" +#include "joystick.h" +#include "keyboard.h" +#include "loudness.h" +#include "musmast.h" +#include "opentyr.h" +#include "params.h" +#include "sndmast.h" +#include "vga256d.h" + +#include "SDL.h" + +Uint32 target, target2; + +JE_boolean notYetLoadedSound = true; + +JE_word frameCount, frameCount2, frameCountMax; + +JE_byte *digiFx[SAMPLE_COUNT] = { NULL }; /* [1..soundnum + 9] */ +JE_word fxSize[SAMPLE_COUNT]; /* [1..soundnum + 9] */ + +JE_word tyrMusicVolume, fxVolume; +JE_word fxPlayVol; +JE_word tempVolume; + +JE_word speed; /* JE: holds timer speed for 70Hz */ + +float jasondelay = 1000.0f / (1193180.0f / 0x4300); + +void setdelay( JE_byte delay ) +{ + target = (delay * 16) + SDL_GetTicks(); +} + +void setjasondelay( int delay ) +{ + target = SDL_GetTicks() + delay * jasondelay; +} + +void setjasondelay2( int delay ) +{ + target2 = SDL_GetTicks() + delay * jasondelay; +} + +int delaycount( void ) +{ + return (SDL_GetTicks() < target ? target - SDL_GetTicks() : 0); +} + +int delaycount2( void ) +{ + return (SDL_GetTicks() < target2 ? target2 - SDL_GetTicks() : 0); +} + +void wait_delay( void ) +{ + Sint32 delay = target - SDL_GetTicks(); + if (delay > 0) + uSDL_Delay(delay); +} + +void service_wait_delay( void ) +{ + while (SDL_GetTicks() < target) + { + uSDL_Delay(SDL_GetTicks() - target > SDL_POLL_INTERVAL ? SDL_POLL_INTERVAL : SDL_GetTicks() - target); + service_SDL_events(false); + } +} + +void wait_delayorinput( JE_boolean keyboard, JE_boolean mouse, JE_boolean joystick ) +{ + service_SDL_events(true); + while (SDL_GetTicks() < target && !((keyboard && keydown) || (mouse && mousedown) || (joystick && joydown))) + { + uSDL_Delay(SDL_GetTicks() - target > SDL_POLL_INTERVAL ? SDL_POLL_INTERVAL : SDL_GetTicks() - target); + push_joysticks_as_keyboard(); + service_SDL_events(false); + } +} + +void JE_loadSndFile( const char *effects_sndfile, const char *voices_sndfile ) +{ + JE_byte y, z; + JE_word x; + JE_longint templ; + JE_longint sndPos[2][SAMPLE_COUNT + 1]; + JE_word sndNum; + + FILE *fi; + + /* SYN: Loading offsets into TYRIAN.SND */ + fi = dir_fopen_die(data_dir(), effects_sndfile, "rb"); + efread(&sndNum, sizeof(sndNum), 1, fi); + + for (x = 0; x < sndNum; x++) + { + efread(&sndPos[0][x], sizeof(sndPos[0][x]), 1, fi); + } + fseek(fi, 0, SEEK_END); + sndPos[0][sndNum] = ftell(fi); /* Store file size */ + + for (z = 0; z < sndNum; z++) + { + fseek(fi, sndPos[0][z], SEEK_SET); + fxSize[z] = (sndPos[0][z+1] - sndPos[0][z]); /* Store sample sizes */ + free(digiFx[z]); + digiFx[z] = malloc(fxSize[z]); + efread(digiFx[z], 1, fxSize[z], fi); /* JE: Load sample to buffer */ + } + + fclose(fi); + + /* SYN: Loading offsets into VOICES.SND */ + fi = dir_fopen_die(data_dir(), voices_sndfile, "rb"); + + efread(&sndNum, sizeof(sndNum), 1, fi); + + for (x = 0; x < sndNum; x++) + { + efread(&sndPos[1][x], sizeof(sndPos[1][x]), 1, fi); + } + fseek(fi, 0, SEEK_END); + sndPos[1][sndNum] = ftell(fi); /* Store file size */ + + z = SAMPLE_COUNT - 9; + + for (y = 0; y < sndNum; y++) + { + fseek(fi, sndPos[1][y], SEEK_SET); + + templ = (sndPos[1][y+1] - sndPos[1][y]) - 100; /* SYN: I'm not entirely sure what's going on here. */ + if (templ < 1) templ = 1; + fxSize[z + y] = templ; /* Store sample sizes */ + digiFx[z + y] = malloc(fxSize[z + y]); + efread(digiFx[z + y], 1, fxSize[z + y], fi); /* JE: Load sample to buffer */ + } + + fclose(fi); + + notYetLoadedSound = false; + +} + +void JE_playSampleNum( JE_byte samplenum ) +{ + JE_multiSamplePlay(digiFx[samplenum-1], fxSize[samplenum-1], 0, fxPlayVol); +} + +void JE_calcFXVol( void ) // TODO: not sure *exactly* what this does +{ + fxPlayVol = (fxVolume - 1) >> 5; +} + +void JE_setTimerInt( void ) +{ + jasondelay = 1000.0f / (1193180.0f / speed); +} + +void JE_resetTimerInt( void ) +{ + jasondelay = 1000.0f / (1193180.0f / 0x4300); +} + +void JE_changeVolume( JE_word *music, int music_delta, JE_word *sample, int sample_delta ) +{ + int music_temp = *music + music_delta, + sample_temp = *sample + sample_delta; + + if (music_delta) + { + if (music_temp > 255) + { + music_temp = 255; + JE_playSampleNum(S_CLINK); + } + else if (music_temp < 0) + { + music_temp = 0; + JE_playSampleNum(S_CLINK); + } + } + + if (sample_delta) + { + if (sample_temp > 255) + { + sample_temp = 255; + JE_playSampleNum(S_CLINK); + } + else if (sample_temp < 0) + { + sample_temp = 0; + JE_playSampleNum(S_CLINK); + } + } + + *music = music_temp; + *sample = sample_temp; + + JE_calcFXVol(); + + set_volume(*music, *sample); +} + diff --git a/contrib/games/opentyrian/src/nortsong.h b/contrib/games/opentyrian/src/nortsong.h new file mode 100644 index 000000000..4d0a88287 --- /dev/null +++ b/contrib/games/opentyrian/src/nortsong.h @@ -0,0 +1,64 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef NORTSONG_H +#define NORTSONG_H + +#include "opentyr.h" + +#include "musmast.h" +#include "sndmast.h" + +#include "SDL.h" + +extern Uint32 target, target2; + +extern JE_word frameCount, frameCount2, frameCountMax; + +extern JE_byte *digiFx[SAMPLE_COUNT]; +extern JE_word fxSize[SAMPLE_COUNT]; + +extern JE_word tyrMusicVolume, fxVolume; +extern JE_word fxPlayVol; +extern JE_word tempVolume; + +extern JE_word speed; + +extern float jasondelay; + +void setdelay( JE_byte delay ); +void setjasondelay( int delay ); +void setjasondelay2( int delay ); +int delaycount( void ); +int delaycount2( void ); + +void wait_delay( void ); +void service_wait_delay( void ); +void wait_delayorinput( JE_boolean keyboard, JE_boolean mouse, JE_boolean joystick ); + +void JE_resetTimerInt( void ); +void JE_setTimerInt( void ); + +void JE_calcFXVol( void ); +void JE_changeVolume( JE_word *music, int music_delta, JE_word *sample, int sample_delta ); + +void JE_loadSndFile( const char *effects_sndfile, const char *voices_sndfile ); +void JE_playSampleNum( JE_byte samplenum ); + +#endif /* NORTSONG_H */ + diff --git a/contrib/games/opentyrian/src/nortvars.c b/contrib/games/opentyrian/src/nortvars.c new file mode 100644 index 000000000..07c285708 --- /dev/null +++ b/contrib/games/opentyrian/src/nortvars.c @@ -0,0 +1,88 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "nortvars.h" + +#include "file.h" +#include "joystick.h" +#include "keyboard.h" +#include "opentyr.h" +#include "vga256d.h" +#include "video.h" + +#include +#include + +JE_boolean inputDetected; + +JE_boolean JE_anyButton( void ) +{ + poll_joysticks(); + service_SDL_events(true); + return newkey || mousedown || joydown; +} + +void JE_dBar3( SDL_Surface *surface, JE_integer x, JE_integer y, JE_integer num, JE_integer col ) +{ + JE_byte z; + JE_byte zWait = 2; + + col += 2; + + for (z = 0; z <= num; z++) + { + JE_rectangle(surface, x, y - 1, x + 8, y, col); /* SEGa000 */ + if (zWait > 0) + { + zWait--; + } else { + col++; + zWait = 1; + } + y -= 2; + } +} + +void JE_barDrawShadow( SDL_Surface *surface, JE_word x, JE_word y, JE_word res, JE_word col, JE_word amt, JE_word xsize, JE_word ysize ) +{ + xsize--; + ysize--; + + for (int z = 1; z <= amt / res; z++) + { + JE_barShade(surface, x+2, y+2, x+xsize+2, y+ysize+2); + fill_rectangle_xy(surface, x, y, x+xsize, y+ysize, col+12); + fill_rectangle_xy(surface, x, y, x+xsize, y, col+13); + JE_pix(surface, x, y, col+15); + fill_rectangle_xy(surface, x, y+ysize, x+xsize, y+ysize, col+11); + x += xsize + 2; + } + + amt %= res; + if (amt > 0) + { + JE_barShade(surface, x+2, y+2, x+xsize+2, y+ysize+2); + fill_rectangle_xy(surface, x,y, x+xsize, y+ysize, col+(12 / res * amt)); + } +} + +void JE_wipeKey( void ) +{ + // /!\ Doesn't seems to affect anything. +} + diff --git a/contrib/games/opentyrian/src/nortvars.h b/contrib/games/opentyrian/src/nortvars.h new file mode 100644 index 000000000..69a3726b0 --- /dev/null +++ b/contrib/games/opentyrian/src/nortvars.h @@ -0,0 +1,37 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef NORTVARS_H +#define NORTVARS_H + +#include "opentyr.h" + +#include "SDL.h" + +extern JE_boolean inputDetected; + +JE_boolean JE_buttonPressed( void ); + +JE_boolean JE_anyButton( void ); + +void JE_dBar3( SDL_Surface *surface, JE_integer x, JE_integer y, JE_integer num, JE_integer col ); +void JE_barDrawShadow( SDL_Surface *surface, JE_word x, JE_word y, JE_word res, JE_word col, JE_word amt, JE_word xsize, JE_word ysize ); +void JE_wipeKey( void ); + +#endif /* NORTVARS_H */ + diff --git a/contrib/games/opentyrian/src/opentyr.c b/contrib/games/opentyrian/src/opentyr.c new file mode 100644 index 000000000..be0177f66 --- /dev/null +++ b/contrib/games/opentyrian/src/opentyr.c @@ -0,0 +1,392 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "opentyr.h" + +#include "config.h" +#include "destruct.h" +#include "editship.h" +#include "episodes.h" +#include "file.h" +#include "font.h" +#include "helptext.h" +#include "joystick.h" +#include "jukebox.h" +#include "keyboard.h" +#include "loudness.h" +#include "mainint.h" +#include "mtrand.h" +#include "musmast.h" +#include "network.h" +#include "nortsong.h" +#include "opentyrian_version.h" +#include "params.h" +#include "picload.h" +#include "scroller.h" +#include "setup.h" +#include "sprite.h" +#include "tyrian2.h" +#include "xmas.h" +#include "varz.h" +#include "vga256d.h" +#include "video.h" +#include "video_scale.h" + +#include "SDL.h" + +#include +#include +#include +#include +#include + +const char *opentyrian_str = "OpenTyrian"; +const char *opentyrian_version = OPENTYRIAN_VERSION; + +void opentyrian_menu( void ) +{ + typedef enum + { + MENU_ABOUT = 0, + MENU_FULLSCREEN, + MENU_SCALER, + // MENU_DESTRUCT, + MENU_JUKEBOX, + MENU_RETURN, + MenuOptions_MAX + } MenuOptions; + + static const char *menu_items[] = + { + "About OpenTyrian", + "Toggle Fullscreen", + "Scaler: None", + // "Play Destruct", + "Jukebox", + "Return to Main Menu", + }; + bool menu_items_disabled[] = + { + false, + !can_init_any_scaler(false) || !can_init_any_scaler(true), + false, + // false, + false, + false, + }; + + assert(COUNTOF(menu_items) == MenuOptions_MAX); + assert(COUNTOF(menu_items_disabled) == MenuOptions_MAX); + + fade_black(10); + JE_loadPic(VGAScreen, 13, false); + + draw_font_hv(VGAScreen, VGAScreen->w / 2, 5, opentyrian_str, large_font, centered, 15, -3); + + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); + + JE_showVGA(); + + play_song(36); // A Field for Mag + + MenuOptions sel = 0; + + uint temp_scaler = scaler; + + bool fade_in = true, quit = false; + do + { + memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->pitch * VGAScreen->h); + + for (MenuOptions i = 0; i < MenuOptions_MAX; i++) + { + const char *text = menu_items[i]; + char buffer[100]; + + if (i == MENU_SCALER) + { + snprintf(buffer, sizeof(buffer), "Scaler: %s", scalers[temp_scaler].name); + text = buffer; + } + + int y = i != MENU_RETURN ? i * 16 + 32 : 118; + draw_font_hv(VGAScreen, VGAScreen->w / 2, y, text, normal_font, centered, 15, menu_items_disabled[i] ? -8 : i != sel ? -4 : -2); + } + + JE_showVGA(); + + if (fade_in) + { + fade_in = false; + fade_palette(colors, 20, 0, 255); + wait_noinput(true, false, false); + } + + tempW = 0; + JE_textMenuWait(&tempW, false); + + if (newkey) + { + switch (lastkey_sym) + { + case SDLK_UP: + do + { + if (sel-- == 0) + sel = MenuOptions_MAX - 1; + } + while (menu_items_disabled[sel]); + + JE_playSampleNum(S_CURSOR); + break; + case SDLK_DOWN: + do + { + if (++sel >= MenuOptions_MAX) + sel = 0; + } + while (menu_items_disabled[sel]); + + JE_playSampleNum(S_CURSOR); + break; + + case SDLK_LEFT: + if (sel == MENU_SCALER) + { + do + { + if (temp_scaler == 0) + temp_scaler = scalers_count; + temp_scaler--; + } + while (!can_init_scaler(temp_scaler, fullscreen_enabled)); + + JE_playSampleNum(S_CURSOR); + } + break; + case SDLK_RIGHT: + if (sel == MENU_SCALER) + { + do + { + temp_scaler++; + if (temp_scaler == scalers_count) + temp_scaler = 0; + } + while (!can_init_scaler(temp_scaler, fullscreen_enabled)); + + JE_playSampleNum(S_CURSOR); + } + break; + + case SDLK_RETURN: + switch (sel) + { + case MENU_ABOUT: + JE_playSampleNum(S_SELECT); + + scroller_sine(about_text); + + memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->pitch * VGAScreen->h); + JE_showVGA(); + fade_in = true; + break; + + case MENU_FULLSCREEN: + JE_playSampleNum(S_SELECT); + + if (!init_scaler(scaler, !fullscreen_enabled) && // try new fullscreen state + !init_any_scaler(!fullscreen_enabled) && // try any scaler in new fullscreen state + !init_scaler(scaler, fullscreen_enabled)) // revert on fail + { + exit(EXIT_FAILURE); + } + set_palette(colors, 0, 255); // for switching between 8 bpp scalers + break; + + case MENU_SCALER: + JE_playSampleNum(S_SELECT); + + if (scaler != temp_scaler) + { + if (!init_scaler(temp_scaler, fullscreen_enabled) && // try new scaler + !init_scaler(temp_scaler, !fullscreen_enabled) && // try other fullscreen state + !init_scaler(scaler, fullscreen_enabled)) // revert on fail + { + exit(EXIT_FAILURE); + } + set_palette(colors, 0, 255); // for switching between 8 bpp scalers + } + break; + + case MENU_JUKEBOX: + JE_playSampleNum(S_SELECT); + + fade_black(10); + jukebox(); + + memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->pitch * VGAScreen->h); + JE_showVGA(); + fade_in = true; + break; + + case MENU_RETURN: + quit = true; + JE_playSampleNum(S_SPRING); + break; + + case MenuOptions_MAX: + assert(false); + break; + } + break; + + case SDLK_ESCAPE: + quit = true; + JE_playSampleNum(S_SPRING); + break; + + default: + break; + } + } + } while (!quit); +} + +int main( int argc, char *argv[] ) +{ + mt_srand(time(NULL)); + + printf("\nWelcome to... >> %s %s <<\n\n", opentyrian_str, opentyrian_version); + + printf("Copyright (C) 2007-2013 The OpenTyrian Development Team\n\n"); + + printf("This program comes with ABSOLUTELY NO WARRANTY.\n"); + printf("This is free software, and you are welcome to redistribute it\n"); + printf("under certain conditions. See the file GPL.txt for details.\n\n"); + + if (SDL_Init(0)) + { + printf("Failed to initialize SDL: %s\n", SDL_GetError()); + return -1; + } + + JE_loadConfiguration(); + + xmas = xmas_time(); // arg handler may override + + JE_paramCheck(argc, argv); + + JE_scanForEpisodes(); + + init_video(); + init_keyboard(); +#ifndef _KOLIBRI + init_joysticks(); + printf("assuming mouse detected\n"); // SDL can't tell us if there isn't one +#endif + + if (xmas && (!dir_file_exists(data_dir(), "tyrianc.shp") || !dir_file_exists(data_dir(), "voicesc.snd"))) + { + xmas = false; + + fprintf(stderr, "warning: Christmas is missing.\n"); + } + + JE_loadPals(); + JE_loadMainShapeTables(xmas ? "tyrianc.shp" : "tyrian.shp"); + + if (xmas && !xmas_prompt()) + { + xmas = false; + + free_main_shape_tables(); + JE_loadMainShapeTables("tyrian.shp"); + } + + + /* Default Options */ + youAreCheating = false; + smoothScroll = true; + loadDestruct = false; + + if (!audio_disabled) + { + printf("initializing SDL audio...\n"); + + init_audio(); + + load_music(); + + JE_loadSndFile("tyrian.snd", xmas ? "voicesc.snd" : "voices.snd"); + } + else + { + printf("audio disabled\n"); + } + + if (record_demo) + printf("demo recording enabled (input limited to keyboard)\n"); + + JE_loadExtraShapes(); /*Editship*/ + + JE_loadHelpText(); + /*debuginfo("Help text complete");*/ + + if (isNetworkGame) + { +#ifdef WITH_NETWORK + if (network_init()) + { + network_tyrian_halt(3, false); + } +#else + fprintf(stderr, "OpenTyrian was compiled without networking support."); + JE_tyrianHalt(5); +#endif + } + +#ifdef NDEBUG + if (!isNetworkGame) + intro_logos(); +#endif + + for (; ; ) + { + JE_initPlayerData(); + JE_sortHighScores(); + + if (JE_titleScreen(true)) + break; // user quit from title screen + + if (loadDestruct) + { + JE_destructGame(); + loadDestruct = false; + } + else + { + JE_main(); + } + } + + JE_tyrianHalt(0); + + return 0; +} + diff --git a/contrib/games/opentyrian/src/opentyr.h b/contrib/games/opentyrian/src/opentyr.h new file mode 100644 index 000000000..e1b52d32d --- /dev/null +++ b/contrib/games/opentyrian/src/opentyr.h @@ -0,0 +1,69 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef OPENTYR_H +#define OPENTYR_H + +#include "SDL_types.h" + +#include +#include +#include +#include +#include + +#define COUNTOF(x) ((unsigned)(sizeof(x) / sizeof *(x))) // use only on arrays! +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +#ifndef M_PI +#define M_PI 3.14159265358979323846 // pi +#endif +#ifndef M_PI_2 +#define M_PI_2 1.57079632679489661923 // pi/2 +#endif +#ifndef M_PI_4 +#define M_PI_4 0.78539816339744830962 // pi/4 +#endif + +typedef unsigned int uint; +typedef unsigned long ulong; + +// Pascal types, yuck. +typedef Sint32 JE_longint; +typedef Sint16 JE_integer; +typedef Sint8 JE_shortint; +typedef Uint16 JE_word; +typedef Uint8 JE_byte; +typedef bool JE_boolean; +typedef char JE_char; +typedef float JE_real; + +#define TYRIAN_VERSION "2.1" + +extern const char *opentyrian_str; +extern const char *opentyrian_version; + +void opentyrian_menu( void ); +#ifdef _KOLIBRI +extern void uSDL_SetWinCenter(uint w, uint h); +extern void uSDL_Delay(uint ms); +#endif + +#endif /* OPENTYR_H */ + diff --git a/contrib/games/opentyrian/src/opentyrian_version.h b/contrib/games/opentyrian/src/opentyrian_version.h new file mode 100644 index 000000000..543cfa46d --- /dev/null +++ b/contrib/games/opentyrian/src/opentyrian_version.h @@ -0,0 +1,8 @@ +#ifndef OPENTYRIAN_VERSION_H +#define OPENTYRIAN_VERSION_H + +#ifndef OPENTYRIAN_VERSION +#define OPENTYRIAN_VERSION "v2.1.???" +#endif + +#endif // OPENTYRIAN_VERSION_H diff --git a/contrib/games/opentyrian/src/opl.c b/contrib/games/opentyrian/src/opl.c new file mode 100644 index 000000000..f15474c4d --- /dev/null +++ b/contrib/games/opentyrian/src/opl.c @@ -0,0 +1,1613 @@ +/* + * Copyright (C) 2002-2010 The DOSBox Team + * OPL2/OPL3 emulation library + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + + +/* + * Originally based on ADLIBEMU.C, an AdLib/OPL2 emulation library by Ken Silverman + * Copyright (C) 1998-2001 Ken Silverman + * Ken Silverman's official web site: "http://www.advsys.net/ken" + */ +#include "opl.h" + +#include +#include +#include // rand() +#include // memset() + +#define fltype double + + /* + define attribution that inlines/forces inlining of a function (optional) + */ +#define OPL_INLINE inline + + +#undef NUM_CHANNELS +#if defined(OPLTYPE_IS_OPL3) +#define NUM_CHANNELS 18 +#else +#define NUM_CHANNELS 9 +#endif + +#define MAXOPERATORS (NUM_CHANNELS*2) + + +#define FL05 ((fltype)0.5) +#define FL2 ((fltype)2.0) +#define PI ((fltype)3.1415926535897932384626433832795) + + +#define FIXEDPT 0x10000 // fixed-point calculations using 16+16 +#define FIXEDPT_LFO 0x1000000 // fixed-point calculations using 8+24 + +#define WAVEPREC 1024 // waveform precision (10 bits) + +#define INTFREQU ((fltype)(14318180.0 / 288.0)) // clocking of the chip + + +#define OF_TYPE_ATT 0 +#define OF_TYPE_DEC 1 +#define OF_TYPE_REL 2 +#define OF_TYPE_SUS 3 +#define OF_TYPE_SUS_NOKEEP 4 +#define OF_TYPE_OFF 5 + +#define ARC_CONTROL 0x00 +#define ARC_TVS_KSR_MUL 0x20 +#define ARC_KSL_OUTLEV 0x40 +#define ARC_ATTR_DECR 0x60 +#define ARC_SUSL_RELR 0x80 +#define ARC_FREQ_NUM 0xa0 +#define ARC_KON_BNUM 0xb0 +#define ARC_PERC_MODE 0xbd +#define ARC_FEEDBACK 0xc0 +#define ARC_WAVE_SEL 0xe0 + +#define ARC_SECONDSET 0x100 // second operator set for OPL3 + + +#define OP_ACT_OFF 0x00 +#define OP_ACT_NORMAL 0x01 // regular channel activated (bitmasked) +#define OP_ACT_PERC 0x02 // percussion channel activated (bitmasked) + +#define BLOCKBUF_SIZE 512 + + + // vibrato constants +#define VIBTAB_SIZE 8 +#define VIBFAC 70/50000 // no braces, integer mul/div + + // tremolo constants and table +#define TREMTAB_SIZE 53 +#define TREM_FREQ ((fltype)(3.7)) // tremolo at 3.7hz + + + /* operator struct definition + For OPL2 all 9 channels consist of two operators each, carrier and modulator. + Channel x has operators x as modulator and operators (9+x) as carrier. + For OPL3 all 18 channels consist either of two operators (2op mode) or four + operators (4op mode) which is determined through register4 of the second + adlib register set. + Only the channels 0,1,2 (first set) and 9,10,11 (second set) can act as + 4op channels. The two additional operators for a channel y come from the + 2op channel y+3 so the operatorss y, (9+y), y+3, (9+y)+3 make up a 4op + channel. + */ +typedef struct operator_struct { + Bit32s cval, lastcval; // current output/last output (used for feedback) + Bit32u tcount, wfpos, tinc; // time (position in waveform) and time increment + fltype amp, step_amp; // and amplification (envelope) + fltype vol; // volume + fltype sustain_level; // sustain level + Bit32s mfbi; // feedback amount + fltype a0, a1, a2, a3; // attack rate function coefficients + fltype decaymul, releasemul; // decay/release rate functions + Bit32u op_state; // current state of operator (attack/decay/sustain/release/off) + Bit32u toff; + Bit32s freq_high; // highest three bits of the frequency, used for vibrato calculations + Bit16s* cur_wform; // start of selected waveform + Bit32u cur_wmask; // mask for selected waveform + Bit32u act_state; // activity state (regular, percussion) + bool sus_keep; // keep sustain level when decay finished + bool vibrato,tremolo; // vibrato/tremolo enable bits + + // variables used to provide non-continuous envelopes + Bit32u generator_pos; // for non-standard sample rates we need to determine how many samples have passed + Bits cur_env_step; // current (standardized) sample position + Bits env_step_a,env_step_d,env_step_r; // number of std samples of one step (for attack/decay/release mode) + Bit8u step_skip_pos_a; // position of 8-cyclic step skipping (always 2^x to check against mask) + Bits env_step_skip_a; // bitmask that determines if a step is skipped (respective bit is zero then) + +#if defined(OPLTYPE_IS_OPL3) + bool is_4op,is_4op_attached; // base of a 4op channel/part of a 4op channel + Bit32s left_pan,right_pan; // opl3 stereo panning amount +#endif +} op_type; + +// per-chip variables +static op_type op[MAXOPERATORS]; + +static Bits int_samplerate; + +static Bit8u status; +static Bit32u opl_index; +#if defined(OPLTYPE_IS_OPL3) +static Bit8u adlibreg[512]; // adlib register set (including second set) +static Bit8u wave_sel[44]; // waveform selection +#else +static Bit8u adlibreg[256]; // adlib register set +static Bit8u wave_sel[22]; // waveform selection +#endif + + + // vibrato/tremolo increment/counter +static Bit32u vibtab_pos; +static Bit32u vibtab_add; +static Bit32u tremtab_pos; +static Bit32u tremtab_add; + + +// enable an operator +void enable_operator(Bitu regbase, op_type* op_pt, Bit32u act_type); + +// functions to change parameters of an operator +void change_frequency(Bitu chanbase, Bitu regbase, op_type* op_pt); + +void change_attackrate(Bitu regbase, op_type* op_pt); +void change_decayrate(Bitu regbase, op_type* op_pt); +void change_releaserate(Bitu regbase, op_type* op_pt); +void change_sustainlevel(Bitu regbase, op_type* op_pt); +void change_waveform(Bitu regbase, op_type* op_pt); +void change_keepsustain(Bitu regbase, op_type* op_pt); +void change_vibrato(Bitu regbase, op_type* op_pt); +void change_feedback(Bitu chanbase, op_type* op_pt); + + +static Bit32u generator_add; // should be a chip parameter + +static fltype recipsamp; // inverse of sampling rate +static Bit16s wavtable[WAVEPREC*3]; // wave form table + +// vibrato/tremolo tables +static Bit32s vib_table[VIBTAB_SIZE]; +static Bit32s trem_table[TREMTAB_SIZE*2]; + +static Bit32s vibval_const[BLOCKBUF_SIZE]; +static Bit32s tremval_const[BLOCKBUF_SIZE]; + +// vibrato value tables (used per-operator) +static Bit32s vibval_var1[BLOCKBUF_SIZE]; +static Bit32s vibval_var2[BLOCKBUF_SIZE]; +//static Bit32s vibval_var3[BLOCKBUF_SIZE]; +//static Bit32s vibval_var4[BLOCKBUF_SIZE]; + +// vibrato/trmolo value table pointers +static Bit32s *vibval1, *vibval2, *vibval3, *vibval4; +static Bit32s *tremval1, *tremval2, *tremval3, *tremval4; + + +// key scale level lookup table +static const fltype kslmul[4] = { + 0.0, 0.5, 0.25, 1.0 // -> 0, 3, 1.5, 6 dB/oct +}; + +// frequency multiplicator lookup table +static const fltype frqmul_tab[16] = { + 0.5,1,2,3,4,5,6,7,8,9,10,10,12,12,15,15 +}; +// calculated frequency multiplication values (depend on sampling rate) +static fltype frqmul[16]; + +// key scale levels +static Bit8u kslev[8][16]; + +// map a channel number to the register offset of the modulator (=register base) +static const Bit8u modulatorbase[9] = { + 0,1,2, + 8,9,10, + 16,17,18 +}; + +// map a register base to a modulator operator number or operator number +#if defined(OPLTYPE_IS_OPL3) +static const Bit8u regbase2modop[44] = { + 0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8, // first set + 18,19,20,18,19,20,0,0,21,22,23,21,22,23,0,0,24,25,26,24,25,26 // second set +}; +static const Bit8u regbase2op[44] = { + 0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17, // first set + 18,19,20,27,28,29,0,0,21,22,23,30,31,32,0,0,24,25,26,33,34,35 // second set +}; +#else +static const Bit8u regbase2modop[22] = { + 0,1,2,0,1,2,0,0,3,4,5,3,4,5,0,0,6,7,8,6,7,8 +}; +static const Bit8u regbase2op[22] = { + 0,1,2,9,10,11,0,0,3,4,5,12,13,14,0,0,6,7,8,15,16,17 +}; +#endif + + +// start of the waveform +static Bit32u waveform[8] = { + WAVEPREC, + WAVEPREC>>1, + WAVEPREC, + (WAVEPREC*3)>>2, + 0, + 0, + (WAVEPREC*5)>>2, + WAVEPREC<<1 +}; + +// length of the waveform as mask +static Bit32u wavemask[8] = { + WAVEPREC-1, + WAVEPREC-1, + (WAVEPREC>>1)-1, + (WAVEPREC>>1)-1, + WAVEPREC-1, + ((WAVEPREC*3)>>2)-1, + WAVEPREC>>1, + WAVEPREC-1 +}; + +// where the first entry resides +static Bit32u wavestart[8] = { + 0, + WAVEPREC>>1, + 0, + WAVEPREC>>2, + 0, + 0, + 0, + WAVEPREC>>3 +}; + +// envelope generator function constants +static fltype attackconst[4] = { + (fltype)(1/2.82624), + (fltype)(1/2.25280), + (fltype)(1/1.88416), + (fltype)(1/1.59744) +}; +static fltype decrelconst[4] = { + (fltype)(1/39.28064), + (fltype)(1/31.41608), + (fltype)(1/26.17344), + (fltype)(1/22.44608) +}; + + +void operator_advance(op_type* op_pt, Bit32s vib) { + op_pt->wfpos = op_pt->tcount; // waveform position + + // advance waveform time + op_pt->tcount += op_pt->tinc; + op_pt->tcount += (Bit32s)(op_pt->tinc)*vib/FIXEDPT; + + op_pt->generator_pos += generator_add; +} + +void operator_advance_drums(op_type* op_pt1, Bit32s vib1, op_type* op_pt2, Bit32s vib2, op_type* op_pt3, Bit32s vib3) { + Bit32u c1 = op_pt1->tcount/FIXEDPT; + Bit32u c3 = op_pt3->tcount/FIXEDPT; + Bit32u phasebit = (((c1 & 0x88) ^ ((c1<<5) & 0x80)) | ((c3 ^ (c3<<2)) & 0x20)) ? 0x02 : 0x00; + + Bit32u noisebit = rand()&1; + + Bit32u snare_phase_bit = (((Bitu)((op_pt1->tcount/FIXEDPT) / 0x100))&1); + + //Hihat + Bit32u inttm = (phasebit<<8) | (0x34<<(phasebit ^ (noisebit<<1))); + op_pt1->wfpos = inttm*FIXEDPT; // waveform position + // advance waveform time + op_pt1->tcount += op_pt1->tinc; + op_pt1->tcount += (Bit32s)(op_pt1->tinc)*vib1/FIXEDPT; + op_pt1->generator_pos += generator_add; + + //Snare + inttm = ((1+snare_phase_bit) ^ noisebit)<<8; + op_pt2->wfpos = inttm*FIXEDPT; // waveform position + // advance waveform time + op_pt2->tcount += op_pt2->tinc; + op_pt2->tcount += (Bit32s)(op_pt2->tinc)*vib2/FIXEDPT; + op_pt2->generator_pos += generator_add; + + //Cymbal + inttm = (1+phasebit)<<8; + op_pt3->wfpos = inttm*FIXEDPT; // waveform position + // advance waveform time + op_pt3->tcount += op_pt3->tinc; + op_pt3->tcount += (Bit32s)(op_pt3->tinc)*vib3/FIXEDPT; + op_pt3->generator_pos += generator_add; +} + + +// output level is sustained, mode changes only when operator is turned off (->release) +// or when the keep-sustained bit is turned off (->sustain_nokeep) +void operator_output(op_type* op_pt, Bit32s modulator, Bit32s trem) { + if (op_pt->op_state != OF_TYPE_OFF) { + op_pt->lastcval = op_pt->cval; + Bit32u i = (Bit32u)((op_pt->wfpos+modulator)/FIXEDPT); + + // wform: -16384 to 16383 (0x4000) + // trem : 32768 to 65535 (0x10000) + // step_amp: 0.0 to 1.0 + // vol : 1/2^14 to 1/2^29 (/0x4000; /1../0x8000) + + op_pt->cval = (Bit32s)(op_pt->step_amp*op_pt->vol*op_pt->cur_wform[i&op_pt->cur_wmask]*trem/16.0); + } +} + + +// no action, operator is off +void operator_off(op_type* op_pt) { + (void) op_pt; +} + +// output level is sustained, mode changes only when operator is turned off (->release) +// or when the keep-sustained bit is turned off (->sustain_nokeep) +void operator_sustain(op_type* op_pt) { + Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + for (Bit32u ct=0; ctcur_env_step++; + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +// operator in release mode, if output level reaches zero the operator is turned off +void operator_release(op_type* op_pt) { + // ??? boundary? + if (op_pt->amp > 0.00000001) { + // release phase + op_pt->amp *= op_pt->releasemul; + } + + Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + for (Bit32u ct=0; ctcur_env_step++; // sample counter + if ((op_pt->cur_env_step & op_pt->env_step_r)==0) { + if (op_pt->amp <= 0.00000001) { + // release phase finished, turn off this operator + op_pt->amp = 0.0; + if (op_pt->op_state == OF_TYPE_REL) { + op_pt->op_state = OF_TYPE_OFF; + } + } + op_pt->step_amp = op_pt->amp; + } + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +// operator in decay mode, if sustain level is reached the output level is either +// kept (sustain level keep enabled) or the operator is switched into release mode +void operator_decay(op_type* op_pt) { + if (op_pt->amp > op_pt->sustain_level) { + // decay phase + op_pt->amp *= op_pt->decaymul; + } + + Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + for (Bit32u ct=0; ctcur_env_step++; + if ((op_pt->cur_env_step & op_pt->env_step_d)==0) { + if (op_pt->amp <= op_pt->sustain_level) { + // decay phase finished, sustain level reached + if (op_pt->sus_keep) { + // keep sustain level (until turned off) + op_pt->op_state = OF_TYPE_SUS; + op_pt->amp = op_pt->sustain_level; + } else { + // next: release phase + op_pt->op_state = OF_TYPE_SUS_NOKEEP; + } + } + op_pt->step_amp = op_pt->amp; + } + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + +// operator in attack mode, if full output level is reached, +// the operator is switched into decay mode +void operator_attack(op_type* op_pt) { + op_pt->amp = ((op_pt->a3*op_pt->amp + op_pt->a2)*op_pt->amp + op_pt->a1)*op_pt->amp + op_pt->a0; + + Bit32u num_steps_add = op_pt->generator_pos/FIXEDPT; // number of (standardized) samples + for (Bit32u ct=0; ctcur_env_step++; // next sample + if ((op_pt->cur_env_step & op_pt->env_step_a)==0) { // check if next step already reached + if (op_pt->amp > 1.0) { + // attack phase finished, next: decay + op_pt->op_state = OF_TYPE_DEC; + op_pt->amp = 1.0; + op_pt->step_amp = 1.0; + } + op_pt->step_skip_pos_a <<= 1; + if (op_pt->step_skip_pos_a==0) op_pt->step_skip_pos_a = 1; + if (op_pt->step_skip_pos_a & op_pt->env_step_skip_a) { // check if required to skip next step + op_pt->step_amp = op_pt->amp; + } + } + } + op_pt->generator_pos -= num_steps_add*FIXEDPT; +} + + +typedef void (*optype_fptr)(op_type*); + +optype_fptr opfuncs[6] = { + operator_attack, + operator_decay, + operator_release, + operator_sustain, // sustain phase (keeping level) + operator_release, // sustain_nokeep phase (release-style) + operator_off +}; + +void change_attackrate(Bitu regbase, op_type* op_pt) { + Bits attackrate = adlibreg[ARC_ATTR_DECR+regbase]>>4; + if (attackrate) { + fltype f = (fltype)(pow(FL2,(fltype)attackrate+(op_pt->toff>>2)-1)*attackconst[op_pt->toff&3]*recipsamp); + // attack rate coefficients + op_pt->a0 = (fltype)(0.0377*f); + op_pt->a1 = (fltype)(10.73*f+1); + op_pt->a2 = (fltype)(-17.57*f); + op_pt->a3 = (fltype)(7.42*f); + + Bits step_skip = attackrate*4 + op_pt->toff; + Bits steps = step_skip >> 2; + op_pt->env_step_a = (1<<(steps<=12?12-steps:0))-1; + + Bits step_num = (step_skip<=48)?(4-(step_skip&3)):0; + static Bit8u step_skip_mask[5] = {0xff, 0xfe, 0xee, 0xba, 0xaa}; + op_pt->env_step_skip_a = step_skip_mask[step_num]; + +#if defined(OPLTYPE_IS_OPL3) + if (step_skip>=60) { +#else + if (step_skip>=62) { +#endif + op_pt->a0 = (fltype)(2.0); // something that triggers an immediate transition to amp:=1.0 + op_pt->a1 = (fltype)(0.0); + op_pt->a2 = (fltype)(0.0); + op_pt->a3 = (fltype)(0.0); + } + } else { + // attack disabled + op_pt->a0 = 0.0; + op_pt->a1 = 1.0; + op_pt->a2 = 0.0; + op_pt->a3 = 0.0; + op_pt->env_step_a = 0; + op_pt->env_step_skip_a = 0; + } +} + +void change_decayrate(Bitu regbase, op_type* op_pt) { + Bits decayrate = adlibreg[ARC_ATTR_DECR+regbase]&15; + // decaymul should be 1.0 when decayrate==0 + if (decayrate) { + fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*recipsamp); + op_pt->decaymul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(decayrate+(op_pt->toff>>2))))); + Bits steps = (decayrate*4 + op_pt->toff) >> 2; + op_pt->env_step_d = (1<<(steps<=12?12-steps:0))-1; + } else { + op_pt->decaymul = 1.0; + op_pt->env_step_d = 0; + } +} + +void change_releaserate(Bitu regbase, op_type* op_pt) { + Bits releaserate = adlibreg[ARC_SUSL_RELR+regbase]&15; + // releasemul should be 1.0 when releaserate==0 + if (releaserate) { + fltype f = (fltype)(-7.4493*decrelconst[op_pt->toff&3]*recipsamp); + op_pt->releasemul = (fltype)(pow(FL2,f*pow(FL2,(fltype)(releaserate+(op_pt->toff>>2))))); + Bits steps = (releaserate*4 + op_pt->toff) >> 2; + op_pt->env_step_r = (1<<(steps<=12?12-steps:0))-1; + } else { + op_pt->releasemul = 1.0; + op_pt->env_step_r = 0; + } +} + +void change_sustainlevel(Bitu regbase, op_type* op_pt) { + Bits sustainlevel = adlibreg[ARC_SUSL_RELR+regbase]>>4; + // sustainlevel should be 0.0 when sustainlevel==15 (max) + if (sustainlevel<15) { + op_pt->sustain_level = (fltype)(pow(FL2,(fltype)sustainlevel * (-FL05))); + } else { + op_pt->sustain_level = 0.0; + } +} + +void change_waveform(Bitu regbase, op_type* op_pt) { +#if defined(OPLTYPE_IS_OPL3) + if (regbase>=ARC_SECONDSET) regbase -= (ARC_SECONDSET-22); // second set starts at 22 +#endif + // waveform selection + op_pt->cur_wmask = wavemask[wave_sel[regbase]]; + op_pt->cur_wform = &wavtable[waveform[wave_sel[regbase]]]; + // (might need to be adapted to waveform type here...) +} + +void change_keepsustain(Bitu regbase, op_type* op_pt) { + op_pt->sus_keep = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x20)>0; + if (op_pt->op_state==OF_TYPE_SUS) { + if (!op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS_NOKEEP; + } else if (op_pt->op_state==OF_TYPE_SUS_NOKEEP) { + if (op_pt->sus_keep) op_pt->op_state = OF_TYPE_SUS; + } +} + +// enable/disable vibrato/tremolo LFO effects +void change_vibrato(Bitu regbase, op_type* op_pt) { + op_pt->vibrato = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x40)!=0; + op_pt->tremolo = (adlibreg[ARC_TVS_KSR_MUL+regbase]&0x80)!=0; +} + +// change amount of self-feedback +void change_feedback(Bitu chanbase, op_type* op_pt) { + Bits feedback = adlibreg[ARC_FEEDBACK+chanbase]&14; + if (feedback) op_pt->mfbi = (Bit32s)(pow(FL2,(fltype)((feedback>>1)+8))); + else op_pt->mfbi = 0; +} + +void change_frequency(Bitu chanbase, Bitu regbase, op_type* op_pt) { + // frequency + Bit32u frn = ((((Bit32u)adlibreg[ARC_KON_BNUM+chanbase])&3)<<8) + (Bit32u)adlibreg[ARC_FREQ_NUM+chanbase]; + // block number/octave + Bit32u oct = ((((Bit32u)adlibreg[ARC_KON_BNUM+chanbase])>>2)&7); + op_pt->freq_high = (Bit32s)((frn>>7)&7); + + // keysplit + Bit32u note_sel = (adlibreg[8]>>6)&1; + op_pt->toff = ((frn>>9)&(note_sel^1)) | ((frn>>8)¬e_sel); + op_pt->toff += (oct<<1); + + // envelope scaling (KSR) + if (!(adlibreg[ARC_TVS_KSR_MUL+regbase]&0x10)) op_pt->toff >>= 2; + + // 20+a0+b0: + op_pt->tinc = (Bit32u)((((fltype)(frn<>6]*kslev[oct][frn>>6]); + op_pt->vol = (fltype)(pow(FL2,(fltype)(vol_in * -0.125 - 14))); + + // operator frequency changed, care about features that depend on it + change_attackrate(regbase,op_pt); + change_decayrate(regbase,op_pt); + change_releaserate(regbase,op_pt); +} + +void enable_operator(Bitu regbase, op_type* op_pt, Bit32u act_type) { + // check if this is really an off-on transition + if (op_pt->act_state == OP_ACT_OFF) { + Bits wselbase = regbase; + if (wselbase>=ARC_SECONDSET) wselbase -= (ARC_SECONDSET-22); // second set starts at 22 + + op_pt->tcount = wavestart[wave_sel[wselbase]]*FIXEDPT; + + // start with attack mode + op_pt->op_state = OF_TYPE_ATT; + op_pt->act_state |= act_type; + } +} + +void disable_operator(op_type* op_pt, Bit32u act_type) { + // check if this is really an on-off transition + if (op_pt->act_state != OP_ACT_OFF) { + op_pt->act_state &= (~act_type); + if (op_pt->act_state == OP_ACT_OFF) { + if (op_pt->op_state != OF_TYPE_OFF) op_pt->op_state = OF_TYPE_REL; + } + } +} + +void adlib_init(Bit32u samplerate) { + Bits i, j, oct; + + int_samplerate = samplerate; + + generator_add = (Bit32u)(INTFREQU*FIXEDPT/int_samplerate); + + + memset((void *)adlibreg,0,sizeof(adlibreg)); + memset((void *)op,0,sizeof(op_type)*MAXOPERATORS); + memset((void *)wave_sel,0,sizeof(wave_sel)); + + for (i=0;i=0;i--) { + frqmul[i] = (fltype)(frqmul_tab[i]*INTFREQU/(fltype)WAVEPREC*(fltype)FIXEDPT*recipsamp); + } + + status = 0; + opl_index = 0; + + + // create vibrato table + vib_table[0] = 8; + vib_table[1] = 4; + vib_table[2] = 0; + vib_table[3] = -4; + for (i=4; i -0.5/6 to 0) + for (i=14; i<41; i++) trem_table_int[i] = -i+14; // downwards (26 to 0 -> 0 to -1/6) + for (i=41; i<53; i++) trem_table_int[i] = i-40-26; // upwards (1 to 12 -> -1/6 to -0.5/6) + + for (i=0; i>1);i++) { + wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1) )*PI*2/WAVEPREC)); + wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<1)+1)*PI*2/WAVEPREC)); + wavtable[i] = wavtable[(i<<1) +WAVEPREC]; + // alternative: (zero-less) +/* wavtable[(i<<1) +WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+1)*PI/WAVEPREC)); + wavtable[(i<<1)+1+WAVEPREC] = (Bit16s)(16384*sin((fltype)((i<<2)+3)*PI/WAVEPREC)); + wavtable[i] = wavtable[(i<<1)-1+WAVEPREC]; */ + } + for (i=0;i<(WAVEPREC>>3);i++) { + wavtable[i+(WAVEPREC<<1)] = wavtable[i+(WAVEPREC>>3)]-16384; + wavtable[i+((WAVEPREC*17)>>3)] = wavtable[i+(WAVEPREC>>2)]+16384; + } + + // key scale level table verified ([table in book]*8/3) + kslev[7][0] = 0; kslev[7][1] = 24; kslev[7][2] = 32; kslev[7][3] = 37; + kslev[7][4] = 40; kslev[7][5] = 43; kslev[7][6] = 45; kslev[7][7] = 47; + kslev[7][8] = 48; + for (i=9;i<16;i++) kslev[7][i] = (Bit8u)(i+41); + for (j=6;j>=0;j--) { + for (i=0;i<16;i++) { + oct = (Bits)kslev[j+1][i]-8; + if (oct < 0) oct = 0; + kslev[j][i] = (Bit8u)oct; + } + } + } + +} + + + +void adlib_write(Bitu idx, Bit8u val) { + Bit32u second_set = idx&0x100; + adlibreg[idx] = val; + + switch (idx&0xf0) { + case ARC_CONTROL: + // here we check for the second set registers, too: + switch (idx) { + case 0x02: // timer1 counter + case 0x03: // timer2 counter + break; + case 0x04: + // IRQ reset, timer mask/start + if (val&0x80) { + // clear IRQ bits in status register + status &= ~0x60; + } else { + status = 0; + } + break; +#if defined(OPLTYPE_IS_OPL3) + case 0x04|ARC_SECONDSET: + // 4op enable/disable switches for each possible channel + op[0].is_4op = (val&1)>0; + op[3].is_4op_attached = op[0].is_4op; + op[1].is_4op = (val&2)>0; + op[4].is_4op_attached = op[1].is_4op; + op[2].is_4op = (val&4)>0; + op[5].is_4op_attached = op[2].is_4op; + op[18].is_4op = (val&8)>0; + op[21].is_4op_attached = op[18].is_4op; + op[19].is_4op = (val&16)>0; + op[22].is_4op_attached = op[19].is_4op; + op[20].is_4op = (val&32)>0; + op[23].is_4op_attached = op[20].is_4op; + break; + case 0x05|ARC_SECONDSET: + break; +#endif + case 0x08: + // CSW, note select + break; + default: + break; + } + break; + case ARC_TVS_KSR_MUL: + case ARC_TVS_KSR_MUL+0x10: { + // tremolo/vibrato/sustain keeping enabled; key scale rate; frequency multiplication + int num = idx&7; + Bitu base = (idx-ARC_TVS_KSR_MUL)&0xff; + if ((num<6) && (base<22)) { + Bitu modop = regbase2modop[second_set?(base+22):base]; + Bitu regbase = base+second_set; + Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop; + + // change tremolo/vibrato and sustain keeping of this operator + op_type* op_ptr = &op[modop+((num<3) ? 0 : 9)]; + change_keepsustain(regbase,op_ptr); + change_vibrato(regbase,op_ptr); + + // change frequency calculations of this operator as + // key scale rate and frequency multiplicator can be changed +#if defined(OPLTYPE_IS_OPL3) + if ((adlibreg[0x105]&1) && (op[modop].is_4op_attached)) { + // operator uses frequency of channel + change_frequency(chanbase-3,regbase,op_ptr); + } else { + change_frequency(chanbase,regbase,op_ptr); + } +#else + change_frequency(chanbase,base,op_ptr); +#endif + } + } + break; + case ARC_KSL_OUTLEV: + case ARC_KSL_OUTLEV+0x10: { + // key scale level; output rate + int num = idx&7; + Bitu base = (idx-ARC_KSL_OUTLEV)&0xff; + if ((num<6) && (base<22)) { + Bitu modop = regbase2modop[second_set?(base+22):base]; + Bitu chanbase = second_set?(modop-18+ARC_SECONDSET):modop; + + // change frequency calculations of this operator as + // key scale level and output rate can be changed + op_type* op_ptr = &op[modop+((num<3) ? 0 : 9)]; +#if defined(OPLTYPE_IS_OPL3) + Bitu regbase = base+second_set; + if ((adlibreg[0x105]&1) && (op[modop].is_4op_attached)) { + // operator uses frequency of channel + change_frequency(chanbase-3,regbase,op_ptr); + } else { + change_frequency(chanbase,regbase,op_ptr); + } +#else + change_frequency(chanbase,base,op_ptr); +#endif + } + } + break; + case ARC_ATTR_DECR: + case ARC_ATTR_DECR+0x10: { + // attack/decay rates + int num = idx&7; + Bitu base = (idx-ARC_ATTR_DECR)&0xff; + if ((num<6) && (base<22)) { + Bitu regbase = base+second_set; + + // change attack rate and decay rate of this operator + op_type* op_ptr = &op[regbase2op[second_set?(base+22):base]]; + change_attackrate(regbase,op_ptr); + change_decayrate(regbase,op_ptr); + } + } + break; + case ARC_SUSL_RELR: + case ARC_SUSL_RELR+0x10: { + // sustain level; release rate + int num = idx&7; + Bitu base = (idx-ARC_SUSL_RELR)&0xff; + if ((num<6) && (base<22)) { + Bitu regbase = base+second_set; + + // change sustain level and release rate of this operator + op_type* op_ptr = &op[regbase2op[second_set?(base+22):base]]; + change_releaserate(regbase,op_ptr); + change_sustainlevel(regbase,op_ptr); + } + } + break; + case ARC_FREQ_NUM: { + // 0xa0-0xa8 low8 frequency + Bitu base = (idx-ARC_FREQ_NUM)&0xff; + if (base<9) { + Bits opbase = second_set?(base+18):base; +#if defined(OPLTYPE_IS_OPL3) + if ((adlibreg[0x105]&1) && op[opbase].is_4op_attached) break; +#endif + // regbase of modulator: + Bits modbase = modulatorbase[base]+second_set; + + Bitu chanbase = base+second_set; + + change_frequency(chanbase,modbase,&op[opbase]); + change_frequency(chanbase,modbase+3,&op[opbase+9]); +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are modified to the frequency of the channel + if ((adlibreg[0x105]&1) && op[second_set?(base+18):base].is_4op) { + change_frequency(chanbase,modbase+8,&op[opbase+3]); + change_frequency(chanbase,modbase+3+8,&op[opbase+3+9]); + } +#endif + } + } + break; + case ARC_KON_BNUM: { + if (idx == ARC_PERC_MODE) { +#if defined(OPLTYPE_IS_OPL3) + if (second_set) return; +#endif + + if ((val&0x30) == 0x30) { // BassDrum active + enable_operator(16,&op[6],OP_ACT_PERC); + change_frequency(6,16,&op[6]); + enable_operator(16+3,&op[6+9],OP_ACT_PERC); + change_frequency(6,16+3,&op[6+9]); + } else { + disable_operator(&op[6],OP_ACT_PERC); + disable_operator(&op[6+9],OP_ACT_PERC); + } + if ((val&0x28) == 0x28) { // Snare active + enable_operator(17+3,&op[16],OP_ACT_PERC); + change_frequency(7,17+3,&op[16]); + } else { + disable_operator(&op[16],OP_ACT_PERC); + } + if ((val&0x24) == 0x24) { // TomTom active + enable_operator(18,&op[8],OP_ACT_PERC); + change_frequency(8,18,&op[8]); + } else { + disable_operator(&op[8],OP_ACT_PERC); + } + if ((val&0x22) == 0x22) { // Cymbal active + enable_operator(18+3,&op[8+9],OP_ACT_PERC); + change_frequency(8,18+3,&op[8+9]); + } else { + disable_operator(&op[8+9],OP_ACT_PERC); + } + if ((val&0x21) == 0x21) { // Hihat active + enable_operator(17,&op[7],OP_ACT_PERC); + change_frequency(7,17,&op[7]); + } else { + disable_operator(&op[7],OP_ACT_PERC); + } + + break; + } + // regular 0xb0-0xb8 + Bitu base = (idx-ARC_KON_BNUM)&0xff; + if (base<9) { + Bits opbase = second_set?(base+18):base; +#if defined(OPLTYPE_IS_OPL3) + if ((adlibreg[0x105]&1) && op[opbase].is_4op_attached) break; +#endif + // regbase of modulator: + Bits modbase = modulatorbase[base]+second_set; + + if (val&32) { + // operator switched on + enable_operator(modbase,&op[opbase],OP_ACT_NORMAL); // modulator (if 2op) + enable_operator(modbase+3,&op[opbase+9],OP_ACT_NORMAL); // carrier (if 2op) +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are switched on + if ((adlibreg[0x105]&1) && op[opbase].is_4op) { + // turn on chan+3 operators as well + enable_operator(modbase+8,&op[opbase+3],OP_ACT_NORMAL); + enable_operator(modbase+3+8,&op[opbase+3+9],OP_ACT_NORMAL); + } +#endif + } else { + // operator switched off + disable_operator(&op[opbase],OP_ACT_NORMAL); + disable_operator(&op[opbase+9],OP_ACT_NORMAL); +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are switched off + if ((adlibreg[0x105]&1) && op[opbase].is_4op) { + // turn off chan+3 operators as well + disable_operator(&op[opbase+3],OP_ACT_NORMAL); + disable_operator(&op[opbase+3+9],OP_ACT_NORMAL); + } +#endif + } + + Bitu chanbase = base+second_set; + + // change frequency calculations of modulator and carrier (2op) as + // the frequency of the channel has changed + change_frequency(chanbase,modbase,&op[opbase]); + change_frequency(chanbase,modbase+3,&op[opbase+9]); +#if defined(OPLTYPE_IS_OPL3) + // for 4op channels all four operators are modified to the frequency of the channel + if ((adlibreg[0x105]&1) && op[second_set?(base+18):base].is_4op) { + // change frequency calculations of chan+3 operators as well + change_frequency(chanbase,modbase+8,&op[opbase+3]); + change_frequency(chanbase,modbase+3+8,&op[opbase+3+9]); + } +#endif + } + } + break; + case ARC_FEEDBACK: { + // 0xc0-0xc8 feedback/modulation type (AM/FM) + Bitu base = (idx-ARC_FEEDBACK)&0xff; + if (base<9) { + Bits opbase = second_set?(base+18):base; + Bitu chanbase = base+second_set; + change_feedback(chanbase,&op[opbase]); +#if defined(OPLTYPE_IS_OPL3) + // OPL3 panning + op[opbase].left_pan = ((val&0x10)>>4); + op[opbase].right_pan = ((val&0x20)>>5); +#endif + } + } + break; + case ARC_WAVE_SEL: + case ARC_WAVE_SEL+0x10: { + int num = idx&7; + Bitu base = (idx-ARC_WAVE_SEL)&0xff; + if ((num<6) && (base<22)) { +#if defined(OPLTYPE_IS_OPL3) + Bits wselbase = second_set?(base+22):base; // for easier mapping onto wave_sel[] + // change waveform + if (adlibreg[0x105]&1) wave_sel[wselbase] = val&7; // opl3 mode enabled, all waveforms accessible + else wave_sel[wselbase] = val&3; + op_type* op_ptr = &op[regbase2modop[wselbase]+((num<3) ? 0 : 9)]; + change_waveform(wselbase,op_ptr); +#else + if (adlibreg[0x01]&0x20) { + // wave selection enabled, change waveform + wave_sel[base] = val&3; + op_type* op_ptr = &op[regbase2modop[base]+((num<3) ? 0 : 9)]; + change_waveform(base,op_ptr); + } +#endif + } + } + break; + default: + break; + } +} + + +Bitu adlib_reg_read(Bitu port) { +#if defined(OPLTYPE_IS_OPL3) + // opl3-detection routines require ret&6 to be zero + if ((port&1)==0) { + return status; + } + return 0x00; +#else + // opl2-detection routines require ret&6 to be 6 + if ((port&1)==0) { + return status|6; + } + return 0xff; +#endif +} + +void adlib_write_index(Bitu port, Bit8u val) { + (void) port; + opl_index = val; +#if defined(OPLTYPE_IS_OPL3) + if ((port&3)!=0) { + // possibly second set + if (((adlibreg[0x105]&1)!=0) || (opl_index==5)) opl_index |= ARC_SECONDSET; + } +#endif +} + +OPL_INLINE static void clipit16(Bit32s ival, Bit16s* outval) { + if (ival<32768) { + if (ival>-32769) { + *outval=(Bit16s)ival; + } else { + *outval = -32768; + } + } else { + *outval = 32767; + } +} + + + +// be careful with this +// uses cptr and chanval, outputs into outbufl(/outbufr) +// for opl3 check if opl3-mode is enabled (which uses stereo panning) +#undef CHANVAL_OUT +#if defined(OPLTYPE_IS_OPL3) +#define CHANVAL_OUT \ + if (adlibreg[0x105]&1) { \ + outbufl[i] += chanval*cptr[0].left_pan; \ + outbufr[i] += chanval*cptr[0].right_pan; \ + } else { \ + outbufl[i] += chanval; \ + } +#else +#define CHANVAL_OUT \ + outbufl[i] += chanval; +#endif + +void adlib_getsample(Bit16s* sndptr, Bits numsamples) { + Bits i, endsamples; + op_type* cptr; + + Bit32s outbufl[BLOCKBUF_SIZE]; +#if defined(OPLTYPE_IS_OPL3) + // second output buffer (right channel for opl3 stereo) + Bit32s outbufr[BLOCKBUF_SIZE]; +#endif + + // vibrato/tremolo lookup tables (global, to possibly be used by all operators) + Bit32s vib_lut[BLOCKBUF_SIZE]; + Bit32s trem_lut[BLOCKBUF_SIZE]; + + Bits samples_to_process = numsamples; + + for (Bits cursmp=0; cursmpBLOCKBUF_SIZE) endsamples = BLOCKBUF_SIZE; + + memset((void*)&outbufl,0,endsamples*sizeof(Bit32s)); +#if defined(OPLTYPE_IS_OPL3) + // clear second output buffer (opl3 stereo) + if (adlibreg[0x105]&1) memset((void*)&outbufr,0,endsamples*sizeof(Bit32s)); +#endif + + // calculate vibrato/tremolo lookup tables + Bit32s vib_tshift = ((adlibreg[ARC_PERC_MODE]&0x40)==0) ? 1 : 0; // 14cents/7cents switching + for (i=0;i=VIBTAB_SIZE) vibtab_pos-=VIBTAB_SIZE*FIXEDPT_LFO; + vib_lut[i] = vib_table[vibtab_pos/FIXEDPT_LFO]>>vib_tshift; // 14cents (14/100 of a semitone) or 7cents + + // cycle through tremolo table + tremtab_pos += tremtab_add; + if (tremtab_pos/FIXEDPT_LFO>=TREMTAB_SIZE) tremtab_pos-=TREMTAB_SIZE*FIXEDPT_LFO; + if (adlibreg[ARC_PERC_MODE]&0x80) trem_lut[i] = trem_table[tremtab_pos/FIXEDPT_LFO]; + else trem_lut[i] = trem_table[TREMTAB_SIZE+tremtab_pos/FIXEDPT_LFO]; + } + + if (adlibreg[ARC_PERC_MODE]&0x20) { + //BassDrum + cptr = &op[6]; + if (adlibreg[ARC_FEEDBACK+6]&1) { + // additive synthesis + if (cptr[9].op_state != OF_TYPE_OFF) { + if (cptr[9].vibrato) { + vibval1 = vibval_var1; + for (i=0;i=0; cur_ch--) { + // skip drum/percussion operators + if ((adlibreg[ARC_PERC_MODE]&0x20) && (cur_ch >= 6) && (cur_ch < 9)) continue; + + Bitu k = cur_ch; +#if defined(OPLTYPE_IS_OPL3) + if (cur_ch < 9) { + cptr = &op[cur_ch]; + } else { + cptr = &op[cur_ch+9]; // second set is operator18-operator35 + k += (-9+256); // second set uses registers 0x100 onwards + } + // check if this operator is part of a 4-op + if ((adlibreg[0x105]&1) && cptr->is_4op_attached) continue; +#else + cptr = &op[cur_ch]; +#endif + + // check for FM/AM + if (adlibreg[ARC_FEEDBACK+k]&1) { +#if defined(OPLTYPE_IS_OPL3) + if ((adlibreg[0x105]&1) && cptr->is_4op) { + if (adlibreg[ARC_FEEDBACK+k+3]&1) { + // AM-AM-style synthesis (op1[fb] + (op2 * op3) + op4) + if (cptr[0].op_state != OF_TYPE_OFF) { + if (cptr[0].vibrato) { + vibval1 = vibval_var1; + for (i=0;iis_4op) { + if (adlibreg[ARC_FEEDBACK+k+3]&1) { + // FM-AM-style synthesis ((op1[fb] * op2) + (op3 * op4)) + if ((cptr[0].op_state != OF_TYPE_OFF) || (cptr[9].op_state != OF_TYPE_OFF)) { + if ((cptr[0].vibrato) && (cptr[0].op_state != OF_TYPE_OFF)) { + vibval1 = vibval_var1; + for (i=0;i + +typedef uintptr_t Bitu; +typedef intptr_t Bits; +typedef uint32_t Bit32u; +typedef int32_t Bit32s; +typedef uint16_t Bit16u; +typedef int16_t Bit16s; +typedef uint8_t Bit8u; +typedef int8_t Bit8s; + +// general functions +void adlib_init(Bit32u samplerate); +void adlib_write(Bitu idx, Bit8u val); +void adlib_getsample(Bit16s* sndptr, Bits numsamples); + +Bitu adlib_reg_read(Bitu port); +void adlib_write_index(Bitu port, Bit8u val); + +#define opl_init() adlib_init(OUTPUT_QUALITY * 11025) +#define opl_write(reg, val) adlib_write(reg, val) +#define opl_update(buf, num) adlib_getsample(buf, num) + +#endif /* OPL_H */ diff --git a/contrib/games/opentyrian/src/palette.c b/contrib/games/opentyrian/src/palette.c new file mode 100644 index 000000000..1dd27c003 --- /dev/null +++ b/contrib/games/opentyrian/src/palette.c @@ -0,0 +1,225 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "palette.h" + +#include "file.h" +#include "nortsong.h" +#include "opentyr.h" +#include "video.h" + +#include + +static Uint32 rgb_to_yuv( int r, int g, int b ); + +#define PALETTE_COUNT 23 + +Palette palettes[PALETTE_COUNT]; +int palette_count; + +static Palette palette; +Uint32 rgb_palette[256], yuv_palette[256]; + +Palette colors; + +void JE_loadPals( void ) +{ + FILE *f = dir_fopen_die(data_dir(), "palette.dat", "rb"); + + palette_count = ftell_eof(f) / (256 * 3); + assert(palette_count == PALETTE_COUNT); + + for (int p = 0; p < palette_count; ++p) + { + for (int i = 0; i < 256; ++i) + { + // The VGA hardware palette used only 6 bits per component, so the values need to be rescaled to + // 8 bits. The naive way to do this is to simply do (c << 2), padding it with 0's, however this + // makes the maximum value 252 instead of the proper 255. A trick to fix this is to use the upper 2 + // bits of the original value instead. This ensures that the value goes to 255 as the original goes + // to 63. + + int c = getc(f); + palettes[p][i].r = (c << 2) | (c >> 4); + c = getc(f); + palettes[p][i].g = (c << 2) | (c >> 4); + c = getc(f); + palettes[p][i].b = (c << 2) | (c >> 4); + } + } + + fclose(f); +} + +void set_palette( Palette colors, unsigned int first_color, unsigned int last_color ) +{ + SDL_Surface *const surface = SDL_GetVideoSurface(); + const uint bpp = surface->format->BitsPerPixel; + + for (uint i = first_color; i <= last_color; ++i) + { + palette[i] = colors[i]; + + if (bpp != 8) + { + rgb_palette[i] = SDL_MapRGB(surface->format, palette[i].r, palette[i].g, palette[i].b); + yuv_palette[i] = rgb_to_yuv(palette[i].r, palette[i].g, palette[i].b); + } + } + + if (bpp == 8) + SDL_SetColors(surface, palette, first_color, last_color - first_color + 1); +} + +void set_colors( SDL_Color color, unsigned int first_color, unsigned int last_color ) +{ + SDL_Surface *const surface = SDL_GetVideoSurface(); + const uint bpp = surface->format->BitsPerPixel; + + for (uint i = first_color; i <= last_color; ++i) + { + palette[i] = color; + + if (bpp != 8) + { + rgb_palette[i] = SDL_MapRGB(surface->format, palette[i].r, palette[i].g, palette[i].b); + yuv_palette[i] = rgb_to_yuv(palette[i].r, palette[i].g, palette[i].b); + } + } + + if (bpp == 8) + SDL_SetColors(surface, palette, first_color, last_color - first_color + 1); +} + +void init_step_fade_palette( int diff[256][3], Palette colors, unsigned int first_color, unsigned int last_color ) +{ + for (unsigned int i = first_color; i <= last_color; i++) + { + diff[i][0] = (int)colors[i].r - palette[i].r; + diff[i][1] = (int)colors[i].g - palette[i].g; + diff[i][2] = (int)colors[i].b - palette[i].b; + } +} + +void init_step_fade_solid( int diff[256][3], SDL_Color color, unsigned int first_color, unsigned int last_color ) +{ + for (unsigned int i = first_color; i <= last_color; i++) + { + diff[i][0] = (int)color.r - palette[i].r; + diff[i][1] = (int)color.g - palette[i].g; + diff[i][2] = (int)color.b - palette[i].b; + } +} + +void step_fade_palette( int diff[256][3], int steps, unsigned int first_color, unsigned int last_color ) +{ + assert(steps > 0); + + SDL_Surface *const surface = SDL_GetVideoSurface(); + const uint bpp = surface->format->BitsPerPixel; + + for (unsigned int i = first_color; i <= last_color; i++) + { + int delta[3] = { diff[i][0] / steps, diff[i][1] / steps, diff[i][2] / steps }; + + diff[i][0] -= delta[0]; + diff[i][1] -= delta[1]; + diff[i][2] -= delta[2]; + + palette[i].r += delta[0]; + palette[i].g += delta[1]; + palette[i].b += delta[2]; + + if (bpp != 8) + { + rgb_palette[i] = SDL_MapRGB(surface->format, palette[i].r, palette[i].g, palette[i].b); + yuv_palette[i] = rgb_to_yuv(palette[i].r, palette[i].g, palette[i].b); + } + } + + if (bpp == 8) + SDL_SetColors(surface, palette, 0, 256); +} + + +void fade_palette( Palette colors, int steps, unsigned int first_color, unsigned int last_color ) +{ + assert(steps > 0); + + SDL_Surface *const surface = SDL_GetVideoSurface(); + const uint bpp = surface->format->BitsPerPixel; + + static int diff[256][3]; + init_step_fade_palette(diff, colors, first_color, last_color); + + for (; steps > 0; steps--) + { + setdelay(1); + + step_fade_palette(diff, steps, first_color, last_color); + + if (bpp != 8) + JE_showVGA(); + + wait_delay(); + } +} + +void fade_solid( SDL_Color color, int steps, unsigned int first_color, unsigned int last_color ) +{ + assert(steps > 0); + + SDL_Surface *const surface = SDL_GetVideoSurface(); + const uint bpp = surface->format->BitsPerPixel; + + static int diff[256][3]; + init_step_fade_solid(diff, color, first_color, last_color); + + for (; steps > 0; steps--) + { + setdelay(1); + + step_fade_palette(diff, steps, first_color, last_color); + + if (bpp != 8) + JE_showVGA(); + + wait_delay(); + } +} + +void fade_black( int steps ) +{ + SDL_Color black = { 0, 0, 0 }; + fade_solid(black, steps, 0, 255); +} + +void fade_white( int steps ) +{ + SDL_Color white = { 255, 255, 255 }; + fade_solid(white, steps, 0, 255); +} + +static Uint32 rgb_to_yuv( int r, int g, int b ) +{ + int y = (r + g + b) >> 2, + u = 128 + ((r - b) >> 2), + v = 128 + ((-r + 2 * g - b) >> 3); + return (y << 16) + (u << 8) + v; +} + diff --git a/contrib/games/opentyrian/src/palette.h b/contrib/games/opentyrian/src/palette.h new file mode 100644 index 000000000..7d62d8782 --- /dev/null +++ b/contrib/games/opentyrian/src/palette.h @@ -0,0 +1,51 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef PALETTE_H +#define PALETTE_H + +#include "opentyr.h" + +#include "SDL.h" + +typedef SDL_Color Palette[256]; + +extern Palette palettes[]; +extern int palette_count; + +extern Uint32 rgb_palette[256], yuv_palette[256]; + +extern Palette colors; // TODO: get rid of this + +void JE_loadPals( void ); + +void set_palette( Palette colors, unsigned int first_color, unsigned int last_color ); +void set_colors( SDL_Color color, unsigned int first_color, unsigned int last_color ); + +void init_step_fade_palette( int diff[256][3], Palette colors, unsigned int first_color, unsigned int last_color ); +void init_step_fade_solid( int diff[256][3], SDL_Color color, unsigned int first_color, unsigned int last_color ); +void step_fade_palette( int diff[256][3], int steps, unsigned int first_color, unsigned int last_color ); + +void fade_palette( Palette colors, int steps, unsigned int first_color, unsigned int last_color ); +void fade_solid( SDL_Color color, int steps, unsigned int first_color, unsigned int last_color ); + +void fade_black( int steps ); +void fade_white( int steps ); + +#endif /* PALETTE_H */ + diff --git a/contrib/games/opentyrian/src/params.c b/contrib/games/opentyrian/src/params.c new file mode 100644 index 000000000..0bf83ea31 --- /dev/null +++ b/contrib/games/opentyrian/src/params.c @@ -0,0 +1,267 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "params.h" + +#include "arg_parse.h" +#include "file.h" +#include "joystick.h" +#include "loudness.h" +#include "network.h" +#include "opentyr.h" +#include "varz.h" +#include "xmas.h" + +#include +#include +#include +#include +#include + +JE_boolean richMode = false, constantPlay = false, constantDie = false; + +/* YKS: Note: LOOT cheat had non letters removed. */ +const char pars[][9] = { + "LOOT", "RECORD", "NOJOY", "CONSTANT", "DEATH", "NOSOUND", "NOXMAS", "YESXMAS" +}; + +void JE_paramCheck( int argc, char *argv[] ) +{ + const Options options[] = + { + { 'h', 'h', "help", false }, + + { 's', 's', "no-sound", false }, + { 'j', 'j', "no-joystick", false }, + { 'x', 'x', "no-xmas", false }, + + { 't', 't', "data", true }, + + { 'n', 'n', "net", true }, + { 256, 0, "net-player-name", true }, // TODO: no short codes because there should + { 257, 0, "net-player-number", true }, // be a menu for entering these in the future + { 'p', 'p', "net-port", true }, + { 'd', 'd', "net-delay", true }, + + { 'X', 'X', "xmas", false }, + { 'c', 'c', "constant", false }, + { 'k', 'k', "death", false }, + { 'r', 'r', "record", false }, + { 'l', 'l', "loot", false }, + + { 0, 0, NULL, false} + }; + + Option option; + + for (; ; ) + { + option = parse_args(argc, (const char **)argv, options); + + if (option.value == NOT_OPTION) + break; + + switch (option.value) + { + case INVALID_OPTION: + case AMBIGUOUS_OPTION: + case OPTION_MISSING_ARG: + fprintf(stderr, "Try `%s --help' for more information.\n", argv[0]); + exit(EXIT_FAILURE); + break; + + case 'h': + printf("Usage: %s [OPTION...]\n\n" + "Options:\n" + " -h, --help Show help about options\n\n" + " -s, --no-sound Disable audio\n" + " -j, --no-joystick Disable joystick/gamepad input\n" + " -x, --no-xmas Disable Christmas mode\n\n" + " -t, --data=DIR Set Tyrian data directory\n\n" + " -n, --net=HOST[:PORT] Start a networked game\n" + " --net-player-name=NAME Sets local player name in a networked game\n" + " --net-player-number=NUMBER Sets local player number in a networked game\n" + " (1 or 2)\n" + " -p, --net-port=PORT Local port to bind (default is 1333)\n" + " -d, --net-delay=FRAMES Set lag-compensation delay (default is 1)\n", argv[0]); + exit(0); + break; + + case 's': + // Disables sound/music usage + audio_disabled = true; + break; + + case 'j': + // Disables joystick detection + ignore_joystick = true; + break; + + case 'x': + xmas = false; + break; + + // set custom Tyrian data directory + case 't': + custom_data_dir = option.arg; + break; + + case 'n': + isNetworkGame = true; + + intptr_t temp = (intptr_t)strchr(option.arg, ':'); + if (temp) + { + temp -= (intptr_t)option.arg; + + int temp_port = atoi(&option.arg[temp + 1]); + if (temp_port > 0 && temp_port < 49152) + network_opponent_port = temp_port; + else + { + fprintf(stderr, "%s: error: invalid network port number\n", argv[0]); + exit(EXIT_FAILURE); + } + + network_opponent_host = malloc(temp + 1); + SDL_strlcpy(network_opponent_host, option.arg, temp + 1); + } + else + { + network_opponent_host = malloc(strlen(option.arg) + 1); + strcpy(network_opponent_host, option.arg); + } + break; + + case 256: // --net-player-name + network_player_name = malloc(strlen(option.arg) + 1); + strcpy(network_player_name, option.arg); + break; + + case 257: // --net-player-number + { + int temp = atoi(option.arg); + if (temp >= 1 && temp <= 2) + thisPlayerNum = temp; + else + { + fprintf(stderr, "%s: error: invalid network player number\n", argv[0]); + exit(EXIT_FAILURE); + } + break; + } + case 'p': + { + int temp = atoi(option.arg); + if (temp > 0 && temp < 49152) + network_player_port = temp; + else + { + fprintf(stderr, "%s: error: invalid network port number\n", argv[0]); + exit(EXIT_FAILURE); + } + break; + } + case 'd': + { + int temp; + if (sscanf(option.arg, "%d", &temp) == 1) + network_delay = 1 + temp; + else + { + fprintf(stderr, "%s: error: invalid network delay value\n", argv[0]); + exit(EXIT_FAILURE); + } + break; + } + case 'X': + xmas = true; + break; + + case 'c': + /* Constant play for testing purposes (C key activates invincibility) + This might be useful for publishers to see everything - especially + those who can't play it */ + constantPlay = true; + break; + + case 'k': + constantDie = true; + break; + + case 'r': + record_demo = true; + break; + + case 'l': + // Gives you mucho bucks + richMode = true; + break; + + default: + assert(false); + break; + } + } + + // legacy parameter support + for (int i = option.argn; i < argc; ++i) + { + for (uint j = 0; j < strlen(argv[i]); ++j) + argv[i][j] = toupper((unsigned char)argv[i][j]); + + for (uint j = 0; j < COUNTOF(pars); ++j) + { + if (strcmp(argv[i], pars[j]) == 0) + { + switch (j) + { + case 0: + richMode = true; + break; + case 1: + record_demo = true; + break; + case 2: + ignore_joystick = true; + break; + case 3: + constantPlay = true; + break; + case 4: + constantDie = true; + break; + case 5: + audio_disabled = true; + break; + case 6: + xmas = false; + break; + case 7: + xmas = true; + break; + + default: + assert(false); + break; + } + } + } + } +} + diff --git a/contrib/games/opentyrian/src/params.h b/contrib/games/opentyrian/src/params.h new file mode 100644 index 000000000..9bdca8906 --- /dev/null +++ b/contrib/games/opentyrian/src/params.h @@ -0,0 +1,29 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef PARAMS_H +#define PARAMS_H + +#include "opentyr.h" + +extern JE_boolean richMode, constantPlay, constantDie; + +void JE_paramCheck( int argc, char *argv[] ); + +#endif /* PARAMS_H */ + diff --git a/contrib/games/opentyrian/src/pcxload.c b/contrib/games/opentyrian/src/pcxload.c new file mode 100644 index 000000000..2561b8e41 --- /dev/null +++ b/contrib/games/opentyrian/src/pcxload.c @@ -0,0 +1,67 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "pcxload.h" + +#include "file.h" +#include "opentyr.h" +#include "palette.h" +#include "video.h" + +void JE_loadPCX( const char *file ) // this is only meant to load tshp2.pcx +{ + Uint8 *s = VGAScreen->pixels; /* 8-bit specific */ + + FILE *f = dir_fopen_die(data_dir(), file, "rb"); + + fseek(f, -769, SEEK_END); + + if (fgetc(f) == 12) + { + for (int i = 0; i < 256; i++) + { + efread(&colors[i].r, 1, 1, f); + efread(&colors[i].g, 1, 1, f); + efread(&colors[i].b, 1, 1, f); + } + } + + fseek(f, 128, SEEK_SET); + + for (int i = 0; i < 320 * 200; ) + { + Uint8 p = fgetc(f); + if ((p & 0xc0) == 0xc0) + { + i += (p & 0x3f); + memset(s, fgetc(f), (p & 0x3f)); + s += (p & 0x3f); + } else { + i++; + *s = p; + s++; + } + if (i && (i % 320 == 0)) + { + s += VGAScreen->pitch - 320; + } + } + + fclose(f); +} + diff --git a/contrib/games/opentyrian/src/pcxload.h b/contrib/games/opentyrian/src/pcxload.h new file mode 100644 index 000000000..c4bb8b731 --- /dev/null +++ b/contrib/games/opentyrian/src/pcxload.h @@ -0,0 +1,27 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef PCXLOAD_H +#define PCXLOAD_H + +#include "opentyr.h" + +void JE_loadPCX( const char *file ); + +#endif /* PCXLOAD_H */ + diff --git a/contrib/games/opentyrian/src/pcxmast.c b/contrib/games/opentyrian/src/pcxmast.c new file mode 100644 index 000000000..c58719905 --- /dev/null +++ b/contrib/games/opentyrian/src/pcxmast.c @@ -0,0 +1,48 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "pcxmast.h" + +#include "opentyr.h" + +const char *pcxfile[PCX_NUM] = /* [1..PCXnum] */ +{ + "INTSHPB.PCX", + "SETUP2.PCX", + "TYRPLAY.PCX", + "TYRLOG2.PCX", + "P1.PCX", + "TYRPLAY2.PCX", + "BUC4.PCX", + "GMOVR4a.PCX", + "GMOVR4b.PCX", + "EPICSKY.PCX", + "DESTRUCT.PCX", + "ECLIPSE.PCX", + "FIREPICA.PCX" +}; + +const JE_byte pcxpal[PCX_NUM] = /* [1..PCXnum] */ +{ 0, 7, 5, 8, 10, 5, 18, 19, 19, 20, 21, 22, 5}; + +/*FACEMAX*/ +const JE_byte facepal[12] = /* [1..12] */ +{ 1, 2, 3, 4, 6, 9, 11, 12, 16, 13, 14, 15}; + +JE_pcxpostype pcxpos; + diff --git a/contrib/games/opentyrian/src/pcxmast.h b/contrib/games/opentyrian/src/pcxmast.h new file mode 100644 index 000000000..4ab96c071 --- /dev/null +++ b/contrib/games/opentyrian/src/pcxmast.h @@ -0,0 +1,35 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef PCXMAST_H +#define PCXMAST_H + +#include "opentyr.h" + + +#define PCX_NUM 13 + +typedef JE_longint JE_pcxpostype[PCX_NUM + 1]; /* [1..PCXnum + 1] */ + +extern const char *pcxfile[PCX_NUM]; /* [1..PCXnum] */ +extern const JE_byte pcxpal[PCX_NUM]; /* [1..PCXnum] */ +extern const JE_byte facepal[12]; /* [1..12] */ +extern JE_pcxpostype pcxpos; + +#endif /* PCXMAST_H */ + diff --git a/contrib/games/opentyrian/src/picload.c b/contrib/games/opentyrian/src/picload.c new file mode 100644 index 000000000..cee373e48 --- /dev/null +++ b/contrib/games/opentyrian/src/picload.c @@ -0,0 +1,87 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "picload.h" + +#include "file.h" +#include "opentyr.h" +#include "palette.h" +#include "pcxmast.h" +#include "video.h" + +#include + +void JE_loadPic(SDL_Surface *screen, JE_byte PCXnumber, JE_boolean storepal ) +{ + PCXnumber--; + + FILE *f = dir_fopen_die(data_dir(), "tyrian.pic", "rb"); + + static bool first = true; + if (first) + { + first = false; + + Uint16 temp; + efread(&temp, sizeof(Uint16), 1, f); + for (int i = 0; i < PCX_NUM; i++) + { + efread(&pcxpos[i], sizeof(JE_longint), 1, f); + } + + pcxpos[PCX_NUM] = ftell_eof(f); + } + + unsigned int size = pcxpos[PCXnumber + 1] - pcxpos[PCXnumber]; + Uint8 *buffer = malloc(size); + + fseek(f, pcxpos[PCXnumber], SEEK_SET); + efread(buffer, sizeof(Uint8), size, f); + fclose(f); + + Uint8 *p = buffer; + Uint8 *s; /* screen pointer, 8-bit specific */ + + s = (Uint8 *)screen->pixels; + + for (int i = 0; i < 320 * 200; ) + { + if ((*p & 0xc0) == 0xc0) + { + i += (*p & 0x3f); + memset(s, *(p + 1), (*p & 0x3f)); + s += (*p & 0x3f); p += 2; + } else { + i++; + *s = *p; + s++; p++; + } + if (i && (i % 320 == 0)) + { + s += screen->pitch - 320; + } + } + + free(buffer); + + memcpy(colors, palettes[pcxpal[PCXnumber]], sizeof(colors)); + + if (storepal) + set_palette(colors, 0, 255); +} + diff --git a/contrib/games/opentyrian/src/picload.h b/contrib/games/opentyrian/src/picload.h new file mode 100644 index 000000000..a678b2b5b --- /dev/null +++ b/contrib/games/opentyrian/src/picload.h @@ -0,0 +1,29 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef PICLOAD_H +#define PICLOAD_H + +#include "opentyr.h" + +#include "SDL.h" + +void JE_loadPic(SDL_Surface *screen, JE_byte PCXnumber, JE_boolean storepal ); + +#endif /* PICLOAD_H */ + diff --git a/contrib/games/opentyrian/src/player.c b/contrib/games/opentyrian/src/player.c new file mode 100644 index 000000000..bfaa1ff61 --- /dev/null +++ b/contrib/games/opentyrian/src/player.c @@ -0,0 +1,55 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "player.h" + +Player player[2]; + +void calc_purple_balls_needed( Player *this_player ) +{ + static const uint purple_balls_required[12] = { 1, 1, 2, 4, 8, 12, 16, 20, 25, 30, 40, 50 }; + + this_player->purple_balls_needed = purple_balls_required[*this_player->lives]; +} + +bool power_up_weapon( Player *this_player, uint port ) +{ + const bool can_power_up = this_player->items.weapon[port].id != 0 && // not None + this_player->items.weapon[port].power < 11; // not at max power + if (can_power_up) + { + ++this_player->items.weapon[port].power; + shotMultiPos[port] = 0; // TODO: should be part of Player structure + + calc_purple_balls_needed(this_player); + } + else // cash consolation prize + { + this_player->cash += 1000; + } + + return can_power_up; +} + +void handle_got_purple_ball( Player *this_player ) +{ + if (this_player->purple_balls_needed > 1) + --this_player->purple_balls_needed; + else + power_up_weapon(this_player, this_player->is_dragonwing ? REAR_WEAPON : FRONT_WEAPON); +} diff --git a/contrib/games/opentyrian/src/player.h b/contrib/games/opentyrian/src/player.h new file mode 100644 index 000000000..b74d2170b --- /dev/null +++ b/contrib/games/opentyrian/src/player.h @@ -0,0 +1,129 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef PLAYER_H +#define PLAYER_H + +#include "config.h" +#include "opentyr.h" + +enum +{ + FRONT_WEAPON = 0, + REAR_WEAPON = 1 +}; + +enum +{ + LEFT_SIDEKICK = 0, + RIGHT_SIDEKICK = 1 +}; + +typedef struct +{ + uint ship; + uint generator; + uint shield; + struct { uint id; uint power; } weapon[2]; + uint sidekick[2]; + uint special; + + // Dragonwing only: + // repeatedly collecting the same powerup gives a series of sidekick upgrades + uint sidekick_series; + uint sidekick_level; + + // Single-player only + uint super_arcade_mode; // stored as an item for compatibility :( +} +PlayerItems; + +typedef struct +{ + ulong cash; + + PlayerItems items, last_items; + + bool is_dragonwing; // i.e., is player 2 + uint *lives; + + // calculatable + uint shield_max; + uint initial_armor; + uint shot_hit_area_x, shot_hit_area_y; + + // state + bool is_alive; + uint invulnerable_ticks; // ticks until ship can be damaged + uint exploding_ticks; // ticks until ship done exploding + uint shield; + uint armor; + uint weapon_mode; + uint superbombs; + uint purple_balls_needed; + + int x, y; + int old_x[20], old_y[20]; + + int x_velocity, y_velocity; + uint x_friction_ticks, y_friction_ticks; // ticks until friction is applied + + int delta_x_shot_move, delta_y_shot_move; + + int last_x_shot_move, last_y_shot_move; + int last_x_explosion_follow, last_y_explosion_follow; + + struct + { + // calculatable + int ammo_max; + uint ammo_refill_ticks_max; + uint style; // affects movement and size + + // state + int x, y; + int ammo; + uint ammo_refill_ticks; + + bool animation_enabled; + uint animation_frame; + + uint charge; + uint charge_ticks; + } + sidekick[2]; +} +Player; + +extern Player player[2]; + +static inline bool all_players_dead( void ) +{ + return (!player[0].is_alive && (!twoPlayerMode || !player[1].is_alive)); +} + +static inline bool all_players_alive( void ) +{ + return (player[0].is_alive && (!twoPlayerMode || player[1].is_alive)); +} + +void calc_purple_balls_needed( Player * ); +bool power_up_weapon( Player *, uint port ); +void handle_got_purple_ball( Player * ); + +#endif // PLAYER_H diff --git a/contrib/games/opentyrian/src/scroller.c b/contrib/games/opentyrian/src/scroller.c new file mode 100644 index 000000000..fc9fa3263 --- /dev/null +++ b/contrib/games/opentyrian/src/scroller.c @@ -0,0 +1,315 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "scroller.h" + +#include "font.h" +#include "joystick.h" +#include "jukebox.h" +#include "keyboard.h" +#include "loudness.h" +#include "mtrand.h" +#include "nortsong.h" +#include "nortvars.h" +#include "opentyr.h" +#include "palette.h" +#include "sprite.h" +#include "varz.h" +#include "vga256d.h" +#include "video.h" + +#include + +const struct about_text_type about_text[] = +{ + {0x30, "----- ~OpenTyrian~ -----"}, + {0x00, ""}, + {0x0b, "...eliminating Microsol,"}, + {0x0b, "one planet at a time..."}, + {0x00, ""}, + {0x00, ""}, + {0x30, "----- ~Developers~ -----"}, + {0x00, ""}, + {0x03, "Carl Reinke // Mindless"}, + {0x07, "Yuri Schlesner // yuriks"}, + {0x04, "Casey McCann // syntaxglitch"}, + {0x00, ""}, + {0x00, ""}, + {0x30, "----- ~Thanks~ -----"}, + {0x00, ""}, + {0x0e, "Thanks to everyone who has"}, + {0x0e, "assisted the developers by testing"}, + {0x0e, "the game and reporting bugs."}, + {0x00, ""}, + {0x00, ""}, + {0x05, "Thanks to ~DOSBox~ for the"}, + {0x05, "FM-Synthesis emulator and"}, + {0x05, "~AdPlug~ for the Loudness player."}, + {0x00, ""}, + {0x00, ""}, + {0x32, "And special thanks to ~Jason Emery~"}, + {0x32, "for making all this possible"}, + {0x32, "by giving Tyrian to its fans."}, + {0x00, ""}, + {0x00, ""}, +/* {0x00, "This is line color test ~0~."}, + {0x01, "This is line color test ~1~."}, + {0x02, "This is line color test ~2~."}, + {0x03, "This is line color test ~3~."}, + {0x04, "This is line color test ~4~."}, + {0x05, "This is line color test ~5~."}, + {0x06, "This is line color test ~6~."}, + {0x07, "This is line color test ~7~."}, + {0x08, "This is line color test ~8~."}, + {0x09, "This is line color test ~9~."}, + {0x0a, "This is line color test ~A~."}, + {0x0b, "This is line color test ~B~."}, + {0x0c, "This is line color test ~C~."}, + {0x0d, "This is line color test ~D~."}, + {0x0e, "This is line color test ~E~."}, + {0x0f, "This is line color test ~F~."},*/ + {0x00, ""}, + {0x00, ""}, + {0x00, ""}, + {0x00, ""}, + {0x00, ""}, + {0x00, ""}, + {0x00, "Press a key to leave."}, + {0x00, NULL} +}; + +#define LINE_HEIGHT 15 + +#define MAX_BEER 5 +#define BEER_SHAPE 241 + +struct coin_def_type { + int shape_num; + int frame_count; + bool reverse_anim; +}; + +#define MAX_COINS 20 +struct coin_def_type coin_defs[] = +{ + {1, 6}, {7, 6}, {20, 6}, {26, 6}, // Coins + {14, 5, true}, {32, 5, true}, {51, 5, true} // Gems +}; + +/* Text is an array of strings terminated by a NULL */ +void scroller_sine( const struct about_text_type text[] ) +{ + bool ale = mt_rand() % 2; + + int visible_lines = vga_height / LINE_HEIGHT + 1; + int current_line = -visible_lines; + int y = 0; + bool fade_in = true; + + struct coin_type { int x, y, vel, type, cur_frame; bool backwards; } coins[MAX_COINS]; + struct { int x, y, ay, vx, vy; } beer[MAX_BEER]; + + if (ale) + { + memset(beer, 0, sizeof(beer)); + } else { + for (int i = 0; i < MAX_COINS; i++) + { + coins[i].x = mt_rand() % (vga_width - 12); + coins[i].y = mt_rand() % (vga_height - 20 - 14); + + coins[i].vel = (mt_rand() % 4) + 1; + coins[i].type = mt_rand() % COUNTOF(coin_defs); + coins[i].cur_frame = mt_rand() % coin_defs[coins[i].type].frame_count; + coins[i].backwards = false; + } + } + + fade_black(10); + + wait_noinput(true, true, true); + + play_song(40); // BEER + + while (!JE_anyButton()) + { + setdelay(3); + + JE_clr256(VGAScreen); + + if (!ale) + { + for (int i = 0; i < MAX_COINS/2; i++) + { + struct coin_type *coin = &coins[i]; + blit_sprite2(VGAScreen, coin->x, coin->y, eShapes[4], coin_defs[coin->type].shape_num + coin->cur_frame); + } + } + + for (int i = 0; i < visible_lines; i++) + { + if (current_line + i >= 0) + { + if (text[current_line + i].text == NULL) + { + break; + } + + int line_x = VGAScreen->w / 2; + int line_y = i * LINE_HEIGHT - y; + + // smooths edges on sine-wave text + if (text[i + current_line].effect & 0x20) + { + draw_font_hv(VGAScreen, line_x + 1, line_y, text[i + current_line].text, normal_font, centered, text[i + current_line].effect & 0x0f, -10); + draw_font_hv(VGAScreen, line_x - 1, line_y, text[i + current_line].text, normal_font, centered, text[i + current_line].effect & 0x0f, -10); + } + + draw_font_hv(VGAScreen, line_x, line_y, text[i + current_line].text, normal_font, centered, text[i + current_line].effect & 0x0f, -4); + + if (text[i + current_line].effect & 0x10) + { + for (int j = 0; j < LINE_HEIGHT; j++) + { + if (line_y + j >= 10 && line_y + j <= vga_height - 10) + { + int waver = sinf((((line_y + j) / 2) % 10) / 5.0f * M_PI) * 3; + memmove(&((Uint8 *)VGAScreen->pixels)[VGAScreen->pitch * (line_y + j) + waver], + &((Uint8 *)VGAScreen->pixels)[VGAScreen->pitch * (line_y + j)], + VGAScreen->pitch); + } + } + } + } + } + + if (++y == LINE_HEIGHT) + { + y = 0; + + if (current_line < 0 || text[current_line].text != NULL) + ++current_line; + else + current_line = -visible_lines; + } + + if (!ale) + { + for (int i = MAX_COINS/2; i < MAX_COINS; i++) + { + struct coin_type *coin = &coins[i]; + blit_sprite2(VGAScreen, coin->x, coin->y, eShapes[4], coin_defs[coin->type].shape_num + coin->cur_frame); + } + } + + fill_rectangle_xy(VGAScreen, 0, 0, vga_width - 1, 14, 0); + fill_rectangle_xy(VGAScreen, 0, vga_height - 14, vga_width - 1, vga_height - 1, 0); + + if (!ale) + { + for (int i = 0; i < MAX_COINS; i++) + { + struct coin_type *coin = &coins[i]; + + if (coin->backwards) + { + coin->cur_frame--; + } else { + coin->cur_frame++; + } + if (coin->cur_frame == coin_defs[coin->type].frame_count) + { + if (coin_defs[coin->type].reverse_anim) + { + coin->backwards = true; + coin->cur_frame -= 2; + } else { + coin->cur_frame = 0; + } + } + if (coin->cur_frame == -1) + { + coin->cur_frame = 1; + coin->backwards = false; + } + + coin->y += coin->vel; + if (coin->y > vga_height - 14) + { + coin->x = mt_rand() % (vga_width - 12); + coin->y = 0; + + coin->vel = (mt_rand() % 4) + 1; + coin->type = mt_rand() % COUNTOF(coin_defs); + coin->cur_frame = mt_rand() % coin_defs[coin->type].frame_count; + } + } + } else { + for (uint i = 0; i < COUNTOF(beer); i++) + { + while (beer[i].vx == 0) + { + beer[i].x = mt_rand() % (vga_width - 24); + beer[i].y = mt_rand() % (vga_height - 28 - 50); + + beer[i].vx = (mt_rand() % 5) - 2; + } + + beer[i].vy++; + + if (beer[i].x + beer[i].vx > vga_width - 24 || beer[i].x + beer[i].vx < 0) // check if the beer hit the sides + { + beer[i].vx = -beer[i].vx; + } + beer[i].x += beer[i].vx; + + if (beer[i].y + beer[i].vy > vga_height - 28) // check if the beer hit the bottom + { + if ((beer[i].vy) < 8) // make sure the beer bounces! + { + beer[i].vy += mt_rand() % 2; + } else if (beer[i].vy > 16) { // make sure the beer doesn't bounce too high + beer[i].vy = 16; + } + beer[i].vy = -beer[i].vy + (mt_rand() % 3 - 1); + + beer[i].x += (beer[i].vx > 0 ? 1 : -1) * (i % 2 ? 1 : -1); + } + beer[i].y += beer[i].vy; + + blit_sprite2x2(VGAScreen, beer[i].x, beer[i].y, eShapes[4], BEER_SHAPE); + } + } + + JE_showVGA(); + + if (fade_in) + { + fade_in = false; + fade_palette(colors, 10, 0, 255); + + SDL_Color white = { 255, 255, 255 }; + set_colors(white, 254, 254); + } + + wait_delay(); + } + + fade_black(10); +} + diff --git a/contrib/games/opentyrian/src/scroller.h b/contrib/games/opentyrian/src/scroller.h new file mode 100644 index 000000000..89ef97666 --- /dev/null +++ b/contrib/games/opentyrian/src/scroller.h @@ -0,0 +1,32 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef SCROLLER_H +#define SCROLLER_H + +#include "opentyr.h" + +extern const struct about_text_type { + int effect; + const char *text; +} about_text[]; + +void scroller_sine( const struct about_text_type text[] ); + +#endif /* SCROLLER_H */ + diff --git a/contrib/games/opentyrian/src/setup.c b/contrib/games/opentyrian/src/setup.c new file mode 100644 index 000000000..402dbe78e --- /dev/null +++ b/contrib/games/opentyrian/src/setup.c @@ -0,0 +1,95 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "setup.h" + +#include "joystick.h" +#include "keyboard.h" +#include "network.h" +#include "nortvars.h" +#include "opentyr.h" +#include "mainint.h" +#include "mouse.h" +#include "video.h" + +#include "SDL.h" + +void JE_textMenuWait( JE_word *waitTime, JE_boolean doGamma ) +{ + set_mouse_position(160, 100); + + do + { + JE_showVGA(); + + push_joysticks_as_keyboard(); + service_SDL_events(true); + + if (doGamma) + JE_gammaCheck(); + + inputDetected = newkey | mousedown; + + if (lastkey_sym == SDLK_SPACE) + { + lastkey_sym = SDLK_RETURN; + } + + if (mousedown) + { + newkey = true; + lastkey_sym = SDLK_RETURN; + } + + if (has_mouse && input_grab_enabled) + { + if (abs(mouse_y - 100) > 10) + { + inputDetected = true; + if (mouse_y - 100 < 0) + { + lastkey_sym = SDLK_UP; + } else { + lastkey_sym = SDLK_DOWN; + } + newkey = true; + } + if (abs(mouse_x - 160) > 10) + { + inputDetected = true; + if (mouse_x - 160 < 0) + { + lastkey_sym = SDLK_LEFT; + } else { + lastkey_sym = SDLK_RIGHT; + } + newkey = true; + } + } + + NETWORK_KEEP_ALIVE(); + + uSDL_Delay(16); + + if (*waitTime > 0) + { + (*waitTime)--; + } + } while (!(inputDetected || *waitTime == 1 || haltGame)); +} + diff --git a/contrib/games/opentyrian/src/setup.h b/contrib/games/opentyrian/src/setup.h new file mode 100644 index 000000000..a10fba854 --- /dev/null +++ b/contrib/games/opentyrian/src/setup.h @@ -0,0 +1,27 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef SETUP_H +#define SETUP_H + +#include "opentyr.h" + +void JE_textMenuWait( JE_word *waitTime, JE_boolean doGamma ); + +#endif /* SETUP_H */ + diff --git a/contrib/games/opentyrian/src/shots.c b/contrib/games/opentyrian/src/shots.c new file mode 100644 index 000000000..a08f1b504 --- /dev/null +++ b/contrib/games/opentyrian/src/shots.c @@ -0,0 +1,500 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2013 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "shots.h" + +#include "player.h" +#include "sprite.h" +#include "video.h" +#include "varz.h" + +// I'm pretty sure the last extra entry is never used. +PlayerShotDataType playerShotData[MAX_PWEAPON + 1]; /* [1..MaxPWeapon+1] */ +JE_byte shotAvail[MAX_PWEAPON]; /* [1..MaxPWeapon] */ /*0:Avail 1-255:Duration left*/ + +void simulate_player_shots( void ) +{ + /* Player Shot Images */ + for (int z = 0; z < MAX_PWEAPON; z++) + { + if (shotAvail[z] != 0) + { + shotAvail[z]--; + if (z != MAX_PWEAPON - 1) + { + PlayerShotDataType* shot = &playerShotData[z]; + + shot->shotXM += shot->shotXC; + + if (shot->shotXM <= 100) + shot->shotX += shot->shotXM; + + shot->shotYM += shot->shotYC; + shot->shotY += shot->shotYM; + + if (shot->shotYM > 100) + { + shot->shotY -= 120; + shot->shotY += player[0].delta_y_shot_move; + } + + if (shot->shotComplicated != 0) + { + shot->shotDevX += shot->shotDirX; + shot->shotX += shot->shotDevX; + + if (abs(shot->shotDevX) == shot->shotCirSizeX) + shot->shotDirX = -shot->shotDirX; + + shot->shotDevY += shot->shotDirY; + shot->shotY += shot->shotDevY; + + if (abs(shot->shotDevY) == shot->shotCirSizeY) + shot->shotDirY = -shot->shotDirY; + /*Double Speed Circle Shots - add a second copy of above loop*/ + } + + int tempShotX = shot->shotX; + int tempShotY = shot->shotY; + + if (shot->shotX < 0 || shot->shotX > 140 || + shot->shotY < 0 || shot->shotY > 170) + { + shotAvail[z] = 0; + goto draw_player_shot_loop_end; + } + +/* if (shot->shotTrail != 255) + { + if (shot->shotTrail == 98) + { + JE_setupExplosion(shot->shotX - shot->shotXM, shot->shotY - shot->shotYM, shot->shotTrail); + } else { + JE_setupExplosion(shot->shotX, shot->shotY, shot->shotTrail); + } + }*/ + + JE_word anim_frame = shot->shotGr + shot->shotAni; + if (++shot->shotAni == shot->shotAniMax) + shot->shotAni = 0; + + if (anim_frame < 6000) + { + if (anim_frame > 1000) + anim_frame = anim_frame % 1000; + if (anim_frame > 500) + blit_sprite2(VGAScreen, tempShotX+1, tempShotY, shapesW2, anim_frame - 500); + else + blit_sprite2(VGAScreen, tempShotX+1, tempShotY, shapesC1, anim_frame); + } + } + +draw_player_shot_loop_end: + ; + } + } +} + +static const JE_word linkMultiGr[17] /* [0..16] */ = + {77,221,183,301,1,282,164,202,58,201,163,281,39,300,182,220,77}; +static const JE_word linkSonicGr[17] /* [0..16] */ = + {85,242,131,303,47,284,150,223,66,224,149,283,9,302,130,243,85}; +static const JE_word linkMult2Gr[17] /* [0..16] */ = + {78,299,295,297,2,278,276,280,59,279,275,277,40,296,294,298,78}; + +void player_shot_set_direction( JE_integer shot_id, uint weapon_id, JE_real direction ) +{ + PlayerShotDataType* shot = &playerShotData[shot_id]; + + shot->shotXM = -roundf(sinf(direction) * shot->shotYM); + shot->shotYM = -roundf(cosf(direction) * shot->shotYM); + + // Some weapons have sprites for each direction, use those. + int rounded_dir; + + switch (weapon_id) + { + case 27: + case 32: + case 10: + rounded_dir = roundf(direction * (16 / (2 * M_PI))); /*16 directions*/ + shot->shotGr = linkMultiGr[rounded_dir]; + break; + case 28: + case 33: + case 11: + rounded_dir = roundf(direction * (16 / (2 * M_PI))); /*16 directions*/ + shot->shotGr = linkSonicGr[rounded_dir]; + break; + case 30: + case 35: + case 14: + if (direction > M_PI_2 && direction < M_PI + M_PI_2) + { + shot->shotYC = 1; + } + break; + case 38: + case 22: + rounded_dir = roundf(direction * (16 / (2 * M_PI))); /*16 directions*/ + shot->shotGr = linkMult2Gr[rounded_dir]; + break; + } +} + +bool player_shot_move_and_draw( + int shot_id, bool* out_is_special, + int* out_shotx, int* out_shoty, + JE_integer* out_shot_damage, JE_byte* out_blast_filter, + JE_byte* out_chain, JE_byte* out_playerNum, + JE_word* out_special_radiusw, JE_word* out_special_radiush ) +{ + PlayerShotDataType* shot = &playerShotData[shot_id]; + + shotAvail[shot_id]--; + if (shot_id != MAX_PWEAPON - 1) + { + shot->shotXM += shot->shotXC; + shot->shotX += shot->shotXM; + JE_integer tmp_shotXM = shot->shotXM; + + if (shot->shotXM > 100) + { + if (shot->shotXM == 101) + { + shot->shotX -= 101; + shot->shotX += player[shot->playerNumber-1].delta_x_shot_move; + shot->shotY += player[shot->playerNumber-1].delta_y_shot_move; + } + else + { + shot->shotX -= 120; + shot->shotX += player[shot->playerNumber-1].delta_x_shot_move; + } + } + + shot->shotYM += shot->shotYC; + shot->shotY += shot->shotYM; + + if (shot->shotYM > 100) + { + shot->shotY -= 120; + shot->shotY += player[shot->playerNumber-1].delta_y_shot_move; + } + + if (shot->shotComplicated != 0) + { + shot->shotDevX += shot->shotDirX; + shot->shotX += shot->shotDevX; + + if (abs(shot->shotDevX) == shot->shotCirSizeX) + shot->shotDirX = -shot->shotDirX; + + shot->shotDevY += shot->shotDirY; + shot->shotY += shot->shotDevY; + + if (abs(shot->shotDevY) == shot->shotCirSizeY) + shot->shotDirY = -shot->shotDirY; + + /*Double Speed Circle Shots - add a second copy of above loop*/ + } + + *out_shotx = shot->shotX; + *out_shoty = shot->shotY; + + if (shot->shotX < -34 || shot->shotX > 290 || + shot->shotY < -15 || shot->shotY > 190) + { + shotAvail[shot_id] = 0; + return false; + } + + if (shot->shotTrail != 255) + { + if (shot->shotTrail == 98) + JE_setupExplosion(shot->shotX - shot->shotXM, shot->shotY - shot->shotYM, 0, shot->shotTrail, false, false); + else + JE_setupExplosion(shot->shotX, shot->shotY, 0, shot->shotTrail, false, false); + } + + if (shot->aimAtEnemy != 0) + { + if (--shot->aimDelay == 0) + { + shot->aimDelay = shot->aimDelayMax; + + if (enemyAvail[shot->aimAtEnemy - 1] != 1) + { + if (shot->shotX < enemy[shot->aimAtEnemy - 1].ex) + shot->shotXM++; + else + shot->shotXM--; + + if (shot->shotY < enemy[shot->aimAtEnemy - 1].ey) + shot->shotYM++; + else + shot->shotYM--; + } + else + { + if (shot->shotXM > 0) + shot->shotXM++; + else + shot->shotXM--; + } + } + } + + JE_word sprite_frame = shot->shotGr + shot->shotAni; + if (++shot->shotAni == shot->shotAniMax) + shot->shotAni = 0; + + *out_shot_damage = shot->shotDmg; + *out_blast_filter = shot->shotBlastFilter; + *out_chain = shot->chainReaction; + *out_playerNum = shot->playerNumber; + + *out_is_special = sprite_frame > 60000; + + if (*out_is_special) + { + blit_sprite_blend(VGAScreen, *out_shotx+1, *out_shoty, OPTION_SHAPES, sprite_frame - 60001); + + *out_special_radiusw = sprite(OPTION_SHAPES, sprite_frame - 60001)->width / 2; + *out_special_radiush = sprite(OPTION_SHAPES, sprite_frame - 60001)->height / 2; + } + else + { + if (sprite_frame > 1000) + { + JE_doSP(*out_shotx+1 + 6, *out_shoty + 6, 5, 3, (sprite_frame / 1000) << 4); + sprite_frame = sprite_frame % 1000; + } + if (sprite_frame > 500) + { + if (background2 && *out_shoty + shadowYDist < 190 && tmp_shotXM < 100) + blit_sprite2_darken(VGAScreen, *out_shotx+1, *out_shoty + shadowYDist, shapesW2, sprite_frame - 500); + blit_sprite2(VGAScreen, *out_shotx+1, *out_shoty, shapesW2, sprite_frame - 500); + } + else + { + if (background2 && *out_shoty + shadowYDist < 190 && tmp_shotXM < 100) + blit_sprite2_darken(VGAScreen, *out_shotx+1, *out_shoty + shadowYDist, shapesC1, sprite_frame); + blit_sprite2(VGAScreen, *out_shotx+1, *out_shoty, shapesC1, sprite_frame); + } + } + } + + return true; +} + +JE_integer player_shot_create( JE_word portNum, uint bay_i, JE_word PX, JE_word PY, JE_word mouseX, JE_word mouseY, JE_word wpNum, JE_byte playerNum ) +{ + static const JE_byte soundChannel[11] /* [1..11] */ = {0, 2, 4, 4, 2, 2, 5, 5, 1, 4, 1}; + + // Bounds check + if (portNum > PORT_NUM || wpNum <= 0 || wpNum > WEAP_NUM) + return MAX_PWEAPON; + + const JE_WeaponType* weapon = &weapons[wpNum]; + + if (power < weaponPort[portNum].poweruse) + return MAX_PWEAPON; + power -= weaponPort[portNum].poweruse; + + if (weapon->sound > 0) + soundQueue[soundChannel[bay_i]] = weapon->sound; + + int shot_id = MAX_PWEAPON; + /*Rot*/ + for (int multi_i = 1; multi_i <= weapon->multi; multi_i++) + { + for (shot_id = 0; shot_id < MAX_PWEAPON; shot_id++) + if (shotAvail[shot_id] == 0) + break; + if (shot_id == MAX_PWEAPON) + return MAX_PWEAPON; + + if (shotMultiPos[bay_i] == weapon->max || shotMultiPos[bay_i] > 8) + shotMultiPos[bay_i] = 1; + else + shotMultiPos[bay_i]++; + + PlayerShotDataType* shot = &playerShotData[shot_id]; + shot->chainReaction = 0; + + shot->playerNumber = playerNum; + + shot->shotAni = 0; + + shot->shotComplicated = weapon->circlesize != 0; + + if (weapon->circlesize == 0) + { + shot->shotDevX = 0; + shot->shotDirX = 0; + shot->shotDevY = 0; + shot->shotDirY = 0; + shot->shotCirSizeX = 0; + shot->shotCirSizeY = 0; + } + else + { + JE_byte circsize = weapon->circlesize; + + if (circsize > 19) + { + JE_byte circsize_mod20 = circsize % 20; + shot->shotCirSizeX = circsize_mod20; + shot->shotDevX = circsize_mod20 >> 1; + + circsize = circsize / 20; + shot->shotCirSizeY = circsize; + shot->shotDevY = circsize >> 1; + } + else + { + shot->shotCirSizeX = circsize; + shot->shotCirSizeY = circsize; + shot->shotDevX = circsize >> 1; + shot->shotDevY = circsize >> 1; + } + shot->shotDirX = 1; + shot->shotDirY = -1; + } + + shot->shotTrail = weapon->trail; + + if (weapon->attack[shotMultiPos[bay_i]-1] > 99 && weapon->attack[shotMultiPos[bay_i]-1] < 250) + { + shot->chainReaction = weapon->attack[shotMultiPos[bay_i]-1] - 100; + shot->shotDmg = 1; + } + else + { + shot->shotDmg = weapon->attack[shotMultiPos[bay_i]-1]; + } + + shot->shotBlastFilter = weapon->shipblastfilter; + + JE_integer tmp_by = weapon->by[shotMultiPos[bay_i]-1]; + + /*Note: Only front selection used for player shots...*/ + + shot->shotX = PX + weapon->bx[shotMultiPos[bay_i]-1]; + + shot->shotY = PY + tmp_by; + shot->shotYC = -weapon->acceleration; + shot->shotXC = weapon->accelerationx; + + shot->shotXM = weapon->sx[shotMultiPos[bay_i]-1]; + + // Not sure what this field does exactly. + JE_byte del = weapon->del[shotMultiPos[bay_i]-1]; + + if (del == 121) + { + shot->shotTrail = 0; + del = 255; + } + + shot->shotGr = weapon->sg[shotMultiPos[bay_i]-1]; + if (shot->shotGr == 0) + shotAvail[shot_id] = 0; + else + shotAvail[shot_id] = del; + + if (del > 100 && del < 120) + shot->shotAniMax = (del - 100 + 1); + else + shot->shotAniMax = weapon->weapani + 1; + + if (del == 99 || del == 98) + { + tmp_by = PX - mouseX; + if (tmp_by < -5) + tmp_by = -5; + else if (tmp_by > 5) + tmp_by = 5; + shot->shotXM += tmp_by; + } + + if (del == 99 || del == 100) + { + tmp_by = PY - mouseY - weapon->sy[shotMultiPos[bay_i]-1]; + if (tmp_by < -4) + tmp_by = -4; + else if (tmp_by > 4) + tmp_by = 4; + shot->shotYM = tmp_by; + } + else if (weapon->sy[shotMultiPos[bay_i]-1] == 98) + { + shot->shotYM = 0; + shot->shotYC = -1; + } + else if (weapon->sy[shotMultiPos[bay_i]-1] > 100) + { + shot->shotYM = weapon->sy[shotMultiPos[bay_i]-1]; + shot->shotY -= player[shot->playerNumber-1].delta_y_shot_move; + } + else + { + shot->shotYM = -weapon->sy[shotMultiPos[bay_i]-1]; + } + + if (weapon->sx[shotMultiPos[bay_i]-1] > 100) + { + shot->shotXM = weapon->sx[shotMultiPos[bay_i]-1]; + shot->shotX -= player[shot->playerNumber-1].delta_x_shot_move; + if (shot->shotXM == 101) + shot->shotY -= player[shot->playerNumber-1].delta_y_shot_move; + } + + + if (weapon->aim > 5) /*Guided Shot*/ + { + uint best_dist = 65000; + JE_byte closest_enemy = 0; + /*Find Closest Enemy*/ + for (x = 0; x < 100; x++) + { + if (enemyAvail[x] != 1 && !enemy[x].scoreitem) + { + y = abs(enemy[x].ex - shot->shotX) + abs(enemy[x].ey - shot->shotY); + if (y < best_dist) + { + best_dist = y; + closest_enemy = x + 1; + } + } + } + shot->aimAtEnemy = closest_enemy; + shot->aimDelay = 5; + shot->aimDelayMax = weapon->aim - 5; + } + else + { + shot->aimAtEnemy = 0; + } + + shotRepeat[bay_i] = weapon->shotrepeat; + } + + return shot_id; +} diff --git a/contrib/games/opentyrian/src/shots.h b/contrib/games/opentyrian/src/shots.h new file mode 100644 index 000000000..ea4a11155 --- /dev/null +++ b/contrib/games/opentyrian/src/shots.h @@ -0,0 +1,58 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2013 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef SHOTS_H +#define SHOTS_H +#include "opentyr.h" + +typedef struct { + JE_integer shotX, shotY, shotXM, shotYM, shotXC, shotYC; + JE_boolean shotComplicated; + JE_integer shotDevX, shotDirX, shotDevY, shotDirY, shotCirSizeX, shotCirSizeY; + JE_byte shotTrail; + JE_word shotGr, shotAni, shotAniMax; + Uint8 shotDmg; + JE_byte shotBlastFilter, chainReaction, playerNumber, aimAtEnemy, aimDelay, aimDelayMax; +} PlayerShotDataType; + +#define MAX_PWEAPON 81 /* 81*/ +extern PlayerShotDataType playerShotData[MAX_PWEAPON + 1]; +extern JE_byte shotAvail[MAX_PWEAPON]; + +/** Used in the shop to show weapon previews. */ +void simulate_player_shots( void ); + +/** Points shot movement in the specified direction. Used for the turret gun. */ +void player_shot_set_direction( JE_integer shot_id, uint weapon_id, JE_real direction ); + +/** Moves and draws a shot. Does \b not collide it with enemies. + * \return False if the shot went offscreen, true otherwise. + */ +bool player_shot_move_and_draw( + int shot_id, bool* out_is_special, + int* out_shotx, int* out_shoty, + JE_integer* out_shot_damage, JE_byte* out_blast_filter, + JE_byte* out_chain, JE_byte* out_playerNum, + JE_word* out_special_radiusw, JE_word* out_special_radiush ); + +/** Creates a player shot. */ +JE_integer player_shot_create( JE_word portnum, uint shot_i, JE_word px, JE_word py, + JE_word mousex, JE_word mousey, + JE_word wpnum, JE_byte playernum ); + +#endif // SHOTS_H diff --git a/contrib/games/opentyrian/src/sizebuf.c b/contrib/games/opentyrian/src/sizebuf.c new file mode 100644 index 000000000..75b36c1de --- /dev/null +++ b/contrib/games/opentyrian/src/sizebuf.c @@ -0,0 +1,224 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * This file is largely based on (and named after) a set of common reading/ + * writing functions used in Quake engines. Its purpose is to allow extraction + * of bytes, words, and dwords in a safe, endian adjused environment and should + * probably be used in any situation where checking for buffer overflows + * manually makes the code a godawful mess. + * + * Currently this is only used by the animation decoding. + * + * This file is written with the intention of being easily converted into a + * class capable of throwing exceptions if data is out of range. + * + * If an operation fails, subsequent operations will also fail. The sizebuf + * is assumed to be in an invalid state. This COULD be changed pretty easily + * and in normal Quake IIRC it is. But our MO is to bail on failure, not + * figure out what went wrong (making throws perfect). + */ +#include "sizebuf.h" + +#include "SDL_endian.h" + +#include +#include +#include + +/* Construct buffer with the passed array and size */ +void SZ_Init(sizebuf_t * sz, Uint8 * buf, unsigned int size) +{ + sz->data = buf; + sz->bufferLen = size; + sz->bufferPos = 0; + sz->error = false; +} +/* Check error flags */ +bool SZ_Error(sizebuf_t * sz) +{ + return(sz->error); +} +/* mimic memset */ +void SZ_Memset(sizebuf_t * sz, int value, size_t count) +{ + /* Do bounds checking before writing */ + if (sz->error || sz->bufferPos + count > sz->bufferLen) + { + sz->error = true; + return; + } + + /* Memset and increment pointer */ + memset(sz->data + sz->bufferPos, value, count); + sz->bufferPos += count; +} +/* Mimic memcpy. Two versions, one for buffers, one for sizebuf objects. + * Overload in C++. */ +void SZ_Memcpy(sizebuf_t * sz, const Uint8 * buf, size_t count) +{ + /* State checking */ + if (sz->error || sz->bufferPos + count > sz->bufferLen) + { + sz->error = true; + return; + } + + /* Memcpy & increment */ + memcpy(sz->data + sz->bufferPos, buf, count); + sz->bufferPos += count; +} +void SZ_Memcpy2(sizebuf_t * sz, sizebuf_t * bf, size_t count) +{ + /* State checking */ + if (sz->error || sz->bufferPos + count > sz->bufferLen) + { + sz->error = true; + return; + } + if (bf->error || bf->bufferPos + count > bf->bufferLen) + { + bf->error = true; + return; + } + + /* Memcpy & increment */ + memcpy(sz->data + sz->bufferPos, bf->data + bf->bufferPos, count); + sz->bufferPos += count; + bf->bufferPos += count; +} +/* Reposition buffer pointer */ +void SZ_Seek(sizebuf_t * sz, long count, int mode) +{ + /* Okay, it's reasonable to reset the error bool on seeking... */ + + switch(mode) + { + case SEEK_SET: + sz->bufferPos = count; + break; + case SEEK_CUR: + sz->bufferPos += count; + break; + case SEEK_END: + sz->bufferPos = sz->bufferLen - count; + break; + default: + assert(false); + } + + /* Check errors */ + if (sz->bufferPos > sz->bufferLen) + { + sz->error = true; + } else { + sz->error = false; + } +} +const Uint8 * SZ_GetCurBufferPtr (sizebuf_t * sz) +{ + return(sz->data); +} + +/* The code below makes use of pointer casts, similar to what is in efread. + * It's not the ONLY way to write ints to a stream, but it's probably the + * cleanest of the lot. Better to have it here than littered all over the code. + */ +void MSG_WriteByte(sizebuf_t * sz, unsigned int value) +{ + if (sz->error || sz->bufferPos + 1 > sz->bufferLen) + { + sz->error = true; + return; + } + + sz->data[sz->bufferPos] = value; + sz->bufferPos++; +} +void MSG_WriteWord(sizebuf_t * sz, unsigned int value) +{ + if (sz->error || sz->bufferPos + 2 > sz->bufferLen) + { + sz->error = true; + return; + } + + *((Uint16 *)(sz->data + sz->bufferPos)) = SDL_SwapLE16( ((Uint16)value) ); + sz->bufferPos += 2; +} +void MSG_WriteDWord(sizebuf_t * sz, unsigned int value) +{ + if (sz->error || sz->bufferPos + 4 > sz->bufferLen) + { + sz->error = true; + return; + } + + *((Uint32 *)(sz->data + sz->bufferPos)) = SDL_SwapLE32( ((Uint32)value) ); + sz->bufferPos += 4; +} + +unsigned int MSG_ReadByte(sizebuf_t * sz) +{ + unsigned int ret; + + + if (sz->error || sz->bufferPos + 1 > sz->bufferLen) + { + sz->error = true; + return(0); + } + + ret = sz->data[sz->bufferPos]; + sz->bufferPos += 1; + + return(ret); +} +unsigned int MSG_ReadWord(sizebuf_t * sz) +{ + unsigned int ret; + + + if (sz->error || sz->bufferPos + 2 > sz->bufferLen) + { + sz->error = true; + return(0); + } + + ret = SDL_SwapLE16(*((Uint16 *)(sz->data + sz->bufferPos))); + sz->bufferPos += 2; + + return(ret); +} +unsigned int MSG_ReadDWord(sizebuf_t * sz) +{ + unsigned int ret; + + + if (sz->error || sz->bufferPos + 4 > sz->bufferLen) + { + sz->error = true; + return(0); + } + + ret = SDL_SwapLE32(*((Uint32 *)(sz->data + sz->bufferPos))); + sz->bufferPos += 4; + + return(ret); +} diff --git a/contrib/games/opentyrian/src/sizebuf.h b/contrib/games/opentyrian/src/sizebuf.h new file mode 100644 index 000000000..b12f2cb76 --- /dev/null +++ b/contrib/games/opentyrian/src/sizebuf.h @@ -0,0 +1,50 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#ifndef SIZEBUF_H +#define SIZEBUF_H + +#include "opentyr.h" + +typedef struct sizebuf_s +{ + Uint8 *data; + unsigned int bufferLen; + unsigned int bufferPos; + bool error; +} sizebuf_t; + +void SZ_Init ( sizebuf_t *, Uint8 *, unsigned int ); /* C style constructor */ +bool SZ_Error ( sizebuf_t * ); +void SZ_Memset ( sizebuf_t *, int, size_t ); /* memset with a sizebuf */ +void SZ_Memcpy ( sizebuf_t *, const Uint8 *, size_t ); /* memcpy with a normal buffer */ +void SZ_Memcpy2 ( sizebuf_t *, sizebuf_t *, size_t ); /* memcpy with a sizebuf */ +void SZ_Seek ( sizebuf_t *, long, int ); /* fseek with a sizebuf. */ + +const Uint8 * SZ_GetCurBufferPtr ( sizebuf_t * ); /* Mimic private member, const return */ + +void MSG_WriteByte ( sizebuf_t *, unsigned int ); +void MSG_WriteWord ( sizebuf_t *, unsigned int ); +void MSG_WriteDWord ( sizebuf_t *, unsigned int ); + +unsigned int MSG_ReadByte ( sizebuf_t * ); +unsigned int MSG_ReadWord ( sizebuf_t * ); +unsigned int MSG_ReadDWord ( sizebuf_t * ); + +#endif diff --git a/contrib/games/opentyrian/src/sndmast.c b/contrib/games/opentyrian/src/sndmast.c new file mode 100644 index 000000000..dd6133a27 --- /dev/null +++ b/contrib/games/opentyrian/src/sndmast.c @@ -0,0 +1,77 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "sndmast.h" + +#include "opentyr.h" + +const char soundTitle[SAMPLE_COUNT][9] = /* [1..soundnum + 9] of string [8] */ +{ + "SCALEDN2", /*1*/ + "F2", /*2*/ + "TEMP10", + "EXPLSM", + "PASS3", /*5*/ + "TEMP2", + "BYPASS1", + "EXP1RT", + "EXPLLOW", + "TEMP13", /*10*/ + "EXPRETAP", + "MT2BOOM", + "TEMP3", + "LAZB", /*28K*/ + "LAZGUN2", /*15*/ + "SPRING", + "WARNING", + "ITEM", + "HIT2", /*14K*/ + "MACHNGUN", /*20*/ + "HYPERD2", + "EXPLHUG", + "CLINK1", + "CLICK", + "SCALEDN1", /*25*/ + "TEMP11", + "TEMP16", + "SMALL1", + "POWERUP", + "VOICE1", + "VOICE2", + "VOICE3", + "VOICE4", + "VOICE5", + "VOICE6", + "VOICE7", + "VOICE8", + "VOICE9" +}; + +const JE_byte windowTextSamples[9] = /* [1..9] */ +{ + V_DANGER, + V_BOSS, + V_ENEMIES, + V_CLEARED_PLATFORM, + V_DANGER, + V_SPIKES, + V_ACCELERATE, + V_DANGER, + V_ENEMIES +}; + diff --git a/contrib/games/opentyrian/src/sndmast.h b/contrib/games/opentyrian/src/sndmast.h new file mode 100644 index 000000000..471f8863f --- /dev/null +++ b/contrib/games/opentyrian/src/sndmast.h @@ -0,0 +1,74 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef SNDMAST_H +#define SNDMAST_H + +#include "opentyr.h" + +#define SAMPLE_COUNT 38 + +enum +{ + S_NONE = 0, + S_WEAPON_1 = 1, + S_WEAPON_2 = 2, + S_ENEMY_HIT = 3, + S_EXPLOSION_4 = 4, + S_WEAPON_5 = 5, + S_WEAPON_6 = 6, + S_WEAPON_7 = 7, + S_SELECT = 8, + S_EXPLOSION_8 = 8, + S_EXPLOSION_9 = 9, + S_WEAPON_10 = 10, + S_EXPLOSION_11 = 11, + S_EXPLOSION_12 = 12, + S_WEAPON_13 = 13, + S_WEAPON_14 = 14, + S_WEAPON_15 = 15, + S_SPRING = 16, + S_WARNING = 17, + S_ITEM = 18, + S_HULL_HIT = 19, + S_MACHINE_GUN = 20, + S_SOUL_OF_ZINGLON = 21, + S_EXPLOSION_22 = 22, + S_CLINK = 23, + S_CLICK = 24, + S_WEAPON_25 = 25, + S_WEAPON_26 = 26, + S_SHIELD_HIT = 27, + S_CURSOR = 28, + S_POWERUP = 29, + V_CLEARED_PLATFORM = 30, // "Cleared enemy platform." + V_BOSS = 31, // "Large enemy approaching." + V_ENEMIES = 32, // "Enemies ahead." + V_GOOD_LUCK = 33, // "Good luck." + V_LEVEL_END = 34, // "Level completed." + V_DANGER = 35, // "Danger." + V_SPIKES = 36, // "Warning: spikes ahead." + V_DATA_CUBE = 37, // "Data acquired." + V_ACCELERATE = 38, // "Unexplained speed increase." +}; + +extern const char soundTitle[SAMPLE_COUNT][9]; +extern const JE_byte windowTextSamples[9]; + +#endif /* SNDMAST_H */ + diff --git a/contrib/games/opentyrian/src/sprite.c b/contrib/games/opentyrian/src/sprite.c new file mode 100644 index 000000000..33e23e62f --- /dev/null +++ b/contrib/games/opentyrian/src/sprite.c @@ -0,0 +1,734 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "sprite.h" + +#include "file.h" +#include "opentyr.h" +#include "video.h" + +#include +#include + +Sprite_array sprite_table[SPRITE_TABLES_MAX]; + +Sprite2_array eShapes[6]; +Sprite2_array shapesC1, shapes6, shapes9, shapesW2; + +void load_sprites_file( unsigned int table, const char *filename ) +{ + free_sprites(table); + + FILE *f = dir_fopen_die(data_dir(), filename, "rb"); + + load_sprites(table, f); + + fclose(f); +} + +void load_sprites( unsigned int table, FILE *f ) +{ + free_sprites(table); + + Uint16 temp; + efread(&temp, sizeof(Uint16), 1, f); + + sprite_table[table].count = temp; + + assert(sprite_table[table].count <= SPRITES_PER_TABLE_MAX); + + for (unsigned int i = 0; i < sprite_table[table].count; ++i) + { + Sprite * const cur_sprite = sprite(table, i); + + if (!getc(f)) // sprite is empty + continue; + + efread(&cur_sprite->width, sizeof(Uint16), 1, f); + efread(&cur_sprite->height, sizeof(Uint16), 1, f); + efread(&cur_sprite->size, sizeof(Uint16), 1, f); + + cur_sprite->data = malloc(cur_sprite->size); + + efread(cur_sprite->data, sizeof(Uint8), cur_sprite->size, f); + } +} + +void free_sprites( unsigned int table ) +{ + for (unsigned int i = 0; i < sprite_table[table].count; ++i) + { + Sprite * const cur_sprite = sprite(table, i); + + cur_sprite->width = 0; + cur_sprite->height = 0; + cur_sprite->size = 0; + + free(cur_sprite->data); + cur_sprite->data = NULL; + } + + sprite_table[table].count = 0; +} + +// does not clip on left or right edges of surface +void blit_sprite( SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index ) +{ + if (index >= sprite_table[table].count || !sprite_exists(table, index)) + { + assert(false); + return; + } + + const Sprite * const cur_sprite = sprite(table, index); + + const Uint8 *data = cur_sprite->data; + const Uint8 * const data_ul = data + cur_sprite->size; + + const unsigned int width = cur_sprite->width; + unsigned int x_offset = 0; + + assert(surface->format->BitsPerPixel == 8); + Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; + const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit + * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit + + for (; data < data_ul; ++data) + { + switch (*data) + { + case 255: // transparent pixels + data++; // next byte tells how many + pixels += *data; + x_offset += *data; + break; + + case 254: // next pixel row + pixels += width - x_offset; + x_offset = width; + break; + + case 253: // 1 transparent pixel + pixels++; + x_offset++; + break; + + default: // set a pixel + if (pixels >= pixels_ul) + return; + if (pixels >= pixels_ll) + *pixels = *data; + + pixels++; + x_offset++; + break; + } + if (x_offset >= width) + { + pixels += surface->pitch - x_offset; + x_offset = 0; + } + } +} + +// does not clip on left or right edges of surface +void blit_sprite_blend( SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index ) +{ + if (index >= sprite_table[table].count || !sprite_exists(table, index)) + { + assert(false); + return; + } + + const Sprite * const cur_sprite = sprite(table, index); + + const Uint8 *data = cur_sprite->data; + const Uint8 * const data_ul = data + cur_sprite->size; + + const unsigned int width = cur_sprite->width; + unsigned int x_offset = 0; + + assert(surface->format->BitsPerPixel == 8); + Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; + const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit + * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit + + for (; data < data_ul; ++data) + { + switch (*data) + { + case 255: // transparent pixels + data++; // next byte tells how many + pixels += *data; + x_offset += *data; + break; + + case 254: // next pixel row + pixels += width - x_offset; + x_offset = width; + break; + + case 253: // 1 transparent pixel + pixels++; + x_offset++; + break; + + default: // set a pixel + if (pixels >= pixels_ul) + return; + if (pixels >= pixels_ll) + *pixels = (*data & 0xf0) | (((*pixels & 0x0f) + (*data & 0x0f)) / 2); + + pixels++; + x_offset++; + break; + } + if (x_offset >= width) + { + pixels += surface->pitch - x_offset; + x_offset = 0; + } + } +} + +// does not clip on left or right edges of surface +// unsafe because it doesn't check that value won't overflow into hue +// we can replace it when we know that we don't rely on that 'feature' +void blit_sprite_hv_unsafe( SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index, Uint8 hue, Sint8 value ) +{ + if (index >= sprite_table[table].count || !sprite_exists(table, index)) + { + assert(false); + return; + } + + hue <<= 4; + + const Sprite * const cur_sprite = sprite(table, index); + + const Uint8 *data = cur_sprite->data; + const Uint8 * const data_ul = data + cur_sprite->size; + + const unsigned int width = cur_sprite->width; + unsigned int x_offset = 0; + + assert(surface->format->BitsPerPixel == 8); + Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; + const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit + * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit + + for (; data < data_ul; ++data) + { + switch (*data) + { + case 255: // transparent pixels + data++; // next byte tells how many + pixels += *data; + x_offset += *data; + break; + + case 254: // next pixel row + pixels += width - x_offset; + x_offset = width; + break; + + case 253: // 1 transparent pixel + pixels++; + x_offset++; + break; + + default: // set a pixel + if (pixels >= pixels_ul) + return; + if (pixels >= pixels_ll) + *pixels = hue | ((*data & 0x0f) + value); + + pixels++; + x_offset++; + break; + } + if (x_offset >= width) + { + pixels += surface->pitch - x_offset; + x_offset = 0; + } + } +} + +// does not clip on left or right edges of surface +void blit_sprite_hv( SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index, Uint8 hue, Sint8 value ) +{ + if (index >= sprite_table[table].count || !sprite_exists(table, index)) + { + assert(false); + return; + } + + hue <<= 4; + + const Sprite * const cur_sprite = sprite(table, index); + + const Uint8 *data = cur_sprite->data; + const Uint8 * const data_ul = data + cur_sprite->size; + + const unsigned int width = cur_sprite->width; + unsigned int x_offset = 0; + + assert(surface->format->BitsPerPixel == 8); + Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; + const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit + * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit + + for (; data < data_ul; ++data) + { + switch (*data) + { + case 255: // transparent pixels + data++; // next byte tells how many + pixels += *data; + x_offset += *data; + break; + + case 254: // next pixel row + pixels += width - x_offset; + x_offset = width; + break; + + case 253: // 1 transparent pixel + pixels++; + x_offset++; + break; + + default: // set a pixel + if (pixels >= pixels_ul) + return; + if (pixels >= pixels_ll) + { + Uint8 temp_value = (*data & 0x0f) + value; + if (temp_value > 0xf) + temp_value = (temp_value >= 0x1f) ? 0x0 : 0xf; + + *pixels = hue | temp_value; + } + + pixels++; + x_offset++; + break; + } + if (x_offset >= width) + { + pixels += surface->pitch - x_offset; + x_offset = 0; + } + } +} + +// does not clip on left or right edges of surface +void blit_sprite_hv_blend( SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index, Uint8 hue, Sint8 value ) +{ + if (index >= sprite_table[table].count || !sprite_exists(table, index)) + { + assert(false); + return; + } + + hue <<= 4; + + const Sprite * const cur_sprite = sprite(table, index); + + const Uint8 *data = cur_sprite->data; + const Uint8 * const data_ul = data + cur_sprite->size; + + const unsigned int width = cur_sprite->width; + unsigned int x_offset = 0; + + assert(surface->format->BitsPerPixel == 8); + Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; + const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit + * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit + + for (; data < data_ul; ++data) + { + switch (*data) + { + case 255: // transparent pixels + data++; // next byte tells how many + pixels += *data; + x_offset += *data; + break; + + case 254: // next pixel row + pixels += width - x_offset; + x_offset = width; + break; + + case 253: // 1 transparent pixel + pixels++; + x_offset++; + break; + + default: // set a pixel + if (pixels >= pixels_ul) + return; + if (pixels >= pixels_ll) + { + Uint8 temp_value = (*data & 0x0f) + value; + if (temp_value > 0xf) + temp_value = (temp_value >= 0x1f) ? 0x0 : 0xf; + + *pixels = hue | (((*pixels & 0x0f) + temp_value) / 2); + } + + pixels++; + x_offset++; + break; + } + if (x_offset >= width) + { + pixels += surface->pitch - x_offset; + x_offset = 0; + } + } +} + +// does not clip on left or right edges of surface +void blit_sprite_dark( SDL_Surface *surface, int x, int y, unsigned int table, unsigned int index, bool black ) +{ + if (index >= sprite_table[table].count || !sprite_exists(table, index)) + { + assert(false); + return; + } + + const Sprite * const cur_sprite = sprite(table, index); + + const Uint8 *data = cur_sprite->data; + const Uint8 * const data_ul = data + cur_sprite->size; + + const unsigned int width = cur_sprite->width; + unsigned int x_offset = 0; + + assert(surface->format->BitsPerPixel == 8); + Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; + const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit + * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit + + for (; data < data_ul; ++data) + { + switch (*data) + { + case 255: // transparent pixels + data++; // next byte tells how many + pixels += *data; + x_offset += *data; + break; + + case 254: // next pixel row + pixels += width - x_offset; + x_offset = width; + break; + + case 253: // 1 transparent pixel + pixels++; + x_offset++; + break; + + default: // set a pixel + if (pixels >= pixels_ul) + return; + if (pixels >= pixels_ll) + *pixels = black ? 0x00 : ((*pixels & 0xf0) | ((*pixels & 0x0f) / 2)); + + pixels++; + x_offset++; + break; + } + if (x_offset >= width) + { + pixels += surface->pitch - x_offset; + x_offset = 0; + } + } +} + + +void JE_loadCompShapes( Sprite2_array *sprite2s, char s ) +{ + char buffer[20]; + snprintf(buffer, sizeof(buffer), "newsh%c.shp", tolower((unsigned char)s)); + + FILE *f = dir_fopen_die(data_dir(), buffer, "rb"); + + sprite2s->size = ftell_eof(f); + + JE_loadCompShapesB(sprite2s, f); + + fclose(f); +} + +void JE_loadCompShapesB( Sprite2_array *sprite2s, FILE *f ) +{ + free_sprite2s(sprite2s); + + sprite2s->data = malloc(sizeof(Uint8) * sprite2s->size); + efread(sprite2s->data, sizeof(Uint8), sprite2s->size, f); +} + +void free_sprite2s( Sprite2_array *sprite2s ) +{ + free(sprite2s->data); + sprite2s->data = NULL; +} + +// does not clip on left or right edges of surface +void blit_sprite2( SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index ) +{ + assert(surface->format->BitsPerPixel == 8); + Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; + const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit + * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit + + const Uint8 *data = sprite2s.data + SDL_SwapLE16(((Uint16 *)sprite2s.data)[index - 1]); + + for (; *data != 0x0f; ++data) + { + pixels += *data & 0x0f; // second nibble: transparent pixel count + unsigned int count = (*data & 0xf0) >> 4; // first nibble: opaque pixel count + + if (count == 0) // move to next pixel row + { + pixels += VGAScreen->pitch - 12; + } + else + { + while (count--) + { + ++data; + + if (pixels >= pixels_ul) + return; + if (pixels >= pixels_ll) + *pixels = *data; + + ++pixels; + } + } + } +} + +// does not clip on left or right edges of surface +void blit_sprite2_blend( SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index ) +{ + assert(surface->format->BitsPerPixel == 8); + Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; + const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit + * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit + + const Uint8 *data = sprite2s.data + SDL_SwapLE16(((Uint16 *)sprite2s.data)[index - 1]); + + for (; *data != 0x0f; ++data) + { + pixels += *data & 0x0f; // second nibble: transparent pixel count + unsigned int count = (*data & 0xf0) >> 4; // first nibble: opaque pixel count + + if (count == 0) // move to next pixel row + { + pixels += VGAScreen->pitch - 12; + } + else + { + while (count--) + { + ++data; + + if (pixels >= pixels_ul) + return; + if (pixels >= pixels_ll) + *pixels = (((*data & 0x0f) + (*pixels & 0x0f)) / 2) | (*data & 0xf0); + + ++pixels; + } + } + } +} + +// does not clip on left or right edges of surface +void blit_sprite2_darken( SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index ) +{ + assert(surface->format->BitsPerPixel == 8); + Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; + const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit + * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit + + const Uint8 *data = sprite2s.data + SDL_SwapLE16(((Uint16 *)sprite2s.data)[index - 1]); + + for (; *data != 0x0f; ++data) + { + pixels += *data & 0x0f; // second nibble: transparent pixel count + unsigned int count = (*data & 0xf0) >> 4; // first nibble: opaque pixel count + + if (count == 0) // move to next pixel row + { + pixels += VGAScreen->pitch - 12; + } + else + { + while (count--) + { + ++data; + + if (pixels >= pixels_ul) + return; + if (pixels >= pixels_ll) + *pixels = ((*pixels & 0x0f) / 2) + (*pixels & 0xf0); + + ++pixels; + } + } + } +} + +// does not clip on left or right edges of surface +void blit_sprite2_filter( SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index, Uint8 filter ) +{ + assert(surface->format->BitsPerPixel == 8); + Uint8 * pixels = (Uint8 *)surface->pixels + (y * surface->pitch) + x; + const Uint8 * const pixels_ll = (Uint8 *)surface->pixels, // lower limit + * const pixels_ul = (Uint8 *)surface->pixels + (surface->h * surface->pitch); // upper limit + + const Uint8 *data = sprite2s.data + SDL_SwapLE16(((Uint16 *)sprite2s.data)[index - 1]); + + for (; *data != 0x0f; ++data) + { + pixels += *data & 0x0f; // second nibble: transparent pixel count + unsigned int count = (*data & 0xf0) >> 4; // first nibble: opaque pixel count + + if (count == 0) // move to next pixel row + { + pixels += VGAScreen->pitch - 12; + } + else + { + while (count--) + { + ++data; + + if (pixels >= pixels_ul) + return; + if (pixels >= pixels_ll) + *pixels = filter | (*data & 0x0f); + + ++pixels; + } + } + } +} + +// does not clip on left or right edges of surface +void blit_sprite2x2( SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index ) +{ + blit_sprite2(surface, x, y, sprite2s, index); + blit_sprite2(surface, x + 12, y, sprite2s, index + 1); + blit_sprite2(surface, x, y + 14, sprite2s, index + 19); + blit_sprite2(surface, x + 12, y + 14, sprite2s, index + 20); +} + +// does not clip on left or right edges of surface +void blit_sprite2x2_blend( SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index ) +{ + blit_sprite2_blend(surface, x, y, sprite2s, index); + blit_sprite2_blend(surface, x + 12, y, sprite2s, index + 1); + blit_sprite2_blend(surface, x, y + 14, sprite2s, index + 19); + blit_sprite2_blend(surface, x + 12, y + 14, sprite2s, index + 20); +} + +// does not clip on left or right edges of surface +void blit_sprite2x2_darken( SDL_Surface *surface, int x, int y, Sprite2_array sprite2s, unsigned int index ) +{ + blit_sprite2_darken(surface, x, y, sprite2s, index); + blit_sprite2_darken(surface, x + 12, y, sprite2s, index + 1); + blit_sprite2_darken(surface, x, y + 14, sprite2s, index + 19); + blit_sprite2_darken(surface, x + 12, y + 14, sprite2s, index + 20); +} + + +void JE_loadMainShapeTables( const char *shpfile ) +{ + enum { SHP_NUM = 12 }; + + FILE *f = dir_fopen_die(data_dir(), shpfile, "rb"); + + JE_word shpNumb; + JE_longint shpPos[SHP_NUM + 1]; // +1 for storing file length + + efread(&shpNumb, sizeof(JE_word), 1, f); + assert(shpNumb + 1u == COUNTOF(shpPos)); + + for (unsigned int i = 0; i < shpNumb; ++i) + efread(&shpPos[i], sizeof(JE_longint), 1, f); + + fseek(f, 0, SEEK_END); + for (unsigned int i = shpNumb; i < COUNTOF(shpPos); ++i) + shpPos[i] = ftell(f); + + int i; + // fonts, interface, option sprites + for (i = 0; i < 7; i++) + { + fseek(f, shpPos[i], SEEK_SET); + load_sprites(i, f); + } + + // player shot sprites + shapesC1.size = shpPos[i + 1] - shpPos[i]; + JE_loadCompShapesB(&shapesC1, f); + i++; + + // player ship sprites + shapes9.size = shpPos[i + 1] - shpPos[i]; + JE_loadCompShapesB(&shapes9 , f); + i++; + + // power-up sprites + eShapes[5].size = shpPos[i + 1] - shpPos[i]; + JE_loadCompShapesB(&eShapes[5], f); + i++; + + // coins, datacubes, etc sprites + eShapes[4].size = shpPos[i + 1] - shpPos[i]; + JE_loadCompShapesB(&eShapes[4], f); + i++; + + // more player shot sprites + shapesW2.size = shpPos[i + 1] - shpPos[i]; + JE_loadCompShapesB(&shapesW2, f); + + fclose(f); +} + +void free_main_shape_tables( void ) +{ + for (uint i = 0; i < COUNTOF(sprite_table); ++i) + free_sprites(i); + + free_sprite2s(&shapesC1); + free_sprite2s(&shapes9); + free_sprite2s(&eShapes[5]); + free_sprite2s(&eShapes[4]); + free_sprite2s(&shapesW2); +} diff --git a/contrib/games/opentyrian/src/sprite.h b/contrib/games/opentyrian/src/sprite.h new file mode 100644 index 000000000..979daf2e8 --- /dev/null +++ b/contrib/games/opentyrian/src/sprite.h @@ -0,0 +1,116 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef SPRITE_H +#define SPRITE_H + +#include "opentyr.h" + +#include "SDL.h" + +#include +#include + +#define FONT_SHAPES 0 +#define SMALL_FONT_SHAPES 1 +#define TINY_FONT 2 +#define PLANET_SHAPES 3 +#define FACE_SHAPES 4 +#define OPTION_SHAPES 5 /*Also contains help shapes*/ +#define WEAPON_SHAPES 6 +#define EXTRA_SHAPES 7 /*Used for Ending pics*/ + +#define SPRITE_TABLES_MAX 8 +#define SPRITES_PER_TABLE_MAX 151 + +typedef struct +{ + Uint16 width, height; + Uint16 size; + Uint8 *data; +} +Sprite; + +typedef struct +{ + unsigned int count; + Sprite sprite[SPRITES_PER_TABLE_MAX]; +} +Sprite_array; + +extern Sprite_array sprite_table[SPRITE_TABLES_MAX]; + +static inline Sprite *sprite( unsigned int table, unsigned int index ) +{ + assert(table < COUNTOF(sprite_table)); + assert(index < COUNTOF(sprite_table->sprite)); + return &sprite_table[table].sprite[index]; +} + +static inline bool sprite_exists( unsigned int table, unsigned int index ) +{ + return (sprite(table, index)->data != NULL); +} +static inline Uint16 get_sprite_width( unsigned int table, unsigned int index ) +{ + return (sprite_exists(table, index) ? sprite(table, index)->width : 0); +} +static inline Uint16 get_sprite_height( unsigned int table, unsigned int index ) +{ + return (sprite_exists(table, index) ? sprite(table, index)->height : 0); +} + +void load_sprites_file( unsigned table, const char *filename ); +void load_sprites( unsigned int table, FILE *f ); +void free_sprites( unsigned int table ); + +void blit_sprite( SDL_Surface *, int x, int y, unsigned int table, unsigned int index ); // JE_newDrawCShapeNum +void blit_sprite_blend( SDL_Surface *, int x, int y, unsigned int table, unsigned int index ); // JE_newDrawCShapeTrick +void blit_sprite_hv_unsafe( SDL_Surface *, int x, int y, unsigned int table, unsigned int index, Uint8 hue, Sint8 value ); // JE_newDrawCShapeBright +void blit_sprite_hv( SDL_Surface *, int x, int y, unsigned int table, unsigned int index, Uint8 hue, Sint8 value ); // JE_newDrawCShapeAdjust +void blit_sprite_hv_blend( SDL_Surface *, int x, int y, unsigned int table, unsigned int index, Uint8 hue, Sint8 value ); // JE_newDrawCShapeModify +void blit_sprite_dark( SDL_Surface *, int x, int y, unsigned int table, unsigned int index, bool black ); // JE_newDrawCShapeDarken, JE_newDrawCShapeShadow + +typedef struct +{ + unsigned int size; + Uint8 *data; +} +Sprite2_array; + +extern Sprite2_array eShapes[6]; +extern Sprite2_array shapesC1, shapes6, shapes9, shapesW2; + +void JE_loadCompShapes( Sprite2_array *, JE_char s ); +void JE_loadCompShapesB( Sprite2_array *, FILE *f ); +void free_sprite2s( Sprite2_array * ); + +void blit_sprite2( SDL_Surface *, int x, int y, Sprite2_array, unsigned int index ); +void blit_sprite2_blend( SDL_Surface *, int x, int y, Sprite2_array, unsigned int index ); +void blit_sprite2_darken( SDL_Surface *, int x, int y, Sprite2_array, unsigned int index ); +void blit_sprite2_filter( SDL_Surface *, int x, int y, Sprite2_array, unsigned int index, Uint8 filter ); + +void blit_sprite2x2( SDL_Surface *, int x, int y, Sprite2_array, unsigned int index ); +void blit_sprite2x2_blend( SDL_Surface *, int x, int y, Sprite2_array, unsigned int index ); +void blit_sprite2x2_darken( SDL_Surface *, int x, int y, Sprite2_array, unsigned int index ); + +void JE_loadMainShapeTables( const char *shpfile ); +void free_main_shape_tables( void ); + +#endif // SPRITE_H + diff --git a/contrib/games/opentyrian/src/starlib.c b/contrib/games/opentyrian/src/starlib.c new file mode 100644 index 000000000..b6f759a1a --- /dev/null +++ b/contrib/games/opentyrian/src/starlib.c @@ -0,0 +1,431 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "starlib.h" + +#include "keyboard.h" +#include "mtrand.h" +#include "opentyr.h" +#include "video.h" + +#include + +#define starlib_MAX_STARS 1000 +#define MAX_TYPES 14 + +struct JE_StarType +{ + JE_integer spX, spY, spZ; + JE_integer lastX, lastY; +}; + +static int tempX, tempY; +static JE_boolean run; +static struct JE_StarType star[starlib_MAX_STARS]; + +static JE_byte setup; +static JE_word stepCounter; + +static JE_word nsp2; +static JE_shortint nspVar2Inc; + +/* JE: new sprite pointer */ +static JE_real nsp; +static JE_real nspVarInc; +static JE_real nspVarVarInc; + +static JE_word changeTime; +static JE_boolean doChange; + +static JE_boolean grayB; + +static JE_integer starlib_speed; +static JE_shortint speedChange; + +static JE_byte pColor; + + +void JE_starlib_main( void ) +{ + int off; + JE_word i; + JE_integer tempZ; + JE_byte tempCol; + struct JE_StarType *stars; + Uint8 *surf; + + JE_wackyCol(); + + grayB = false; + + starlib_speed += speedChange; + + + for(stars = star, i = starlib_MAX_STARS; i > 0; stars++, i--) + { + /* Make a pointer to the screen... */ + surf = VGAScreen->pixels; + + /* Calculate the offset to where we wish to draw */ + off = (stars->lastX)+(stars->lastY)*320; + + + /* We don't want trails in our star field. Erase the old graphic */ + if (off >= 640 && off < (320*200)-640) + { + surf[off] = 0; /* Shade Level 0 */ + + surf[off-1] = 0; /* Shade Level 1, 2 */ + surf[off+1] = 0; + surf[off-2] = 0; + surf[off+2] = 0; + + surf[off-320] = 0; + surf[off+320] = 0; + surf[off-640] = 0; + surf[off+640] = 0; + } + + /* Move star */ + tempZ = stars->spZ; + tempX = (stars->spX / tempZ) + 160; + tempY = (stars->spY / tempZ) + 100; + tempZ -= starlib_speed; + + + /* If star is out of range, make a new one */ + if (tempZ <= 0 || + tempY == 0 || tempY > 198 || + tempX > 318 || tempX < 1) + { + stars->spZ = 500; + + JE_newStar(); + + stars->spX = tempX; + stars->spY = tempY; + } + else /* Otherwise, update & draw it */ + { + stars->lastX = tempX; + stars->lastY = tempY; + stars->spZ = tempZ; + + off = tempX+tempY*320; + + if (grayB) + { + tempCol = tempZ >> 1; + } else { + tempCol = pColor+((tempZ >> 4) & 31); + } + + /* Draw the pixel! */ + if (off >= 640 && off < (320*200)-640) + { + surf[off] = tempCol; + + tempCol += 72; + surf[off-1] = tempCol; + surf[off+1] = tempCol; + surf[off-320] = tempCol; + surf[off+320] = tempCol; + + tempCol += 72; + surf[off-2] = tempCol; + surf[off+2] = tempCol; + surf[off-640] = tempCol; + surf[off+640] = tempCol; + } + } + } + + if (newkey) + { + switch (toupper(lastkey_char)) + { + case '+': + starlib_speed++; + speedChange = 0; + break; + case '-': + starlib_speed--; + speedChange = 0; + break; + case '1': + JE_changeSetup(1); + break; + case '2': + JE_changeSetup(2); + break; + case '3': + JE_changeSetup(3); + break; + case '4': + JE_changeSetup(4); + break; + case '5': + JE_changeSetup(5); + break; + case '6': + JE_changeSetup(6); + break; + case '7': + JE_changeSetup(7); + break; + case '8': + JE_changeSetup(8); + break; + case '9': + JE_changeSetup(9); + break; + case '0': + JE_changeSetup(10); + break; + case '!': + JE_changeSetup(11); + break; + case '@': + JE_changeSetup(12); + break; + case '#': + JE_changeSetup(13); + break; + case '$': + JE_changeSetup(14); + break; + + case 'C': + JE_resetValues(); + break; + case 'S': + nspVarVarInc = mt_rand_1() * 0.01f - 0.005f; + break; + case 'X': + case 27: + run = false; + break; + case '[': + pColor--; + break; + case ']': + pColor++; + break; + case '{': + pColor -= 72; + break; + case '}': + pColor += 72; + break; + case '`': /* ` */ + doChange = !doChange; + break; + case 'P': + wait_noinput(true, false, false); + wait_input(true, false, false); + break; + default: + break; + } + } + + if (doChange) + { + stepCounter++; + if (stepCounter > changeTime) + { + JE_changeSetup(0); + } + } + + if ((mt_rand() % 1000) == 1) + { + nspVarVarInc = mt_rand_1() * 0.01f - 0.005f; + } + + nspVarInc += nspVarVarInc; +} + +void JE_wackyCol( void ) +{ + /* YKS: Does nothing */ +} + +void JE_starlib_init( void ) +{ + static JE_boolean initialized = false; + + if (!initialized) + { + initialized = true; + + JE_resetValues(); + JE_changeSetup(2); + doChange = true; + + /* RANDOMIZE; */ + for (int x = 0; x < starlib_MAX_STARS; x++) + { + star[x].spX = (mt_rand() % 64000) - 32000; + star[x].spY = (mt_rand() % 40000) - 20000; + star[x].spZ = x+1; + } + } +} + +void JE_resetValues( void ) +{ + nsp2 = 1; + nspVar2Inc = 1; + nspVarInc = 0.1f; + nspVarVarInc = 0.0001f; + nsp = 0; + pColor = 32; + starlib_speed = 2; + speedChange = 0; +} + +void JE_changeSetup( JE_byte setupType ) +{ + stepCounter = 0; + changeTime = (mt_rand() % 1000); + + if (setupType > 0) + { + setup = setupType; + } else { + setup = mt_rand() % (MAX_TYPES + 1); + } + + if (setup == 1) + { + nspVarInc = 0.1f; + } + if (nspVarInc > 2.2f) + { + nspVarInc = 0.1f; + } +} + +void JE_newStar( void ) +{ + if (setup == 0) + { + tempX = (mt_rand() % 64000) - 32000; + tempY = (mt_rand() % 40000) - 20000; + } else { + nsp = nsp + nspVarInc; /* YKS: < lol */ + switch (setup) + { + case 1: + tempX = (int)(sinf(nsp / 30) * 20000); + tempY = (mt_rand() % 40000) - 20000; + break; + case 2: + tempX = (int)(cosf(nsp) * 20000); + tempY = (int)(sinf(nsp) * 20000); + break; + case 3: + tempX = (int)(cosf(nsp * 15) * 100) * ((int)(nsp / 6) % 200); + tempY = (int)(sinf(nsp * 15) * 100) * ((int)(nsp / 6) % 200); + break; + case 4: + tempX = (int)(sinf(nsp / 60) * 20000); + tempY = (int)(cosf(nsp) * (int)(sinf(nsp / 200) * 300) * 100); + break; + case 5: + tempX = (int)(sinf(nsp / 2) * 20000); + tempY = (int)(cosf(nsp) * (int)(sinf(nsp / 200) * 300) * 100); + break; + case 6: + tempX = (int)(sinf(nsp) * 40000); + tempY = (int)(cosf(nsp) * 20000); + break; + case 8: + tempX = (int)(sinf(nsp / 2) * 40000); + tempY = (int)(cosf(nsp) * 20000); + break; + case 7: + tempX = mt_rand() % 65535; + if ((mt_rand() % 2) == 0) + { + tempY = (int)(cosf(nsp / 80) * 10000) + 15000; + } else { + tempY = 50000 - (int)(cosf(nsp / 80) * 13000); + } + break; + case 9: + nsp2 += nspVar2Inc; + if ((nsp2 == 65535) || (nsp2 == 0)) + { + nspVar2Inc = -nspVar2Inc; + } + tempX = (int)(cosf(sinf(nsp2 / 10.0f) + (nsp / 500)) * 32000); + tempY = (int)(sinf(cosf(nsp2 / 10.0f) + (nsp / 500)) * 30000); + break; + case 10: + nsp2 += nspVar2Inc; + if ((nsp2 == 65535) || (nsp2 == 0)) + { + nspVar2Inc = -nspVar2Inc; + } + tempX = (int)(cosf(sinf(nsp2 / 5.0f) + (nsp / 100)) * 32000); + tempY = (int)(sinf(cosf(nsp2 / 5.0f) + (nsp / 100)) * 30000); + break;; + case 11: + nsp2 += nspVar2Inc; + if ((nsp2 == 65535) || (nsp2 == 0)) + { + nspVar2Inc = -nspVar2Inc; + } + tempX = (int)(cosf(sinf(nsp2 / 1000.0f) + (nsp / 2)) * 32000); + tempY = (int)(sinf(cosf(nsp2 / 1000.0f) + (nsp / 2)) * 30000); + break; + case 12: + if (nsp != 0) + { + nsp2 += nspVar2Inc; + if ((nsp2 == 65535) || (nsp2 == 0)) + { + nspVar2Inc = -nspVar2Inc; + } + tempX = (int)(cosf(sinf(nsp2 / 2.0f) / (sqrtf(fabsf(nsp)) / 10.0f + 1) + (nsp2 / 100.0f)) * 32000); + tempY = (int)(sinf(cosf(nsp2 / 2.0f) / (sqrtf(fabsf(nsp)) / 10.0f + 1) + (nsp2 / 100.0f)) * 30000); + } + break; + case 13: + if (nsp != 0) + { + nsp2 += nspVar2Inc; + if ((nsp2 == 65535) || (nsp2 == 0)) + { + nspVar2Inc = -nspVar2Inc; + } + tempX = (int)(cosf(sinf(nsp2 / 10.0f) / 2 + (nsp / 20)) * 32000); + tempY = (int)(sinf(sinf(nsp2 / 11.0f) / 2 + (nsp / 20)) * 30000); + } + break; + case 14: + nsp2 += nspVar2Inc; + tempX = (int)((sinf(nsp) + cosf(nsp2 / 1000.0f) * 3) * 12000); + tempY = (int)(cosf(nsp) * 10000) + nsp2; + break; + } + } +} + diff --git a/contrib/games/opentyrian/src/starlib.h b/contrib/games/opentyrian/src/starlib.h new file mode 100644 index 000000000..bc529d735 --- /dev/null +++ b/contrib/games/opentyrian/src/starlib.h @@ -0,0 +1,32 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef STARLIB_H +#define STARLIB_H + +#include "opentyr.h" + +void JE_starlib_main( void ); +void JE_wackyCol( void ); +void JE_starlib_init( void ); +void JE_resetValues( void ); +void JE_changeSetup( JE_byte setupType ); +void JE_newStar( void ); + +#endif /* STARLIB_H */ + diff --git a/contrib/games/opentyrian/src/std_support.c b/contrib/games/opentyrian/src/std_support.c new file mode 100644 index 000000000..d6f51ae96 --- /dev/null +++ b/contrib/games/opentyrian/src/std_support.c @@ -0,0 +1,26 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "std_support.h" + +char *ot_strchrnul( const char *s, int c ) +{ + for (; *s != c && *s != '\0'; ++s) + ; + return (char *)s; +} diff --git a/contrib/games/opentyrian/src/std_support.h b/contrib/games/opentyrian/src/std_support.h new file mode 100644 index 000000000..ba8c37c03 --- /dev/null +++ b/contrib/games/opentyrian/src/std_support.h @@ -0,0 +1,37 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef STD_SUPPORT_H +#define STD_SUPPORT_H + +/*! + * \file std_support.h + * \brief Standard library support functions + */ + +/*! + * \brief Locate a character in a a string. + * + * \param[in] s the string + * \param[in] c the character + * \return the pointer to the first occurrence of \p c in \p s if there is an occurrences; + * otherwise the pointer to the terminating null character of \p s + */ +char *ot_strchrnul( const char *s, int c ); + +#endif // STD_SUPPORT_H diff --git a/contrib/games/opentyrian/src/tyrian2.c b/contrib/games/opentyrian/src/tyrian2.c new file mode 100644 index 000000000..edcced21a --- /dev/null +++ b/contrib/games/opentyrian/src/tyrian2.c @@ -0,0 +1,5112 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "tyrian2.h" + +#include "animlib.h" +#include "backgrnd.h" +#include "episodes.h" +#include "file.h" +#include "font.h" +#include "fonthand.h" +#include "game_menu.h" +#include "joystick.h" +#include "keyboard.h" +#include "lds_play.h" +#include "loudness.h" +#include "lvllib.h" +#include "menus.h" +#include "mainint.h" +#include "mouse.h" +#include "mtrand.h" +#include "network.h" +#include "nortsong.h" +#include "nortvars.h" +#include "opentyr.h" +#include "params.h" +#include "pcxload.h" +#include "pcxmast.h" +#include "picload.h" +#include "setup.h" +#include "shots.h" +#include "sprite.h" +#include "vga256d.h" +#include "video.h" + +#include +#include +#include +#include +#include +#include + +inline static void blit_enemy( SDL_Surface *surface, unsigned int i, signed int x_offset, signed int y_offset, signed int sprite_offset ); + +boss_bar_t boss_bar[2]; + +/* Level Event Data */ +JE_boolean quit, loadLevelOk; + +struct JE_EventRecType eventRec[EVENT_MAXIMUM]; /* [1..eventMaximum] */ +JE_word levelEnemyMax; +JE_word levelEnemyFrequency; +JE_word levelEnemy[40]; /* [1..40] */ + +char tempStr[31]; + +/* Data used for ItemScreen procedure to indicate items available */ +JE_byte itemAvail[9][10]; /* [1..9, 1..10] */ +JE_byte itemAvailMax[9]; /* [1..9] */ + +void JE_starShowVGA( void ) +{ + JE_byte *src; + Uint8 *s = NULL; /* screen pointer, 8-bit specific */ + + int x, y, lightx, lighty, lightdist; + + if (!playerEndLevel && !skipStarShowVGA) + { + + s = VGAScreenSeg->pixels; + + src = game_screen->pixels; + src += 24; + + if (smoothScroll != 0 /*&& thisPlayerNum != 2*/) + { + wait_delay(); + setjasondelay(frameCountMax); + } + + if (starShowVGASpecialCode == 1) + { + src += game_screen->pitch * 183; + for (y = 0; y < 184; y++) + { + memmove(s, src, 264); + s += VGAScreenSeg->pitch; + src -= game_screen->pitch; + } + } + else if (starShowVGASpecialCode == 2 && processorType >= 2) + { + lighty = 172 - player[0].y; + lightx = 281 - player[0].x; + + for (y = 184; y; y--) + { + if (lighty > y) + { + for (x = 320 - 56; x; x--) + { + *s = (*src & 0xf0) | ((*src >> 2) & 0x03); + s++; + src++; + } + } + else + { + for (x = 320 - 56; x; x--) + { + lightdist = abs(lightx - x) + lighty; + if (lightdist < y) + *s = *src; + else if (lightdist - y <= 5) + *s = (*src & 0xf0) | (((*src & 0x0f) + (3 * (5 - (lightdist - y)))) / 4); + else + *s = (*src & 0xf0) | ((*src & 0x0f) >> 2); + s++; + src++; + } + } + s += 56 + VGAScreenSeg->pitch - 320; + src += 56 + VGAScreenSeg->pitch - 320; + } + } + else + { + for (y = 0; y < 184; y++) + { + memmove(s, src, 264); + s += VGAScreenSeg->pitch; + src += game_screen->pitch; + } + } + JE_showVGA(); + } + + quitRequested = false; + skipStarShowVGA = false; +} + +inline static void blit_enemy( SDL_Surface *surface, unsigned int i, signed int x_offset, signed int y_offset, signed int sprite_offset ) +{ + if (enemy[i].sprite2s == NULL) + { + fprintf(stderr, "warning: enemy %d sprite missing\n", i); + return; + } + + const int x = enemy[i].ex + x_offset + tempMapXOfs, + y = enemy[i].ey + y_offset; + const unsigned int index = enemy[i].egr[enemy[i].enemycycle - 1] + sprite_offset; + + if (enemy[i].filter != 0) + blit_sprite2_filter(surface, x, y, *enemy[i].sprite2s, index, enemy[i].filter); + else + blit_sprite2(surface, x, y, *enemy[i].sprite2s, index); +} + +void JE_drawEnemy( int enemyOffset ) // actually does a whole lot more than just drawing +{ + player[0].x -= 25; + + for (int i = enemyOffset - 25; i < enemyOffset; i++) + { + if (enemyAvail[i] != 1) + { + enemy[i].mapoffset = tempMapXOfs; + + if (enemy[i].xaccel && enemy[i].xaccel - 89u > mt_rand() % 11) + { + if (player[0].x > enemy[i].ex) + { + if (enemy[i].exc < enemy[i].xaccel - 89) + enemy[i].exc++; + } + else + { + if (enemy[i].exc >= 0 || -enemy[i].exc < enemy[i].xaccel - 89) + enemy[i].exc--; + } + } + + if (enemy[i].yaccel && enemy[i].yaccel - 89u > mt_rand() % 11) + { + if (player[0].y > enemy[i].ey) + { + if (enemy[i].eyc < enemy[i].yaccel - 89) + enemy[i].eyc++; + } + else + { + if (enemy[i].eyc >= 0 || -enemy[i].eyc < enemy[i].yaccel - 89) + enemy[i].eyc--; + } + } + + if (enemy[i].ex + tempMapXOfs > -29 && enemy[i].ex + tempMapXOfs < 300) + { + if (enemy[i].aniactive == 1) + { + enemy[i].enemycycle++; + + if (enemy[i].enemycycle == enemy[i].animax) + enemy[i].aniactive = enemy[i].aniwhenfire; + else if (enemy[i].enemycycle > enemy[i].ani) + enemy[i].enemycycle = enemy[i].animin; + } + + if (enemy[i].egr[enemy[i].enemycycle - 1] == 999) + goto enemy_gone; + + if (enemy[i].size == 1) // 2x2 enemy + { + if (enemy[i].ey > -13) + { + blit_enemy(VGAScreen, i, -6, -7, 0); + blit_enemy(VGAScreen, i, 6, -7, 1); + } + if (enemy[i].ey > -26 && enemy[i].ey < 182) + { + blit_enemy(VGAScreen, i, -6, 7, 19); + blit_enemy(VGAScreen, i, 6, 7, 20); + } + } + else + { + if (enemy[i].ey > -13) + blit_enemy(VGAScreen, i, 0, 0, 0); + } + + enemy[i].filter = 0; + } + + if (enemy[i].excc) + { + if (--enemy[i].exccw <= 0) + { + if (enemy[i].exc == enemy[i].exrev) + { + enemy[i].excc = -enemy[i].excc; + enemy[i].exrev = -enemy[i].exrev; + enemy[i].exccadd = -enemy[i].exccadd; + } + else + { + enemy[i].exc += enemy[i].exccadd; + enemy[i].exccw = enemy[i].exccwmax; + if (enemy[i].exc == enemy[i].exrev) + { + enemy[i].excc = -enemy[i].excc; + enemy[i].exrev = -enemy[i].exrev; + enemy[i].exccadd = -enemy[i].exccadd; + } + } + } + } + + if (enemy[i].eycc) + { + if (--enemy[i].eyccw <= 0) + { + if (enemy[i].eyc == enemy[i].eyrev) + { + enemy[i].eycc = -enemy[i].eycc; + enemy[i].eyrev = -enemy[i].eyrev; + enemy[i].eyccadd = -enemy[i].eyccadd; + } + else + { + enemy[i].eyc += enemy[i].eyccadd; + enemy[i].eyccw = enemy[i].eyccwmax; + if (enemy[i].eyc == enemy[i].eyrev) + { + enemy[i].eycc = -enemy[i].eycc; + enemy[i].eyrev = -enemy[i].eyrev; + enemy[i].eyccadd = -enemy[i].eyccadd; + } + } + } + } + + enemy[i].ey += enemy[i].fixedmovey; + + enemy[i].ex += enemy[i].exc; + if (enemy[i].ex < -80 || enemy[i].ex > 340) + goto enemy_gone; + + enemy[i].ey += enemy[i].eyc; + if (enemy[i].ey < -112 || enemy[i].ey > 190) + goto enemy_gone; + + goto enemy_still_exists; + +enemy_gone: + /* enemy[i].egr[10] &= 0x00ff; madness? */ + enemyAvail[i] = 1; + goto draw_enemy_end; + +enemy_still_exists: + + /*X bounce*/ + if (enemy[i].ex <= enemy[i].xminbounce || enemy[i].ex >= enemy[i].xmaxbounce) + enemy[i].exc = -enemy[i].exc; + + /*Y bounce*/ + if (enemy[i].ey <= enemy[i].yminbounce || enemy[i].ey >= enemy[i].ymaxbounce) + enemy[i].eyc = -enemy[i].eyc; + + /* Evalue != 0 - score item at boundary */ + if (enemy[i].scoreitem) + { + if (enemy[i].ex < -5) + enemy[i].ex++; + if (enemy[i].ex > 245) + enemy[i].ex--; + } + + enemy[i].ey += tempBackMove; + + if (enemy[i].ex <= -24 || enemy[i].ex >= 296) + goto draw_enemy_end; + + tempX = enemy[i].ex; + tempY = enemy[i].ey; + + temp = enemy[i].enemytype; + + /* Enemy Shots */ + if (enemy[i].edamaged == 1) + goto draw_enemy_end; + + enemyOnScreen++; + + if (enemy[i].iced) + { + enemy[i].iced--; + if (enemy[i].enemyground != 0) + { + enemy[i].filter = 0x09; + } + goto draw_enemy_end; + } + + for (int j = 3; j > 0; j--) + { + if (enemy[i].freq[j-1]) + { + temp3 = enemy[i].tur[j-1]; + + if (--enemy[i].eshotwait[j-1] == 0 && temp3) + { + enemy[i].eshotwait[j-1] = enemy[i].freq[j-1]; + if (difficultyLevel > 2) + { + enemy[i].eshotwait[j-1] = (enemy[i].eshotwait[j-1] / 2) + 1; + if (difficultyLevel > 7) + enemy[i].eshotwait[j-1] = (enemy[i].eshotwait[j-1] / 2) + 1; + } + + if (galagaMode && (enemy[i].eyc == 0 || (mt_rand() % 400) >= galagaShotFreq)) + goto draw_enemy_end; + + switch (temp3) + { + case 252: /* Savara Boss DualMissile */ + if (enemy[i].ey > 20) + { + JE_setupExplosion(tempX - 8 + tempMapXOfs, tempY - 20 - backMove * 8, -2, 6, false, false); + JE_setupExplosion(tempX + 4 + tempMapXOfs, tempY - 20 - backMove * 8, -2, 6, false, false); + } + break; + case 251:; /* Suck-O-Magnet */ + const int attractivity = 4 - (abs(player[0].x - tempX) + abs(player[0].y - tempY)) / 100; + player[0].x_velocity += (player[0].x > tempX) ? -attractivity : attractivity; + break; + case 253: /* Left ShortRange Magnet */ + if (abs(player[0].x + 25 - 14 - tempX) < 24 && abs(player[0].y - tempY) < 28) + { + player[0].x_velocity += 2; + } + if (twoPlayerMode && + (abs(player[1].x - 14 - tempX) < 24 && abs(player[1].y - tempY) < 28)) + { + player[1].x_velocity += 2; + } + break; + case 254: /* Left ShortRange Magnet */ + if (abs(player[0].x + 25 - 14 - tempX) < 24 && abs(player[0].y - tempY) < 28) + { + player[0].x_velocity -= 2; + } + if (twoPlayerMode && + (abs(player[1].x - 14 - tempX) < 24 && abs(player[1].y - tempY) < 28)) + { + player[1].x_velocity -= 2; + } + break; + case 255: /* Magneto RePulse!! */ + if (difficultyLevel != 1) /*DIF*/ + { + if (j == 3) + { + enemy[i].filter = 0x70; + } + else + { + const int repulsivity = 4 - (abs(player[0].x - tempX) + abs(player[0].y - tempY)) / 20; + if (repulsivity > 0) + player[0].x_velocity += (player[0].x > tempX) ? repulsivity : -repulsivity; + } + } + break; + default: + /*Rot*/ + for (int tempCount = weapons[temp3].multi; tempCount > 0; tempCount--) + { + for (b = 0; b < ENEMY_SHOT_MAX; b++) + { + if (enemyShotAvail[b] == 1) + break; + } + if (b == ENEMY_SHOT_MAX) + goto draw_enemy_end; + + enemyShotAvail[b] = !enemyShotAvail[b]; + + if (weapons[temp3].sound > 0) + { + do + temp = mt_rand() % 8; + while (temp == 3); + soundQueue[temp] = weapons[temp3].sound; + } + + if (enemy[i].aniactive == 2) + enemy[i].aniactive = 1; + + if (++enemy[i].eshotmultipos[j-1] > weapons[temp3].max) + enemy[i].eshotmultipos[j-1] = 1; + + int tempPos = enemy[i].eshotmultipos[j-1] - 1; + + if (j == 1) + temp2 = 4; + + enemyShot[b].sx = tempX + weapons[temp3].bx[tempPos] + tempMapXOfs; + enemyShot[b].sy = tempY + weapons[temp3].by[tempPos]; + enemyShot[b].sdmg = weapons[temp3].attack[tempPos]; + enemyShot[b].tx = weapons[temp3].tx; + enemyShot[b].ty = weapons[temp3].ty; + enemyShot[b].duration = weapons[temp3].del[tempPos]; + enemyShot[b].animate = 0; + enemyShot[b].animax = weapons[temp3].weapani; + + enemyShot[b].sgr = weapons[temp3].sg[tempPos]; + switch (j) + { + case 1: + enemyShot[b].syc = weapons[temp3].acceleration; + enemyShot[b].sxc = weapons[temp3].accelerationx; + + enemyShot[b].sxm = weapons[temp3].sx[tempPos]; + enemyShot[b].sym = weapons[temp3].sy[tempPos]; + break; + case 3: + enemyShot[b].sxc = -weapons[temp3].acceleration; + enemyShot[b].syc = weapons[temp3].accelerationx; + + enemyShot[b].sxm = -weapons[temp3].sy[tempPos]; + enemyShot[b].sym = -weapons[temp3].sx[tempPos]; + break; + case 2: + enemyShot[b].sxc = weapons[temp3].acceleration; + enemyShot[b].syc = -weapons[temp3].acceleration; + + enemyShot[b].sxm = weapons[temp3].sy[tempPos]; + enemyShot[b].sym = -weapons[temp3].sx[tempPos]; + break; + } + + if (weapons[temp3].aim > 0) + { + int aim = weapons[temp3].aim; + + /*DIF*/ + if (difficultyLevel > 2) + { + aim += difficultyLevel - 2; + } + + JE_word target_x = player[0].x; + JE_word target_y = player[0].y; + + if (twoPlayerMode) + { + // fire at live player(s) + if (player[0].is_alive && !player[1].is_alive) + temp = 0; + else if (player[1].is_alive && !player[0].is_alive) + temp = 1; + else + temp = mt_rand() % 2; + + if (temp == 1) + { + target_x = player[1].x - 25; + target_y = player[1].y; + } + } + + int relative_x = (target_x + 25) - tempX - tempMapXOfs - 4; + if (relative_x == 0) + relative_x = 1; + int relative_y = target_y - tempY; + if (relative_y == 0) + relative_y = 1; + const int longest_side = MAX(abs(relative_x), abs(relative_y)); + enemyShot[b].sxm = roundf((float)relative_x / longest_side * aim); + enemyShot[b].sym = roundf((float)relative_y / longest_side * aim); + } + } + break; + } + } + } + } + + /* Enemy Launch Routine */ + if (enemy[i].launchfreq) + { + if (--enemy[i].launchwait == 0) + { + enemy[i].launchwait = enemy[i].launchfreq; + + if (enemy[i].launchspecial != 0) + { + /*Type 1 : Must be inline with player*/ + if (abs(enemy[i].ey - player[0].y) > 5) + goto draw_enemy_end; + } + + if (enemy[i].aniactive == 2) + { + enemy[i].aniactive = 1; + } + + if (enemy[i].launchtype == 0) + goto draw_enemy_end; + + tempW = enemy[i].launchtype; + b = JE_newEnemy(enemyOffset == 50 ? 75 : enemyOffset - 25, tempW, 0); + + /*Launch Enemy Placement*/ + if (b > 0) + { + struct JE_SingleEnemyType* e = &enemy[b-1]; + + e->ex = tempX; + e->ey = tempY + enemyDat[e->enemytype].startyc; + if (e->size == 0) + e->ey -= 7; + + if (e->launchtype > 0 && e->launchfreq == 0) + { + if (e->launchtype > 90) + { + e->ex += mt_rand() % ((e->launchtype - 90) * 4) - (e->launchtype - 90) * 2; + } + else + { + int target_x = (player[0].x + 25) - tempX - tempMapXOfs - 4; + if (target_x == 0) + target_x = 1; + int tempI5 = player[0].y - tempY; + if (tempI5 == 0) + tempI5 = 1; + const int longest_side = MAX(abs(target_x), abs(tempI5)); + e->exc = roundf(((float)target_x / longest_side) * e->launchtype); + e->eyc = roundf(((float)tempI5 / longest_side) * e->launchtype); + } + } + + do + temp = mt_rand() % 8; + while (temp == 3); + soundQueue[temp] = randomEnemyLaunchSounds[(mt_rand() % 3)]; + + if (enemy[i].launchspecial == 1 + && enemy[i].linknum < 100) + { + e->linknum = enemy[i].linknum; + } + } + } + } + } +draw_enemy_end: + ; + } + + player[0].x += 25; +} + +void JE_main( void ) +{ + char buffer[256]; + + int lastEnemyOnScreen; + + /* NOTE: BEGIN MAIN PROGRAM HERE AFTER LOADING A GAME OR STARTING A NEW ONE */ + + /* ----------- GAME ROUTINES ------------------------------------- */ + /* We need to jump to the beginning to make space for the routines */ + /* --------------------------------------------------------------- */ + goto start_level_first; + + + /*------------------------------GAME LOOP-----------------------------------*/ + + + /* Startlevel is called after a previous level is over. If the first level + is started for a gaming session, startlevelfirst is called instead and + this code is skipped. The code here finishes the level and prepares for + the loadmap function. */ + +start_level: + + if (galagaMode) + twoPlayerMode = false; + + JE_clearKeyboard(); + + free_sprite2s(&eShapes[0]); + free_sprite2s(&eShapes[1]); + free_sprite2s(&eShapes[2]); + free_sprite2s(&eShapes[3]); + + /* Normal speed */ + if (fastPlay != 0) + { + smoothScroll = true; + speed = 0x4300; + JE_resetTimerInt(); + JE_setTimerInt(); + } + + if (play_demo || record_demo) + { + if (demo_file) + { + fclose(demo_file); + demo_file = NULL; + } + + if (play_demo) + { + stop_song(); + fade_black(10); + + wait_noinput(true, true, true); + } + } + + difficultyLevel = oldDifficultyLevel; /*Return difficulty to normal*/ + + if (!play_demo) + { + if ((!all_players_dead() || normalBonusLevelCurrent || bonusLevelCurrent) && !playerEndLevel) + { + mainLevel = nextLevel; + JE_endLevelAni(); + + fade_song(); + } + else + { + fade_song(); + fade_black(10); + + JE_loadGame(twoPlayerMode ? 22 : 11); + if (doNotSaveBackup) + { + superTyrian = false; + onePlayerAction = false; + player[0].items.super_arcade_mode = SA_NONE; + } + if (bonusLevelCurrent && !playerEndLevel) + { + mainLevel = nextLevel; + } + } + } + doNotSaveBackup = false; + + if (play_demo) + return; + +start_level_first: + + set_volume(tyrMusicVolume, fxVolume); + + endLevel = false; + reallyEndLevel = false; + playerEndLevel = false; + extraGame = false; + + doNotSaveBackup = false; + JE_loadMap(); + + if (mainLevel == 0) // if quit itemscreen + return; // back to titlescreen + + fade_song(); + + for (uint i = 0; i < COUNTOF(player); ++i) + player[i].is_alive = true; + + oldDifficultyLevel = difficultyLevel; + if (episodeNum == EPISODE_AVAILABLE) + difficultyLevel--; + if (difficultyLevel < 1) + difficultyLevel = 1; + + player[0].x = 100; + player[0].y = 180; + + player[1].x = 190; + player[1].y = 180; + + assert(COUNTOF(player->old_x) == COUNTOF(player->old_y)); + + for (uint i = 0; i < COUNTOF(player); ++i) + { + for (uint j = 0; j < COUNTOF(player->old_x); ++j) + { + player[i].old_x[j] = player[i].x - (19 - j); + player[i].old_y[j] = player[i].y - 18; + } + + player[i].last_x_shot_move = player[i].x; + player[i].last_y_shot_move = player[i].y; + } + + JE_loadPic(VGAScreen, twoPlayerMode ? 6 : 3, false); + + JE_drawOptions(); + + JE_outText(VGAScreen, 268, twoPlayerMode ? 76 : 118, levelName, 12, 4); + + JE_showVGA(); + JE_gammaCorrect(&colors, gammaCorrection); + fade_palette(colors, 50, 0, 255); + + free_sprite2s(&shapes6); + JE_loadCompShapes(&shapes6, '6'); // explosion sprites + + /* MAPX will already be set correctly */ + mapY = 300 - 8; + mapY2 = 600 - 8; + mapY3 = 600 - 8; + mapYPos = &megaData1.mainmap[mapY][0] - 1; + mapY2Pos = &megaData2.mainmap[mapY2][0] - 1; + mapY3Pos = &megaData3.mainmap[mapY3][0] - 1; + mapXPos = 0; + mapXOfs = 0; + mapX2Pos = 0; + mapX3Pos = 0; + mapX3Ofs = 0; + mapXbpPos = 0; + mapX2bpPos = 0; + mapX3bpPos = 0; + + map1YDelay = 1; + map1YDelayMax = 1; + map2YDelay = 1; + map2YDelayMax = 1; + + musicFade = false; + + backPos = 0; + backPos2 = 0; + backPos3 = 0; + power = 0; + starfield_speed = 1; + + /* Setup player ship graphics */ + JE_getShipInfo(); + + for (uint i = 0; i < COUNTOF(player); ++i) + { + player[i].x_velocity = 0; + player[i].y_velocity = 0; + + player[i].invulnerable_ticks = 100; + } + + newkey = newmouse = false; + + /* Initialize Level Data and Debug Mode */ + levelEnd = 255; + levelEndWarp = -4; + levelEndFxWait = 0; + warningCol = 120; + warningColChange = 1; + warningSoundDelay = 0; + armorShipDelay = 50; + + bonusLevel = false; + readyToEndLevel = false; + firstGameOver = true; + eventLoc = 1; + curLoc = 0; + backMove = 1; + backMove2 = 2; + backMove3 = 3; + explodeMove = 2; + enemiesActive = true; + for(temp = 0; temp < 3; temp++) + { + button[temp] = false; + } + stopBackgrounds = false; + stopBackgroundNum = 0; + background3x1 = false; + background3x1b = false; + background3over = 0; + background2over = 1; + topEnemyOver = false; + skyEnemyOverAll = false; + smallEnemyAdjust = false; + starActive = true; + enemyContinualDamage = false; + levelEnemyFrequency = 96; + quitRequested = false; + + for (unsigned int i = 0; i < COUNTOF(boss_bar); i++) + boss_bar[i].link_num = 0; + + forceEvents = false; /*Force events to continue if background movement = 0*/ + + superEnemy254Jump = 0; /*When Enemy with PL 254 dies*/ + + /* Filter Status */ + filterActive = true; + filterFade = true; + filterFadeStart = false; + levelFilter = -99; + levelBrightness = -14; + levelBrightnessChg = 1; + + background2notTransparent = false; + + uint old_weapon_bar[2] = { 0, 0 }; // only redrawn when they change + + /* Initially erase power bars */ + lastPower = power / 10; + + /* Initial Text */ + JE_drawTextWindow(miscText[20]); + + /* Setup Armor/Shield Data */ + shieldWait = 1; + shieldT = shields[player[0].items.shield].tpwr * 20; + + for (uint i = 0; i < COUNTOF(player); ++i) + { + player[i].shield = shields[player[i].items.shield].mpwr; + player[i].shield_max = player[i].shield * 2; + } + + JE_drawShield(); + JE_drawArmor(); + + for (uint i = 0; i < COUNTOF(player); ++i) + player[i].superbombs = 0; + + /* Set cubes to 0 */ + cubeMax = 0; + + /* Secret Level Display */ + flash = 0; + flashChange = 1; + displayTime = 0; + + play_song(levelSong - 1); + + JE_drawPortConfigButtons(); + + /* --- MAIN LOOP --- */ + + newkey = false; + +#ifdef WITH_NETWORK + if (isNetworkGame) + { + JE_clearSpecialRequests(); + mt_srand(32402394); + } +#endif + + initialize_starfield(); + + JE_setNewGameSpeed(); + + /* JE_setVol(tyrMusicVolume, fxPlayVol >> 2); NOTE: MXD killed this because it was broken */ + + /*Save backup game*/ + if (!play_demo && !doNotSaveBackup) + { + temp = twoPlayerMode ? 22 : 11; + JE_saveGame(temp, "LAST LEVEL "); + } + + if (!play_demo && record_demo) + { + Uint8 new_demo_num = 0; + + do + { + sprintf(tempStr, "demorec.%d", new_demo_num++); + } + while (dir_file_exists(get_user_directory(), tempStr)); // until file doesn't exist + + demo_file = dir_fopen_warn(get_user_directory(), tempStr, "wb"); + if (!demo_file) + exit(1); + + efwrite(&episodeNum, 1, 1, demo_file); + efwrite(levelName, 1, 10, demo_file); + efwrite(&lvlFileNum, 1, 1, demo_file); + + fputc(player[0].items.weapon[FRONT_WEAPON].id, demo_file); + fputc(player[0].items.weapon[REAR_WEAPON].id, demo_file); + fputc(player[0].items.super_arcade_mode, demo_file); + fputc(player[0].items.sidekick[LEFT_SIDEKICK], demo_file); + fputc(player[0].items.sidekick[RIGHT_SIDEKICK], demo_file); + fputc(player[0].items.generator, demo_file); + + fputc(player[0].items.sidekick_level, demo_file); + fputc(player[0].items.sidekick_series, demo_file); + + fputc(initial_episode_num, demo_file); + + fputc(player[0].items.shield, demo_file); + fputc(player[0].items.special, demo_file); + fputc(player[0].items.ship, demo_file); + + for (uint i = 0; i < 2; ++i) + fputc(player[0].items.weapon[i].power, demo_file); + + for (uint i = 0; i < 3; ++i) + fputc(0, demo_file); + + efwrite(&levelSong, 1, 1, demo_file); + + demo_keys = 0; + demo_keys_wait = 0; + } + + twoPlayerLinked = false; + linkGunDirec = M_PI; + + for (uint i = 0; i < COUNTOF(player); ++i) + calc_purple_balls_needed(&player[i]); + + damageRate = 2; /*Normal Rate for Collision Damage*/ + + chargeWait = 5; + chargeLevel = 0; + chargeMax = 5; + chargeGr = 0; + chargeGrWait = 3; + + portConfigChange = false; + + /*Destruction Ratio*/ + totalEnemy = 0; + enemyKilled = 0; + + astralDuration = 0; + + superArcadePowerUp = 1; + + yourInGameMenuRequest = false; + + constantLastX = -1; + + for (uint i = 0; i < COUNTOF(player); ++i) + player[i].exploding_ticks = 0; + + if (isNetworkGame) + { + JE_loadItemDat(); + } + + memset(enemyAvail, 1, sizeof(enemyAvail)); + for (uint i = 0; i < COUNTOF(enemyShotAvail); i++) + enemyShotAvail[i] = 1; + + /*Initialize Shots*/ + memset(playerShotData, 0, sizeof(playerShotData)); + memset(shotAvail, 0, sizeof(shotAvail)); + memset(shotMultiPos, 0, sizeof(shotMultiPos)); + memset(shotRepeat, 1, sizeof(shotRepeat)); + + memset(button, 0, sizeof(button)); + + memset(globalFlags, 0, sizeof(globalFlags)); + + memset(explosions, 0, sizeof(explosions)); + memset(rep_explosions, 0, sizeof(rep_explosions)); + + /* --- Clear Sound Queue --- */ + memset(soundQueue, 0, sizeof(soundQueue)); + soundQueue[3] = V_GOOD_LUCK; + + memset(enemyShapeTables, 0, sizeof(enemyShapeTables)); + memset(enemy, 0, sizeof(enemy)); + + memset(SFCurrentCode, 0, sizeof(SFCurrentCode)); + memset(SFExecuted, 0, sizeof(SFExecuted)); + + zinglonDuration = 0; + specialWait = 0; + nextSpecialWait = 0; + optionAttachmentMove = 0; /*Launch the Attachments!*/ + optionAttachmentLinked = true; + + editShip1 = false; + editShip2 = false; + + memset(smoothies, 0, sizeof(smoothies)); + + levelTimer = false; + randomExplosions = false; + + last_superpixel = 0; + memset(superpixels, 0, sizeof(superpixels)); + + returnActive = false; + + galagaShotFreq = 0; + + if (galagaMode) + { + difficultyLevel = 2; + } + galagaLife = 10000; + + JE_drawOptionLevel(); + + // keeps map from scrolling past the top + BKwrap1 = BKwrap1to = &megaData1.mainmap[1][0]; + BKwrap2 = BKwrap2to = &megaData2.mainmap[1][0]; + BKwrap3 = BKwrap3to = &megaData3.mainmap[1][0]; + +level_loop: + + //tempScreenSeg = game_screen; /* side-effect of game_screen */ + + if (isNetworkGame) + { + smoothies[9-1] = false; + smoothies[6-1] = false; + } else { + starShowVGASpecialCode = smoothies[9-1] + (smoothies[6-1] << 1); + } + + /*Background Wrapping*/ + if (mapYPos <= BKwrap1) + { + mapYPos = BKwrap1to; + } + if (mapY2Pos <= BKwrap2) + { + mapY2Pos = BKwrap2to; + } + if (mapY3Pos <= BKwrap3) + { + mapY3Pos = BKwrap3to; + } + + + allPlayersGone = all_players_dead() && + ((*player[0].lives == 1 && player[0].exploding_ticks == 0) || (!onePlayerAction && !twoPlayerMode)) && + ((*player[1].lives == 1 && player[1].exploding_ticks == 0) || !twoPlayerMode); + + + /*-----MUSIC FADE------*/ + if (musicFade) + { + if (tempVolume > 10) + { + tempVolume--; + set_volume(tempVolume, fxVolume); + } + else + { + musicFade = false; + } + } + + if (!allPlayersGone && levelEnd > 0 && endLevel) + { + play_song(9); + musicFade = false; + } + else if (!playing && firstGameOver) + { + play_song(levelSong - 1); + } + + + if (!endLevel) // draw HUD + { + VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ + + /*-----------------------Message Bar------------------------*/ + if (textErase > 0 && --textErase == 0) + blit_sprite(VGAScreenSeg, 16, 189, OPTION_SHAPES, 36); // in-game message area + + /*------------------------Shield Gen-------------------------*/ + if (galagaMode) + { + for (uint i = 0; i < COUNTOF(player); ++i) + player[i].shield = 0; + + // spawned dragonwing died :( + if (*player[1].lives == 0 || player[1].armor == 0) + twoPlayerMode = false; + + if (player[0].cash >= (unsigned)galagaLife) + { + soundQueue[6] = S_EXPLOSION_11; + soundQueue[7] = S_SOUL_OF_ZINGLON; + + if (*player[0].lives < 11) + ++(*player[0].lives); + else + player[0].cash += 1000; + + if (galagaLife == 10000) + galagaLife = 20000; + else + galagaLife += 25000; + } + } + else // not galagaMode + { + if (twoPlayerMode) + { + if (--shieldWait == 0) + { + shieldWait = 15; + + for (uint i = 0; i < COUNTOF(player); ++i) + { + if (player[i].shield < player[i].shield_max && player[i].is_alive) + ++player[i].shield; + } + + JE_drawShield(); + } + } + else if (player[0].is_alive && player[0].shield < player[0].shield_max && power > shieldT) + { + if (--shieldWait == 0) + { + shieldWait = 15; + + power -= shieldT; + + ++player[0].shield; + if (player[1].shield < player[0].shield_max) + ++player[1].shield; + + JE_drawShield(); + } + } + } + + /*---------------------Weapon Display-------------------------*/ + for (uint i = 0; i < 2; ++i) + { + uint item_power = player[twoPlayerMode ? i : 0].items.weapon[i].power; + + if (old_weapon_bar[i] != item_power) + { + old_weapon_bar[i] = item_power; + + int x = twoPlayerMode ? 286 : 289, + y = (i == 0) ? (twoPlayerMode ? 6 : 17) : (twoPlayerMode ? 100 : 38); + + fill_rectangle_xy(VGAScreenSeg, x, y, x + 1 + 10 * 2, y + 2, 0); + + for (uint j = 1; j <= item_power; ++j) + { + JE_rectangle(VGAScreen, x, y, x + 1, y + 2, 115 + j); /* SEGa000 */ + x += 2; + } + } + } + + /*------------------------Power Bar-------------------------*/ + if (twoPlayerMode || onePlayerAction) + { + power = 900; + } + else + { + power += powerAdd; + if (power > 900) + power = 900; + + temp = power / 10; + + if (temp != lastPower) + { + if (temp > lastPower) + fill_rectangle_xy(VGAScreenSeg, 269, 113 - 11 - temp, 276, 114 - 11 - lastPower, 113 + temp / 7); + else + fill_rectangle_xy(VGAScreenSeg, 269, 113 - 11 - lastPower, 276, 114 - 11 - temp, 0); + + lastPower = temp; + } + } + + oldMapX3Ofs = mapX3Ofs; + + enemyOnScreen = 0; + } + + /* use game_screen for all the generic drawing functions */ + VGAScreen = game_screen; + + /*---------------------------EVENTS-------------------------*/ + while (eventRec[eventLoc-1].eventtime <= curLoc && eventLoc <= maxEvent) + JE_eventSystem(); + + if (isNetworkGame && reallyEndLevel) + goto start_level; + + + /* SMOOTHIES! */ + JE_checkSmoothies(); + if (anySmoothies) + VGAScreen = VGAScreen2; // this makes things complicated, but we do it anyway :( + + /* --- BACKGROUNDS --- */ + /* --- BACKGROUND 1 --- */ + + if (forceEvents && !backMove) + curLoc++; + + if (map1YDelayMax > 1 && backMove < 2) + backMove = (map1YDelay == 1) ? 1 : 0; + + /*Draw background*/ + if (astralDuration == 0) + draw_background_1(VGAScreen); + else + JE_clr256(VGAScreen); + + /*Set Movement of background 1*/ + if (--map1YDelay == 0) + { + map1YDelay = map1YDelayMax; + + curLoc += backMove; + + backPos += backMove; + + if (backPos > 27) + { + backPos -= 28; + mapY--; + mapYPos -= 14; /*Map Width*/ + } + } + + if (starActive || astralDuration > 0) + { + update_and_draw_starfield(VGAScreen, starfield_speed); + } + + if (processorType > 1 && smoothies[5-1]) + { + iced_blur_filter(game_screen, VGAScreen); + VGAScreen = game_screen; + } + + /*-----------------------BACKGROUNDS------------------------*/ + /*-----------------------BACKGROUND 2------------------------*/ + if (background2over == 3) + { + draw_background_2(VGAScreen); + background2 = true; + } + + if (background2over == 0) + { + if (!(smoothies[2-1] && processorType < 4) && !(smoothies[1-1] && processorType == 3)) + { + if (wild && !background2notTransparent) + draw_background_2_blend(VGAScreen); + else + draw_background_2(VGAScreen); + } + } + + if (smoothies[0] && processorType > 2 && smoothie_data[0] == 0) + { + lava_filter(game_screen, VGAScreen); + VGAScreen = game_screen; + } + if (smoothies[2-1] && processorType > 2) + { + water_filter(game_screen, VGAScreen); + VGAScreen = game_screen; + } + + /*-----------------------Ground Enemy------------------------*/ + lastEnemyOnScreen = enemyOnScreen; + + tempMapXOfs = mapXOfs; + tempBackMove = backMove; + JE_drawEnemy(50); + JE_drawEnemy(100); + + if (enemyOnScreen == 0 || enemyOnScreen == lastEnemyOnScreen) + { + if (stopBackgroundNum == 1) + stopBackgroundNum = 9; + } + + if (smoothies[0] && processorType > 2 && smoothie_data[0] > 0) + { + lava_filter(game_screen, VGAScreen); + VGAScreen = game_screen; + } + + if (superWild) + { + neat += 3; + JE_darkenBackground(neat); + } + + /*-----------------------BACKGROUNDS------------------------*/ + /*-----------------------BACKGROUND 2------------------------*/ + if (!(smoothies[2-1] && processorType < 4) && + !(smoothies[1-1] && processorType == 3)) + { + if (background2over == 1) + { + if (wild && !background2notTransparent) + draw_background_2_blend(VGAScreen); + else + draw_background_2(VGAScreen); + } + } + + if (superWild) + { + neat++; + JE_darkenBackground(neat); + } + + if (background3over == 2) + draw_background_3(VGAScreen); + + /* New Enemy */ + if (enemiesActive && mt_rand() % 100 > levelEnemyFrequency) + { + tempW = levelEnemy[mt_rand() % levelEnemyMax]; + if (tempW == 2) + soundQueue[3] = S_WEAPON_7; + b = JE_newEnemy(0, tempW, 0); + } + + if (processorType > 1 && smoothies[3-1]) + { + iced_blur_filter(game_screen, VGAScreen); + VGAScreen = game_screen; + } + if (processorType > 1 && smoothies[4-1]) + { + blur_filter(game_screen, VGAScreen); + VGAScreen = game_screen; + } + + /* Draw Sky Enemy */ + if (!skyEnemyOverAll) + { + lastEnemyOnScreen = enemyOnScreen; + + tempMapXOfs = mapX2Ofs; + tempBackMove = 0; + JE_drawEnemy(25); + + if (enemyOnScreen == lastEnemyOnScreen) + { + if (stopBackgroundNum == 2) + stopBackgroundNum = 9; + } + } + + if (background3over == 0) + draw_background_3(VGAScreen); + + /* Draw Top Enemy */ + if (!topEnemyOver) + { + tempMapXOfs = (background3x1 == 0) ? oldMapX3Ofs : mapXOfs; + tempBackMove = backMove3; + JE_drawEnemy(75); + } + + /* Player Shot Images */ + for (int z = 0; z < MAX_PWEAPON; z++) + { + if (shotAvail[z] != 0) + { + bool is_special = false; + int tempShotX = 0, tempShotY = 0; + JE_byte chain; + JE_byte playerNum; + JE_word tempX2, tempY2; + JE_integer damage; + + if (!player_shot_move_and_draw(z, &is_special, &tempShotX, &tempShotY, &damage, &temp2, &chain, &playerNum, &tempX2, &tempY2)) + { + goto draw_player_shot_loop_end; + } + + for (b = 0; b < 100; b++) + { + if (enemyAvail[b] == 0) + { + bool collided; + + if (z == MAX_PWEAPON - 1) + { + temp = 25 - abs(zinglonDuration - 25); + collided = abs(enemy[b].ex + enemy[b].mapoffset - (player[0].x + 7)) < temp; + temp2 = 9; + chain = 0; + damage = 10; + } + else if (is_special) + { + collided = ((enemy[b].enemycycle == 0) && + (abs(enemy[b].ex + enemy[b].mapoffset - tempShotX - tempX2) < (25 + tempX2)) && + (abs(enemy[b].ey - tempShotY - 12 - tempY2) < (29 + tempY2))) || + ((enemy[b].enemycycle > 0) && + (abs(enemy[b].ex + enemy[b].mapoffset - tempShotX - tempX2) < (13 + tempX2)) && + (abs(enemy[b].ey - tempShotY - 6 - tempY2) < (15 + tempY2))); + } + else + { + collided = ((enemy[b].enemycycle == 0) && + (abs(enemy[b].ex + enemy[b].mapoffset - tempShotX) < 25) && (abs(enemy[b].ey - tempShotY - 12) < 29)) || + ((enemy[b].enemycycle > 0) && + (abs(enemy[b].ex + enemy[b].mapoffset - tempShotX) < 13) && (abs(enemy[b].ey - tempShotY - 6) < 15)); + } + + if (collided) + { + if (chain > 0) + { + shotMultiPos[SHOT_MISC] = 0; + b = player_shot_create(0, SHOT_MISC, tempShotX, tempShotY, mouseX, mouseY, chain, playerNum); + shotAvail[z] = 0; + goto draw_player_shot_loop_end; + } + + infiniteShot = false; + + if (damage == 99) + { + damage = 0; + doIced = 40; + enemy[b].iced = 40; + } + else + { + doIced = 0; + if (damage >= 250) + { + damage = damage - 250; + infiniteShot = true; + } + } + + int armorleft = enemy[b].armorleft; + + temp = enemy[b].linknum; + if (temp == 0) + temp = 255; + + if (enemy[b].armorleft < 255) + { + for (unsigned int i = 0; i < COUNTOF(boss_bar); i++) + if (temp == boss_bar[i].link_num) + boss_bar[i].color = 6; + + if (enemy[b].enemyground) + enemy[b].filter = temp2; + + for (unsigned int e = 0; e < COUNTOF(enemy); e++) + { + if (enemy[e].linknum == temp && + enemyAvail[e] != 1 && + enemy[e].enemyground != 0) + { + if (doIced) + enemy[e].iced = doIced; + enemy[e].filter = temp2; + } + } + } + + if (armorleft > damage) + { + if (z != MAX_PWEAPON - 1) + { + if (enemy[b].armorleft != 255) + { + enemy[b].armorleft -= damage; + JE_setupExplosion(tempShotX, tempShotY, 0, 0, false, false); + } + else + { + JE_doSP(tempShotX + 6, tempShotY + 6, damage / 2 + 3, damage / 4 + 2, temp2); + } + } + + soundQueue[5] = S_ENEMY_HIT; + + if ((armorleft - damage <= enemy[b].edlevel) && + ((!enemy[b].edamaged) ^ (enemy[b].edani < 0))) + { + + for (temp3 = 0; temp3 < 100; temp3++) + { + if (enemyAvail[temp3] != 1) + { + int linknum = enemy[temp3].linknum; + if ( + (temp3 == b) || + ( + (temp != 255) && + ( + ((enemy[temp3].edlevel > 0) && (linknum == temp)) || + ( + (enemyContinualDamage && (temp - 100 == linknum)) || + ((linknum > 40) && (linknum / 20 == temp / 20) && (linknum <= temp)) + ) + ) + ) + ) + { + enemy[temp3].enemycycle = 1; + + enemy[temp3].edamaged = !enemy[temp3].edamaged; + + if (enemy[temp3].edani != 0) + { + enemy[temp3].ani = abs(enemy[temp3].edani); + enemy[temp3].aniactive = 1; + enemy[temp3].animax = 0; + enemy[temp3].animin = enemy[temp3].edgr; + enemy[temp3].enemycycle = enemy[temp3].animin - 1; + + } + else if (enemy[temp3].edgr > 0) + { + enemy[temp3].egr[1-1] = enemy[temp3].edgr; + enemy[temp3].ani = 1; + enemy[temp3].aniactive = 0; + enemy[temp3].animax = 0; + enemy[temp3].animin = 1; + } + else + { + enemyAvail[temp3] = 1; + enemyKilled++; + } + + enemy[temp3].aniwhenfire = 0; + + if (enemy[temp3].armorleft > (unsigned char)enemy[temp3].edlevel) + enemy[temp3].armorleft = enemy[temp3].edlevel; + + tempX = enemy[temp3].ex + enemy[temp3].mapoffset; + tempY = enemy[temp3].ey; + + if (enemyDat[enemy[temp3].enemytype].esize != 1) + JE_setupExplosion(tempX, tempY - 6, 0, 1, false, false); + else + JE_setupExplosionLarge(enemy[temp3].enemyground, enemy[temp3].explonum / 2, tempX, tempY); + } + } + } + } + } + else + { + + if ((temp == 254) && (superEnemy254Jump > 0)) + JE_eventJump(superEnemy254Jump); + + for (temp2 = 0; temp2 < 100; temp2++) + { + if (enemyAvail[temp2] != 1) + { + temp3 = enemy[temp2].linknum; + if ((temp2 == b) || (temp == 254) || + ((temp != 255) && ((temp == temp3) || (temp - 100 == temp3) + || ((temp3 > 40) && (temp3 / 20 == temp / 20) && (temp3 <= temp))))) + { + + int enemy_screen_x = enemy[temp2].ex + enemy[temp2].mapoffset; + + if (enemy[temp2].special) + { + assert((unsigned int) enemy[temp2].flagnum-1 < COUNTOF(globalFlags)); + globalFlags[enemy[temp2].flagnum-1] = enemy[temp2].setto; + } + + if ((enemy[temp2].enemydie > 0) && + !((superArcadeMode != SA_NONE) && + (enemyDat[enemy[temp2].enemydie].value == 30000))) + { + int temp_b = b; + tempW = enemy[temp2].enemydie; + int enemy_offset = temp2 - (temp2 % 25); + if (enemyDat[tempW].value > 30000) + { + enemy_offset = 0; + } + b = JE_newEnemy(enemy_offset, tempW, 0); + if (b != 0) { + if ((superArcadeMode != SA_NONE) && (enemy[b-1].evalue > 30000)) + { + superArcadePowerUp++; + if (superArcadePowerUp > 5) + superArcadePowerUp = 1; + enemy[b-1].egr[1-1] = 5 + superArcadePowerUp * 2; + enemy[b-1].evalue = 30000 + superArcadePowerUp; + } + + if (enemy[b-1].evalue != 0) + enemy[b-1].scoreitem = true; + else + enemy[b-1].scoreitem = false; + + enemy[b-1].ex = enemy[temp2].ex; + enemy[b-1].ey = enemy[temp2].ey; + } + b = temp_b; + } + + if ((enemy[temp2].evalue > 0) && (enemy[temp2].evalue < 10000)) + { + if (enemy[temp2].evalue == 1) + { + cubeMax++; + } + else + { + // in galaga mode player 2 is sidekick, so give cash to player 1 + player[galagaMode ? 0 : playerNum - 1].cash += enemy[temp2].evalue; + } + } + + if ((enemy[temp2].edlevel == -1) && (temp == temp3)) + { + enemy[temp2].edlevel = 0; + enemyAvail[temp2] = 2; + enemy[temp2].egr[1-1] = enemy[temp2].edgr; + enemy[temp2].ani = 1; + enemy[temp2].aniactive = 0; + enemy[temp2].animax = 0; + enemy[temp2].animin = 1; + enemy[temp2].edamaged = true; + enemy[temp2].enemycycle = 1; + } else { + enemyAvail[temp2] = 1; + enemyKilled++; + } + + if (enemyDat[enemy[temp2].enemytype].esize == 1) + { + JE_setupExplosionLarge(enemy[temp2].enemyground, enemy[temp2].explonum, enemy_screen_x, enemy[temp2].ey); + soundQueue[6] = S_EXPLOSION_9; + } + else + { + JE_setupExplosion(enemy_screen_x, enemy[temp2].ey, 0, 1, false, false); + soundQueue[6] = S_EXPLOSION_8; + } + } + } + } + } + + if (infiniteShot) + { + damage += 250; + } + else if (z != MAX_PWEAPON - 1) + { + if (damage <= armorleft) + { + shotAvail[z] = 0; + goto draw_player_shot_loop_end; + } + else + { + playerShotData[z].shotDmg -= armorleft; + } + } + } + } + } + +draw_player_shot_loop_end: + ; + } + } + + /* Player movement indicators for shots that track your ship */ + for (uint i = 0; i < COUNTOF(player); ++i) + { + player[i].last_x_shot_move = player[i].x; + player[i].last_y_shot_move = player[i].y; + } + + /*=================================*/ + /*=======Collisions Detection======*/ + /*=================================*/ + + for (uint i = 0; i < (twoPlayerMode ? 2 : 1); ++i) + if (player[i].is_alive && !endLevel) + JE_playerCollide(&player[i], i + 1); + + if (firstGameOver) + JE_mainGamePlayerFunctions(); /*--------PLAYER DRAW+MOVEMENT---------*/ + + if (!endLevel) + { /*MAIN DRAWING IS STOPPED STARTING HERE*/ + + /* Draw Enemy Shots */ + for (int z = 0; z < ENEMY_SHOT_MAX; z++) + { + if (enemyShotAvail[z] == 0) + { + enemyShot[z].sxm += enemyShot[z].sxc; + enemyShot[z].sx += enemyShot[z].sxm; + + if (enemyShot[z].tx != 0) + { + if (enemyShot[z].sx > player[0].x) + { + if (enemyShot[z].sxm > -enemyShot[z].tx) + { + enemyShot[z].sxm--; + } + } else { + if (enemyShot[z].sxm < enemyShot[z].tx) + { + enemyShot[z].sxm++; + } + } + } + + enemyShot[z].sym += enemyShot[z].syc; + enemyShot[z].sy += enemyShot[z].sym; + + if (enemyShot[z].ty != 0) + { + if (enemyShot[z].sy > player[0].y) + { + if (enemyShot[z].sym > -enemyShot[z].ty) + { + enemyShot[z].sym--; + } + } else { + if (enemyShot[z].sym < enemyShot[z].ty) + { + enemyShot[z].sym++; + } + } + } + + if (enemyShot[z].duration-- == 0 || enemyShot[z].sy > 190 || enemyShot[z].sy <= -14 || enemyShot[z].sx > 275 || enemyShot[z].sx <= 0) + { + enemyShotAvail[z] = true; + } + else // check if shot collided with player + { + for (uint i = 0; i < (twoPlayerMode ? 2 : 1); ++i) + { + if (player[i].is_alive && + enemyShot[z].sx > player[i].x - (signed)player[i].shot_hit_area_x && + enemyShot[z].sx < player[i].x + (signed)player[i].shot_hit_area_x && + enemyShot[z].sy > player[i].y - (signed)player[i].shot_hit_area_y && + enemyShot[z].sy < player[i].y + (signed)player[i].shot_hit_area_y) + { + tempX = enemyShot[z].sx; + tempY = enemyShot[z].sy; + temp = enemyShot[z].sdmg; + + enemyShotAvail[z] = true; + + JE_setupExplosion(tempX, tempY, 0, 0, false, false); + + if (player[i].invulnerable_ticks == 0) + { + if ((temp = JE_playerDamage(temp, &player[i])) > 0) + { + player[i].x_velocity += (enemyShot[z].sxm * temp) / 2; + player[i].y_velocity += (enemyShot[z].sym * temp) / 2; + } + } + + break; + } + } + + if (enemyShotAvail[z] == false) + { + if (enemyShot[z].animax != 0) + { + if (++enemyShot[z].animate >= enemyShot[z].animax) + enemyShot[z].animate = 0; + } + + if (enemyShot[z].sgr >= 500) + blit_sprite2(VGAScreen, enemyShot[z].sx, enemyShot[z].sy, shapesW2, enemyShot[z].sgr + enemyShot[z].animate - 500); + else + blit_sprite2(VGAScreen, enemyShot[z].sx, enemyShot[z].sy, shapesC1, enemyShot[z].sgr + enemyShot[z].animate); + } + } + + } + } + } + + if (background3over == 1) + draw_background_3(VGAScreen); + + /* Draw Top Enemy */ + if (topEnemyOver) + { + tempMapXOfs = (background3x1 == 0) ? oldMapX3Ofs : oldMapXOfs; + tempBackMove = backMove3; + JE_drawEnemy(75); + } + + /* Draw Sky Enemy */ + if (skyEnemyOverAll) + { + lastEnemyOnScreen = enemyOnScreen; + + tempMapXOfs = mapX2Ofs; + tempBackMove = 0; + JE_drawEnemy(25); + + if (enemyOnScreen == lastEnemyOnScreen) + { + if (stopBackgroundNum == 2) + stopBackgroundNum = 9; + } + } + + /*-------------------------- Sequenced Explosions -------------------------*/ + enemyStillExploding = false; + for (int i = 0; i < MAX_REPEATING_EXPLOSIONS; i++) + { + if (rep_explosions[i].ttl != 0) + { + enemyStillExploding = true; + + if (rep_explosions[i].delay > 0) + { + rep_explosions[i].delay--; + continue; + } + + rep_explosions[i].y += backMove2 + 1; + tempX = rep_explosions[i].x + (mt_rand() % 24) - 12; + tempY = rep_explosions[i].y + (mt_rand() % 27) - 24; + + if (rep_explosions[i].big) + { + JE_setupExplosionLarge(false, 2, tempX, tempY); + + if (rep_explosions[i].ttl == 1 || mt_rand() % 5 == 1) + soundQueue[7] = S_EXPLOSION_11; + else + soundQueue[6] = S_EXPLOSION_9; + + rep_explosions[i].delay = 4 + (mt_rand() % 3); + } + else + { + JE_setupExplosion(tempX, tempY, 0, 1, false, false); + + soundQueue[5] = S_EXPLOSION_4; + + rep_explosions[i].delay = 3; + } + + rep_explosions[i].ttl--; + } + } + + /*---------------------------- Draw Explosions ----------------------------*/ + for (int j = 0; j < MAX_EXPLOSIONS; j++) + { + if (explosions[j].ttl != 0) + { + if (explosions[j].fixed_position != true) + { + explosions[j].sprite++; + explosions[j].y += explodeMove; + } + else if (explosions[j].follow_player == true) + { + explosions[j].x += explosionFollowAmountX; + explosions[j].y += explosionFollowAmountY; + } + explosions[j].y += explosions[j].delta_y; + explosions[j].x += explosions[j].delta_x; + + if (explosions[j].y > 200 - 14) + { + explosions[j].ttl = 0; + } + else + { + if (explosionTransparent) + blit_sprite2_blend(VGAScreen, explosions[j].x, explosions[j].y, shapes6, explosions[j].sprite + 1); + else + blit_sprite2(VGAScreen, explosions[j].x, explosions[j].y, shapes6, explosions[j].sprite + 1); + + explosions[j].ttl--; + } + } + } + + if (!portConfigChange) + portConfigDone = true; + + + /*-----------------------BACKGROUNDS------------------------*/ + /*-----------------------BACKGROUND 2------------------------*/ + if (!(smoothies[2-1] && processorType < 4) && + !(smoothies[1-1] && processorType == 3)) + { + if (background2over == 2) + { + if (wild && !background2notTransparent) + draw_background_2_blend(VGAScreen); + else + draw_background_2(VGAScreen); + } + } + + /*-------------------------Warning---------------------------*/ + if ((player[0].is_alive && player[0].armor < 6) || + (twoPlayerMode && !galagaMode && player[1].is_alive && player[1].armor < 6)) + { + int armor_amount = (player[0].is_alive && player[0].armor < 6) ? player[0].armor : player[1].armor; + + if (armorShipDelay > 0) + { + armorShipDelay--; + } + else + { + tempW = 560; + b = JE_newEnemy(50, tempW, 0); + if (b > 0) + { + enemy[b-1].enemydie = 560 + (mt_rand() % 3) + 1; + enemy[b-1].eyc -= backMove3; + enemy[b-1].armorleft = 4; + } + armorShipDelay = 500; + } + + if ((player[0].is_alive && player[0].armor < 6 && (!isNetworkGame || thisPlayerNum == 1)) || + (twoPlayerMode && player[1].is_alive && player[1].armor < 6 && (!isNetworkGame || thisPlayerNum == 2))) + { + + tempW = armor_amount * 4 + 8; + if (warningSoundDelay > tempW) + warningSoundDelay = tempW; + + if (warningSoundDelay > 1) + { + warningSoundDelay--; + } + else + { + soundQueue[7] = S_WARNING; + warningSoundDelay = tempW; + } + + warningCol += warningColChange; + if (warningCol > 113 + (14 - (armor_amount * 2))) + { + warningColChange = -warningColChange; + warningCol = 113 + (14 - (armor_amount * 2)); + } + else if (warningCol < 113) + { + warningColChange = -warningColChange; + } + fill_rectangle_xy(VGAScreen, 24, 181, 138, 183, warningCol); + fill_rectangle_xy(VGAScreen, 175, 181, 287, 183, warningCol); + fill_rectangle_xy(VGAScreen, 24, 0, 287, 3, warningCol); + + JE_outText(VGAScreen, 140, 178, "WARNING", 7, (warningCol % 16) / 2); + + } + } + + /*------- Random Explosions --------*/ + if (randomExplosions && mt_rand() % 10 == 1) + JE_setupExplosionLarge(false, 20, mt_rand() % 280, mt_rand() % 180); + + /*=================================*/ + /*=======The Sound Routine=========*/ + /*=================================*/ + if (firstGameOver) + { + temp = 0; + for (temp2 = 0; temp2 < SFX_CHANNELS; temp2++) + { + if (soundQueue[temp2] != S_NONE) + { + temp = soundQueue[temp2]; + if (temp2 == 3) + temp3 = fxPlayVol; + else if (temp == 15) + temp3 = fxPlayVol / 4; + else /*Lightning*/ + temp3 = fxPlayVol / 2; + + JE_multiSamplePlay(digiFx[temp-1], fxSize[temp-1], temp2, temp3); + + soundQueue[temp2] = S_NONE; + } + } + } + + if (returnActive && enemyOnScreen == 0) + { + JE_eventJump(65535); + returnActive = false; + } + + /*------- DEbug ---------*/ + debugTime = SDL_GetTicks(); + tempW = lastmouse_but; + tempX = mouse_x; + tempY = mouse_y; + + if (debug) + { + for (size_t i = 0; i < 9; i++) + { + tempStr[i] = '0' + smoothies[i]; + } + tempStr[9] = '\0'; + sprintf(buffer, "SM = %s", tempStr); + JE_outText(VGAScreen, 30, 70, buffer, 4, 0); + + sprintf(buffer, "Memory left = %d", -1); + JE_outText(VGAScreen, 30, 80, buffer, 4, 0); + sprintf(buffer, "Enemies onscreen = %d", enemyOnScreen); + JE_outText(VGAScreen, 30, 90, buffer, 6, 0); + + debugHist = debugHist + abs((JE_longint)debugTime - (JE_longint)lastDebugTime); + debugHistCount++; + sprintf(tempStr, "%2.3f", 1000.0f / roundf(debugHist / debugHistCount)); + sprintf(buffer, "X:%d Y:%-5d %s FPS %d %d %d %d", (mapX - 1) * 12 + player[0].x, curLoc, tempStr, player[0].x_velocity, player[0].y_velocity, player[0].x, player[0].y); + JE_outText(VGAScreen, 45, 175, buffer, 15, 3); + lastDebugTime = debugTime; + } + + if (displayTime > 0) + { + displayTime--; + JE_outTextAndDarken(VGAScreen, 90, 10, miscText[59], 15, (JE_byte)flash - 8, FONT_SHAPES); + flash += flashChange; + if (flash > 4 || flash == 0) + flashChange = -flashChange; + } + + /*Pentium Speed Mode?*/ + if (pentiumMode) + { + frameCountMax = (frameCountMax == 2) ? 3 : 2; + } + + /*-------- Level Timer ---------*/ + if (levelTimer && levelTimerCountdown > 0) + { + levelTimerCountdown--; + if (levelTimerCountdown == 0) + JE_eventJump(levelTimerJumpTo); + + if (levelTimerCountdown > 200) + { + if (levelTimerCountdown % 100 == 0) + soundQueue[7] = S_WARNING; + + if (levelTimerCountdown % 10 == 0) + soundQueue[6] = S_CLICK; + } + else if (levelTimerCountdown % 20 == 0) + { + soundQueue[7] = S_WARNING; + } + + JE_textShade (VGAScreen, 140, 6, miscText[66], 7, (levelTimerCountdown % 20) / 3, FULL_SHADE); + sprintf(buffer, "%.1f", levelTimerCountdown / 100.0f); + JE_dString (VGAScreen, 100, 2, buffer, SMALL_FONT_SHAPES); + } + + /*GAME OVER*/ + if (!constantPlay && !constantDie) + { + if (allPlayersGone) + { + if (player[0].exploding_ticks > 0 || player[1].exploding_ticks > 0) + { + if (galagaMode) + player[1].exploding_ticks = 0; + + musicFade = true; + } + else + { + if (play_demo || normalBonusLevelCurrent || bonusLevelCurrent) + reallyEndLevel = true; + else + JE_dString(VGAScreen, 120, 60, miscText[21], FONT_SHAPES); // game over + + set_mouse_position(159, 100); + if (firstGameOver) + { + if (!play_demo) + { + play_song(SONG_GAMEOVER); + set_volume(tyrMusicVolume, fxVolume); + } + firstGameOver = false; + } + + if (!play_demo) + { + push_joysticks_as_keyboard(); + service_SDL_events(true); + if ((newkey || button[0] || button[1] || button[2]) || newmouse) + { + reallyEndLevel = true; + } + } + + if (isNetworkGame) + reallyEndLevel = true; + } + } + } + + if (play_demo) // input kills demo + { + push_joysticks_as_keyboard(); + service_SDL_events(false); + + if (newkey || newmouse) + { + reallyEndLevel = true; + + stopped_demo = true; + } + } + else // input handling for pausing, menu, cheats + { + service_SDL_events(false); + + if (newkey) + { + skipStarShowVGA = false; + JE_mainKeyboardInput(); + newkey = false; + if (skipStarShowVGA) + goto level_loop; + } + + if (pause_pressed) + { + pause_pressed = false; + + if (isNetworkGame) + pauseRequest = true; + else + JE_pauseGame(); + } + + if (ingamemenu_pressed) + { + ingamemenu_pressed = false; + + if (isNetworkGame) + { + inGameMenuRequest = true; + } + else + { + yourInGameMenuRequest = true; + JE_doInGameSetup(); + skipStarShowVGA = true; + } + } + } + + /*Network Update*/ +#ifdef WITH_NETWORK + if (isNetworkGame) + { + if (!reallyEndLevel) + { + Uint16 requests = (pauseRequest == true) | + (inGameMenuRequest == true) << 1 | + (skipLevelRequest == true) << 2 | + (nortShipRequest == true) << 3; + SDLNet_Write16(requests, &packet_state_out[0]->data[14]); + + SDLNet_Write16(difficultyLevel, &packet_state_out[0]->data[16]); + SDLNet_Write16(player[0].x, &packet_state_out[0]->data[18]); + SDLNet_Write16(player[1].x, &packet_state_out[0]->data[20]); + SDLNet_Write16(player[0].y, &packet_state_out[0]->data[22]); + SDLNet_Write16(player[1].y, &packet_state_out[0]->data[24]); + SDLNet_Write16(curLoc, &packet_state_out[0]->data[26]); + + network_state_send(); + + if (network_state_update()) + { + assert(SDLNet_Read16(&packet_state_in[0]->data[26]) == SDLNet_Read16(&packet_state_out[network_delay]->data[26])); + + requests = SDLNet_Read16(&packet_state_in[0]->data[14]) ^ SDLNet_Read16(&packet_state_out[network_delay]->data[14]); + if (requests & 1) + { + JE_pauseGame(); + } + if (requests & 2) + { + yourInGameMenuRequest = SDLNet_Read16(&packet_state_out[network_delay]->data[14]) & 2; + JE_doInGameSetup(); + yourInGameMenuRequest = false; + if (haltGame) + reallyEndLevel = true; + } + if (requests & 4) + { + levelTimer = true; + levelTimerCountdown = 0; + endLevel = true; + levelEnd = 40; + } + if (requests & 8) // nortship + { + player[0].items.ship = 12; // Nort Ship + player[0].items.special = 13; // Astral Zone + player[0].items.weapon[FRONT_WEAPON].id = 36; // NortShip Super Pulse + player[0].items.weapon[REAR_WEAPON].id = 37; // NortShip Spreader + shipGr = 1; + } + + for (int i = 0; i < 2; i++) + { + if (SDLNet_Read16(&packet_state_in[0]->data[18 + i * 2]) != SDLNet_Read16(&packet_state_out[network_delay]->data[18 + i * 2]) || SDLNet_Read16(&packet_state_in[0]->data[20 + i * 2]) != SDLNet_Read16(&packet_state_out[network_delay]->data[20 + i * 2])) + { + char temp[64]; + sprintf(temp, "Player %d is unsynchronized!", i + 1); + + JE_textShade(game_screen, 40, 110 + i * 10, temp, 9, 2, FULL_SHADE); + } + } + } + } + + JE_clearSpecialRequests(); + } +#endif + + /** Test **/ + JE_drawSP(); + + /*Filtration*/ + if (filterActive) + { + JE_filterScreen(levelFilter, levelBrightness); + } + + draw_boss_bar(); + + JE_inGameDisplays(); + + VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ + + JE_starShowVGA(); + + /*Start backgrounds if no enemies on screen + End level if number of enemies left to kill equals 0.*/ + if (stopBackgroundNum == 9 && backMove == 0 && !enemyStillExploding) + { + backMove = 1; + backMove2 = 2; + backMove3 = 3; + explodeMove = 2; + stopBackgroundNum = 0; + stopBackgrounds = false; + if (waitToEndLevel) + { + endLevel = true; + levelEnd = 40; + } + if (allPlayersGone) + { + reallyEndLevel = true; + } + } + + if (!endLevel && enemyOnScreen == 0) + { + if (readyToEndLevel && !enemyStillExploding) + { + if (levelTimerCountdown > 0) + { + levelTimer = false; + } + readyToEndLevel = false; + endLevel = true; + levelEnd = 40; + if (allPlayersGone) + { + reallyEndLevel = true; + } + } + if (stopBackgrounds) + { + stopBackgrounds = false; + backMove = 1; + backMove2 = 2; + backMove3 = 3; + explodeMove = 2; + } + } + + + /*Other Network Functions*/ + JE_handleChat(); + + if (reallyEndLevel) + { + goto start_level; + } + goto level_loop; +} + +/* --- Load Level/Map Data --- */ +void JE_loadMap( void ) +{ + JE_DanCShape shape; + + JE_word x, y; + JE_integer yy; + JE_word mapSh[3][128]; /* [1..3, 0..127] */ + JE_byte *ref[3][128]; /* [1..3, 0..127] */ + char s[256]; + + JE_byte mapBuf[15 * 600]; /* [1..15 * 600] */ + JE_word bufLoc; + + char buffer[256]; + int i; + Uint8 pic_buffer[320*200]; /* screen buffer, 8-bit specific */ + Uint8 *vga, *pic, *vga2; /* screen pointer, 8-bit specific */ + + lastCubeMax = cubeMax; + + /*Defaults*/ + songBuy = DEFAULT_SONG_BUY; /*Item Screen default song*/ + + /* Load LEVELS.DAT - Section = MAINLEVEL */ + saveLevel = mainLevel; + +new_game: + galagaMode = false; + useLastBank = false; + extraGame = false; + haltGame = false; + + gameLoaded = false; + + if (!play_demo) + { + do + { + FILE *ep_f = dir_fopen_die(data_dir(), episode_file, "rb"); + + jumpSection = false; + loadLevelOk = false; + + /* Seek Section # Mainlevel */ + int x = 0; + while (x < mainLevel) + { + read_encrypted_pascal_string(s, sizeof(s), ep_f); + if (s[0] == '*') + { + x++; + s[0] = ' '; + } + } + + ESCPressed = false; + + do + { + if (gameLoaded) + { + fclose(ep_f); + + if (mainLevel == 0) // if quit itemscreen + return; // back to title screen + else + goto new_game; + } + + strcpy(s, " "); + read_encrypted_pascal_string(s, sizeof(s), ep_f); + + if (s[0] == ']') + { + switch (s[1]) + { + case 'A': + JE_playAnim("tyrend.anm", 0, 7); + break; + + case 'G': + mapOrigin = atoi(s + 4); + mapPNum = atoi(s + 7); + for (i = 0; i < mapPNum; i++) + { + mapPlanet[i] = atoi(s + 1 + (i + 1) * 8); + mapSection[i] = atoi(s + 4 + (i + 1) * 8); + } + break; + + case '?': + temp = atoi(s + 4); + for (i = 0; i < temp; i++) + { + cubeList[i] = atoi(s + 3 + (i + 1) * 4); + } + if (cubeMax > temp) + cubeMax = temp; + break; + + case '!': + cubeMax = atoi(s + 4); /*Auto set CubeMax*/ + break; + + case '+': + temp = atoi(s + 4); + cubeMax += temp; + if (cubeMax > 4) + cubeMax = 4; + break; + + case 'g': + galagaMode = true; /*GALAGA mode*/ + + player[1].items = player[0].items; + player[1].items.weapon[REAR_WEAPON].id = 15; // Vulcan Cannon + for (uint i = 0; i < COUNTOF(player[1].items.sidekick); ++i) + player[1].items.sidekick[i] = 0; // None + break; + + case 'x': + extraGame = true; + break; + + case 'e': // ENGAGE mode, used for mini-games + doNotSaveBackup = true; + constantDie = false; + onePlayerAction = true; + superTyrian = true; + twoPlayerMode = false; + + player[0].cash = 0; + + player[0].items.ship = 13; // The Stalker 21.126 + player[0].items.weapon[FRONT_WEAPON].id = 39; // Atomic RailGun + player[0].items.weapon[REAR_WEAPON].id = 0; // None + for (uint i = 0; i < COUNTOF(player[0].items.sidekick); ++i) + player[0].items.sidekick[i] = 0; // None + player[0].items.generator = 2; // Advanced MR-12 + player[0].items.shield = 4; // Advanced Integrity Field + player[0].items.special = 0; // None + + player[0].items.weapon[FRONT_WEAPON].power = 3; + player[0].items.weapon[REAR_WEAPON].power = 1; + break; + + case 'J': // section jump + temp = atoi(s + 3); + mainLevel = temp; + jumpSection = true; + break; + + case '2': // two-player section jump + temp = atoi(s + 3); + if (twoPlayerMode || onePlayerAction) + { + mainLevel = temp; + jumpSection = true; + } + break; + + case 'w': // Stalker 21.126 section jump + temp = atoi(s + 3); /*Allowed to go to Time War?*/ + if (player[0].items.ship == 13) + { + mainLevel = temp; + jumpSection = true; + } + break; + + case 't': + temp = atoi(s + 3); + if (levelTimer && levelTimerCountdown == 0) + { + mainLevel = temp; + jumpSection = true; + } + break; + + case 'l': + temp = atoi(s + 3); + if (!all_players_alive()) + { + mainLevel = temp; + jumpSection = true; + } + break; + + case 's': + saveLevel = mainLevel; + break; /*store savepoint*/ + + case 'b': + if (twoPlayerMode) + { + temp = 22; + } else { + temp = 11; + } + JE_saveGame(11, "LAST LEVEL "); + break; + + case 'i': + temp = atoi(s + 3); + songBuy = temp - 1; + break; + + case 'I': /*Load Items Available Information*/ + memset(&itemAvail, 0, sizeof(itemAvail)); + + for (int i = 0; i < 9; ++i) + { + read_encrypted_pascal_string(s, sizeof(s), ep_f); + + char buf[256]; + strncpy(buf, (strlen(s) > 8) ? s + 8 : "", sizeof(buf)); + + int j = 0, temp; + while (str_pop_int(buf, &temp)) + itemAvail[i][j++] = temp; + itemAvailMax[i] = j; + } + + JE_itemScreen(); + break; + + case 'L': + nextLevel = atoi(s + 9); + SDL_strlcpy(levelName, s + 13, 10); + levelSong = atoi(s + 22); + if (nextLevel == 0) + { + nextLevel = mainLevel + 1; + } + lvlFileNum = atoi(s + 25); + loadLevelOk = true; + bonusLevelCurrent = (strlen(s) > 28) & (s[28] == '$'); + normalBonusLevelCurrent = (strlen(s) > 27) & (s[27] == '$'); + gameJustLoaded = false; + break; + + case '@': + useLastBank = !useLastBank; + break; + + case 'Q': + ESCPressed = false; + temp = secretHint + (mt_rand() % 3) * 3; + + if (twoPlayerMode) + { + for (uint i = 0; i < 2; ++i) + snprintf(levelWarningText[i], sizeof(*levelWarningText), "%s %lu", miscText[40], player[i].cash); + strcpy(levelWarningText[2], ""); + levelWarningLines = 3; + } + else + { + sprintf(levelWarningText[0], "%s %lu", miscText[37], JE_totalScore(&player[0])); + strcpy(levelWarningText[1], ""); + levelWarningLines = 2; + } + + for (x = 0; x < temp - 1; x++) + { + do + read_encrypted_pascal_string(s, sizeof(s), ep_f); + while (s[0] != '#'); + } + + do + { + read_encrypted_pascal_string(s, sizeof(s), ep_f); + strcpy(levelWarningText[levelWarningLines], s); + levelWarningLines++; + } + while (s[0] != '#'); + levelWarningLines--; + + JE_wipeKey(); + frameCountMax = 4; + if (!constantPlay) + JE_displayText(); + + fade_black(15); + + JE_nextEpisode(); + + if (jumpBackToEpisode1 && !twoPlayerMode) + { + JE_loadPic(VGAScreen, 1, false); // huh? + JE_clr256(VGAScreen); + + if (superTyrian) + { + // if completed Zinglon's Revenge, show SuperTyrian and Destruct codes + // if completed SuperTyrian, show Nort-Ship Z code + superArcadeMode = (initialDifficulty == 8) ? 8 : 1; + } + + if (superArcadeMode < SA_ENGAGE) + { + if (SANextShip[superArcadeMode] == SA_ENGAGE) + { + sprintf(buffer, "%s %s", miscTextB[4], pName[0]); + JE_dString(VGAScreen, JE_fontCenter(buffer, FONT_SHAPES), 100, buffer, FONT_SHAPES); + + sprintf(buffer, "Or play... %s", specialName[7]); + JE_dString(VGAScreen, 80, 180, buffer, SMALL_FONT_SHAPES); + } + else + { + JE_dString(VGAScreen, JE_fontCenter(superShips[0], FONT_SHAPES), 30, superShips[0], FONT_SHAPES); + JE_dString(VGAScreen, JE_fontCenter(superShips[SANextShip[superArcadeMode]], SMALL_FONT_SHAPES), 100, superShips[SANextShip[superArcadeMode]], SMALL_FONT_SHAPES); + } + + if (SANextShip[superArcadeMode] < SA_NORTSHIPZ) + blit_sprite2x2(VGAScreen, 148, 70, shapes9, ships[SAShip[SANextShip[superArcadeMode]-1]].shipgraphic); + else if (SANextShip[superArcadeMode] == SA_NORTSHIPZ) + trentWin = true; + + sprintf(buffer, "Type %s at Title", specialName[SANextShip[superArcadeMode]-1]); + JE_dString(VGAScreen, JE_fontCenter(buffer, SMALL_FONT_SHAPES), 160, buffer, SMALL_FONT_SHAPES); + JE_showVGA(); + + fade_palette(colors, 50, 0, 255); + + if (!constantPlay) + wait_input(true, true, true); + } + + jumpSection = true; + + if (isNetworkGame) + JE_readTextSync(); + + if (superTyrian) + { + fade_black(10); + + // back to titlescreen + mainLevel = 0; + return; + } + } + break; + + case 'P': + if (!constantPlay) + { + tempX = atoi(s + 3); + if (tempX > 900) + { + memcpy(colors, palettes[pcxpal[tempX-1 - 900]], sizeof(colors)); + JE_clr256(VGAScreen); + JE_showVGA(); + fade_palette(colors, 1, 0, 255); + } + else + { + if (tempX == 0) + JE_loadPCX("tshp2.pcx"); + else + JE_loadPic(VGAScreen, tempX, false); + + JE_showVGA(); + fade_palette(colors, 10, 0, 255); + } + } + break; + + case 'U': + if (!constantPlay) + { + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); + + tempX = atoi(s + 3); + JE_loadPic(VGAScreen, tempX, false); + memcpy(pic_buffer, VGAScreen->pixels, sizeof(pic_buffer)); + + service_SDL_events(true); + + for (int z = 0; z <= 199; z++) + { + if (!newkey) + { + vga = VGAScreen->pixels; + vga2 = VGAScreen2->pixels; + pic = pic_buffer + (199 - z) * 320; + + setjasondelay(1); /* attempting to emulate JE_waitRetrace();*/ + + for (y = 0; y <= 199; y++) + { + if (y <= z) + { + memcpy(vga, pic, 320); + pic += 320; + } + else + { + memcpy(vga, vga2, VGAScreen->pitch); + vga2 += VGAScreen->pitch; + } + vga += VGAScreen->pitch; + } + + JE_showVGA(); + + if (isNetworkGame) + { + /* TODO: NETWORK */ + } + + service_wait_delay(); + } + } + + memcpy(VGAScreen->pixels, pic_buffer, sizeof(pic_buffer)); + } + break; + + case 'V': + if (!constantPlay) + { + /* TODO: NETWORK */ + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); + + tempX = atoi(s + 3); + JE_loadPic(VGAScreen, tempX, false); + memcpy(pic_buffer, VGAScreen->pixels, sizeof(pic_buffer)); + + service_SDL_events(true); + for (int z = 0; z <= 199; z++) + { + if (!newkey) + { + vga = VGAScreen->pixels; + vga2 = VGAScreen2->pixels; + pic = pic_buffer; + + setjasondelay(1); /* attempting to emulate JE_waitRetrace();*/ + + for (y = 0; y < 199; y++) + { + if (y <= 199 - z) + { + memcpy(vga, vga2, VGAScreen->pitch); + vga2 += VGAScreen->pitch; + } + else + { + memcpy(vga, pic, 320); + pic += 320; + } + vga += VGAScreen->pitch; + } + + JE_showVGA(); + + if (isNetworkGame) + { + /* TODO: NETWORK */ + } + + service_wait_delay(); + } + } + + memcpy(VGAScreen->pixels, pic_buffer, sizeof(pic_buffer)); + } + break; + + case 'R': + if (!constantPlay) + { + /* TODO: NETWORK */ + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); + + tempX = atoi(s + 3); + JE_loadPic(VGAScreen, tempX, false); + memcpy(pic_buffer, VGAScreen->pixels, sizeof(pic_buffer)); + + service_SDL_events(true); + + for (int z = 0; z <= 318; z++) + { + if (!newkey) + { + vga = VGAScreen->pixels; + vga2 = VGAScreen2->pixels; + pic = pic_buffer; + + setjasondelay(1); /* attempting to emulate JE_waitRetrace();*/ + + for(y = 0; y < 200; y++) + { + memcpy(vga, vga2 + z, 319 - z); + vga += 320 - z; + vga2 += VGAScreen2->pitch; + memcpy(vga, pic, z + 1); + vga += z; + pic += 320; + } + + JE_showVGA(); + + if (isNetworkGame) + { + /* TODO: NETWORK */ + } + + service_wait_delay(); + } + } + + memcpy(VGAScreen->pixels, pic_buffer, sizeof(pic_buffer)); + } + break; + + case 'C': + if (!isNetworkGame) + { + fade_black(10); + } + JE_clr256(VGAScreen); + JE_showVGA(); + memcpy(colors, palettes[7], sizeof(colors)); + set_palette(colors, 0, 255); + break; + + case 'B': + if (!isNetworkGame) + { + fade_black(10); + } + break; + case 'F': + if (!isNetworkGame) + { + fade_white(100); + fade_black(30); + } + JE_clr256(VGAScreen); + JE_showVGA(); + break; + + case 'W': + if (!constantPlay) + { + if (!ESCPressed) + { + JE_wipeKey(); + warningCol = 14 * 16 + 5; + warningColChange = 1; + warningSoundDelay = 0; + levelWarningDisplay = (s[2] == 'y'); + levelWarningLines = 0; + frameCountMax = atoi(s + 4); + setjasondelay2(6); + warningRed = frameCountMax / 10; + frameCountMax = frameCountMax % 10; + + do + { + read_encrypted_pascal_string(s, sizeof(s), ep_f); + + if (s[0] != '#') + { + strcpy(levelWarningText[levelWarningLines], s); + levelWarningLines++; + } + } + while (!(s[0] == '#')); + + JE_displayText(); + newkey = false; + } + } + break; + + case 'H': + if (initialDifficulty < 3) + { + mainLevel = atoi(s + 4); + jumpSection = true; + } + break; + + case 'h': + if (initialDifficulty > 2) + { + read_encrypted_pascal_string(s, sizeof(s), ep_f); + } + break; + + case 'S': + if (isNetworkGame) + { + JE_readTextSync(); + } + break; + + case 'n': + ESCPressed = false; + break; + + case 'M': + temp = atoi(s + 3); + play_song(temp - 1); + break; + } + } + + } while (!(loadLevelOk || jumpSection)); + + + fclose(ep_f); + + } while (!loadLevelOk); + } + + if (play_demo) + load_next_demo(); + else + fade_black(50); + + FILE *level_f = dir_fopen_die(data_dir(), levelFile, "rb"); + fseek(level_f, lvlPos[(lvlFileNum-1) * 2], SEEK_SET); + + fgetc(level_f); // char_mapFile + JE_char char_shapeFile = fgetc(level_f); + efread(&mapX, sizeof(JE_word), 1, level_f); + efread(&mapX2, sizeof(JE_word), 1, level_f); + efread(&mapX3, sizeof(JE_word), 1, level_f); + + efread(&levelEnemyMax, sizeof(JE_word), 1, level_f); + for (x = 0; x < levelEnemyMax; x++) + { + efread(&levelEnemy[x], sizeof(JE_word), 1, level_f); + } + + efread(&maxEvent, sizeof(JE_word), 1, level_f); + for (x = 0; x < maxEvent; x++) + { + efread(&eventRec[x].eventtime, sizeof(JE_word), 1, level_f); + efread(&eventRec[x].eventtype, sizeof(JE_byte), 1, level_f); + efread(&eventRec[x].eventdat, sizeof(JE_integer), 1, level_f); + efread(&eventRec[x].eventdat2, sizeof(JE_integer), 1, level_f); + efread(&eventRec[x].eventdat3, sizeof(JE_shortint), 1, level_f); + efread(&eventRec[x].eventdat5, sizeof(JE_shortint), 1, level_f); + efread(&eventRec[x].eventdat6, sizeof(JE_shortint), 1, level_f); + efread(&eventRec[x].eventdat4, sizeof(JE_byte), 1, level_f); + } + eventRec[x].eventtime = 65500; /*Not needed but just in case*/ + + /*debuginfo('Level loaded.');*/ + + /*debuginfo('Loading Map');*/ + + /* MAP SHAPE LOOKUP TABLE - Each map is directly after level */ + efread(mapSh, sizeof(JE_word), sizeof(mapSh) / sizeof(JE_word), level_f); + for (temp = 0; temp < 3; temp++) + { + for (temp2 = 0; temp2 < 128; temp2++) + { + mapSh[temp][temp2] = SDL_Swap16(mapSh[temp][temp2]); + } + } + + /* Read Shapes.DAT */ + sprintf(tempStr, "shapes%c.dat", tolower((unsigned char)char_shapeFile)); + FILE *shpFile = dir_fopen_die(data_dir(), tempStr, "rb"); + + for (int z = 0; z < 600; z++) + { + JE_boolean shapeBlank = fgetc(shpFile); + + if (shapeBlank) + memset(shape, 0, sizeof(shape)); + else + efread(shape, sizeof(JE_byte), sizeof(shape), shpFile); + + /* Match 1 */ + for (int x = 0; x <= 71; ++x) + { + if (mapSh[0][x] == z+1) + { + memcpy(megaData1.shapes[x].sh, shape, sizeof(JE_DanCShape)); + + ref[0][x] = (JE_byte *)megaData1.shapes[x].sh; + } + } + + /* Match 2 */ + for (int x = 0; x <= 71; ++x) + { + if (mapSh[1][x] == z+1) + { + if (x != 71 && !shapeBlank) + { + memcpy(megaData2.shapes[x].sh, shape, sizeof(JE_DanCShape)); + + y = 1; + for (yy = 0; yy < (24 * 28) >> 1; yy++) + if (shape[yy] == 0) + y = 0; + + megaData2.shapes[x].fill = y; + ref[1][x] = (JE_byte *)megaData2.shapes[x].sh; + } + else + { + ref[1][x] = NULL; + } + } + } + + /*Match 3*/ + for (int x = 0; x <= 71; ++x) + { + if (mapSh[2][x] == z+1) + { + if (x < 70 && !shapeBlank) + { + memcpy(megaData3.shapes[x].sh, shape, sizeof(JE_DanCShape)); + + y = 1; + for (yy = 0; yy < (24 * 28) >> 1; yy++) + if (shape[yy] == 0) + y = 0; + + megaData3.shapes[x].fill = y; + ref[2][x] = (JE_byte *)megaData3.shapes[x].sh; + } + else + { + ref[2][x] = NULL; + } + } + } + } + + fclose(shpFile); + + efread(mapBuf, sizeof(JE_byte), 14 * 300, level_f); + bufLoc = 0; /* MAP NUMBER 1 */ + for (y = 0; y < 300; y++) + { + for (x = 0; x < 14; x++) + { + megaData1.mainmap[y][x] = ref[0][mapBuf[bufLoc]]; + bufLoc++; + } + } + + efread(mapBuf, sizeof(JE_byte), 14 * 600, level_f); + bufLoc = 0; /* MAP NUMBER 2 */ + for (y = 0; y < 600; y++) + { + for (x = 0; x < 14; x++) + { + megaData2.mainmap[y][x] = ref[1][mapBuf[bufLoc]]; + bufLoc++; + } + } + + efread(mapBuf, sizeof(JE_byte), 15 * 600, level_f); + bufLoc = 0; /* MAP NUMBER 3 */ + for (y = 0; y < 600; y++) + { + for (x = 0; x < 15; x++) + { + megaData3.mainmap[y][x] = ref[2][mapBuf[bufLoc]]; + bufLoc++; + } + } + + fclose(level_f); + + /* Note: The map data is automatically calculated with the correct mapsh + value and then the pointer is calculated using the formula (MAPSH-1)*168. + Then, we'll automatically add S2Ofs to get the exact offset location into + the shape table! This makes it VERY FAST! */ + + /*debuginfo('Map file done.');*/ + /* End of find loop for LEVEL??.DAT */ +} + +bool JE_titleScreen( JE_boolean animate ) +{ + bool quit = false; + + const int menunum = 7; + + unsigned int arcade_code_i[SA_ENGAGE] = { 0 }; + + JE_word waitForDemo; + JE_byte menu = 0; + JE_boolean redraw = true, + fadeIn = false; + + JE_word temp; /* JE_byte temp; from varz.h will overflow in for loop */ + + play_demo = false; + stopped_demo = false; + + redraw = true; + fadeIn = false; + + gameLoaded = false; + jumpSection = false; + +#ifdef WITH_NETWORK + if (isNetworkGame) + { + JE_loadPic(VGAScreen, 2, false); + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); + JE_dString(VGAScreen, JE_fontCenter("Waiting for other player.", SMALL_FONT_SHAPES), 140, "Waiting for other player.", SMALL_FONT_SHAPES); + JE_showVGA(); + fade_palette(colors, 10, 0, 255); + + network_connect(); + + twoPlayerMode = true; + if (thisPlayerNum == 1) + { + fade_black(10); + + if (select_episode() && select_difficulty()) + { + initialDifficulty = difficultyLevel; + + difficultyLevel++; /*Make it one step harder for 2-player mode!*/ + + network_prepare(PACKET_DETAILS); + SDLNet_Write16(episodeNum, &packet_out_temp->data[4]); + SDLNet_Write16(difficultyLevel, &packet_out_temp->data[6]); + network_send(8); // PACKET_DETAILS + } + else + { + network_prepare(PACKET_QUIT); + network_send(4); // PACKET QUIT + + network_tyrian_halt(0, true); + } + } + else + { + memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->pitch * VGAScreen->h); + JE_dString(VGAScreen, JE_fontCenter(networkText[4-1], SMALL_FONT_SHAPES), 140, networkText[4-1], SMALL_FONT_SHAPES); + JE_showVGA(); + + // until opponent sends details packet + while (true) + { + service_SDL_events(false); + JE_showVGA(); + + if (packet_in[0] && SDLNet_Read16(&packet_in[0]->data[0]) == PACKET_DETAILS) + break; + + network_update(); + network_check(); + + uSDL_Delay(16); + } + + JE_initEpisode(SDLNet_Read16(&packet_in[0]->data[4])); + difficultyLevel = SDLNet_Read16(&packet_in[0]->data[6]); + initialDifficulty = difficultyLevel - 1; + fade_black(10); + + network_update(); + } + + for (uint i = 0; i < COUNTOF(player); ++i) + player[i].cash = 0; + + player[0].items.ship = 11; // Silver Ship + + while (!network_is_sync()) + { + service_SDL_events(false); + JE_showVGA(); + + network_check(); + uSDL_Delay(16); + } + } + else +#endif + { + do + { + /* Animate instead of quickly fading in */ + if (redraw) + { + play_song(SONG_TITLE); + + menu = 0; + redraw = false; + if (animate) + { + if (fadeIn) + { + fade_black(10); + fadeIn = false; + } + + JE_loadPic(VGAScreen, 4, false); + + draw_font_hv_shadow(VGAScreen, 2, 192, opentyrian_version, small_font, left_aligned, 15, 0, false, 1); + + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); + + temp = moveTyrianLogoUp ? 62 : 4; + + blit_sprite(VGAScreenSeg, 11, temp, PLANET_SHAPES, 146); // tyrian logo + + JE_showVGA(); + + fade_palette(colors, 10, 0, 255 - 16); + + if (moveTyrianLogoUp) + { + for (temp = 61; temp >= 4; temp -= 2) + { + setjasondelay(2); + + memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->pitch * VGAScreen->h); + + blit_sprite(VGAScreenSeg, 11, temp, PLANET_SHAPES, 146); // tyrian logo + + JE_showVGA(); + + service_wait_delay(); + } + moveTyrianLogoUp = false; + } + + strcpy(menuText[4], opentyrian_str); // OpenTyrian override + + /* Draw Menu Text on Screen */ + for (int i = 0; i < menunum; ++i) + { + int x = VGAScreen->w / 2, y = 104 + i * 13; + + draw_font_hv(VGAScreen, x - 1, y - 1, menuText[i], normal_font, centered, 15, -10); + draw_font_hv(VGAScreen, x + 1, y + 1, menuText[i], normal_font, centered, 15, -10); + draw_font_hv(VGAScreen, x + 1, y - 1, menuText[i], normal_font, centered, 15, -10); + draw_font_hv(VGAScreen, x - 1, y + 1, menuText[i], normal_font, centered, 15, -10); + draw_font_hv(VGAScreen, x, y, menuText[i], normal_font, centered, 15, -3); + } + + JE_showVGA(); + + fade_palette(colors, 20, 255 - 16 + 1, 255); // fade in menu items + + memcpy(VGAScreen2->pixels, VGAScreen->pixels, VGAScreen2->pitch * VGAScreen2->h); + } + } + + memcpy(VGAScreen->pixels, VGAScreen2->pixels, VGAScreen->pitch * VGAScreen->h); + + // highlight selected menu item + draw_font_hv(VGAScreen, VGAScreen->w / 2, 104 + menu * 13, menuText[menu], normal_font, centered, 15, -1); + + JE_showVGA(); + + if (trentWin) + { + quit = true; + goto trentWinsGame; + } + + waitForDemo = 2000; + JE_textMenuWait(&waitForDemo, false); + + if (waitForDemo == 1) + play_demo = true; + + if (newkey) + { + switch (lastkey_sym) + { + case SDLK_UP: + if (menu == 0) + menu = menunum-1; + else + menu--; + JE_playSampleNum(S_CURSOR); + break; + case SDLK_DOWN: + if (menu == menunum-1) + menu = 0; + else + menu++; + JE_playSampleNum(S_CURSOR); + break; + default: + break; + } + } + + for (unsigned int i = 0; i < SA_ENGAGE; i++) + { + if (toupper(lastkey_char) == specialName[i][arcade_code_i[i]]) + arcade_code_i[i]++; + else + arcade_code_i[i] = 0; + + if (arcade_code_i[i] > 0 && arcade_code_i[i] == strlen(specialName[i])) + { + if (i+1 == SA_DESTRUCT) + { + loadDestruct = true; + } + else if (i+1 == SA_ENGAGE) + { + /* SuperTyrian */ + + JE_playSampleNum(V_DATA_CUBE); + JE_whoa(); + + initialDifficulty = keysactive[SDLK_SCROLLOCK] ? 6 : 8; + + JE_clr256(VGAScreen); + JE_outText(VGAScreen, 10, 10, "Cheat codes have been disabled.", 15, 4); + if (initialDifficulty == 8) + JE_outText(VGAScreen, 10, 20, "Difficulty level has been set to Lord of Game.", 15, 4); + else + JE_outText(VGAScreen, 10, 20, "Difficulty level has been set to Suicide.", 15, 4); + JE_outText(VGAScreen, 10, 30, "It is imperative that you discover the special codes.", 15, 4); + if (initialDifficulty == 8) + JE_outText(VGAScreen, 10, 40, "(Next time, for an easier challenge hold down SCROLL LOCK.)", 15, 4); + JE_outText(VGAScreen, 10, 60, "Prepare to play...", 15, 4); + + char buf[10+1+15+1]; + snprintf(buf, sizeof(buf), "%s %s", miscTextB[4], pName[0]); + JE_dString(VGAScreen, JE_fontCenter(buf, FONT_SHAPES), 110, buf, FONT_SHAPES); + + play_song(16); + JE_playSampleNum(V_DANGER); + JE_showVGA(); + + wait_noinput(true, true, true); + wait_input(true, true, true); + + JE_initEpisode(1); + constantDie = false; + superTyrian = true; + onePlayerAction = true; + gameLoaded = true; + difficultyLevel = initialDifficulty; + + player[0].cash = 0; + + player[0].items.ship = 13; // The Stalker 21.126 + player[0].items.weapon[FRONT_WEAPON].id = 39; // Atomic RailGun + } + else + { + player[0].items.ship = SAShip[i]; + + fade_black(10); + if (select_episode() && select_difficulty()) + { + /* Start special mode! */ + fade_black(10); + JE_loadPic(VGAScreen, 1, false); + JE_clr256(VGAScreen); + JE_dString(VGAScreen, JE_fontCenter(superShips[0], FONT_SHAPES), 30, superShips[0], FONT_SHAPES); + JE_dString(VGAScreen, JE_fontCenter(superShips[i+1], SMALL_FONT_SHAPES), 100, superShips[i+1], SMALL_FONT_SHAPES); + tempW = ships[player[0].items.ship].shipgraphic; + if (tempW != 1) + blit_sprite2x2(VGAScreen, 148, 70, shapes9, tempW); + + JE_showVGA(); + fade_palette(colors, 50, 0, 255); + + wait_input(true, true, true); + + twoPlayerMode = false; + onePlayerAction = true; + superArcadeMode = i+1; + gameLoaded = true; + initialDifficulty = ++difficultyLevel; + + player[0].cash = 0; + + player[0].items.weapon[FRONT_WEAPON].id = SAWeapon[i][0]; + player[0].items.special = SASpecialWeapon[i]; + if (superArcadeMode == SA_NORTSHIPZ) + { + for (uint i = 0; i < COUNTOF(player[0].items.sidekick); ++i) + player[0].items.sidekick[i] = 24; // Companion Ship Quicksilver + } + } + else + { + redraw = true; + fadeIn = true; + } + } + newkey = false; + } + } + lastkey_char = '\0'; + + if (newkey) + { + switch (lastkey_sym) + { + case SDLK_ESCAPE: + quit = true; + break; + case SDLK_RETURN: + JE_playSampleNum(S_SELECT); + switch (menu) + { + case 0: /* New game */ + fade_black(10); + + if (select_gameplay()) + { + if (select_episode() && select_difficulty()) + gameLoaded = true; + + initialDifficulty = difficultyLevel; + + if (onePlayerAction) + { + player[0].cash = 0; + + player[0].items.ship = 8; // Stalker + } + else if (twoPlayerMode) + { + for (uint i = 0; i < COUNTOF(player); ++i) + player[i].cash = 0; + + player[0].items.ship = 11; // Silver Ship + + difficultyLevel++; + + inputDevice[0] = 1; + inputDevice[1] = 2; + } + else if (richMode) + { + player[0].cash = 1000000; + } + else if (gameLoaded) + { + // allows player to smuggle arcade/super-arcade ships into full game + + ulong initial_cash[] = { 10000, 15000, 20000, 30000 }; + + assert(episodeNum >= 1 && episodeNum <= EPISODE_AVAILABLE); + player[0].cash = initial_cash[episodeNum-1]; + } + } + fadeIn = true; + break; + case 1: /* Load game */ + JE_loadScreen(); + fadeIn = true; + break; + case 2: /* High scores */ + JE_highScoreScreen(); + fadeIn = true; + break; + case 3: /* Instructions */ + JE_helpSystem(1); + fadeIn = true; + break; + case 4: /* Ordering info, now OpenTyrian menu */ + opentyrian_menu(); + fadeIn = true; + break; + case 5: /* Demo */ + play_demo = true; + break; + case 6: /* Quit */ + quit = true; + break; + } + redraw = true; + break; + default: + break; + } + } + } + while (!(quit || gameLoaded || jumpSection || play_demo || loadDestruct)); + +trentWinsGame: + fade_black(15); + } + + return quit; +} + +void intro_logos( void ) +{ + SDL_FillRect(VGAScreen, NULL, 0); + + fade_white(50); + + JE_loadPic(VGAScreen, 10, false); + JE_showVGA(); + + fade_palette(colors, 50, 0, 255); + + setjasondelay(200); + wait_delayorinput(true, true, true); + + fade_black(10); + + JE_loadPic(VGAScreen, 12, false); + JE_showVGA(); + + fade_palette(colors, 10, 0, 255); + + setjasondelay(200); + wait_delayorinput(true, true, true); + + fade_black(10); +} + +void JE_readTextSync( void ) +{ +#if 0 // this function seems to be unnecessary + JE_clr256(VGAScreen); + JE_showVGA(); + JE_loadPic(VGAScreen, 1, true); + + JE_barShade(VGAScreen, 3, 3, 316, 196); + JE_barShade(VGAScreen, 1, 1, 318, 198); + JE_dString(VGAScreen, 10, 160, "Waiting for other player.", SMALL_FONT_SHAPES); + JE_showVGA(); + + /* TODO: NETWORK */ + + do + { + setjasondelay(2); + + /* TODO: NETWORK */ + + wait_delay(); + + } while (0 /* TODO: NETWORK */); +#endif +} + + +void JE_displayText( void ) +{ + /* Display Warning Text */ + tempY = 55; + if (warningRed) + { + tempY = 2; + } + for (temp = 0; temp < levelWarningLines; temp++) + { + if (!ESCPressed) + { + JE_outCharGlow(10, tempY, levelWarningText[temp]); + + if (haltGame) + { + JE_tyrianHalt(5); + } + + tempY += 10; + } + } + if (frameCountMax != 0) + { + frameCountMax = 6; + temp = 1; + } else { + temp = 0; + } + textGlowFont = TINY_FONT; + tempW = 184; + if (warningRed) + { + tempW = 7 * 16 + 6; + } + + JE_outCharGlow(JE_fontCenter(miscText[4], TINY_FONT), tempW, miscText[4]); + + do + { + if (levelWarningDisplay) + { + JE_updateWarning(VGAScreen); + } + + setjasondelay(1); + + NETWORK_KEEP_ALIVE(); + + wait_delay(); + + } while (!(JE_anyButton() || (frameCountMax == 0 && temp == 1) || ESCPressed)); + levelWarningDisplay = false; +} + +Sint16 JE_newEnemy( int enemyOffset, Uint16 eDatI, Sint16 uniqueShapeTableI ) +{ + for (int i = enemyOffset; i < enemyOffset + 25; ++i) + { + if (enemyAvail[i] == 1) + { + enemyAvail[i] = JE_makeEnemy(&enemy[i], eDatI, uniqueShapeTableI); + return i + 1; + } + } + + return 0; +} + +uint JE_makeEnemy( struct JE_SingleEnemyType *enemy, Uint16 eDatI, Sint16 uniqueShapeTableI ) +{ + uint avail; + + JE_byte shapeTableI; + + if (superArcadeMode != SA_NONE && eDatI == 534) + eDatI = 533; + + enemyShapeTables[5-1] = 21; /*Coins&Gems*/ + enemyShapeTables[6-1] = 26; /*Two-Player Stuff*/ + + if (uniqueShapeTableI > 0) + { + shapeTableI = uniqueShapeTableI; + } + else + { + shapeTableI = enemyDat[eDatI].shapebank; + } + + Sprite2_array *sprite2s = NULL; + for (uint i = 0; i < 6; ++i) + if (shapeTableI == enemyShapeTables[i]) + sprite2s = &eShapes[i]; + + if (sprite2s != NULL) + enemy->sprite2s = sprite2s; + else + // maintain buggy Tyrian behavior (use shape table value from previous enemy that occupied this index in the enemy array) + fprintf(stderr, "warning: ignoring sprite from unloaded shape table %d\n", shapeTableI); + + enemy->enemydatofs = &enemyDat[eDatI]; + + enemy->mapoffset = 0; + + for (uint i = 0; i < 3; ++i) + { + enemy->eshotmultipos[i] = 0; + } + + enemy->enemyground = (enemyDat[eDatI].explosiontype & 1) == 0; + enemy->explonum = enemyDat[eDatI].explosiontype >> 1; + + enemy->launchfreq = enemyDat[eDatI].elaunchfreq; + enemy->launchwait = enemyDat[eDatI].elaunchfreq; + enemy->launchtype = enemyDat[eDatI].elaunchtype % 1000; + enemy->launchspecial = enemyDat[eDatI].elaunchtype / 1000; + + enemy->xaccel = enemyDat[eDatI].xaccel; + enemy->yaccel = enemyDat[eDatI].yaccel; + + enemy->xminbounce = -10000; + enemy->xmaxbounce = 10000; + enemy->yminbounce = -10000; + enemy->ymaxbounce = 10000; + /*Far enough away to be impossible to reach*/ + + for (uint i = 0; i < 3; ++i) + { + enemy->tur[i] = enemyDat[eDatI].tur[i]; + } + + enemy->ani = enemyDat[eDatI].ani; + enemy->animin = 1; + + switch (enemyDat[eDatI].animate) + { + case 0: + enemy->enemycycle = 1; + enemy->aniactive = 0; + enemy->animax = 0; + enemy->aniwhenfire = 0; + break; + case 1: + enemy->enemycycle = 0; + enemy->aniactive = 1; + enemy->animax = 0; + enemy->aniwhenfire = 0; + break; + case 2: + enemy->enemycycle = 1; + enemy->aniactive = 2; + enemy->animax = enemy->ani; + enemy->aniwhenfire = 2; + break; + } + + if (enemyDat[eDatI].startxc != 0) + enemy->ex = enemyDat[eDatI].startx + (mt_rand() % (enemyDat[eDatI].startxc * 2)) - enemyDat[eDatI].startxc + 1; + else + enemy->ex = enemyDat[eDatI].startx + 1; + + if (enemyDat[eDatI].startyc != 0) + enemy->ey = enemyDat[eDatI].starty + (mt_rand() % (enemyDat[eDatI].startyc * 2)) - enemyDat[eDatI].startyc + 1; + else + enemy->ey = enemyDat[eDatI].starty + 1; + + enemy->exc = enemyDat[eDatI].xmove; + enemy->eyc = enemyDat[eDatI].ymove; + enemy->excc = enemyDat[eDatI].xcaccel; + enemy->eycc = enemyDat[eDatI].ycaccel; + enemy->exccw = abs(enemy->excc); + enemy->exccwmax = enemy->exccw; + enemy->eyccw = abs(enemy->eycc); + enemy->eyccwmax = enemy->eyccw; + enemy->exccadd = (enemy->excc > 0) ? 1 : -1; + enemy->eyccadd = (enemy->eycc > 0) ? 1 : -1; + enemy->special = false; + enemy->iced = 0; + + if (enemyDat[eDatI].xrev == 0) + enemy->exrev = 100; + else if (enemyDat[eDatI].xrev == -99) + enemy->exrev = 0; + else + enemy->exrev = enemyDat[eDatI].xrev; + + if (enemyDat[eDatI].yrev == 0) + enemy->eyrev = 100; + else if (enemyDat[eDatI].yrev == -99) + enemy->eyrev = 0; + else + enemy->eyrev = enemyDat[eDatI].yrev; + + enemy->exca = (enemy->xaccel > 0) ? 1 : -1; + enemy->eyca = (enemy->yaccel > 0) ? 1 : -1; + + enemy->enemytype = eDatI; + + for (uint i = 0; i < 3; ++i) + { + if (enemy->tur[i] == 252) + enemy->eshotwait[i] = 1; + else if (enemy->tur[i] > 0) + enemy->eshotwait[i] = 20; + else + enemy->eshotwait[i] = 255; + } + for (uint i = 0; i < 20; ++i) + enemy->egr[i] = enemyDat[eDatI].egraphic[i]; + enemy->size = enemyDat[eDatI].esize; + enemy->linknum = 0; + enemy->edamaged = enemyDat[eDatI].dani < 0; + enemy->enemydie = enemyDat[eDatI].eenemydie; + + enemy->freq[1-1] = enemyDat[eDatI].freq[1-1]; + enemy->freq[2-1] = enemyDat[eDatI].freq[2-1]; + enemy->freq[3-1] = enemyDat[eDatI].freq[3-1]; + + enemy->edani = enemyDat[eDatI].dani; + enemy->edgr = enemyDat[eDatI].dgr; + enemy->edlevel = enemyDat[eDatI].dlevel; + + enemy->fixedmovey = 0; + + enemy->filter = 0x00; + + int tempValue = 0; + if (enemyDat[eDatI].value > 1 && enemyDat[eDatI].value < 10000) + { + switch (difficultyLevel) + { + case -1: + case 0: + tempValue = enemyDat[eDatI].value * 0.75f; + break; + case 1: + case 2: + tempValue = enemyDat[eDatI].value; + break; + case 3: + tempValue = enemyDat[eDatI].value * 1.125f; + break; + case 4: + tempValue = enemyDat[eDatI].value * 1.5f; + break; + case 5: + tempValue = enemyDat[eDatI].value * 2; + break; + case 6: + tempValue = enemyDat[eDatI].value * 2.5f; + break; + case 7: + case 8: + tempValue = enemyDat[eDatI].value * 4; + break; + case 9: + case 10: + tempValue = enemyDat[eDatI].value * 8; + break; + } + if (tempValue > 10000) + tempValue = 10000; + enemy->evalue = tempValue; + } + else + { + enemy->evalue = enemyDat[eDatI].value; + } + + int tempArmor = 1; + if (enemyDat[eDatI].armor > 0) + { + if (enemyDat[eDatI].armor != 255) + { + switch (difficultyLevel) + { + case -1: + case 0: + tempArmor = enemyDat[eDatI].armor * 0.5f + 1; + break; + case 1: + tempArmor = enemyDat[eDatI].armor * 0.75f + 1; + break; + case 2: + tempArmor = enemyDat[eDatI].armor; + break; + case 3: + tempArmor = enemyDat[eDatI].armor * 1.2f; + break; + case 4: + tempArmor = enemyDat[eDatI].armor * 1.5f; + break; + case 5: + tempArmor = enemyDat[eDatI].armor * 1.8f; + break; + case 6: + tempArmor = enemyDat[eDatI].armor * 2; + break; + case 7: + tempArmor = enemyDat[eDatI].armor * 3; + break; + case 8: + tempArmor = enemyDat[eDatI].armor * 4; + break; + case 9: + case 10: + tempArmor = enemyDat[eDatI].armor * 8; + break; + } + + if (tempArmor > 254) + { + tempArmor = 254; + } + } + else + { + tempArmor = 255; + } + + enemy->armorleft = tempArmor; + + avail = 0; + enemy->scoreitem = false; + } + else + { + avail = 2; + enemy->armorleft = 255; + if (enemy->evalue != 0) + enemy->scoreitem = true; + } + + if (!enemy->scoreitem) + { + totalEnemy++; /*Destruction ratio*/ + } + + /* indicates what to set ENEMYAVAIL to */ + return avail; +} + +void JE_createNewEventEnemy( JE_byte enemyTypeOfs, JE_word enemyOffset, Sint16 uniqueShapeTableI ) +{ + int i; + + b = 0; + + for(i = enemyOffset; i < enemyOffset + 25; i++) + { + if (enemyAvail[i] == 1) + { + b = i + 1; + break; + } + } + + if (b == 0) + { + return; + } + + tempW = eventRec[eventLoc-1].eventdat + enemyTypeOfs; + + enemyAvail[b-1] = JE_makeEnemy(&enemy[b-1], tempW, uniqueShapeTableI); + + if (eventRec[eventLoc-1].eventdat2 != -99) + { + switch (enemyOffset) + { + case 0: + enemy[b-1].ex = eventRec[eventLoc-1].eventdat2 - (mapX - 1) * 24; + enemy[b-1].ey -= backMove2; + break; + case 25: + case 75: + enemy[b-1].ex = eventRec[eventLoc-1].eventdat2 - (mapX - 1) * 24 - 12; + enemy[b-1].ey -= backMove; + break; + case 50: + if (background3x1) + { + enemy[b-1].ex = eventRec[eventLoc-1].eventdat2 - (mapX - 1) * 24 - 12; + } else { + enemy[b-1].ex = eventRec[eventLoc-1].eventdat2 - mapX3 * 24 - 24 * 2 + 6; + } + enemy[b-1].ey -= backMove3; + + if (background3x1b) + { + enemy[b-1].ex -= 6; + } + break; + } + enemy[b-1].ey = -28; + if (background3x1b && enemyOffset == 50) + { + enemy[b-1].ey += 4; + } + } + + if (smallEnemyAdjust && enemy[b-1].size == 0) + { + enemy[b-1].ex -= 10; + enemy[b-1].ey -= 7; + } + + enemy[b-1].ey += eventRec[eventLoc-1].eventdat5; + enemy[b-1].eyc += eventRec[eventLoc-1].eventdat3; + enemy[b-1].linknum = eventRec[eventLoc-1].eventdat4; + enemy[b-1].fixedmovey = eventRec[eventLoc-1].eventdat6; +} + +void JE_eventJump( JE_word jump ) +{ + JE_word tempW; + + if (jump == 65535) + { + curLoc = returnLoc; + } + else + { + returnLoc = curLoc + 1; + curLoc = jump; + } + tempW = 0; + do + { + tempW++; + } + while (!(eventRec[tempW-1].eventtime >= curLoc)); + eventLoc = tempW - 1; +} + +bool JE_searchFor/*enemy*/( JE_byte PLType, JE_byte* out_index ) +{ + int found_id = -1; + + for (int i = 0; i < 100; i++) + { + if (enemyAvail[i] == 0 && enemy[i].linknum == PLType) + { + found_id = i; + if (galagaMode) + { + enemy[i].evalue += enemy[i].evalue; + } + } + } + + if (found_id != -1) { + if (out_index) { + *out_index = found_id; + } + return true; + } else { + return false; + } +} + +void JE_eventSystem( void ) +{ + switch (eventRec[eventLoc-1].eventtype) + { + case 1: + starfield_speed = eventRec[eventLoc-1].eventdat; + break; + + case 2: + map1YDelay = 1; + map1YDelayMax = 1; + map2YDelay = 1; + map2YDelayMax = 1; + + backMove = eventRec[eventLoc-1].eventdat; + backMove2 = eventRec[eventLoc-1].eventdat2; + + if (backMove2 > 0) + explodeMove = backMove2; + else + explodeMove = backMove; + + backMove3 = eventRec[eventLoc-1].eventdat3; + + if (backMove > 0) + stopBackgroundNum = 0; + break; + + case 3: + backMove = 1; + map1YDelay = 3; + map1YDelayMax = 3; + backMove2 = 1; + map2YDelay = 2; + map2YDelayMax = 2; + backMove3 = 1; + break; + + case 4: + stopBackgrounds = true; + switch (eventRec[eventLoc-1].eventdat) + { + case 0: + case 1: + stopBackgroundNum = 1; + break; + case 2: + stopBackgroundNum = 2; + break; + case 3: + stopBackgroundNum = 3; + break; + } + break; + + case 5: // load enemy shape banks + { + Uint8 newEnemyShapeTables[] = + { + eventRec[eventLoc-1].eventdat > 0 ? eventRec[eventLoc-1].eventdat : 0, + eventRec[eventLoc-1].eventdat2 > 0 ? eventRec[eventLoc-1].eventdat2 : 0, + eventRec[eventLoc-1].eventdat3 > 0 ? eventRec[eventLoc-1].eventdat3 : 0, + eventRec[eventLoc-1].eventdat4 > 0 ? eventRec[eventLoc-1].eventdat4 : 0, + }; + + for (unsigned int i = 0; i < COUNTOF(newEnemyShapeTables); ++i) + { + if (enemyShapeTables[i] != newEnemyShapeTables[i]) + { + if (newEnemyShapeTables[i] > 0) + { + assert(newEnemyShapeTables[i] <= COUNTOF(shapeFile)); + JE_loadCompShapes(&eShapes[i], shapeFile[newEnemyShapeTables[i] - 1]); + } + else + free_sprite2s(&eShapes[i]); + + enemyShapeTables[i] = newEnemyShapeTables[i]; + } + } + } + break; + + case 6: /* Ground Enemy */ + JE_createNewEventEnemy(0, 25, 0); + break; + + case 7: /* Top Enemy */ + JE_createNewEventEnemy(0, 50, 0); + break; + + case 8: + starActive = false; + break; + + case 9: + starActive = true; + break; + + case 10: /* Ground Enemy 2 */ + JE_createNewEventEnemy(0, 75, 0); + break; + + case 11: + if (allPlayersGone || eventRec[eventLoc-1].eventdat == 1) + reallyEndLevel = true; + else + if (!endLevel) + { + readyToEndLevel = false; + endLevel = true; + levelEnd = 40; + } + break; + + case 12: /* Custom 4x4 Ground Enemy */ + { + uint temp = 0; + switch (eventRec[eventLoc-1].eventdat6) + { + case 0: + case 1: + temp = 25; + break; + case 2: + temp = 0; + break; + case 3: + temp = 50; + break; + case 4: + temp = 75; + break; + } + eventRec[eventLoc-1].eventdat6 = 0; /* We use EVENTDAT6 for the background */ + JE_createNewEventEnemy(0, temp, 0); + JE_createNewEventEnemy(1, temp, 0); + if (b > 0) + enemy[b-1].ex += 24; + JE_createNewEventEnemy(2, temp, 0); + if (b > 0) + enemy[b-1].ey -= 28; + JE_createNewEventEnemy(3, temp, 0); + if (b > 0) + { + enemy[b-1].ex += 24; + enemy[b-1].ey -= 28; + } + break; + } + case 13: + enemiesActive = false; + break; + + case 14: + enemiesActive = true; + break; + + case 15: /* Sky Enemy */ + JE_createNewEventEnemy(0, 0, 0); + break; + + case 16: + if (eventRec[eventLoc-1].eventdat > 9) + { + fprintf(stderr, "warning: event 16: bad event data\n"); + } + else + { + JE_drawTextWindow(outputs[eventRec[eventLoc-1].eventdat-1]); + soundQueue[3] = windowTextSamples[eventRec[eventLoc-1].eventdat-1]; + } + break; + + case 17: /* Ground Bottom */ + JE_createNewEventEnemy(0, 25, 0); + if (b > 0) + { + enemy[b-1].ey = 190 + eventRec[eventLoc-1].eventdat5; + } + break; + + case 18: /* Sky Enemy on Bottom */ + JE_createNewEventEnemy(0, 0, 0); + if (b > 0) + { + enemy[b-1].ey = 190 + eventRec[eventLoc-1].eventdat5; + } + break; + + case 19: /* Enemy Global Move */ + { + int initial_i = 0, max_i = 0; + bool all_enemies = false; + + if (eventRec[eventLoc-1].eventdat3 > 79 && eventRec[eventLoc-1].eventdat3 < 90) + { + initial_i = 0; + max_i = 100; + all_enemies = false; + eventRec[eventLoc-1].eventdat4 = newPL[eventRec[eventLoc-1].eventdat3 - 80]; + } + else + { + switch (eventRec[eventLoc-1].eventdat3) + { + case 0: + initial_i = 0; + max_i = 100; + all_enemies = false; + break; + case 2: + initial_i = 0; + max_i = 25; + all_enemies = true; + break; + case 1: + initial_i = 25; + max_i = 50; + all_enemies = true; + break; + case 3: + initial_i = 50; + max_i = 75; + all_enemies = true; + break; + case 99: + initial_i = 0; + max_i = 100; + all_enemies = true; + break; + } + } + + for (int i = initial_i; i < max_i; i++) + { + if (all_enemies || enemy[i].linknum == eventRec[eventLoc-1].eventdat4) + { + if (eventRec[eventLoc-1].eventdat != -99) + enemy[i].exc = eventRec[eventLoc-1].eventdat; + + if (eventRec[eventLoc-1].eventdat2 != -99) + enemy[i].eyc = eventRec[eventLoc-1].eventdat2; + + if (eventRec[eventLoc-1].eventdat6 != 0) + enemy[i].fixedmovey = eventRec[eventLoc-1].eventdat6; + + if (eventRec[eventLoc-1].eventdat6 == -99) + enemy[i].fixedmovey = 0; + + if (eventRec[eventLoc-1].eventdat5 > 0) + enemy[i].enemycycle = eventRec[eventLoc-1].eventdat5; + } + } + break; + } + + case 20: /* Enemy Global Accel */ + if (eventRec[eventLoc-1].eventdat3 > 79 && eventRec[eventLoc-1].eventdat3 < 90) + eventRec[eventLoc-1].eventdat4 = newPL[eventRec[eventLoc-1].eventdat3 - 80]; + + for (temp = 0; temp < 100; temp++) + { + if (enemyAvail[temp] != 1 + && (enemy[temp].linknum == eventRec[eventLoc-1].eventdat4 || eventRec[eventLoc-1].eventdat4 == 0)) + { + if (eventRec[eventLoc-1].eventdat != -99) + { + enemy[temp].excc = eventRec[eventLoc-1].eventdat; + enemy[temp].exccw = abs(eventRec[eventLoc-1].eventdat); + enemy[temp].exccwmax = abs(eventRec[eventLoc-1].eventdat); + if (eventRec[eventLoc-1].eventdat > 0) + enemy[temp].exccadd = 1; + else + enemy[temp].exccadd = -1; + } + + if (eventRec[eventLoc-1].eventdat2 != -99) + { + enemy[temp].eycc = eventRec[eventLoc-1].eventdat2; + enemy[temp].eyccw = abs(eventRec[eventLoc-1].eventdat2); + enemy[temp].eyccwmax = abs(eventRec[eventLoc-1].eventdat2); + if (eventRec[eventLoc-1].eventdat2 > 0) + enemy[temp].eyccadd = 1; + else + enemy[temp].eyccadd = -1; + } + + if (eventRec[eventLoc-1].eventdat5 > 0) + { + enemy[temp].enemycycle = eventRec[eventLoc-1].eventdat5; + } + if (eventRec[eventLoc-1].eventdat6 > 0) + { + enemy[temp].ani = eventRec[eventLoc-1].eventdat6; + enemy[temp].animin = eventRec[eventLoc-1].eventdat5; + enemy[temp].animax = 0; + enemy[temp].aniactive = 1; + } + } + } + break; + + case 21: + background3over = 1; + break; + + case 22: + background3over = 0; + break; + + case 23: /* Sky Enemy on Bottom */ + JE_createNewEventEnemy(0, 50, 0); + if (b > 0) + enemy[b-1].ey = 180 + eventRec[eventLoc-1].eventdat5; + break; + + case 24: /* Enemy Global Animate */ + for (temp = 0; temp < 100; temp++) + { + if (enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) + { + enemy[temp].aniactive = 1; + enemy[temp].aniwhenfire = 0; + if (eventRec[eventLoc-1].eventdat2 > 0) + { + enemy[temp].enemycycle = eventRec[eventLoc-1].eventdat2; + enemy[temp].animin = enemy[temp].enemycycle; + } + else + { + enemy[temp].enemycycle = 0; + } + + if (eventRec[eventLoc-1].eventdat > 0) + enemy[temp].ani = eventRec[eventLoc-1].eventdat; + + if (eventRec[eventLoc-1].eventdat3 == 1) + { + enemy[temp].animax = enemy[temp].ani; + } + else if (eventRec[eventLoc-1].eventdat3 == 2) + { + enemy[temp].aniactive = 2; + enemy[temp].animax = enemy[temp].ani; + enemy[temp].aniwhenfire = 2; + } + } + } + break; + + case 25: /* Enemy Global Damage change */ + for (temp = 0; temp < 100; temp++) + { + if (eventRec[eventLoc-1].eventdat4 == 0 || enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) + { + if (galagaMode) + enemy[temp].armorleft = roundf(eventRec[eventLoc-1].eventdat * (difficultyLevel / 2)); + else + enemy[temp].armorleft = eventRec[eventLoc-1].eventdat; + } + } + break; + + case 26: + smallEnemyAdjust = eventRec[eventLoc-1].eventdat; + break; + + case 27: /* Enemy Global AccelRev */ + if (eventRec[eventLoc-1].eventdat3 > 79 && eventRec[eventLoc-1].eventdat3 < 90) + eventRec[eventLoc-1].eventdat4 = newPL[eventRec[eventLoc-1].eventdat3 - 80]; + + for (temp = 0; temp < 100; temp++) + { + if (eventRec[eventLoc-1].eventdat4 == 0 || enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) + { + if (eventRec[eventLoc-1].eventdat != -99) + enemy[temp].exrev = eventRec[eventLoc-1].eventdat; + if (eventRec[eventLoc-1].eventdat2 != -99) + enemy[temp].eyrev = eventRec[eventLoc-1].eventdat2; + if (eventRec[eventLoc-1].eventdat3 != 0 && eventRec[eventLoc-1].eventdat3 < 17) + enemy[temp].filter = eventRec[eventLoc-1].eventdat3; + } + } + break; + + case 28: + topEnemyOver = false; + break; + + case 29: + topEnemyOver = true; + break; + + case 30: + map1YDelay = 1; + map1YDelayMax = 1; + map2YDelay = 1; + map2YDelayMax = 1; + + backMove = eventRec[eventLoc-1].eventdat; + backMove2 = eventRec[eventLoc-1].eventdat2; + explodeMove = backMove2; + backMove3 = eventRec[eventLoc-1].eventdat3; + break; + + case 31: /* Enemy Fire Override */ + for (temp = 0; temp < 100; temp++) + { + if (eventRec[eventLoc-1].eventdat4 == 99 || enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) + { + enemy[temp].freq[1-1] = eventRec[eventLoc-1].eventdat ; + enemy[temp].freq[2-1] = eventRec[eventLoc-1].eventdat2; + enemy[temp].freq[3-1] = eventRec[eventLoc-1].eventdat3; + for (temp2 = 0; temp2 < 3; temp2++) + { + enemy[temp].eshotwait[temp2] = 1; + } + if (enemy[temp].launchtype > 0) + { + enemy[temp].launchfreq = eventRec[eventLoc-1].eventdat5; + enemy[temp].launchwait = 1; + } + } + } + break; + + case 32: // create enemy + JE_createNewEventEnemy(0, 50, 0); + if (b > 0) + enemy[b-1].ey = 190; + break; + + case 33: /* Enemy From other Enemies */ + if (!((eventRec[eventLoc-1].eventdat == 512 || eventRec[eventLoc-1].eventdat == 513) && (twoPlayerMode || onePlayerAction || superTyrian))) + { + if (superArcadeMode != SA_NONE) + { + if (eventRec[eventLoc-1].eventdat == 534) + eventRec[eventLoc-1].eventdat = 827; + } + else if (!superTyrian) + { + const uint lives = *player[0].lives; + + if (eventRec[eventLoc-1].eventdat == 533 && (lives == 11 || (mt_rand() % 15) < lives)) + { + // enemy will drop random special weapon + eventRec[eventLoc-1].eventdat = 829 + (mt_rand() % 6); + } + } + if (eventRec[eventLoc-1].eventdat == 534 && superTyrian) + eventRec[eventLoc-1].eventdat = 828 + superTyrianSpecials[mt_rand() % 4]; + + for (temp = 0; temp < 100; temp++) + { + if (enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) + enemy[temp].enemydie = eventRec[eventLoc-1].eventdat; + } + } + break; + + case 34: /* Start Music Fade */ + if (firstGameOver) + { + musicFade = true; + tempVolume = tyrMusicVolume; + } + break; + + case 35: /* Play new song */ + if (firstGameOver) + { + play_song(eventRec[eventLoc-1].eventdat - 1); + set_volume(tyrMusicVolume, fxVolume); + } + musicFade = false; + break; + + case 36: + readyToEndLevel = true; + break; + + case 37: + levelEnemyFrequency = eventRec[eventLoc-1].eventdat; + break; + + case 38: + curLoc = eventRec[eventLoc-1].eventdat; + int new_event_loc = 1; + for (tempW = 0; tempW < maxEvent; tempW++) + { + if (eventRec[tempW].eventtime <= curLoc) + { + new_event_loc = tempW+1 - 1; + } + } + eventLoc = new_event_loc; + break; + + case 39: /* Enemy Global Linknum Change */ + for (temp = 0; temp < 100; temp++) + { + if (enemy[temp].linknum == eventRec[eventLoc-1].eventdat) + enemy[temp].linknum = eventRec[eventLoc-1].eventdat2; + } + break; + + case 40: /* Enemy Continual Damage */ + enemyContinualDamage = true; + break; + + case 41: + if (eventRec[eventLoc-1].eventdat == 0) + { + memset(enemyAvail, 1, sizeof(enemyAvail)); + } + else + { + for (x = 0; x <= 24; x++) + enemyAvail[x] = 1; + } + break; + + case 42: + background3over = 2; + break; + + case 43: + background2over = eventRec[eventLoc-1].eventdat; + break; + + case 44: + filterActive = (eventRec[eventLoc-1].eventdat > 0); + filterFade = (eventRec[eventLoc-1].eventdat == 2); + levelFilter = eventRec[eventLoc-1].eventdat2; + levelBrightness = eventRec[eventLoc-1].eventdat3; + levelFilterNew = eventRec[eventLoc-1].eventdat4; + levelBrightnessChg = eventRec[eventLoc-1].eventdat5; + filterFadeStart = (eventRec[eventLoc-1].eventdat6 == 0); + break; + + case 45: /* arcade-only enemy from other enemies */ + if (!superTyrian) + { + const uint lives = *player[0].lives; + + if (eventRec[eventLoc-1].eventdat == 533 && (lives == 11 || (mt_rand() % 15) < lives)) + { + eventRec[eventLoc-1].eventdat = 829 + (mt_rand() % 6); + } + if (twoPlayerMode || onePlayerAction) + { + for (temp = 0; temp < 100; temp++) + { + if (enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) + enemy[temp].enemydie = eventRec[eventLoc-1].eventdat; + } + } + } + break; + + case 46: // change difficulty + if (eventRec[eventLoc-1].eventdat3 != 0) + damageRate = eventRec[eventLoc-1].eventdat3; + + if (eventRec[eventLoc-1].eventdat2 == 0 || twoPlayerMode || onePlayerAction) + { + difficultyLevel += eventRec[eventLoc-1].eventdat; + if (difficultyLevel < 1) + difficultyLevel = 1; + if (difficultyLevel > 10) + difficultyLevel = 10; + } + break; + + case 47: /* Enemy Global AccelRev */ + for (temp = 0; temp < 100; temp++) + { + if (eventRec[eventLoc-1].eventdat4 == 0 || enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) + enemy[temp].armorleft = eventRec[eventLoc-1].eventdat; + } + break; + + case 48: /* Background 2 Cannot be Transparent */ + background2notTransparent = true; + break; + + case 49: + case 50: + case 51: + case 52: + tempDat2 = eventRec[eventLoc-1].eventdat; + eventRec[eventLoc-1].eventdat = 0; + tempDat = eventRec[eventLoc-1].eventdat3; + eventRec[eventLoc-1].eventdat3 = 0; + tempDat3 = eventRec[eventLoc-1].eventdat6; + eventRec[eventLoc-1].eventdat6 = 0; + enemyDat[0].armor = tempDat3; + enemyDat[0].egraphic[1-1] = tempDat2; + switch (eventRec[eventLoc-1].eventtype - 48) + { + case 1: + temp = 25; + break; + case 2: + temp = 0; + break; + case 3: + temp = 50; + break; + case 4: + temp = 75; + break; + } + JE_createNewEventEnemy(0, temp, tempDat); + eventRec[eventLoc-1].eventdat = tempDat2; + eventRec[eventLoc-1].eventdat3 = tempDat; + eventRec[eventLoc-1].eventdat6 = tempDat3; + break; + + case 53: + forceEvents = (eventRec[eventLoc-1].eventdat != 99); + break; + + case 54: + JE_eventJump(eventRec[eventLoc-1].eventdat); + break; + + case 55: /* Enemy Global AccelRev */ + if (eventRec[eventLoc-1].eventdat3 > 79 && eventRec[eventLoc-1].eventdat3 < 90) + eventRec[eventLoc-1].eventdat4 = newPL[eventRec[eventLoc-1].eventdat3 - 80]; + + for (temp = 0; temp < 100; temp++) + { + if (eventRec[eventLoc-1].eventdat4 == 0 || enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) + { + if (eventRec[eventLoc-1].eventdat != -99) + enemy[temp].xaccel = eventRec[eventLoc-1].eventdat; + if (eventRec[eventLoc-1].eventdat2 != -99) + enemy[temp].yaccel = eventRec[eventLoc-1].eventdat2; + } + } + break; + + case 56: /* Ground2 Bottom */ + JE_createNewEventEnemy(0, 75, 0); + if (b > 0) + enemy[b-1].ey = 190; + break; + + case 57: + superEnemy254Jump = eventRec[eventLoc-1].eventdat; + break; + + case 60: /*Assign Special Enemy*/ + for (temp = 0; temp < 100; temp++) + { + if (enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) + { + enemy[temp].special = true; + enemy[temp].flagnum = eventRec[eventLoc-1].eventdat; + enemy[temp].setto = (eventRec[eventLoc-1].eventdat2 == 1); + } + } + break; + + case 61: // if specific flag set to specific value, skip events + if (globalFlags[eventRec[eventLoc-1].eventdat-1] == eventRec[eventLoc-1].eventdat2) + eventLoc += eventRec[eventLoc-1].eventdat3; + break; + + case 62: /*Play sound effect*/ + soundQueue[3] = eventRec[eventLoc-1].eventdat; + break; + + case 63: // skip events if not in 2-player mode + if (!twoPlayerMode && !onePlayerAction) + eventLoc += eventRec[eventLoc-1].eventdat; + break; + + case 64: + if (!(eventRec[eventLoc-1].eventdat == 6 && twoPlayerMode && difficultyLevel > 2)) + { + smoothies[eventRec[eventLoc-1].eventdat-1] = eventRec[eventLoc-1].eventdat2; + temp = eventRec[eventLoc-1].eventdat; + if (temp == 5) + temp = 3; + smoothie_data[temp-1] = eventRec[eventLoc-1].eventdat3; + } + break; + + case 65: + background3x1 = (eventRec[eventLoc-1].eventdat == 0); + break; + + case 66: /*If not on this difficulty level or higher then...*/ + if (initialDifficulty <= eventRec[eventLoc-1].eventdat) + eventLoc += eventRec[eventLoc-1].eventdat2; + break; + + case 67: + levelTimer = (eventRec[eventLoc-1].eventdat == 1); + levelTimerCountdown = eventRec[eventLoc-1].eventdat3 * 100; + levelTimerJumpTo = eventRec[eventLoc-1].eventdat2; + break; + + case 68: + randomExplosions = (eventRec[eventLoc-1].eventdat == 1); + break; + + case 69: + for (uint i = 0; i < COUNTOF(player); ++i) + player[i].invulnerable_ticks = eventRec[eventLoc-1].eventdat; + break; + + case 70: + if (eventRec[eventLoc-1].eventdat2 == 0) + { /*1-10*/ + bool found = false; + + for (temp = 1; temp <= 19; temp++) + found = found || JE_searchFor(temp, NULL); + + if (!found) + JE_eventJump(eventRec[eventLoc-1].eventdat); + } + else if (!JE_searchFor(eventRec[eventLoc-1].eventdat2, NULL) + && (eventRec[eventLoc-1].eventdat3 == 0 || !JE_searchFor(eventRec[eventLoc-1].eventdat3, NULL)) + && (eventRec[eventLoc-1].eventdat4 == 0 || !JE_searchFor(eventRec[eventLoc-1].eventdat4, NULL))) + { + JE_eventJump(eventRec[eventLoc-1].eventdat); + } + break; + + case 71: + if (((((intptr_t)mapYPos - (intptr_t)&megaData1.mainmap) / sizeof(JE_byte *)) * 2) <= (unsigned)eventRec[eventLoc-1].eventdat2) + { + JE_eventJump(eventRec[eventLoc-1].eventdat); + } + break; + + case 72: + background3x1b = (eventRec[eventLoc-1].eventdat == 1); + break; + + case 73: + skyEnemyOverAll = (eventRec[eventLoc-1].eventdat == 1); + break; + + case 74: /* Enemy Global BounceParams */ + for (temp = 0; temp < 100; temp++) + { + if (eventRec[eventLoc-1].eventdat4 == 0 || enemy[temp].linknum == eventRec[eventLoc-1].eventdat4) + { + if (eventRec[eventLoc-1].eventdat5 != -99) + enemy[temp].xminbounce = eventRec[eventLoc-1].eventdat5; + + if (eventRec[eventLoc-1].eventdat6 != -99) + enemy[temp].yminbounce = eventRec[eventLoc-1].eventdat6; + + if (eventRec[eventLoc-1].eventdat != -99) + enemy[temp].xmaxbounce = eventRec[eventLoc-1].eventdat; + + if (eventRec[eventLoc-1].eventdat2 != -99) + enemy[temp].ymaxbounce = eventRec[eventLoc-1].eventdat2; + } + } + break; + + case 75:; + bool temp_no_clue = false; // TODO: figure out what this is doing + + for (temp = 0; temp < 100; temp++) + { + if (enemyAvail[temp] == 0 + && enemy[temp].eyc == 0 + && enemy[temp].linknum >= eventRec[eventLoc-1].eventdat + && enemy[temp].linknum <= eventRec[eventLoc-1].eventdat2) + { + temp_no_clue = true; + } + } + + if (temp_no_clue) + { + JE_byte enemy_i; + do + { + temp = (mt_rand() % (eventRec[eventLoc-1].eventdat2 + 1 - eventRec[eventLoc-1].eventdat)) + eventRec[eventLoc-1].eventdat; + } + while (!(JE_searchFor(temp, &enemy_i) && enemy[enemy_i].eyc == 0)); + + newPL[eventRec[eventLoc-1].eventdat3 - 80] = temp; + } + else + { + newPL[eventRec[eventLoc-1].eventdat3 - 80] = 255; + if (eventRec[eventLoc-1].eventdat4 > 0) + { /*Skip*/ + curLoc = eventRec[eventLoc-1 + eventRec[eventLoc-1].eventdat4].eventtime - 1; + eventLoc += eventRec[eventLoc-1].eventdat4 - 1; + } + } + + break; + + case 76: + returnActive = true; + break; + + case 77: + mapYPos = &megaData1.mainmap[0][0]; + mapYPos += eventRec[eventLoc-1].eventdat / 2; + if (eventRec[eventLoc-1].eventdat2 > 0) + { + mapY2Pos = &megaData2.mainmap[0][0]; + mapY2Pos += eventRec[eventLoc-1].eventdat2 / 2; + } + else + { + mapY2Pos = &megaData2.mainmap[0][0]; + mapY2Pos += eventRec[eventLoc-1].eventdat / 2; + } + break; + + case 78: + if (galagaShotFreq < 10) + galagaShotFreq++; + break; + + case 79: + boss_bar[0].link_num = eventRec[eventLoc-1].eventdat; + boss_bar[1].link_num = eventRec[eventLoc-1].eventdat2; + break; + + case 80: // skip events if in 2-player mode + if (twoPlayerMode) + eventLoc += eventRec[eventLoc-1].eventdat; + break; + + case 81: /*WRAP2*/ + BKwrap2 = &megaData2.mainmap[0][0]; + BKwrap2 += eventRec[eventLoc-1].eventdat / 2; + BKwrap2to = &megaData2.mainmap[0][0]; + BKwrap2to += eventRec[eventLoc-1].eventdat2 / 2; + break; + + case 82: /*Give SPECIAL WEAPON*/ + player[0].items.special = eventRec[eventLoc-1].eventdat; + shotMultiPos[SHOT_SPECIAL] = 0; + shotRepeat[SHOT_SPECIAL] = 0; + shotMultiPos[SHOT_SPECIAL2] = 0; + shotRepeat[SHOT_SPECIAL2] = 0; + break; + + default: + fprintf(stderr, "warning: ignoring unknown event %d\n", eventRec[eventLoc-1].eventtype); + break; + } + + eventLoc++; +} + +void JE_whoa( void ) +{ + unsigned int i, j, color, offset, timer; + unsigned int screenSize, topBorder, bottomBorder; + Uint8 * TempScreen1, * TempScreen2, * TempScreenSwap; + + /* 'whoa' gets us that nifty screen fade used when you type in + * 'engage'. We need two temporary screen buffers (char arrays can + * work too, but these screens already exist) for our effect. + * This could probably be a lot more efficient (there's probably a + * way to get vgascreen as one of the temp buffers), but it's only called + * once so don't worry about it. */ + + TempScreen1 = game_screen->pixels; + TempScreen2 = VGAScreen2->pixels; + + screenSize = VGAScreenSeg->h * VGAScreenSeg->pitch; + topBorder = VGAScreenSeg->pitch * 4; /* Seems an arbitrary number of lines */ + bottomBorder = VGAScreenSeg->pitch * 7; + + /* Okay, one disadvantage to using other screens as temp buffers: they + * need to be the right size. I doubt they'l ever be anything but 320x200, + * but just in case, these asserts will clue in whoever stumbles across + * the problem. You can fix it with the stack or malloc. */ + assert( (unsigned)VGAScreen2->h * VGAScreen2->pitch >= screenSize + && (unsigned)game_screen->h * game_screen->pitch >= screenSize); + + + /* Clear the top and bottom borders. We don't want to process + * them and we don't want to draw them. */ + memset((Uint8 *)VGAScreenSeg->pixels, 0, topBorder); + memset((Uint8 *)VGAScreenSeg->pixels + screenSize - bottomBorder, 0, bottomBorder); + + /* Copy our test subject to one of the temporary buffers. Blank the other */ + memset(TempScreen1, 0, screenSize); + memcpy(TempScreen2, VGAScreenSeg->pixels, VGAScreenSeg->h * VGAScreenSeg->pitch); + + + service_SDL_events(true); + timer = 300; /* About 300 rounds is enough to make the screen mostly black */ + + do + { + setjasondelay(1); + + /* This gets us our 'whoa' effect with pixel bleeding magic. + * I'm willing to bet the guy who originally wrote the asm was goofing + * around on acid and thought this looked good enough to use. */ + for (i = screenSize - bottomBorder, j = topBorder / 2; i > 0; i--, j++) + { + offset = j + i/8192 - 4; + color = (TempScreen2[offset ] * 12 + + TempScreen1[offset-VGAScreenSeg->pitch] + + TempScreen1[offset-1 ] + + TempScreen1[offset+1 ] + + TempScreen1[offset+VGAScreenSeg->pitch]) / 16; + + TempScreen1[j] = color; + } + + /* Now copy that mess to the buffer. */ + memcpy((Uint8 *)VGAScreenSeg->pixels + topBorder, TempScreen1 + topBorder, screenSize - bottomBorder); + + JE_showVGA(); + + timer--; + wait_delay(); + + /* Flip the buffer. */ + TempScreenSwap = TempScreen1; + TempScreen1 = TempScreen2; + TempScreen2 = TempScreenSwap; + + } while (!(timer == 0 || JE_anyButton())); + + levelWarningLines = 4; +} + +void JE_barX( JE_word x1, JE_word y1, JE_word x2, JE_word y2, JE_byte col ) +{ + fill_rectangle_xy(VGAScreen, x1, y1, x2, y1, col + 1); + fill_rectangle_xy(VGAScreen, x1, y1 + 1, x2, y2 - 1, col ); + fill_rectangle_xy(VGAScreen, x1, y2, x2, y2, col - 1); +} + +void draw_boss_bar( void ) +{ + for (unsigned int b = 0; b < COUNTOF(boss_bar); b++) + { + if (boss_bar[b].link_num == 0) + continue; + + unsigned int armor = 256; // higher than armor max + + for (unsigned int e = 0; e < COUNTOF(enemy); e++) // find most damaged + { + if (enemyAvail[e] != 1 && enemy[e].linknum == boss_bar[b].link_num) + if (enemy[e].armorleft < armor) + armor = enemy[e].armorleft; + } + + if (armor > 255 || armor == 0) // boss dead? + boss_bar[b].link_num = 0; + else + boss_bar[b].armor = (armor == 255) ? 254 : armor; // 255 would make the bar too long + } + + unsigned int bars = (boss_bar[0].link_num != 0 ? 1 : 0) + + (boss_bar[1].link_num != 0 ? 1 : 0); + + // if only one bar left, make it the first one + if (bars == 1 && boss_bar[0].link_num == 0) + { + memcpy(&boss_bar[0], &boss_bar[1], sizeof(boss_bar_t)); + boss_bar[1].link_num = 0; + } + + for (unsigned int b = 0; b < bars; b++) + { + unsigned int x = (bars == 2) + ? ((b == 0) ? 125 : 185) + : ((levelTimer) ? 250 : 155); // level timer and boss bar would overlap + + JE_barX(x - 25, 7, x + 25, 12, 115); + JE_barX(x - (boss_bar[b].armor / 10), 7, x + (boss_bar[b].armor + 5) / 10, 12, 118 + boss_bar[b].color); + + if (boss_bar[b].color > 0) + boss_bar[b].color--; + } +} + diff --git a/contrib/games/opentyrian/src/tyrian2.h b/contrib/games/opentyrian/src/tyrian2.h new file mode 100644 index 000000000..74478b069 --- /dev/null +++ b/contrib/games/opentyrian/src/tyrian2.h @@ -0,0 +1,70 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef TYRIAN2_H +#define TYRIAN2_H + +#include "opentyr.h" + +#include "varz.h" +#include "helptext.h" + +void intro_logos( void ); + +typedef struct +{ + Uint8 link_num; + Uint8 armor; + Uint8 color; +} +boss_bar_t; + +extern boss_bar_t boss_bar[2]; + +extern char tempStr[31]; +extern JE_byte itemAvail[9][10], itemAvailMax[9]; + +void JE_createNewEventEnemy( JE_byte enemytypeofs, JE_word enemyoffset, Sint16 uniqueShapeTableI ); + +void JE_doNetwork( void ); + +uint JE_makeEnemy( struct JE_SingleEnemyType *enemy, Uint16 eDatI, Sint16 uniqueShapeTableI ); + +void JE_eventJump( JE_word jump ); + +void JE_whoa( void ); + +void JE_barX ( JE_word x1, JE_word y1, JE_word x2, JE_word y2, JE_byte col ); + +Sint16 JE_newEnemy( int enemyOffset, Uint16 eDatI, Sint16 uniqueShapeTableI ); +void JE_drawEnemy( int enemyOffset ); +void JE_starShowVGA( void ); + +void JE_main( void ); +void JE_loadMap( void ); +bool JE_titleScreen( JE_boolean animate ); +void JE_readTextSync( void ); +void JE_displayText( void ); + +bool JE_searchFor( JE_byte PLType, JE_byte* out_index ); +void JE_eventSystem( void ); + +void draw_boss_bar( void ); + +#endif /* TYRIAN2_H */ + diff --git a/contrib/games/opentyrian/src/varz.c b/contrib/games/opentyrian/src/varz.c new file mode 100644 index 000000000..5a57e7bfb --- /dev/null +++ b/contrib/games/opentyrian/src/varz.c @@ -0,0 +1,1185 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "varz.h" + +#include "config.h" +#include "editship.h" +#include "episodes.h" +#include "joystick.h" +#include "lds_play.h" +#include "loudness.h" +#include "mainint.h" +#include "mouse.h" +#include "mtrand.h" +#include "network.h" +#include "nortsong.h" +#include "nortvars.h" +#include "opentyr.h" +#include "shots.h" +#include "sprite.h" +#include "vga256d.h" +#include "video.h" + +JE_integer tempDat, tempDat2, tempDat3; + +const JE_byte SANextShip[SA + 2] /* [0..SA + 1] */ = { 3, 9, 6, 2, 5, 1, 4, 3, 7 }; // 0 -> 3 -> 2 -> 6 -> 4 -> 5 -> 1 -> 9 -> 7 +const JE_word SASpecialWeapon[SA] /* [1..SA] */ = { 7, 8, 9, 10, 11, 12, 13 }; +const JE_word SASpecialWeaponB[SA] /* [1..SA] */ = {37, 6, 15, 40, 16, 14, 41 }; +const JE_byte SAShip[SA] /* [1..SA] */ = { 3, 1, 5, 10, 2, 11, 12 }; +const JE_word SAWeapon[SA][5] /* [1..SA, 1..5] */ = +{ /* R Bl Bk G P */ + { 9, 31, 32, 33, 34 }, /* Stealth Ship */ + { 19, 8, 22, 41, 34 }, /* StormWind */ + { 27, 5, 20, 42, 31 }, /* Techno */ + { 15, 3, 28, 22, 12 }, /* Enemy */ + { 23, 35, 25, 14, 6 }, /* Weird */ + { 2, 5, 21, 4, 7 }, /* Unknown */ + { 40, 38, 37, 41, 36 } /* NortShip Z */ +}; + +const JE_byte specialArcadeWeapon[PORT_NUM] /* [1..Portnum] */ = +{ + 17,17,18,0,0,0,10,0,0,0,0,0,44,0,10,0,19,0,0,-0,0,0,0,0,0,0, + -0,0,0,0,45,0,0,0,0,0,0,0,0,0,0,0 +}; + +const JE_byte optionSelect[16][3][2] /* [0..15, 1..3, 1..2] */ = +{ /* MAIN OPT FRONT */ + { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ + { { 1, 1},{16,16},{30,30} }, /*Single Shot*/ + { { 2, 2},{29,29},{29,20} }, /*Dual Shot*/ + { { 3, 3},{21,21},{12, 0} }, /*Charge Cannon*/ + { { 4, 4},{18,18},{16,23} }, /*Vulcan*/ + { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ + { { 6, 6},{29,16},{ 0,22} }, /*Super Missile*/ + { { 7, 7},{19,19},{19,28} }, /*Atom Bomb*/ + { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ + { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ + { {10,10},{21,21},{21,27} }, /*Mini Missile*/ + { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ + { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ + { {13,13},{17,17},{13,26} }, /*MicroBomb*/ + { { 0, 0},{ 0, 0},{ 0, 0} }, /**/ + { {15,15},{15,16},{15,16} } /*Post-It*/ +}; + +const JE_word PGR[21] /* [1..21] */ = +{ + 4, + 1,2,3, + 41-21,57-21,73-21,89-21,105-21, + 121-21,137-21,153-21, + 151,151,151,151,73-21,73-21,1,2,4 + /*151,151,151*/ +}; +const JE_byte PAni[21] /* [1..21] */ = {1,0,0,0,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,0,1}; + +const JE_word linkGunWeapons[38] /* [1..38] */ = +{ + 0,0,0,0,0,0,0,0,444,445,446,447,0,448,449,0,0,0,0,0,450,451,0,506,0,564, + 445,446,447,448,449,445,446,447,448,449,450,451 +}; +const JE_word chargeGunWeapons[38] /* [1..38] */ = +{ + 0,0,0,0,0,0,0,0,476,458,464,482,0,488,470,0,0,0,0,0,494,500,0,528,0,558, + 458,458,458,458,458,458,458,458,458,458,458,458 +}; +const JE_byte randomEnemyLaunchSounds[3] /* [1..3] */ = {13,6,26}; + +/* YKS: Twiddle cheat sheet: + * 1: UP + * 2: DOWN + * 3: LEFT + * 4: RIGHT + * 5: UP+FIRE + * 6: DOWN+FIRE + * 7: LEFT+FIRE + * 8: RIGHT+FIRE + * 9: Release all keys (directions and fire) + */ +const JE_byte keyboardCombos[26][8] /* [1..26, 1..8] */ = +{ + { 2, 1, 2, 5, 137, 0, 0, 0}, /*Invulnerability*/ + { 4, 3, 2, 5, 138, 0, 0, 0}, /*Atom Bomb*/ + { 3, 4, 6, 139, 0, 0, 0, 0}, /*Seeker Bombs*/ + { 2, 5, 142, 0, 0, 0, 0, 0}, /*Ice Blast*/ + { 6, 2, 6, 143, 0, 0, 0, 0}, /*Auto Repair*/ + { 6, 7, 5, 8, 6, 7, 5, 112 }, /*Spin Wave*/ + { 7, 8, 101, 0, 0, 0, 0, 0}, /*Repulsor*/ + { 1, 7, 6, 146, 0, 0, 0, 0}, /*Protron Field*/ + { 8, 6, 7, 1, 120, 0, 0, 0}, /*Minefield*/ + { 3, 6, 8, 5, 121, 0, 0, 0}, /*Post-It Blast*/ + { 1, 2, 7, 8, 119, 0, 0, 0}, /*Drone Ship - TBC*/ + { 3, 4, 3, 6, 123, 0, 0, 0}, /*Repair Player 2*/ + { 6, 7, 5, 8, 124, 0, 0, 0}, /*Super Bomb - TBC*/ + { 1, 6, 125, 0, 0, 0, 0, 0}, /*Hot Dog*/ + { 9, 5, 126, 0, 0, 0, 0, 0}, /*Lightning UP */ + { 1, 7, 127, 0, 0, 0, 0, 0}, /*Lightning UP+LEFT */ + { 1, 8, 128, 0, 0, 0, 0, 0}, /*Lightning UP+RIGHT*/ + { 9, 7, 129, 0, 0, 0, 0, 0}, /*Lightning LEFT */ + { 9, 8, 130, 0, 0, 0, 0, 0}, /*Lightning RIGHT*/ + { 4, 2, 3, 5, 131, 0, 0, 0}, /*Warfly */ + { 3, 1, 2, 8, 132, 0, 0, 0}, /*FrontBlaster */ + { 2, 4, 5, 133, 0, 0, 0, 0}, /*Gerund */ + { 3, 4, 2, 8, 134, 0, 0, 0}, /*FireBomb */ + { 1, 4, 6, 135, 0, 0, 0, 0}, /*Indigo */ + { 1, 3, 6, 137, 0, 0, 0, 0}, /*Invulnerability [easier] */ + { 1, 4, 3, 4, 7, 136, 0, 0} /*D-Media Protron Drone */ +}; + +const JE_byte shipCombosB[21] /* [1..21] */ = + {15,16,17,18,19,20,21,22,23,24, 7, 8, 5,25,14, 4, 6, 3, 9, 2,26}; + /*!! SUPER Tyrian !!*/ +const JE_byte superTyrianSpecials[4] /* [1..4] */ = {1,2,4,5}; + +const JE_byte shipCombos[14][3] /* [0..12, 1..3] */ = +{ + { 5, 4, 7}, /*2nd Player ship*/ + { 1, 2, 0}, /*USP Talon*/ + {14, 4, 0}, /*Super Carrot*/ + { 4, 5, 0}, /*Gencore Phoenix*/ + { 6, 5, 0}, /*Gencore Maelstrom*/ + { 7, 8, 0}, /*MicroCorp Stalker*/ + { 7, 9, 0}, /*MicroCorp Stalker-B*/ + {10, 3, 5}, /*Prototype Stalker-C*/ + { 5, 8, 9}, /*Stalker*/ + { 1, 3, 0}, /*USP Fang*/ + { 7,16,17}, /*U-Ship*/ + { 2,11,12}, /*1st Player ship*/ + { 3, 8,10}, /*Nort ship*/ + { 0, 0, 0} // Dummy entry added for Stalker 21.126 +}; + +/*Street-Fighter Commands*/ +JE_byte SFCurrentCode[2][21]; /* [1..2, 1..21] */ +JE_byte SFExecuted[2]; /* [1..2] */ + +/*Special General Data*/ +JE_byte lvlFileNum; +JE_word maxEvent, eventLoc; +/*JE_word maxenemies;*/ +JE_word tempBackMove, explodeMove; /*Speed of background movement*/ +JE_byte levelEnd; +JE_word levelEndFxWait; +JE_shortint levelEndWarp; +JE_boolean endLevel, reallyEndLevel, waitToEndLevel, playerEndLevel, + normalBonusLevelCurrent, bonusLevelCurrent, + smallEnemyAdjust, readyToEndLevel, quitRequested; + +JE_byte newPL[10]; /* [0..9] */ /*Eventsys event 75 parameter*/ +JE_word returnLoc; +JE_boolean returnActive; +JE_word galagaShotFreq; +JE_longint galagaLife; + +JE_boolean debug = false; /*Debug Mode*/ +Uint32 debugTime, lastDebugTime; +JE_longint debugHistCount; +JE_real debugHist; +JE_word curLoc; /*Current Pixel location of background 1*/ + +JE_boolean firstGameOver, gameLoaded, enemyStillExploding; + + +/* Destruction Ratio */ +JE_word totalEnemy; +JE_word enemyKilled; + +/* Shape/Map Data - All in one Segment! */ +struct JE_MegaDataType1 megaData1; +struct JE_MegaDataType2 megaData2; +struct JE_MegaDataType3 megaData3; + +/* Secret Level Display */ +JE_byte flash; +JE_shortint flashChange; +JE_byte displayTime; + +/* Demo Stuff */ +bool play_demo = false, record_demo = false, stopped_demo = false; +Uint8 demo_num = 0; +FILE *demo_file = NULL; + +Uint8 demo_keys, next_demo_keys; +Uint16 demo_keys_wait; + +/* Sound Effects Queue */ +JE_byte soundQueue[8]; /* [0..7] */ + +/*Level Event Data*/ +JE_boolean enemyContinualDamage; +JE_boolean enemiesActive; +JE_boolean forceEvents; +JE_boolean stopBackgrounds; +JE_byte stopBackgroundNum; +JE_byte damageRate; /*Rate at which a player takes damage*/ +JE_boolean background3x1; /*Background 3 enemies use Background 1 X offset*/ +JE_boolean background3x1b; /*Background 3 enemies moved 8 pixels left*/ + +JE_boolean levelTimer; +JE_word levelTimerCountdown; +JE_word levelTimerJumpTo; +JE_boolean randomExplosions; + +JE_boolean editShip1, editShip2; + +JE_boolean globalFlags[10]; /* [1..10] */ +JE_byte levelSong; + +/* DESTRUCT game */ +JE_boolean loadDestruct; + +/* MapView Data */ +JE_word mapOrigin, mapPNum; +JE_byte mapPlanet[5], mapSection[5]; /* [1..5] */ + +/* Interface Constants */ +JE_boolean moveTyrianLogoUp; +JE_boolean skipStarShowVGA; + +/*EnemyData*/ +JE_MultiEnemyType enemy; +JE_EnemyAvailType enemyAvail; /* values: 0: used, 1: free, 2: secret pick-up */ +JE_word enemyOffset; +JE_word enemyOnScreen; +JE_byte enemyShapeTables[6]; /* [1..6] */ +JE_word superEnemy254Jump; + +/*EnemyShotData*/ +JE_boolean fireButtonHeld; +JE_boolean enemyShotAvail[ENEMY_SHOT_MAX]; /* [1..Enemyshotmax] */ +EnemyShotType enemyShot[ENEMY_SHOT_MAX]; /* [1..Enemyshotmax] */ + +/* Player Shot Data */ +JE_byte zinglonDuration; +JE_byte astralDuration; +JE_word flareDuration; +JE_boolean flareStart; +JE_shortint flareColChg; +JE_byte specialWait; +JE_byte nextSpecialWait; +JE_boolean spraySpecial; +JE_byte doIced; +JE_boolean infiniteShot; + +/*PlayerData*/ +JE_boolean allPlayersGone; /*Both players dead and finished exploding*/ + +const uint shadowYDist = 10; + +JE_real optionSatelliteRotate; + +JE_integer optionAttachmentMove; +JE_boolean optionAttachmentLinked, optionAttachmentReturn; + + +JE_byte chargeWait, chargeLevel, chargeMax, chargeGr, chargeGrWait; + +JE_word neat; + + +/*ExplosionData*/ +explosion_type explosions[MAX_EXPLOSIONS]; /* [1..ExplosionMax] */ +JE_integer explosionFollowAmountX, explosionFollowAmountY; + +/*Repeating Explosions*/ +rep_explosion_type rep_explosions[MAX_REPEATING_EXPLOSIONS]; /* [1..20] */ + +/*SuperPixels*/ +superpixel_type superpixels[MAX_SUPERPIXELS]; /* [0..MaxSP] */ +unsigned int last_superpixel; + +/*Temporary Numbers*/ +JE_byte temp, temp2, temp3; +JE_word tempX, tempY; +JE_word tempW; + +JE_boolean doNotSaveBackup; + +JE_word x, y; +JE_integer b; + +JE_byte **BKwrap1to, **BKwrap2to, **BKwrap3to, + **BKwrap1, **BKwrap2, **BKwrap3; + +JE_shortint specialWeaponFilter, specialWeaponFreq; +JE_word specialWeaponWpn; +JE_boolean linkToPlayer; + +JE_word shipGr, shipGr2; +Sprite2_array *shipGrPtr, *shipGr2ptr; + +void JE_getShipInfo( void ) +{ + JE_boolean extraShip, extraShip2; + + shipGrPtr = &shapes9; + shipGr2ptr = &shapes9; + + powerAdd = powerSys[player[0].items.generator].power; + + extraShip = player[0].items.ship > 90; + if (extraShip) + { + JE_byte base = (player[0].items.ship - 91) * 15; + shipGr = JE_SGr(player[0].items.ship - 90, &shipGrPtr); + player[0].armor = extraShips[base + 7]; + } + else + { + shipGr = ships[player[0].items.ship].shipgraphic; + player[0].armor = ships[player[0].items.ship].dmg; + } + + extraShip2 = player[1].items.ship > 90; + if (extraShip2) + { + JE_byte base2 = (player[1].items.ship - 91) * 15; + shipGr2 = JE_SGr(player[1].items.ship - 90, &shipGr2ptr); + player[1].armor = extraShips[base2 + 7]; /* bug? */ + } + else + { + shipGr2 = 0; + player[1].armor = 10; + } + + for (uint i = 0; i < COUNTOF(player); ++i) + { + player[i].initial_armor = player[i].armor; + + + uint temp = ((i == 0 && extraShip) || + (i == 1 && extraShip2)) ? 2 : ships[player[i].items.ship].ani; + + if (temp == 0) + { + player[i].shot_hit_area_x = 12; + player[i].shot_hit_area_y = 10; + } + else + { + player[i].shot_hit_area_x = 11; + player[i].shot_hit_area_y = 14; + } + } +} + +JE_word JE_SGr( JE_word ship, Sprite2_array **ptr ) +{ + const JE_word GR[15] /* [1..15] */ = {233, 157, 195, 271, 81, 0, 119, 5, 43, 81, 119, 157, 195, 233, 271}; + + JE_word tempW = extraShips[(ship - 1) * 15]; + if (tempW > 7) + *ptr = extraShapes; + + return GR[tempW-1]; +} + +void JE_drawOptions( void ) +{ + SDL_Surface *temp_surface = VGAScreen; + VGAScreen = VGAScreenSeg; + + Player *this_player = &player[twoPlayerMode ? 1 : 0]; + + for (uint i = 0; i < COUNTOF(this_player->sidekick); ++i) + { + JE_OptionType *this_option = &options[this_player->items.sidekick[i]]; + + this_player->sidekick[i].ammo = + this_player->sidekick[i].ammo_max = this_option->ammo; + + this_player->sidekick[i].ammo_refill_ticks = + this_player->sidekick[i].ammo_refill_ticks_max = (105 - this_player->sidekick[i].ammo) * 4; + + this_player->sidekick[i].style = this_option->tr; + + this_player->sidekick[i].animation_enabled = (this_option->option == 1); + this_player->sidekick[i].animation_frame = 0; + + this_player->sidekick[i].charge = 0; + this_player->sidekick[i].charge_ticks = 20; + + + // draw initial sidekick HUD + const int y = hud_sidekick_y[twoPlayerMode ? 1 : 0][i]; + + fill_rectangle_xy(VGAScreenSeg, 284, y, 284 + 28, y + 15, 0); + if (this_option->icongr > 0) + blit_sprite(VGAScreenSeg, 284, y, OPTION_SHAPES, this_option->icongr - 1); // sidekick HUD icon + draw_segmented_gauge(VGAScreenSeg, 284, y + 13, 112, 2, 2, MAX(1, this_player->sidekick[i].ammo_max / 10), this_player->sidekick[i].ammo); + } + + VGAScreen = temp_surface; + + JE_drawOptionLevel(); +} + +void JE_drawOptionLevel( void ) +{ + if (twoPlayerMode) + { + for (temp = 1; temp <= 3; temp++) + { + fill_rectangle_xy(VGAScreenSeg, 268, 127 + (temp - 1) * 6, 269, 127 + 3 + (temp - 1) * 6, 193 + ((player[1].items.sidekick_level - 100) == temp) * 11); + } + } +} + +void JE_tyrianHalt( JE_byte code ) +{ + deinit_audio(); + deinit_video(); + deinit_joysticks(); + + /* TODO: NETWORK */ + + free_main_shape_tables(); + + free_sprite2s(&shapes6); + + for (int i = 0; i < SAMPLE_COUNT; i++) + { + free(digiFx[i]); + } + + if (code != 9) + { + /* + TODO? + JE_drawANSI("exitmsg.bin"); + JE_gotoXY(1,22);*/ + + JE_saveConfiguration(); + } + + /* endkeyboard; */ + + if (code == 9) + { + /* OutputString('call=file0002.EXE' + #0'); TODO? */ + } + + if (code == 5) + { + code = 0; + } + + if (trentWin) + { + printf("\n" + "\n" + "\n" + "\n" + "Sleep well, Trent, you deserve the rest.\n" + "You now have permission to borrow my ship on your next mission.\n" + "\n" + "Also, you might want to try out the YESXMAS parameter.\n" + " Type: File0001 YESXMAS\n" + "\n" + "You'll need the 2.1 patch, though!\n" + "\n"); + } + + SDL_Quit(); + exit(code); +} + +void JE_specialComplete( JE_byte playerNum, JE_byte specialType ) +{ + nextSpecialWait = 0; + switch (special[specialType].stype) + { + /*Weapon*/ + case 1: + if (playerNum == 1) + b = player_shot_create(0, SHOT_SPECIAL2, player[0].x, player[0].y, mouseX, mouseY, special[specialType].wpn, playerNum); + else + b = player_shot_create(0, SHOT_SPECIAL2, player[1].x, player[1].y, mouseX, mouseY, special[specialType].wpn, playerNum); + + shotRepeat[SHOT_SPECIAL] = shotRepeat[SHOT_SPECIAL2]; + break; + /*Repulsor*/ + case 2: + for (temp = 0; temp < ENEMY_SHOT_MAX; temp++) + { + if (!enemyShotAvail[temp]) + { + if (player[0].x > enemyShot[temp].sx) + enemyShot[temp].sxm--; + else if (player[0].x < enemyShot[temp].sx) + enemyShot[temp].sxm++; + + if (player[0].y > enemyShot[temp].sy) + enemyShot[temp].sym--; + else if (player[0].y < enemyShot[temp].sy) + enemyShot[temp].sym++; + } + } + break; + /*Zinglon Blast*/ + case 3: + zinglonDuration = 50; + shotRepeat[SHOT_SPECIAL] = 100; + soundQueue[7] = S_SOUL_OF_ZINGLON; + break; + /*Attractor*/ + case 4: + for (temp = 0; temp < 100; temp++) + { + if (enemyAvail[temp] != 1 && enemy[temp].scoreitem + && enemy[temp].evalue != 0) + { + if (player[0].x > enemy[temp].ex) + enemy[temp].exc++; + else if (player[0].x < enemy[temp].ex) + enemy[temp].exc--; + + if (player[0].y > enemy[temp].ey) + enemy[temp].eyc++; + else if (player[0].y < enemy[temp].ey) + enemy[temp].eyc--; + } + } + break; + /*Flare*/ + case 5: + case 6: + case 7: + case 8: + case 9: + case 10: + case 11: + case 16: + if (flareDuration == 0) + flareStart = true; + + specialWeaponWpn = special[specialType].wpn; + linkToPlayer = false; + spraySpecial = false; + switch (special[specialType].stype) + { + case 5: + specialWeaponFilter = 7; + specialWeaponFreq = 2; + flareDuration = 50; + break; + case 6: + specialWeaponFilter = 1; + specialWeaponFreq = 7; + flareDuration = 200 + 25 * player[0].items.weapon[FRONT_WEAPON].power; + break; + case 7: + specialWeaponFilter = 3; + specialWeaponFreq = 3; + flareDuration = 50 + 10 * player[0].items.weapon[FRONT_WEAPON].power; + zinglonDuration = 50; + shotRepeat[SHOT_SPECIAL] = 100; + soundQueue[7] = S_SOUL_OF_ZINGLON; + break; + case 8: + specialWeaponFilter = -99; + specialWeaponFreq = 7; + flareDuration = 10 + player[0].items.weapon[FRONT_WEAPON].power; + break; + case 9: + specialWeaponFilter = -99; + specialWeaponFreq = 8; + flareDuration = 8 + 2 * player[0].items.weapon[FRONT_WEAPON].power; + linkToPlayer = true; + nextSpecialWait = special[specialType].pwr; + break; + case 10: + specialWeaponFilter = -99; + specialWeaponFreq = 8; + flareDuration = 14 + 4 * player[0].items.weapon[FRONT_WEAPON].power; + linkToPlayer = true; + break; + case 11: + specialWeaponFilter = -99; + specialWeaponFreq = special[specialType].pwr; + flareDuration = 10 + 10 * player[0].items.weapon[FRONT_WEAPON].power; + astralDuration = 20 + 10 * player[0].items.weapon[FRONT_WEAPON].power; + break; + case 16: + specialWeaponFilter = -99; + specialWeaponFreq = 8; + flareDuration = temp2 * 16 + 8; + linkToPlayer = true; + spraySpecial = true; + break; + } + break; + case 12: + player[playerNum-1].invulnerable_ticks = temp2 * 10; + + if (superArcadeMode > 0 && superArcadeMode <= SA) + { + shotRepeat[SHOT_SPECIAL] = 250; + b = player_shot_create(0, SHOT_SPECIAL2, player[0].x, player[0].y, mouseX, mouseY, 707, 1); + player[0].invulnerable_ticks = 100; + } + break; + case 13: + player[0].armor += temp2 / 4 + 1; + + soundQueue[3] = S_POWERUP; + break; + case 14: + player[1].armor += temp2 / 4 + 1; + + soundQueue[3] = S_POWERUP; + break; + + case 17: // spawn left or right sidekick + soundQueue[3] = S_POWERUP; + + if (player[0].items.sidekick[LEFT_SIDEKICK] == special[specialType].wpn) + { + player[0].items.sidekick[RIGHT_SIDEKICK] = special[specialType].wpn; + shotMultiPos[RIGHT_SIDEKICK] = 0; + } + else + { + player[0].items.sidekick[LEFT_SIDEKICK] = special[specialType].wpn; + shotMultiPos[LEFT_SIDEKICK] = 0; + } + + JE_drawOptions(); + break; + + case 18: // spawn right sidekick + player[0].items.sidekick[RIGHT_SIDEKICK] = special[specialType].wpn; + + JE_drawOptions(); + + soundQueue[4] = S_POWERUP; + + shotMultiPos[RIGHT_SIDEKICK] = 0; + break; + } +} + +void JE_doSpecialShot( JE_byte playerNum, uint *armor, uint *shield ) +{ + if (player[0].items.special > 0) + { + if (shotRepeat[SHOT_SPECIAL] == 0 && specialWait == 0 && flareDuration < 2 && zinglonDuration < 2) + blit_sprite2(VGAScreen, 47, 4, shapes9, 94); + else + blit_sprite2(VGAScreen, 47, 4, shapes9, 93); + } + + if (shotRepeat[SHOT_SPECIAL] > 0) + { + --shotRepeat[SHOT_SPECIAL]; + } + if (specialWait > 0) + { + specialWait--; + } + temp = SFExecuted[playerNum-1]; + if (temp > 0 && shotRepeat[SHOT_SPECIAL] == 0 && flareDuration == 0) + { + temp2 = special[temp].pwr; + + bool can_afford = true; + + if (temp2 > 0) + { + if (temp2 < 98) // costs some shield + { + if (*shield >= temp2) + *shield -= temp2; + else + can_afford = false; + } + else if (temp2 == 98) // costs all shield + { + if (*shield < 4) + can_afford = false; + temp2 = *shield; + *shield = 0; + } + else if (temp2 == 99) // costs half shield + { + temp2 = *shield / 2; + *shield = temp2; + } + else // costs some armor + { + temp2 -= 100; + if (*armor > temp2) + *armor -= temp2; + else + can_afford = false; + } + } + + shotMultiPos[SHOT_SPECIAL] = 0; + shotMultiPos[SHOT_SPECIAL2] = 0; + + if (can_afford) + JE_specialComplete(playerNum, temp); + + SFExecuted[playerNum-1] = 0; + + JE_wipeShieldArmorBars(); + VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ + JE_drawShield(); + JE_drawArmor(); + VGAScreen = game_screen; /* side-effect of game_screen */ + } + + if (playerNum == 1 && player[0].items.special > 0) + { /*Main Begin*/ + + if (superArcadeMode > 0 && (button[2-1] || button[3-1])) + { + fireButtonHeld = false; + } + if (!button[1-1] && !(superArcadeMode != SA_NONE && (button[2-1] || button[3-1]))) + { + fireButtonHeld = false; + } + else if (shotRepeat[SHOT_SPECIAL] == 0 && !fireButtonHeld && !(flareDuration > 0) && specialWait == 0) + { + fireButtonHeld = true; + JE_specialComplete(playerNum, player[0].items.special); + } + + } /*Main End*/ + + if (astralDuration > 0) + astralDuration--; + + shotAvail[MAX_PWEAPON-1] = 0; + if (flareDuration > 1) + { + if (specialWeaponFilter != -99) + { + if (levelFilter == -99 && levelBrightness == -99) + { + filterActive = false; + } + if (!filterActive) + { + levelFilter = specialWeaponFilter; + if (levelFilter == 7) + { + levelBrightness = 0; + } + filterActive = true; + } + + if (mt_rand() % 2 == 0) + flareColChg = -1; + else + flareColChg = 1; + + if (levelFilter == 7) + { + if (levelBrightness < -6) + { + flareColChg = 1; + } + if (levelBrightness > 6) + { + flareColChg = -1; + } + levelBrightness += flareColChg; + } + } + + if ((signed)(mt_rand() % 6) < specialWeaponFreq) + { + b = MAX_PWEAPON; + + if (linkToPlayer) + { + if (shotRepeat[SHOT_SPECIAL] == 0) + { + b = player_shot_create(0, SHOT_SPECIAL, player[0].x, player[0].y, mouseX, mouseY, specialWeaponWpn, playerNum); + } + } + else + { + b = player_shot_create(0, SHOT_SPECIAL, mt_rand() % 280, mt_rand() % 180, mouseX, mouseY, specialWeaponWpn, playerNum); + } + + if (spraySpecial && b != MAX_PWEAPON) + { + playerShotData[b].shotXM = (mt_rand() % 5) - 2; + playerShotData[b].shotYM = (mt_rand() % 5) - 2; + if (playerShotData[b].shotYM == 0) + { + playerShotData[b].shotYM++; + } + } + } + + flareDuration--; + if (flareDuration == 1) + { + specialWait = nextSpecialWait; + } + } + else if (flareStart) + { + flareStart = false; + shotRepeat[SHOT_SPECIAL] = linkToPlayer ? 15 : 200; + flareDuration = 0; + if (levelFilter == specialWeaponFilter) + { + levelFilter = -99; + levelBrightness = -99; + filterActive = false; + } + } + + if (zinglonDuration > 1) + { + temp = 25 - abs(zinglonDuration - 25); + + JE_barBright(VGAScreen, player[0].x + 7 - temp, 0, player[0].x + 7 + temp, 184); + JE_barBright(VGAScreen, player[0].x + 7 - temp - 2, 0, player[0].x + 7 + temp + 2, 184); + + zinglonDuration--; + if (zinglonDuration % 5 == 0) + { + shotAvail[MAX_PWEAPON-1] = 1; + } + } +} + +void JE_setupExplosion( signed int x, signed int y, signed int delta_y, unsigned int type, bool fixed_position, bool follow_player ) +{ + const struct { + JE_word sprite; + JE_byte ttl; + } explosion_data[53] /* [1..53] */ = { + { 144, 7 }, + { 120, 12 }, + { 190, 12 }, + { 209, 12 }, + { 152, 12 }, + { 171, 12 }, + { 133, 7 }, /*White Smoke*/ + { 1, 12 }, + { 20, 12 }, + { 39, 12 }, + { 58, 12 }, + { 110, 3 }, + { 76, 7 }, + { 91, 3 }, +/*15*/ { 227, 3 }, + { 230, 3 }, + { 233, 3 }, + { 252, 3 }, + { 246, 3 }, +/*20*/ { 249, 3 }, + { 265, 3 }, + { 268, 3 }, + { 271, 3 }, + { 236, 3 }, +/*25*/ { 239, 3 }, + { 242, 3 }, + { 261, 3 }, + { 274, 3 }, + { 277, 3 }, +/*30*/ { 280, 3 }, + { 299, 3 }, + { 284, 3 }, + { 287, 3 }, + { 290, 3 }, +/*35*/ { 293, 3 }, + { 165, 8 }, /*Coin Values*/ + { 184, 8 }, + { 203, 8 }, + { 222, 8 }, + { 168, 8 }, + { 187, 8 }, + { 206, 8 }, + { 225, 10 }, + { 169, 10 }, + { 188, 10 }, + { 207, 20 }, + { 226, 14 }, + { 170, 14 }, + { 189, 14 }, + { 208, 14 }, + { 246, 14 }, + { 227, 14 }, + { 265, 14 } + }; + + if (y > -16 && y < 190) + { + for (int i = 0; i < MAX_EXPLOSIONS; i++) + { + if (explosions[i].ttl == 0) + { + explosions[i].x = x; + explosions[i].y = y; + if (type == 6) + { + explosions[i].y += 12; + explosions[i].x += 2; + } else if (type == 98) + { + type = 6; + } + explosions[i].sprite = explosion_data[type].sprite; + explosions[i].ttl = explosion_data[type].ttl; + explosions[i].follow_player = follow_player; + explosions[i].fixed_position = fixed_position; + explosions[i].delta_x = 0; + explosions[i].delta_y = delta_y; + break; + } + } + } +} + +void JE_setupExplosionLarge( JE_boolean enemyGround, JE_byte exploNum, JE_integer x, JE_integer y ) +{ + if (y >= 0) + { + if (enemyGround) + { + JE_setupExplosion(x - 6, y - 14, 0, 2, false, false); + JE_setupExplosion(x + 6, y - 14, 0, 4, false, false); + JE_setupExplosion(x - 6, y, 0, 3, false, false); + JE_setupExplosion(x + 6, y, 0, 5, false, false); + } else { + JE_setupExplosion(x - 6, y - 14, 0, 7, false, false); + JE_setupExplosion(x + 6, y - 14, 0, 9, false, false); + JE_setupExplosion(x - 6, y, 0, 8, false, false); + JE_setupExplosion(x + 6, y, 0, 10, false, false); + } + + bool big; + + if (exploNum > 10) + { + exploNum -= 10; + big = true; + } + else + { + big = false; + } + + if (exploNum) + { + for (int i = 0; i < MAX_REPEATING_EXPLOSIONS; i++) + { + if (rep_explosions[i].ttl == 0) + { + rep_explosions[i].ttl = exploNum; + rep_explosions[i].delay = 2; + rep_explosions[i].x = x; + rep_explosions[i].y = y; + rep_explosions[i].big = big; + break; + } + } + } + } +} + +void JE_wipeShieldArmorBars( void ) +{ + if (!twoPlayerMode || galagaMode) + { + fill_rectangle_xy(VGAScreenSeg, 270, 137, 278, 194 - player[0].shield * 2, 0); + } + else + { + fill_rectangle_xy(VGAScreenSeg, 270, 60 - 44, 278, 60, 0); + fill_rectangle_xy(VGAScreenSeg, 270, 194 - 44, 278, 194, 0); + } + if (!twoPlayerMode || galagaMode) + { + fill_rectangle_xy(VGAScreenSeg, 307, 137, 315, 194 - player[0].armor * 2, 0); + } + else + { + fill_rectangle_xy(VGAScreenSeg, 307, 60 - 44, 315, 60, 0); + fill_rectangle_xy(VGAScreenSeg, 307, 194 - 44, 315, 194, 0); + } +} + +JE_byte JE_playerDamage( JE_byte temp, + Player *this_player ) +{ + int playerDamage = 0; + soundQueue[7] = S_SHIELD_HIT; + + /* Player Damage Routines */ + if (this_player->shield < temp) + { + playerDamage = temp; + temp -= this_player->shield; + this_player->shield = 0; + + if (temp > 0) + { + /*Through Shields - Now Armor */ + + if (this_player->armor < temp) + { + temp -= this_player->armor; + this_player->armor = 0; + + if (this_player->is_alive && !youAreCheating) + { + levelTimer = false; + this_player->is_alive = false; + this_player->exploding_ticks = 60; + levelEnd = 40; + tempVolume = tyrMusicVolume; + soundQueue[1] = S_EXPLOSION_22; + } + } + else + { + this_player->armor -= temp; + soundQueue[7] = S_HULL_HIT; + } + } + } + else + { + this_player->shield -= temp; + + JE_setupExplosion(this_player->x - 17, this_player->y - 12, 0, 14, false, !twoPlayerMode); + JE_setupExplosion(this_player->x - 5 , this_player->y - 12, 0, 15, false, !twoPlayerMode); + JE_setupExplosion(this_player->x + 7 , this_player->y - 12, 0, 16, false, !twoPlayerMode); + JE_setupExplosion(this_player->x + 19, this_player->y - 12, 0, 17, false, !twoPlayerMode); + + JE_setupExplosion(this_player->x - 17, this_player->y + 2, 0, 18, false, !twoPlayerMode); + JE_setupExplosion(this_player->x + 19, this_player->y + 2, 0, 19, false, !twoPlayerMode); + + JE_setupExplosion(this_player->x - 17, this_player->y + 16, 0, 20, false, !twoPlayerMode); + JE_setupExplosion(this_player->x - 5 , this_player->y + 16, 0, 21, false, !twoPlayerMode); + JE_setupExplosion(this_player->x + 7 , this_player->y + 16, 0, 22, false, !twoPlayerMode); + } + + JE_wipeShieldArmorBars(); + VGAScreen = VGAScreenSeg; /* side-effect of game_screen */ + JE_drawShield(); + JE_drawArmor(); + VGAScreen = game_screen; /* side-effect of game_screen */ + + return playerDamage; +} + +JE_word JE_portConfigs( void ) +{ + const uint player_index = twoPlayerMode ? 1 : 0; + return tempW = weaponPort[player[player_index].items.weapon[REAR_WEAPON].id].opnum; +} + +void JE_drawShield( void ) +{ + if (twoPlayerMode && !galagaMode) + { + for (uint i = 0; i < COUNTOF(player); ++i) + JE_dBar3(VGAScreen, 270, 60 + 134 * i, roundf(player[i].shield * 0.8f), 144); + } + else + { + JE_dBar3(VGAScreen, 270, 194, player[0].shield, 144); + if (player[0].shield != player[0].shield_max) + { + const uint y = 193 - (player[0].shield_max * 2); + JE_rectangle(VGAScreen, 270, y, 278, y, 68); /* SEGa000 */ + } + } +} + +void JE_drawArmor( void ) +{ + for (uint i = 0; i < COUNTOF(player); ++i) + if (player[i].armor > 28) + player[i].armor = 28; + + if (twoPlayerMode && !galagaMode) + { + for (uint i = 0; i < COUNTOF(player); ++i) + JE_dBar3(VGAScreen, 307, 60 + 134 * i, roundf(player[i].armor * 0.8f), 224); + } + else + { + JE_dBar3(VGAScreen, 307, 194, player[0].armor, 224); + } +} + +void JE_doSP( JE_word x, JE_word y, JE_word num, JE_byte explowidth, JE_byte color ) /* superpixels */ +{ + for (temp = 0; temp < num; temp++) + { + JE_real tempr = mt_rand_lt1() * (2 * M_PI); + signed int tempy = roundf(cosf(tempr) * mt_rand_1() * explowidth); + signed int tempx = roundf(sinf(tempr) * mt_rand_1() * explowidth); + + if (++last_superpixel >= MAX_SUPERPIXELS) + last_superpixel = 0; + superpixels[last_superpixel].x = tempx + x; + superpixels[last_superpixel].y = tempy + y; + superpixels[last_superpixel].delta_x = tempx; + superpixels[last_superpixel].delta_y = tempy + 1; + superpixels[last_superpixel].color = color; + superpixels[last_superpixel].z = 15; + } +} + +void JE_drawSP( void ) +{ + for (int i = MAX_SUPERPIXELS; i--; ) + { + if (superpixels[i].z) + { + superpixels[i].x += superpixels[i].delta_x; + superpixels[i].y += superpixels[i].delta_y; + + if (superpixels[i].x < (unsigned)VGAScreen->w && superpixels[i].y < (unsigned)VGAScreen->h) + { + Uint8 *s = (Uint8 *)VGAScreen->pixels; /* screen pointer, 8-bit specific */ + s += superpixels[i].y * VGAScreen->pitch; + s += superpixels[i].x; + + *s = (((*s & 0x0f) + superpixels[i].z) >> 1) + superpixels[i].color; + if (superpixels[i].x > 0) + *(s - 1) = (((*(s - 1) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color; + if (superpixels[i].x < VGAScreen->w - 1u) + *(s + 1) = (((*(s + 1) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color; + if (superpixels[i].y > 0) + *(s - VGAScreen->pitch) = (((*(s - VGAScreen->pitch) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color; + if (superpixels[i].y < VGAScreen->h - 1u) + *(s + VGAScreen->pitch) = (((*(s + VGAScreen->pitch) & 0x0f) + (superpixels[i].z >> 1)) >> 1) + superpixels[i].color; + } + + superpixels[i].z--; + } + } +} + diff --git a/contrib/games/opentyrian/src/varz.h b/contrib/games/opentyrian/src/varz.h new file mode 100644 index 000000000..fad4114fe --- /dev/null +++ b/contrib/games/opentyrian/src/varz.h @@ -0,0 +1,359 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef VARZ_H +#define VARZ_H + +#include "episodes.h" +#include "opentyr.h" +#include "player.h" +#include "sprite.h" + +#include + +#define SA 7 + +enum +{ + SA_NONE = 0, + SA_NORTSHIPZ = 7, + + // only used for code entry + SA_DESTRUCT = 8, + SA_ENGAGE = 9, + + // only used in pItems[P_SUPERARCADE] + SA_SUPERTYRIAN = 254, + SA_ARCADE = 255 +}; + +#define ENEMY_SHOT_MAX 60 /* 60*/ + +#define CURRENT_KEY_SPEED 1 /*Keyboard/Joystick movement rate*/ + +#define MAX_EXPLOSIONS 200 +#define MAX_REPEATING_EXPLOSIONS 20 +#define MAX_SUPERPIXELS 101 + +struct JE_SingleEnemyType +{ + JE_byte fillbyte; + JE_integer ex, ey; /* POSITION */ + JE_shortint exc, eyc; /* CURRENT SPEED */ + JE_shortint exca, eyca; /* RANDOM ACCELERATION */ + JE_shortint excc, eycc; /* FIXED ACCELERATION WAITTIME */ + JE_shortint exccw, eyccw; + JE_byte armorleft; + JE_byte eshotwait[3], eshotmultipos[3]; /* [1..3] */ + JE_byte enemycycle; + JE_byte ani; + JE_word egr[20]; /* [1..20] */ + JE_byte size; + JE_byte linknum; + JE_byte aniactive; + JE_byte animax; + JE_byte aniwhenfire; + Sprite2_array *sprite2s; + JE_shortint exrev, eyrev; + JE_integer exccadd, eyccadd; + JE_byte exccwmax, eyccwmax; + void *enemydatofs; + JE_boolean edamaged; + JE_word enemytype; + JE_byte animin; + JE_word edgr; + JE_shortint edlevel; + JE_shortint edani; + JE_byte fill1; + JE_byte filter; + JE_integer evalue; + JE_integer fixedmovey; + JE_byte freq[3]; /* [1..3] */ + JE_byte launchwait; + JE_word launchtype; + JE_byte launchfreq; + JE_byte xaccel; + JE_byte yaccel; + JE_byte tur[3]; /* [1..3] */ + JE_word enemydie; /* Enemy created when this one dies */ + JE_boolean enemyground; + JE_byte explonum; + JE_word mapoffset; + JE_boolean scoreitem; + + JE_boolean special; + JE_byte flagnum; + JE_boolean setto; + + JE_byte iced; /*Duration*/ + + JE_byte launchspecial; + + JE_integer xminbounce; + JE_integer xmaxbounce; + JE_integer yminbounce; + JE_integer ymaxbounce; + JE_byte fill[3]; /* [1..3] */ +}; + +typedef struct JE_SingleEnemyType JE_MultiEnemyType[100]; /* [1..100] */ + +typedef JE_word JE_DanCShape[(24 * 28) / 2]; /* [1..(24*28) div 2] */ + +typedef JE_char JE_CharString[256]; /* [1..256] */ + +typedef JE_byte JE_Map1Buffer[24 * 28 * 13 * 4]; /* [1..24*28*13*4] */ + +typedef JE_byte *JE_MapType[300][14]; /* [1..300, 1..14] */ +typedef JE_byte *JE_MapType2[600][14]; /* [1..600, 1..14] */ +typedef JE_byte *JE_MapType3[600][15]; /* [1..600, 1..15] */ + +struct JE_EventRecType +{ + JE_word eventtime; + JE_byte eventtype; + JE_integer eventdat, eventdat2; + JE_shortint eventdat3, eventdat5, eventdat6; + JE_byte eventdat4; +}; + +struct JE_MegaDataType1 +{ + JE_MapType mainmap; + struct + { + JE_DanCShape sh; + } shapes[72]; /* [0..71] */ + JE_byte tempdat1; + /*JE_DanCShape filler;*/ +}; + +struct JE_MegaDataType2 +{ + JE_MapType2 mainmap; + struct + { + JE_byte nothing[3]; /* [1..3] */ + JE_byte fill; + JE_DanCShape sh; + } shapes[71]; /* [0..70] */ + JE_byte tempdat2; +}; + +struct JE_MegaDataType3 +{ + JE_MapType3 mainmap; + struct + { + JE_byte nothing[3]; /* [1..3] */ + JE_byte fill; + JE_DanCShape sh; + } shapes[70]; /* [0..69] */ + JE_byte tempdat3; +}; + +typedef JE_byte JE_EnemyAvailType[100]; /* [1..100] */ + +typedef struct { + JE_integer sx, sy; + JE_integer sxm, sym; + JE_shortint sxc, syc; + JE_byte tx, ty; + JE_word sgr; + JE_byte sdmg; + JE_byte duration; + JE_word animate; + JE_word animax; + JE_byte fill[12]; +} EnemyShotType; + +typedef struct { + unsigned int ttl; + signed int x, y; + signed int delta_x, delta_y; + bool fixed_position; + bool follow_player; + unsigned int sprite; +} explosion_type; + +typedef struct { + unsigned int delay; + unsigned int ttl; + unsigned int x, y; + bool big; +} rep_explosion_type; + +typedef struct { + unsigned int x, y, z; + signed int delta_x, delta_y; + Uint8 color; +} superpixel_type; + +extern JE_integer tempDat, tempDat2, tempDat3; +extern const JE_byte SANextShip[SA + 2]; +extern const JE_word SASpecialWeapon[SA]; +extern const JE_word SASpecialWeaponB[SA]; +extern const JE_byte SAShip[SA]; +extern const JE_word SAWeapon[SA][5]; +extern const JE_byte specialArcadeWeapon[PORT_NUM]; +extern const JE_byte optionSelect[16][3][2]; +extern const JE_word PGR[21]; +extern const JE_byte PAni[21]; +extern const JE_word linkGunWeapons[38]; +extern const JE_word chargeGunWeapons[38]; +extern const JE_byte randomEnemyLaunchSounds[3]; +extern const JE_byte keyboardCombos[26][8]; +extern const JE_byte shipCombosB[21]; +extern const JE_byte superTyrianSpecials[4]; +extern const JE_byte shipCombos[14][3]; +extern JE_byte SFCurrentCode[2][21]; +extern JE_byte SFExecuted[2]; +extern JE_byte lvlFileNum; +extern JE_word maxEvent, eventLoc; +extern JE_word tempBackMove, explodeMove; +extern JE_byte levelEnd; +extern JE_word levelEndFxWait; +extern JE_shortint levelEndWarp; +extern JE_boolean endLevel, reallyEndLevel, waitToEndLevel, playerEndLevel, normalBonusLevelCurrent, bonusLevelCurrent, smallEnemyAdjust, readyToEndLevel, quitRequested; +extern JE_byte newPL[10]; +extern JE_word returnLoc; +extern JE_boolean returnActive; +extern JE_word galagaShotFreq; +extern JE_longint galagaLife; +extern JE_boolean debug; +extern Uint32 debugTime, lastDebugTime; +extern JE_longint debugHistCount; +extern JE_real debugHist; +extern JE_word curLoc; +extern JE_boolean firstGameOver, gameLoaded, enemyStillExploding; +extern JE_word totalEnemy; +extern JE_word enemyKilled; +extern struct JE_MegaDataType1 megaData1; +extern struct JE_MegaDataType2 megaData2; +extern struct JE_MegaDataType3 megaData3; +extern JE_byte flash; +extern JE_shortint flashChange; +extern JE_byte displayTime; + +extern bool play_demo, record_demo, stopped_demo; +extern Uint8 demo_num; +extern FILE *demo_file; + +extern Uint8 demo_keys, next_demo_keys; +extern Uint16 demo_keys_wait; + +extern JE_byte soundQueue[8]; +extern JE_boolean enemyContinualDamage; +extern JE_boolean enemiesActive; +extern JE_boolean forceEvents; +extern JE_boolean stopBackgrounds; +extern JE_byte stopBackgroundNum; +extern JE_byte damageRate; +extern JE_boolean background3x1; +extern JE_boolean background3x1b; +extern JE_boolean levelTimer; +extern JE_word levelTimerCountdown; +extern JE_word levelTimerJumpTo; +extern JE_boolean randomExplosions; +extern JE_boolean editShip1, editShip2; +extern JE_boolean globalFlags[10]; +extern JE_byte levelSong; +extern JE_boolean loadDestruct; +extern JE_word mapOrigin, mapPNum; +extern JE_byte mapPlanet[5], mapSection[5]; +extern JE_boolean moveTyrianLogoUp; +extern JE_boolean skipStarShowVGA; +extern JE_MultiEnemyType enemy; +extern JE_EnemyAvailType enemyAvail; +extern JE_word enemyOffset; +extern JE_word enemyOnScreen; +extern JE_byte enemyShapeTables[6]; +extern JE_word superEnemy254Jump; +extern explosion_type explosions[MAX_EXPLOSIONS]; +extern JE_integer explosionFollowAmountX, explosionFollowAmountY; +extern JE_boolean fireButtonHeld; +extern JE_boolean enemyShotAvail[ENEMY_SHOT_MAX]; +extern EnemyShotType enemyShot[ENEMY_SHOT_MAX]; +extern JE_byte zinglonDuration; +extern JE_byte astralDuration; +extern JE_word flareDuration; +extern JE_boolean flareStart; +extern JE_shortint flareColChg; +extern JE_byte specialWait; +extern JE_byte nextSpecialWait; +extern JE_boolean spraySpecial; +extern JE_byte doIced; +extern JE_boolean infiniteShot; +extern JE_boolean allPlayersGone; +extern const uint shadowYDist; +extern JE_real optionSatelliteRotate; +extern JE_integer optionAttachmentMove; +extern JE_boolean optionAttachmentLinked, optionAttachmentReturn; +extern JE_byte chargeWait, chargeLevel, chargeMax, chargeGr, chargeGrWait; +extern JE_word neat; +extern rep_explosion_type rep_explosions[MAX_REPEATING_EXPLOSIONS]; +extern superpixel_type superpixels[MAX_SUPERPIXELS]; +extern unsigned int last_superpixel; +extern JE_byte temp, temp2, temp3; +extern JE_word tempX, tempY; +extern JE_word tempW; +extern JE_boolean doNotSaveBackup; +extern JE_word x, y; +extern JE_integer b; +extern JE_byte **BKwrap1to, **BKwrap2to, **BKwrap3to, **BKwrap1, **BKwrap2, **BKwrap3; +extern JE_shortint specialWeaponFilter, specialWeaponFreq; +extern JE_word specialWeaponWpn; +extern JE_boolean linkToPlayer; +extern JE_word shipGr, shipGr2; +extern Sprite2_array *shipGrPtr, *shipGr2ptr; + +static const int hud_sidekick_y[2][2] = +{ + { 64, 82 }, // one player HUD + { 108, 126 }, // two player HUD +}; + +void JE_getShipInfo( void ); +JE_word JE_SGr( JE_word ship, Sprite2_array **ptr ); + +void JE_drawOptions( void ); + +void JE_tyrianHalt( JE_byte code ); /* This ends the game */ +void JE_specialComplete( JE_byte playernum, JE_byte specialType ); +void JE_doSpecialShot( JE_byte playernum, uint *armor, uint *shield ); + +void JE_wipeShieldArmorBars( void ); +JE_byte JE_playerDamage( JE_byte temp, Player * ); + +void JE_setupExplosion( signed int x, signed int y, signed int delta_y, unsigned int type, bool fixed_position, bool follow_player ); +void JE_setupExplosionLarge( JE_boolean enemyground, JE_byte explonum, JE_integer x, JE_integer y ); + +void JE_drawShield( void ); +void JE_drawArmor( void ); + +JE_word JE_portConfigs( void ); + +/*SuperPixels*/ +void JE_doSP( JE_word x, JE_word y, JE_word num, JE_byte explowidth, JE_byte color ); +void JE_drawSP( void ); + +void JE_drawOptionLevel( void ); + + +#endif /* VARZ_H */ + diff --git a/contrib/games/opentyrian/src/vga256d.c b/contrib/games/opentyrian/src/vga256d.c new file mode 100644 index 000000000..c8752e37b --- /dev/null +++ b/contrib/games/opentyrian/src/vga256d.c @@ -0,0 +1,161 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "vga256d.h" + +#include "config.h" // For fullscreen stuff +#include "keyboard.h" +#include "opentyr.h" +#include "palette.h" +#include "video.h" + +#include "SDL.h" + +#include +#include +#include +#include +#include + +void JE_pix( SDL_Surface *surface, int x, int y, JE_byte c ) +{ + /* Bad things happen if we don't clip */ + if (x < surface->pitch && y < surface->h) + { + Uint8 *vga = surface->pixels; + vga[y * surface->pitch + x] = c; + } +} + +void JE_pix3( SDL_Surface *surface, int x, int y, JE_byte c ) +{ + /* Originally impemented as several direct accesses */ + JE_pix(surface, x, y, c); + JE_pix(surface, x - 1, y, c); + JE_pix(surface, x + 1, y, c); + JE_pix(surface, x, y - 1, c); + JE_pix(surface, x, y + 1, c); +} + +void JE_rectangle( SDL_Surface *surface, int a, int b, int c, int d, int e ) /* x1, y1, x2, y2, color */ +{ + if (a < surface->pitch && b < surface->h && + c < surface->pitch && d < surface->h) + { + Uint8 *vga = surface->pixels; + int i; + + /* Top line */ + memset(&vga[b * surface->pitch + a], e, c - a + 1); + + /* Bottom line */ + memset(&vga[d * surface->pitch + a], e, c - a + 1); + + /* Left line */ + for (i = (b + 1) * surface->pitch + a; i < (d * surface->pitch + a); i += surface->pitch) + { + vga[i] = e; + } + + /* Right line */ + for (i = (b + 1) * surface->pitch + c; i < (d * surface->pitch + c); i += surface->pitch) + { + vga[i] = e; + } + } else { + printf("!!! WARNING: Rectangle clipped: %d %d %d %d %d\n", a, b, c, d, e); + } +} + +void fill_rectangle_xy( SDL_Surface *surface, int x, int y, int x2, int y2, Uint8 color ) +{ + SDL_Rect rect = { x, y, x2 - x + 1, y2 - y + 1 }; + SDL_FillRect(surface, &rect, color); +} + +void JE_barShade( SDL_Surface *surface, int a, int b, int c, int d ) /* x1, y1, x2, y2 */ +{ + if (a < surface->pitch && b < surface->h && + c < surface->pitch && d < surface->h) + { + Uint8 *vga = surface->pixels; + int i, j, width; + + width = c - a + 1; + + for (i = b * surface->pitch + a; i <= d * surface->pitch + a; i += surface->pitch) + { + for (j = 0; j < width; j++) + { + vga[i + j] = ((vga[i + j] & 0x0F) >> 1) | (vga[i + j] & 0xF0); + } + } + } else { + printf("!!! WARNING: Darker Rectangle clipped: %d %d %d %d\n", a,b,c,d); + } +} + +void JE_barBright( SDL_Surface *surface, int a, int b, int c, int d ) /* x1, y1, x2, y2 */ +{ + if (a < surface->pitch && b < surface->h && + c < surface->pitch && d < surface->h) + { + Uint8 *vga = surface->pixels; + int i, j, width; + + width = c-a+1; + + for (i = b * surface->pitch + a; i <= d * surface->pitch + a; i += surface->pitch) + { + for (j = 0; j < width; j++) + { + JE_byte al, ah; + al = ah = vga[i + j]; + + ah &= 0xF0; + al = (al & 0x0F) + 2; + + if (al > 0x0F) + { + al = 0x0F; + } + + vga[i + j] = al + ah; + } + } + } else { + printf("!!! WARNING: Brighter Rectangle clipped: %d %d %d %d\n", a,b,c,d); + } +} + +void draw_segmented_gauge( SDL_Surface *surface, int x, int y, Uint8 color, uint segment_width, uint segment_height, uint segment_value, uint value ) +{ + assert(segment_width > 0 && segment_height > 0); + + const uint segments = value / segment_value, + partial_segment = value % segment_value; + + for (uint i = 0; i < segments; ++i) + { + fill_rectangle_hw(surface, x, y, segment_width, segment_height, color + 12); + x += segment_width + 1; + } + if (partial_segment > 0) + fill_rectangle_hw(surface, x, y, segment_width, segment_height, color + (12 * partial_segment / segment_value)); +} + diff --git a/contrib/games/opentyrian/src/vga256d.h b/contrib/games/opentyrian/src/vga256d.h new file mode 100644 index 000000000..71e446eb8 --- /dev/null +++ b/contrib/games/opentyrian/src/vga256d.h @@ -0,0 +1,44 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef VGA256D_H +#define VGA256D_H + +#include "opentyr.h" + +#include "SDL.h" + +void JE_pix( SDL_Surface *surface, int x, int y, JE_byte c ); +void JE_pix3( SDL_Surface *surface, int x, int y, JE_byte c ); +void JE_rectangle( SDL_Surface *surface, int a, int b, int c, int d, int e ); + +void fill_rectangle_xy( SDL_Surface *, int x, int y, int x2, int y2, Uint8 color ); + +void JE_barShade( SDL_Surface *surface, int a, int b, int c, int d ); +void JE_barBright( SDL_Surface *surface, int a, int b, int c, int d ); + +static inline void fill_rectangle_hw( SDL_Surface *surface, int x, int y, uint h, uint w, Uint8 color ) +{ + SDL_Rect rect = { x, y, h, w }; + SDL_FillRect(surface, &rect, color); +} + +void draw_segmented_gauge( SDL_Surface *surface, int x, int y, Uint8 color, uint segment_width, uint segment_height, uint segment_value, uint value ); + +#endif /* VGA256D_H */ + diff --git a/contrib/games/opentyrian/src/vga_palette.c b/contrib/games/opentyrian/src/vga_palette.c new file mode 100644 index 000000000..ce3cf6184 --- /dev/null +++ b/contrib/games/opentyrian/src/vga_palette.c @@ -0,0 +1,71 @@ +#include "vga_palette.h" + +// only used for the jukebox + +Palette vga_palette = +{ + { 0, 0, 0}, { 0, 0, 168}, { 0, 168, 0}, { 0, 168, 168}, + {168, 0, 0}, {168, 0, 168}, {168, 84, 0}, {168, 168, 168}, + { 84, 84, 84}, { 84, 84, 252}, { 84, 252, 84}, { 84, 252, 252}, + {252, 84, 84}, {252, 84, 252}, {252, 252, 84}, {252, 252, 252}, + { 0, 0, 0}, { 20, 20, 20}, { 32, 32, 32}, { 44, 44, 44}, + { 56, 56, 56}, { 68, 68, 68}, { 80, 80, 80}, { 96, 96, 96}, + {112, 112, 112}, {128, 128, 128}, {144, 144, 144}, {160, 160, 160}, + {180, 180, 180}, {200, 200, 200}, {224, 224, 224}, {252, 252, 252}, + { 0, 0, 252}, { 64, 0, 252}, {124, 0, 252}, {188, 0, 252}, + {252, 0, 252}, {252, 0, 188}, {252, 0, 124}, {252, 0, 64}, + {252, 0, 0}, {252, 64, 0}, {252, 124, 0}, {252, 188, 0}, + {252, 252, 0}, {188, 252, 0}, {124, 252, 0}, { 64, 252, 0}, + { 0, 252, 0}, { 0, 252, 64}, { 0, 252, 124}, { 0, 252, 188}, + { 0, 252, 252}, { 0, 188, 252}, { 0, 124, 252}, { 0, 64, 252}, + {124, 124, 252}, {156, 124, 252}, {188, 124, 252}, {220, 124, 252}, + {252, 124, 252}, {252, 124, 220}, {252, 124, 188}, {252, 124, 156}, + {252, 124, 124}, {252, 156, 124}, {252, 188, 124}, {252, 220, 124}, + {252, 252, 124}, {220, 252, 124}, {188, 252, 124}, {156, 252, 124}, + {124, 252, 124}, {124, 252, 156}, {124, 252, 188}, {124, 252, 220}, + {124, 252, 252}, {124, 220, 252}, {124, 188, 252}, {124, 156, 252}, + {180, 180, 252}, {196, 180, 252}, {216, 180, 252}, {232, 180, 252}, + {252, 180, 252}, {252, 180, 232}, {252, 180, 216}, {252, 180, 196}, + {252, 180, 180}, {252, 196, 180}, {252, 216, 180}, {252, 232, 180}, + {252, 252, 180}, {232, 252, 180}, {216, 252, 180}, {196, 252, 180}, + {180, 252, 180}, {180, 252, 196}, {180, 252, 216}, {180, 252, 232}, + {180, 252, 252}, {180, 232, 252}, {180, 216, 252}, {180, 196, 252}, + { 0, 0, 112}, { 28, 0, 112}, { 56, 0, 112}, { 84, 0, 112}, + {112, 0, 112}, {112, 0, 84}, {112, 0, 56}, {112, 0, 28}, + {112, 0, 0}, {112, 28, 0}, {112, 56, 0}, {112, 84, 0}, + {112, 112, 0}, { 84, 112, 0}, { 56, 112, 0}, { 28, 112, 0}, + { 0, 112, 0}, { 0, 112, 28}, { 0, 112, 56}, { 0, 112, 84}, + { 0, 112, 112}, { 0, 84, 112}, { 0, 56, 112}, { 0, 28, 112}, + { 56, 56, 112}, { 68, 56, 112}, { 84, 56, 112}, { 96, 56, 112}, + {112, 56, 112}, {112, 56, 96}, {112, 56, 84}, {112, 56, 68}, + {112, 56, 56}, {112, 68, 56}, {112, 84, 56}, {112, 96, 56}, + {112, 112, 56}, { 96, 112, 56}, { 84, 112, 56}, { 68, 112, 56}, + { 56, 112, 56}, { 56, 112, 68}, { 56, 112, 84}, { 56, 112, 96}, + { 56, 112, 112}, { 56, 96, 112}, { 56, 84, 112}, { 56, 68, 112}, + { 80, 80, 112}, { 88, 80, 112}, { 96, 80, 112}, {104, 80, 112}, + {112, 80, 112}, {112, 80, 104}, {112, 80, 96}, {112, 80, 88}, + {112, 80, 80}, {112, 88, 80}, {112, 96, 80}, {112, 104, 80}, + {112, 112, 80}, {104, 112, 80}, { 96, 112, 80}, { 88, 112, 80}, + { 80, 112, 80}, { 80, 112, 88}, { 80, 112, 96}, { 80, 112, 104}, + { 80, 112, 112}, { 80, 104, 112}, { 80, 96, 112}, { 80, 88, 112}, + { 0, 0, 64}, { 16, 0, 64}, { 32, 0, 64}, { 48, 0, 64}, + { 64, 0, 64}, { 64, 0, 48}, { 64, 0, 32}, { 64, 0, 16}, + { 64, 0, 0}, { 64, 16, 0}, { 64, 32, 0}, { 64, 48, 0}, + { 64, 64, 0}, { 48, 64, 0}, { 32, 64, 0}, { 16, 64, 0}, + { 0, 64, 0}, { 0, 64, 16}, { 0, 64, 32}, { 0, 64, 48}, + { 0, 64, 64}, { 0, 48, 64}, { 0, 32, 64}, { 0, 16, 64}, + { 32, 32, 64}, { 40, 32, 64}, { 48, 32, 64}, { 56, 32, 64}, + { 64, 32, 64}, { 64, 32, 56}, { 64, 32, 48}, { 64, 32, 40}, + { 64, 32, 32}, { 64, 40, 32}, { 64, 48, 32}, { 64, 56, 32}, + { 64, 64, 32}, { 56, 64, 32}, { 48, 64, 32}, { 40, 64, 32}, + { 32, 64, 32}, { 32, 64, 40}, { 32, 64, 48}, { 32, 64, 56}, + { 32, 64, 64}, { 32, 56, 64}, { 32, 48, 64}, { 32, 40, 64}, + { 44, 44, 64}, { 48, 44, 64}, { 52, 44, 64}, { 60, 44, 64}, + { 64, 44, 64}, { 64, 44, 60}, { 64, 44, 52}, { 64, 44, 48}, + { 64, 44, 44}, { 64, 48, 44}, { 64, 52, 44}, { 64, 60, 44}, + { 64, 64, 44}, { 60, 64, 44}, { 52, 64, 44}, { 48, 64, 44}, + { 44, 64, 44}, { 44, 64, 48}, { 44, 64, 52}, { 44, 64, 60}, + { 44, 64, 64}, { 44, 60, 64}, { 44, 52, 64}, { 44, 48, 64}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, + { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0}, { 0, 0, 0} +}; diff --git a/contrib/games/opentyrian/src/vga_palette.h b/contrib/games/opentyrian/src/vga_palette.h new file mode 100644 index 000000000..19f28e3e9 --- /dev/null +++ b/contrib/games/opentyrian/src/vga_palette.h @@ -0,0 +1,8 @@ +#ifndef VGA_PALETTE_H +#define VGA_PALETTE_H + +#include "palette.h" + +extern Palette vga_palette; + +#endif // VGA_PALETTE_H diff --git a/contrib/games/opentyrian/src/video.c b/contrib/games/opentyrian/src/video.c new file mode 100644 index 000000000..b97c52b2d --- /dev/null +++ b/contrib/games/opentyrian/src/video.c @@ -0,0 +1,204 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "video.h" + +#include "keyboard.h" +#include "opentyr.h" +#include "palette.h" +#include "video_scale.h" + +#include +#include +#include +#include + +bool fullscreen_enabled = false; + +SDL_Surface *VGAScreen, *VGAScreenSeg; +SDL_Surface *VGAScreen2; +SDL_Surface *game_screen; + +static ScalerFunction scaler_function; + +void init_video( void ) +{ + SDL_ShowCursor(0); + if (SDL_WasInit(SDL_INIT_VIDEO)) + return; + + if (SDL_InitSubSystem(SDL_INIT_VIDEO) == -1) + { + fprintf(stderr, "error: failed to initialize SDL video: %s\n", SDL_GetError()); + exit(1); + } + + SDL_WM_SetCaption("OpenTyrian", NULL); + + VGAScreen = VGAScreenSeg = SDL_CreateRGBSurface(SDL_SWSURFACE, vga_width, vga_height, 8, 0, 0, 0, 0); + VGAScreen2 = SDL_CreateRGBSurface(SDL_SWSURFACE, vga_width, vga_height, 8, 0, 0, 0, 0); + game_screen = SDL_CreateRGBSurface(SDL_SWSURFACE, vga_width, vga_height, 8, 0, 0, 0, 0); + + SDL_FillRect(VGAScreen, NULL, 0); + if (!init_scaler(scaler, fullscreen_enabled) && // try desired scaler and desired fullscreen state + !init_any_scaler(fullscreen_enabled) && // try any scaler in desired fullscreen state + !init_any_scaler(!fullscreen_enabled)) // try any scaler in other fullscreen state + { + fprintf(stderr, "error: failed to initialize any supported video mode\n"); + exit(EXIT_FAILURE); + } +} + +int can_init_scaler( unsigned int new_scaler, bool fullscreen ) +{ + if (new_scaler >= scalers_count) + return false; + + int w = scalers[new_scaler].width, + h = scalers[new_scaler].height; + int flags = SDL_SWSURFACE | SDL_HWPALETTE | (fullscreen ? SDL_FULLSCREEN : 0); + + // test each bitdepth + for (uint bpp = 32; bpp > 0; bpp -= 8) + { + uint temp_bpp = SDL_VideoModeOK(w, h, bpp, flags); + + if ((temp_bpp == 32 && scalers[new_scaler].scaler32) || + (temp_bpp == 16 && scalers[new_scaler].scaler16) || + (temp_bpp == 8 && scalers[new_scaler].scaler8 )) + { + return temp_bpp; + } + else if (temp_bpp == 24 && scalers[new_scaler].scaler32) + { + // scalers don't support 24 bpp because it's a pain + // so let SDL handle the conversion + return 32; + } + } + + return 0; +} + +bool init_scaler( unsigned int new_scaler, bool fullscreen ) +{ + int w = scalers[new_scaler].width, + h = scalers[new_scaler].height; + int bpp = can_init_scaler(new_scaler, fullscreen); + int flags = SDL_SWSURFACE | SDL_HWPALETTE | (fullscreen ? SDL_FULLSCREEN : 0); + + if (bpp == 0) + return false; + #ifdef _KOLIBRI + bpp=16; // DEBUG +#endif + SDL_Surface *const surface = SDL_SetVideoMode(w, h, bpp, flags); + + if (surface == NULL) + { + fprintf(stderr, "error: failed to initialize %s video mode %dx%dx%d: %s\n", fullscreen ? "fullscreen" : "windowed", w, h, bpp, SDL_GetError()); + return false; + } + + w = surface->w; + h = surface->h; + +#ifdef _KOLIBRI + uSDL_SetWinCenter(w,h); +#endif + //bpp = surface->format->BitsPerPixel; + + printf("initialized video: %dx%dx%d %s\n", w, h, bpp, fullscreen ? "fullscreen" : "windowed"); + + scaler = new_scaler; + fullscreen_enabled = fullscreen; + + switch (bpp) + { + case 32: + scaler_function = scalers[scaler].scaler32; + break; + case 16: + scaler_function = scalers[scaler].scaler16; + break; + case 8: + scaler_function = scalers[scaler].scaler8; + break; + default: + scaler_function = NULL; + break; + } + + if (scaler_function == NULL) + { + assert(false); + return false; + } + + input_grab(input_grab_enabled); + JE_showVGA(); + + return true; +} + +bool can_init_any_scaler( bool fullscreen ) +{ + for (int i = scalers_count - 1; i >= 0; --i) + if (can_init_scaler(i, fullscreen) != 0) + return true; + + return false; +} + +bool init_any_scaler( bool fullscreen ) +{ + // attempts all scalers from last to first + for (int i = scalers_count - 1; i >= 0; --i) + if (init_scaler(i, fullscreen)) + return true; + + return false; +} + +void deinit_video( void ) +{ + SDL_FreeSurface(VGAScreenSeg); + SDL_FreeSurface(VGAScreen2); + SDL_FreeSurface(game_screen); + + SDL_QuitSubSystem(SDL_INIT_VIDEO); +} + +void JE_clr256( SDL_Surface * screen) +{ + memset(screen->pixels, 0, screen->pitch * screen->h); +} +void JE_showVGA( void ) { scale_and_flip(VGAScreen); } + +void scale_and_flip( SDL_Surface *src_surface ) +{ + assert(src_surface->format->BitsPerPixel == 8); + + SDL_Surface *dst_surface = SDL_GetVideoSurface(); + + assert(scaler_function != NULL); + scaler_function(src_surface, dst_surface); + + SDL_Flip(dst_surface); +} + diff --git a/contrib/games/opentyrian/src/video.h b/contrib/games/opentyrian/src/video.h new file mode 100644 index 000000000..7405084aa --- /dev/null +++ b/contrib/games/opentyrian/src/video.h @@ -0,0 +1,49 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef VIDEO_H +#define VIDEO_H + +#include "opentyr.h" + +#include "SDL.h" + +#define vga_width 320 +#define vga_height 200 + +extern bool fullscreen_enabled; + +extern SDL_Surface *VGAScreen, *VGAScreenSeg; +extern SDL_Surface *game_screen; +extern SDL_Surface *VGAScreen2; + +void init_video( void ); + +int can_init_scaler( unsigned int new_scaler, bool fullscreen ); +bool init_scaler( unsigned int new_scaler, bool fullscreen ); +bool can_init_any_scaler( bool fullscreen ); +bool init_any_scaler( bool fullscreen ); + +void deinit_video( void ); + +void JE_clr256( SDL_Surface * ); +void JE_showVGA( void ); +void scale_and_flip( SDL_Surface * ); + +#endif /* VIDEO_H */ + diff --git a/contrib/games/opentyrian/src/video_scale.c b/contrib/games/opentyrian/src/video_scale.c new file mode 100644 index 000000000..c537a1e1f --- /dev/null +++ b/contrib/games/opentyrian/src/video_scale.c @@ -0,0 +1,437 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2010 The OpenTyrian Development Team + * + * Scale2x, Scale3x + * Copyright (C) 2001, 2002, 2003, 2004 Andrea Mazzoleni + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "video_scale.h" + +#include "palette.h" +#include "video.h" + +#include + +static void no_scale( SDL_Surface *src_surface, SDL_Surface *dst_surface ); +static void nn_32( SDL_Surface *src_surface, SDL_Surface *dst_surface ); +static void nn_16( SDL_Surface *src_surface, SDL_Surface *dst_surface ); + +static void scale2x_32( SDL_Surface *src_surface, SDL_Surface *dst_surface ); +static void scale2x_16( SDL_Surface *src_surface, SDL_Surface *dst_surface ); +static void scale3x_32( SDL_Surface *src_surface, SDL_Surface *dst_surface ); +static void scale3x_16( SDL_Surface *src_surface, SDL_Surface *dst_surface ); + +void hq2x_32( SDL_Surface *src_surface, SDL_Surface *dst_surface ); +void hq3x_32( SDL_Surface *src_surface, SDL_Surface *dst_surface ); +void hq4x_32( SDL_Surface *src_surface, SDL_Surface *dst_surface ); + +uint scaler; + +const struct Scalers scalers[] = +{ +#if defined(TARGET_GP2X) || defined(TARGET_DINGUX) + { 320, 240, no_scale, nn_16, nn_32, "None" }, +#else + { 1 * vga_width, 1 * vga_height, no_scale, nn_16, nn_32, "None" }, + { 2 * vga_width, 2 * vga_height, NULL, nn_16, nn_32, "2x" }, + { 2 * vga_width, 2 * vga_height, NULL, scale2x_16, scale2x_32, "Scale2x" }, + { 2 * vga_width, 2 * vga_height, NULL, NULL, hq2x_32, "hq2x" }, + { 3 * vga_width, 3 * vga_height, NULL, nn_16, nn_32, "3x" }, + { 3 * vga_width, 3 * vga_height, NULL, scale3x_16, scale3x_32, "Scale3x" }, + { 3 * vga_width, 3 * vga_height, NULL, NULL, hq3x_32, "hq3x" }, + { 4 * vga_width, 4 * vga_height, NULL, nn_16, nn_32, "4x" }, + { 4 * vga_width, 4 * vga_height, NULL, NULL, hq4x_32, "hq4x" }, +#endif +}; +const uint scalers_count = COUNTOF(scalers); + +void set_scaler_by_name( const char *name ) +{ + for (uint i = 0; i < scalers_count; ++i) + { + if (strcmp(name, scalers[i].name) == 0) + { + scaler = i; + break; + } + } +} + +#if defined(TARGET_GP2X) || defined(TARGET_DINGUX) +#define VGA_CENTERED +#endif + +void no_scale( SDL_Surface *src_surface, SDL_Surface *dst_surface ) +{ + Uint8 *src = src_surface->pixels, + *dst = dst_surface->pixels; + +#ifdef VGA_CENTERED + size_t blank = (dst_surface->h - src_surface->h) / 2 * dst_surface->pitch; + memset(dst, 0, blank); + dst += blank; +#endif + + memcpy(dst, src, src_surface->pitch * src_surface->h); + +#ifdef VGA_CENTERED + dst += src_surface->pitch * src_surface->h; + memset(dst, 0, blank); +#endif +} + + +void nn_32( SDL_Surface *src_surface, SDL_Surface *dst_surface ) +{ + Uint8 *src = src_surface->pixels, *src_temp, + *dst = dst_surface->pixels, *dst_temp; + int src_pitch = src_surface->pitch, + dst_pitch = dst_surface->pitch; + const int dst_Bpp = 4; // dst_surface->format->BytesPerPixel + + const int height = vga_height, // src_surface->h + width = vga_width, // src_surface->w + scale = dst_surface->w / width; + assert(scale == dst_surface->h / height); + +#ifdef VGA_CENTERED + size_t blank = (dst_surface->h - src_surface->h) / 2 * dst_surface->pitch; + memset(dst, 0, blank); + dst += blank; +#endif + + for (int y = height; y > 0; y--) + { + src_temp = src; + dst_temp = dst; + + for (int x = width; x > 0; x--) + { + for (int z = scale; z > 0; z--) + { + *(Uint32 *)dst = rgb_palette[*src]; + dst += dst_Bpp; + } + src++; + } + + src = src_temp + src_pitch; + dst = dst_temp + dst_pitch; + + for (int z = scale; z > 1; z--) + { + memcpy(dst, dst_temp, dst_pitch); + dst += dst_pitch; + } + } + +#ifdef VGA_CENTERED + memset(dst, 0, blank); +#endif +} + +void nn_16( SDL_Surface *src_surface, SDL_Surface *dst_surface ) +{ + Uint8 *src = src_surface->pixels, *src_temp, + *dst = dst_surface->pixels, *dst_temp; + int src_pitch = src_surface->pitch, + dst_pitch = dst_surface->pitch; + const int dst_Bpp = 2; // dst_surface->format->BytesPerPixel + + const int height = vga_height, // src_surface->h + width = vga_width, // src_surface->w + scale = dst_surface->w / width; + assert(scale == dst_surface->h / height); + +#ifdef VGA_CENTERED + size_t blank = (dst_surface->h - src_surface->h) / 2 * dst_surface->pitch; + memset(dst, 0, blank); + dst += blank; +#endif + + for (int y = height; y > 0; y--) + { + src_temp = src; + dst_temp = dst; + + for (int x = width; x > 0; x--) + { + for (int z = scale; z > 0; z--) + { + *(Uint16 *)dst = rgb_palette[*src]; + dst += dst_Bpp; + } + src++; + } + + src = src_temp + src_pitch; + dst = dst_temp + dst_pitch; + + for (int z = scale; z > 1; z--) + { + memcpy(dst, dst_temp, dst_pitch); + dst += dst_pitch; + } + } + +#ifdef VGA_CENTERED + memset(dst, 0, blank); +#endif +} + + +void scale2x_32( SDL_Surface *src_surface, SDL_Surface *dst_surface ) +{ + Uint8 *src = src_surface->pixels, *src_temp, + *dst = dst_surface->pixels, *dst_temp; + int src_pitch = src_surface->pitch, + dst_pitch = dst_surface->pitch; + const int dst_Bpp = 4; // dst_surface->format->BytesPerPixel + + const int height = vga_height, // src_surface->h + width = vga_width; // src_surface->w + + int prevline, nextline; + + Uint32 E0, E1, E2, E3, B, D, E, F, H; + for (int y = 0; y < height; y++) + { + src_temp = src; + dst_temp = dst; + + prevline = (y > 0) ? -src_pitch : 0; + nextline = (y < height - 1) ? src_pitch : 0; + + for (int x = 0; x < width; x++) + { + B = rgb_palette[*(src + prevline)]; + D = rgb_palette[*(x > 0 ? src - 1 : src)]; + E = rgb_palette[*src]; + F = rgb_palette[*(x < width - 1 ? src + 1 : src)]; + H = rgb_palette[*(src + nextline)]; + + if (B != H && D != F) { + E0 = D == B ? D : E; + E1 = B == F ? F : E; + E2 = D == H ? D : E; + E3 = H == F ? F : E; + } else { + E0 = E1 = E2 = E3 = E; + } + + *(Uint32 *)dst = E0; + *(Uint32 *)(dst + dst_Bpp) = E1; + *(Uint32 *)(dst + dst_pitch) = E2; + *(Uint32 *)(dst + dst_pitch + dst_Bpp) = E3; + + src++; + dst += 2 * dst_Bpp; + } + + src = src_temp + src_pitch; + dst = dst_temp + 2 * dst_pitch; + } +} + +void scale2x_16( SDL_Surface *src_surface, SDL_Surface *dst_surface ) +{ + Uint8 *src = src_surface->pixels, *src_temp, + *dst = dst_surface->pixels, *dst_temp; + int src_pitch = src_surface->pitch, + dst_pitch = dst_surface->pitch; + const int dst_Bpp = 2; // dst_surface->format->BytesPerPixel + + const int height = vga_height, // src_surface->h + width = vga_width; // src_surface->w + + int prevline, nextline; + + Uint16 E0, E1, E2, E3, B, D, E, F, H; + for (int y = 0; y < height; y++) + { + src_temp = src; + dst_temp = dst; + + prevline = (y > 0) ? -src_pitch : 0; + nextline = (y < height - 1) ? src_pitch : 0; + + for (int x = 0; x < width; x++) + { + B = rgb_palette[*(src + prevline)]; + D = rgb_palette[*(x > 0 ? src - 1 : src)]; + E = rgb_palette[*src]; + F = rgb_palette[*(x < width - 1 ? src + 1 : src)]; + H = rgb_palette[*(src + nextline)]; + + if (B != H && D != F) { + E0 = D == B ? D : E; + E1 = B == F ? F : E; + E2 = D == H ? D : E; + E3 = H == F ? F : E; + } else { + E0 = E1 = E2 = E3 = E; + } + + *(Uint16 *)dst = E0; + *(Uint16 *)(dst + dst_Bpp) = E1; + *(Uint16 *)(dst + dst_pitch) = E2; + *(Uint16 *)(dst + dst_pitch + dst_Bpp) = E3; + + src++; + dst += 2 * dst_Bpp; + } + + src = src_temp + src_pitch; + dst = dst_temp + 2 * dst_pitch; + } +} + + +void scale3x_32( SDL_Surface *src_surface, SDL_Surface *dst_surface ) +{ + Uint8 *src = src_surface->pixels, *src_temp, + *dst = dst_surface->pixels, *dst_temp; + int src_pitch = src_surface->pitch, + dst_pitch = dst_surface->pitch; + const int dst_Bpp = 4; // dst_surface->format->BytesPerPixel + + const int height = vga_height, // src_surface->h + width = vga_width; // src_surface->w + + int prevline, nextline; + + Uint32 E0, E1, E2, E3, E4, E5, E6, E7, E8, A, B, C, D, E, F, G, H, I; + for (int y = 0; y < height; y++) + { + src_temp = src; + dst_temp = dst; + + prevline = (y > 0) ? -src_pitch : 0; + nextline = (y < height - 1) ? src_pitch : 0; + + for (int x = 0; x < width; x++) + { + A = rgb_palette[*(src + prevline - (x > 0 ? 1 : 0))]; + B = rgb_palette[*(src + prevline)]; + C = rgb_palette[*(src + prevline + (x < width - 1 ? 1 : 0))]; + D = rgb_palette[*(src - (x > 0 ? 1 : 0))]; + E = rgb_palette[*src]; + F = rgb_palette[*(src + (x < width - 1 ? 1 : 0))]; + G = rgb_palette[*(src + nextline - (x > 0 ? 1 : 0))]; + H = rgb_palette[*(src + nextline)]; + I = rgb_palette[*(src + nextline + (x < width - 1 ? 1 : 0))]; + + if (B != H && D != F) { + E0 = D == B ? D : E; + E1 = (D == B && E != C) || (B == F && E != A) ? B : E; + E2 = B == F ? F : E; + E3 = (D == B && E != G) || (D == H && E != A) ? D : E; + E4 = E; + E5 = (B == F && E != I) || (H == F && E != C) ? F : E; + E6 = D == H ? D : E; + E7 = (D == H && E != I) || (H == F && E != G) ? H : E; + E8 = H == F ? F : E; + } else { + E0 = E1 = E2 = E3 = E4 = E5 = E6 = E7 = E8 = E; + } + + *(Uint32 *)dst = E0; + *(Uint32 *)(dst + dst_Bpp) = E1; + *(Uint32 *)(dst + 2 * dst_Bpp) = E2; + *(Uint32 *)(dst + dst_pitch) = E3; + *(Uint32 *)(dst + dst_pitch + dst_Bpp) = E4; + *(Uint32 *)(dst + dst_pitch + 2 * dst_Bpp) = E5; + *(Uint32 *)(dst + 2 * dst_pitch) = E6; + *(Uint32 *)(dst + 2 * dst_pitch + dst_Bpp) = E7; + *(Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp) = E8; + + src++; + dst += 3 * dst_Bpp; + } + + src = src_temp + src_pitch; + dst = dst_temp + 3 * dst_pitch; + } +} + +void scale3x_16( SDL_Surface *src_surface, SDL_Surface *dst_surface ) +{ + Uint8 *src = src_surface->pixels, *src_temp, + *dst = dst_surface->pixels, *dst_temp; + int src_pitch = src_surface->pitch, + dst_pitch = dst_surface->pitch; + const int dst_Bpp = 2; // dst_surface->format->BytesPerPixel + + const int height = vga_height, // src_surface->h + width = vga_width; // src_surface->w + + int prevline, nextline; + + Uint16 E0, E1, E2, E3, E4, E5, E6, E7, E8, A, B, C, D, E, F, G, H, I; + for (int y = 0; y < height; y++) + { + src_temp = src; + dst_temp = dst; + + prevline = (y > 0) ? -src_pitch : 0; + nextline = (y < height - 1) ? src_pitch : 0; + + for (int x = 0; x < width; x++) + { + A = rgb_palette[*(src + prevline - (x > 0 ? 1 : 0))]; + B = rgb_palette[*(src + prevline)]; + C = rgb_palette[*(src + prevline + (x < width - 1 ? 1 : 0))]; + D = rgb_palette[*(src - (x > 0 ? 1 : 0))]; + E = rgb_palette[*src]; + F = rgb_palette[*(src + (x < width - 1 ? 1 : 0))]; + G = rgb_palette[*(src + nextline - (x > 0 ? 1 : 0))]; + H = rgb_palette[*(src + nextline)]; + I = rgb_palette[*(src + nextline + (x < width - 1 ? 1 : 0))]; + + if (B != H && D != F) { + E0 = D == B ? D : E; + E1 = (D == B && E != C) || (B == F && E != A) ? B : E; + E2 = B == F ? F : E; + E3 = (D == B && E != G) || (D == H && E != A) ? D : E; + E4 = E; + E5 = (B == F && E != I) || (H == F && E != C) ? F : E; + E6 = D == H ? D : E; + E7 = (D == H && E != I) || (H == F && E != G) ? H : E; + E8 = H == F ? F : E; + } else { + E0 = E1 = E2 = E3 = E4 = E5 = E6 = E7 = E8 = E; + } + + *(Uint16 *)dst = E0; + *(Uint16 *)(dst + dst_Bpp) = E1; + *(Uint16 *)(dst + 2 * dst_Bpp) = E2; + *(Uint16 *)(dst + dst_pitch) = E3; + *(Uint16 *)(dst + dst_pitch + dst_Bpp) = E4; + *(Uint16 *)(dst + dst_pitch + 2 * dst_Bpp) = E5; + *(Uint16 *)(dst + 2 * dst_pitch) = E6; + *(Uint16 *)(dst + 2 * dst_pitch + dst_Bpp) = E7; + *(Uint16 *)(dst + 2 * dst_pitch + 2 * dst_Bpp) = E8; + + src++; + dst += 3 * dst_Bpp; + } + + src = src_temp + src_pitch; + dst = dst_temp + 3 * dst_pitch; + } +} + diff --git a/contrib/games/opentyrian/src/video_scale.h b/contrib/games/opentyrian/src/video_scale.h new file mode 100644 index 000000000..19d4891eb --- /dev/null +++ b/contrib/games/opentyrian/src/video_scale.h @@ -0,0 +1,42 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2010 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef VIDEO_SCALE_H +#define VIDEO_SCALE_H + +#include "opentyr.h" + +#include "SDL.h" + +typedef void (*ScalerFunction)( SDL_Surface *dst, SDL_Surface *src ); + +struct Scalers +{ + int width, height; + ScalerFunction scaler8, scaler16, scaler32; + const char *name; +}; + +extern uint scaler; +extern const struct Scalers scalers[]; +extern const uint scalers_count; + +void set_scaler_by_name( const char *name ); + +#endif /* VIDEO_SCALE_H */ + diff --git a/contrib/games/opentyrian/src/video_scale_hqNx.c b/contrib/games/opentyrian/src/video_scale_hqNx.c new file mode 100644 index 000000000..2e4741fec --- /dev/null +++ b/contrib/games/opentyrian/src/video_scale_hqNx.c @@ -0,0 +1,11895 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2010 The OpenTyrian Development Team + * + * hq2x, hq3x, hq4x + * Copyright (C) 2003 MaxSt ( maxst@hiend3d.com ) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "palette.h" +#include "video.h" + +void interp1(Uint32 *pc, Uint32 c1, Uint32 c2); +void interp2(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3); +void interp3(Uint32 *pc, Uint32 c1, Uint32 c2); +void interp4(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3); +void interp5(Uint32 *pc, Uint32 c1, Uint32 c2); +void interp6(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3); +void interp7(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3); +void interp8(Uint32 *pc, Uint32 c1, Uint32 c2); +void interp9(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3); +void interp10(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3); +bool diff(unsigned int w1, unsigned int w2); + +void hq2x_32( SDL_Surface *src_surface, SDL_Surface *dst_surface ); +void hq3x_32( SDL_Surface *src_surface, SDL_Surface *dst_surface ); +void hq4x_32( SDL_Surface *src_surface, SDL_Surface *dst_surface ); + +static int YUV1, YUV2; +const int Ymask = 0x00FF0000; +const int Umask = 0x0000FF00; +const int Vmask = 0x000000FF; +const int trY = 0x00300000; +const int trU = 0x00000700; +const int trV = 0x00000006; + +inline void interp1(Uint32 *pc, Uint32 c1, Uint32 c2) +{ + *pc = (c1*3+c2) >> 2; +} + +inline void interp2(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3) +{ + *pc = (c1*2+c2+c3) >> 2; +} + +inline void interp3(Uint32 *pc, Uint32 c1, Uint32 c2) +{ + //*((int*)pc) = (c1*7+c2)/8; + + *((int*)pc) = ((((c1 & 0x00FF00)*7 + (c2 & 0x00FF00) ) & 0x0007F800) + + (((c1 & 0xFF00FF)*7 + (c2 & 0xFF00FF) ) & 0x07F807F8)) >> 3; +} + +inline void interp4(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3) +{ + //*((int*)pc) = (c1*2+(c2+c3)*7)/16; + + *((int*)pc) = ((((c1 & 0x00FF00)*2 + ((c2 & 0x00FF00) + (c3 & 0x00FF00))*7 ) & 0x000FF000) + + (((c1 & 0xFF00FF)*2 + ((c2 & 0xFF00FF) + (c3 & 0xFF00FF))*7 ) & 0x0FF00FF0)) >> 4; +} + +inline void interp5(Uint32 *pc, Uint32 c1, Uint32 c2) +{ + *pc = (c1+c2) >> 1; +} + +inline void interp6(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3) +{ + //*pc = (c1*5+c2*2+c3)/8; + + *pc = ((((c1 & 0x00FF00)*5 + (c2 & 0x00FF00)*2 + (c3 & 0x00FF00) ) & 0x0007F800) + + (((c1 & 0xFF00FF)*5 + (c2 & 0xFF00FF)*2 + (c3 & 0xFF00FF) ) & 0x07F807F8)) >> 3; +} + +inline void interp7(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3) +{ + //*pc = (c1*6+c2+c3)/8; + + *pc = ((((c1 & 0x00FF00)*6 + (c2 & 0x00FF00) + (c3 & 0x00FF00) ) & 0x0007F800) + + (((c1 & 0xFF00FF)*6 + (c2 & 0xFF00FF) + (c3 & 0xFF00FF) ) & 0x07F807F8)) >> 3; +} + +inline void interp8(Uint32 *pc, Uint32 c1, Uint32 c2) +{ + //*pc = (c1*5+c2*3)/8; + + *pc = ((((c1 & 0x00FF00)*5 + (c2 & 0x00FF00)*3 ) & 0x0007F800) + + (((c1 & 0xFF00FF)*5 + (c2 & 0xFF00FF)*3 ) & 0x07F807F8)) >> 3; +} + +inline void interp9(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3) +{ + //*pc = (c1*2+(c2+c3)*3)/8; + + *pc = ((((c1 & 0x00FF00)*2 + ((c2 & 0x00FF00) + (c3 & 0x00FF00))*3 ) & 0x0007F800) + + (((c1 & 0xFF00FF)*2 + ((c2 & 0xFF00FF) + (c3 & 0xFF00FF))*3 ) & 0x07F807F8)) >> 3; +} + +inline void interp10(Uint32 *pc, Uint32 c1, Uint32 c2, Uint32 c3) +{ + //*pc = (c1*14+c2+c3)/16; + + *pc = ((((c1 & 0x00FF00)*14 + (c2 & 0x00FF00) + (c3 & 0x00FF00) ) & 0x000FF000) + + (((c1 & 0xFF00FF)*14 + (c2 & 0xFF00FF) + (c3 & 0xFF00FF) ) & 0x0FF00FF0)) >> 4; +} + +inline bool diff(unsigned int w1, unsigned int w2) +{ + Uint32 YUV1 = yuv_palette[w1]; + Uint32 YUV2 = yuv_palette[w2]; + return ( ( abs((YUV1 & Ymask) - (YUV2 & Ymask)) > trY ) || + ( abs((YUV1 & Umask) - (YUV2 & Umask)) > trU ) || + ( abs((YUV1 & Vmask) - (YUV2 & Vmask)) > trV ) ); +} + + +#define PIXEL00_0 *(Uint32 *)dst = c[5]; +#define PIXEL00_10 interp1((Uint32 *)dst, c[5], c[1]); +#define PIXEL00_11 interp1((Uint32 *)dst, c[5], c[4]); +#define PIXEL00_12 interp1((Uint32 *)dst, c[5], c[2]); +#define PIXEL00_20 interp2((Uint32 *)dst, c[5], c[4], c[2]); +#define PIXEL00_21 interp2((Uint32 *)dst, c[5], c[1], c[2]); +#define PIXEL00_22 interp2((Uint32 *)dst, c[5], c[1], c[4]); +#define PIXEL00_60 interp6((Uint32 *)dst, c[5], c[2], c[4]); +#define PIXEL00_61 interp6((Uint32 *)dst, c[5], c[4], c[2]); +#define PIXEL00_70 interp7((Uint32 *)dst, c[5], c[4], c[2]); +#define PIXEL00_90 interp9((Uint32 *)dst, c[5], c[4], c[2]); +#define PIXEL00_100 interp10((Uint32 *)dst, c[5], c[4], c[2]); +#define PIXEL01_0 *(Uint32 *)(dst + dst_Bpp) = c[5]; +#define PIXEL01_10 interp1((Uint32 *)(dst + dst_Bpp), c[5], c[3]); +#define PIXEL01_11 interp1((Uint32 *)(dst + dst_Bpp), c[5], c[2]); +#define PIXEL01_12 interp1((Uint32 *)(dst + dst_Bpp), c[5], c[6]); +#define PIXEL01_20 interp2((Uint32 *)(dst + dst_Bpp), c[5], c[2], c[6]); +#define PIXEL01_21 interp2((Uint32 *)(dst + dst_Bpp), c[5], c[3], c[6]); +#define PIXEL01_22 interp2((Uint32 *)(dst + dst_Bpp), c[5], c[3], c[2]); +#define PIXEL01_60 interp6((Uint32 *)(dst + dst_Bpp), c[5], c[6], c[2]); +#define PIXEL01_61 interp6((Uint32 *)(dst + dst_Bpp), c[5], c[2], c[6]); +#define PIXEL01_70 interp7((Uint32 *)(dst + dst_Bpp), c[5], c[2], c[6]); +#define PIXEL01_90 interp9((Uint32 *)(dst + dst_Bpp), c[5], c[2], c[6]); +#define PIXEL01_100 interp10((Uint32 *)(dst + dst_Bpp), c[5], c[2], c[6]); +#define PIXEL10_0 *(Uint32 *)(dst + dst_pitch) = c[5]; +#define PIXEL10_10 interp1((Uint32 *)(dst + dst_pitch), c[5], c[7]); +#define PIXEL10_11 interp1((Uint32 *)(dst + dst_pitch), c[5], c[8]); +#define PIXEL10_12 interp1((Uint32 *)(dst + dst_pitch), c[5], c[4]); +#define PIXEL10_20 interp2((Uint32 *)(dst + dst_pitch), c[5], c[8], c[4]); +#define PIXEL10_21 interp2((Uint32 *)(dst + dst_pitch), c[5], c[7], c[4]); +#define PIXEL10_22 interp2((Uint32 *)(dst + dst_pitch), c[5], c[7], c[8]); +#define PIXEL10_60 interp6((Uint32 *)(dst + dst_pitch), c[5], c[4], c[8]); +#define PIXEL10_61 interp6((Uint32 *)(dst + dst_pitch), c[5], c[8], c[4]); +#define PIXEL10_70 interp7((Uint32 *)(dst + dst_pitch), c[5], c[8], c[4]); +#define PIXEL10_90 interp9((Uint32 *)(dst + dst_pitch), c[5], c[8], c[4]); +#define PIXEL10_100 interp10((Uint32 *)(dst + dst_pitch), c[5], c[8], c[4]); +#define PIXEL11_0 *(Uint32 *)(dst + dst_pitch + dst_Bpp) = c[5]; +#define PIXEL11_10 interp1((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[9]); +#define PIXEL11_11 interp1((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[6]); +#define PIXEL11_12 interp1((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[8]); +#define PIXEL11_20 interp2((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[6], c[8]); +#define PIXEL11_21 interp2((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[9], c[8]); +#define PIXEL11_22 interp2((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[9], c[6]); +#define PIXEL11_60 interp6((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[8], c[6]); +#define PIXEL11_61 interp6((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[6], c[8]); +#define PIXEL11_70 interp7((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[6], c[8]); +#define PIXEL11_90 interp9((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[6], c[8]); +#define PIXEL11_100 interp10((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[6], c[8]); + +void hq2x_32( SDL_Surface *src_surface, SDL_Surface *dst_surface ) +{ + Uint8 *src = src_surface->pixels, *src_temp, + *dst = dst_surface->pixels, *dst_temp; + int src_pitch = src_surface->pitch, + dst_pitch = dst_surface->pitch; + const int dst_Bpp = 4; // dst_surface->format->BytesPerPixel + + const int height = vga_height, // src_surface->h + width = vga_width; // src_surface->w + + int prevline, nextline; + + Uint32 w[10]; + Uint32 c[10]; + + // +----+----+----+ + // | | | | + // | w1 | w2 | w3 | + // +----+----+----+ + // | | | | + // | w4 | w5 | w6 | + // +----+----+----+ + // | | | | + // | w7 | w8 | w9 | + // +----+----+----+ + + for (int j = 0; j < height; j++) + { + src_temp = src; + dst_temp = dst; + + prevline = (j > 0) ? -width : 0; + nextline = (j < height - 1) ? width : 0; + + for (int i = 0; i < width; i++) + { + w[2] = *(src + prevline); + w[5] = *src; + w[8] = *(src + nextline); + + if (i > 0) + { + w[1] = *(src + prevline - 1); + w[4] = *(src - 1); + w[7] = *(src + nextline - 1); + } else { + w[1] = w[2]; + w[4] = w[5]; + w[7] = w[8]; + } + + if (i < width - 1) + { + w[3] = *(src + prevline + 1); + w[6] = *(src + 1); + w[9] = *(src + nextline + 1); + } else { + w[3] = w[2]; + w[6] = w[5]; + w[9] = w[8]; + } + + int pattern = 0; + int flag = 1; + + YUV1 = yuv_palette[w[5]]; + + for (int k=1; k<=9; k++) + { + if (k==5) continue; + + if ( w[k] != w[5] ) + { + YUV2 = yuv_palette[w[k]]; + if ( ( abs((YUV1 & Ymask) - (YUV2 & Ymask)) > trY ) || + ( abs((YUV1 & Umask) - (YUV2 & Umask)) > trU ) || + ( abs((YUV1 & Vmask) - (YUV2 & Vmask)) > trV ) ) + pattern |= flag; + } + flag <<= 1; + } + + for (int k=1; k<=9; k++) + c[k] = rgb_palette[w[k]] & 0xfcfcfcfc; // hq2x has a nasty inability to accept more than 6 bits for each component + + switch (pattern) + { + case 0: + case 1: + case 4: + case 32: + case 128: + case 5: + case 132: + case 160: + case 33: + case 129: + case 36: + case 133: + case 164: + case 161: + case 37: + case 165: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_20 + PIXEL11_20 + break; + } + case 2: + case 34: + case 130: + case 162: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_20 + PIXEL11_20 + break; + } + case 16: + case 17: + case 48: + case 49: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_20 + PIXEL11_21 + break; + } + case 64: + case 65: + case 68: + case 69: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_21 + PIXEL11_22 + break; + } + case 8: + case 12: + case 136: + case 140: + { + PIXEL00_21 + PIXEL01_20 + PIXEL10_22 + PIXEL11_20 + break; + } + case 3: + case 35: + case 131: + case 163: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_20 + PIXEL11_20 + break; + } + case 6: + case 38: + case 134: + case 166: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_20 + PIXEL11_20 + break; + } + case 20: + case 21: + case 52: + case 53: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_20 + PIXEL11_21 + break; + } + case 144: + case 145: + case 176: + case 177: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_20 + PIXEL11_12 + break; + } + case 192: + case 193: + case 196: + case 197: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_21 + PIXEL11_11 + break; + } + case 96: + case 97: + case 100: + case 101: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_12 + PIXEL11_22 + break; + } + case 40: + case 44: + case 168: + case 172: + { + PIXEL00_21 + PIXEL01_20 + PIXEL10_11 + PIXEL11_20 + break; + } + case 9: + case 13: + case 137: + case 141: + { + PIXEL00_12 + PIXEL01_20 + PIXEL10_22 + PIXEL11_20 + break; + } + case 18: + case 50: + { + PIXEL00_22 + if (diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_20 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 80: + case 81: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_21 + if (diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_20 + } + break; + } + case 72: + case 76: + { + PIXEL00_21 + PIXEL01_20 + if (diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 10: + case 138: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_22 + PIXEL11_20 + break; + } + case 66: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_21 + PIXEL11_22 + break; + } + case 24: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_22 + PIXEL11_21 + break; + } + case 7: + case 39: + case 135: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_20 + PIXEL11_20 + break; + } + case 148: + case 149: + case 180: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_20 + PIXEL11_12 + break; + } + case 224: + case 228: + case 225: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_12 + PIXEL11_11 + break; + } + case 41: + case 169: + case 45: + { + PIXEL00_12 + PIXEL01_20 + PIXEL10_11 + PIXEL11_20 + break; + } + case 22: + case 54: + { + PIXEL00_22 + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 208: + case 209: + { + PIXEL00_20 + PIXEL01_22 + PIXEL10_21 + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 104: + case 108: + { + PIXEL00_21 + PIXEL01_20 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 11: + case 139: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_22 + PIXEL11_20 + break; + } + case 19: + case 51: + { + if (diff(w[2], w[6])) + { + PIXEL00_11 + PIXEL01_10 + } + else + { + PIXEL00_60 + PIXEL01_90 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 146: + case 178: + { + PIXEL00_22 + if (diff(w[2], w[6])) + { + PIXEL01_10 + PIXEL11_12 + } + else + { + PIXEL01_90 + PIXEL11_61 + } + PIXEL10_20 + break; + } + case 84: + case 85: + { + PIXEL00_20 + if (diff(w[6], w[8])) + { + PIXEL01_11 + PIXEL11_10 + } + else + { + PIXEL01_60 + PIXEL11_90 + } + PIXEL10_21 + break; + } + case 112: + case 113: + { + PIXEL00_20 + PIXEL01_22 + if (diff(w[6], w[8])) + { + PIXEL10_12 + PIXEL11_10 + } + else + { + PIXEL10_61 + PIXEL11_90 + } + break; + } + case 200: + case 204: + { + PIXEL00_21 + PIXEL01_20 + if (diff(w[8], w[4])) + { + PIXEL10_10 + PIXEL11_11 + } + else + { + PIXEL10_90 + PIXEL11_60 + } + break; + } + case 73: + case 77: + { + if (diff(w[8], w[4])) + { + PIXEL00_12 + PIXEL10_10 + } + else + { + PIXEL00_61 + PIXEL10_90 + } + PIXEL01_20 + PIXEL11_22 + break; + } + case 42: + case 170: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + PIXEL10_11 + } + else + { + PIXEL00_90 + PIXEL10_60 + } + PIXEL01_21 + PIXEL11_20 + break; + } + case 14: + case 142: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + PIXEL01_12 + } + else + { + PIXEL00_90 + PIXEL01_61 + } + PIXEL10_22 + PIXEL11_20 + break; + } + case 67: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_21 + PIXEL11_22 + break; + } + case 70: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_21 + PIXEL11_22 + break; + } + case 28: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_22 + PIXEL11_21 + break; + } + case 152: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_22 + PIXEL11_12 + break; + } + case 194: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_21 + PIXEL11_11 + break; + } + case 98: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_12 + PIXEL11_22 + break; + } + case 56: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_11 + PIXEL11_21 + break; + } + case 25: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_22 + PIXEL11_21 + break; + } + case 26: + case 31: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_22 + PIXEL11_21 + break; + } + case 82: + case 214: + { + PIXEL00_22 + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_21 + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 88: + case 248: + { + PIXEL00_21 + PIXEL01_22 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 74: + case 107: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 27: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + PIXEL10_22 + PIXEL11_21 + break; + } + case 86: + { + PIXEL00_22 + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_21 + PIXEL11_10 + break; + } + case 216: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_10 + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 106: + { + PIXEL00_10 + PIXEL01_21 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 30: + { + PIXEL00_10 + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_22 + PIXEL11_21 + break; + } + case 210: + { + PIXEL00_22 + PIXEL01_10 + PIXEL10_21 + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 120: + { + PIXEL00_21 + PIXEL01_22 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 75: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_10 + PIXEL11_22 + break; + } + case 29: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_22 + PIXEL11_21 + break; + } + case 198: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_21 + PIXEL11_11 + break; + } + case 184: + { + PIXEL00_21 + PIXEL01_22 + PIXEL10_11 + PIXEL11_12 + break; + } + case 99: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_12 + PIXEL11_22 + break; + } + case 57: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_11 + PIXEL11_21 + break; + } + case 71: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_21 + PIXEL11_22 + break; + } + case 156: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_22 + PIXEL11_12 + break; + } + case 226: + { + PIXEL00_22 + PIXEL01_21 + PIXEL10_12 + PIXEL11_11 + break; + } + case 60: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_11 + PIXEL11_21 + break; + } + case 195: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_21 + PIXEL11_11 + break; + } + case 102: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_12 + PIXEL11_22 + break; + } + case 153: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_22 + PIXEL11_12 + break; + } + case 58: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 83: + { + PIXEL00_11 + if (diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_21 + if (diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 92: + { + PIXEL00_21 + PIXEL01_11 + if (diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 202: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_21 + if (diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_11 + break; + } + case 78: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_12 + if (diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_22 + break; + } + case 154: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_22 + PIXEL11_12 + break; + } + case 114: + { + PIXEL00_22 + if (diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_12 + if (diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 89: + { + PIXEL00_12 + PIXEL01_22 + if (diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 90: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 55: + case 23: + { + if (diff(w[2], w[6])) + { + PIXEL00_11 + PIXEL01_0 + } + else + { + PIXEL00_60 + PIXEL01_90 + } + PIXEL10_20 + PIXEL11_21 + break; + } + case 182: + case 150: + { + PIXEL00_22 + if (diff(w[2], w[6])) + { + PIXEL01_0 + PIXEL11_12 + } + else + { + PIXEL01_90 + PIXEL11_61 + } + PIXEL10_20 + break; + } + case 213: + case 212: + { + PIXEL00_20 + if (diff(w[6], w[8])) + { + PIXEL01_11 + PIXEL11_0 + } + else + { + PIXEL01_60 + PIXEL11_90 + } + PIXEL10_21 + break; + } + case 241: + case 240: + { + PIXEL00_20 + PIXEL01_22 + if (diff(w[6], w[8])) + { + PIXEL10_12 + PIXEL11_0 + } + else + { + PIXEL10_61 + PIXEL11_90 + } + break; + } + case 236: + case 232: + { + PIXEL00_21 + PIXEL01_20 + if (diff(w[8], w[4])) + { + PIXEL10_0 + PIXEL11_11 + } + else + { + PIXEL10_90 + PIXEL11_60 + } + break; + } + case 109: + case 105: + { + if (diff(w[8], w[4])) + { + PIXEL00_12 + PIXEL10_0 + } + else + { + PIXEL00_61 + PIXEL10_90 + } + PIXEL01_20 + PIXEL11_22 + break; + } + case 171: + case 43: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL10_11 + } + else + { + PIXEL00_90 + PIXEL10_60 + } + PIXEL01_21 + PIXEL11_20 + break; + } + case 143: + case 15: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_12 + } + else + { + PIXEL00_90 + PIXEL01_61 + } + PIXEL10_22 + PIXEL11_20 + break; + } + case 124: + { + PIXEL00_21 + PIXEL01_11 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 203: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + PIXEL10_10 + PIXEL11_11 + break; + } + case 62: + { + PIXEL00_10 + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 211: + { + PIXEL00_11 + PIXEL01_10 + PIXEL10_21 + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 118: + { + PIXEL00_22 + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_12 + PIXEL11_10 + break; + } + case 217: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_10 + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 110: + { + PIXEL00_10 + PIXEL01_12 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 155: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + PIXEL10_22 + PIXEL11_12 + break; + } + case 188: + { + PIXEL00_21 + PIXEL01_11 + PIXEL10_11 + PIXEL11_12 + break; + } + case 185: + { + PIXEL00_12 + PIXEL01_22 + PIXEL10_11 + PIXEL11_12 + break; + } + case 61: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_11 + PIXEL11_21 + break; + } + case 157: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_22 + PIXEL11_12 + break; + } + case 103: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_12 + PIXEL11_22 + break; + } + case 227: + { + PIXEL00_11 + PIXEL01_21 + PIXEL10_12 + PIXEL11_11 + break; + } + case 230: + { + PIXEL00_22 + PIXEL01_12 + PIXEL10_12 + PIXEL11_11 + break; + } + case 199: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_21 + PIXEL11_11 + break; + } + case 220: + { + PIXEL00_21 + PIXEL01_11 + if (diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 158: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_22 + PIXEL11_12 + break; + } + case 234: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_21 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_11 + break; + } + case 242: + { + PIXEL00_22 + if (diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_12 + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 59: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 121: + { + PIXEL00_12 + PIXEL01_22 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 87: + { + PIXEL00_11 + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_21 + if (diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 79: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_12 + if (diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_22 + break; + } + case 122: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 94: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 218: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 91: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + if (diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 229: + { + PIXEL00_20 + PIXEL01_20 + PIXEL10_12 + PIXEL11_11 + break; + } + case 167: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_20 + PIXEL11_20 + break; + } + case 173: + { + PIXEL00_12 + PIXEL01_20 + PIXEL10_11 + PIXEL11_20 + break; + } + case 181: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_20 + PIXEL11_12 + break; + } + case 186: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + if (diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_11 + PIXEL11_12 + break; + } + case 115: + { + PIXEL00_11 + if (diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_12 + if (diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 93: + { + PIXEL00_12 + PIXEL01_11 + if (diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + if (diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 206: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_12 + if (diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_11 + break; + } + case 205: + case 201: + { + PIXEL00_12 + PIXEL01_20 + if (diff(w[8], w[4])) + { + PIXEL10_10 + } + else + { + PIXEL10_70 + } + PIXEL11_11 + break; + } + case 174: + case 46: + { + if (diff(w[4], w[2])) + { + PIXEL00_10 + } + else + { + PIXEL00_70 + } + PIXEL01_12 + PIXEL10_11 + PIXEL11_20 + break; + } + case 179: + case 147: + { + PIXEL00_11 + if (diff(w[2], w[6])) + { + PIXEL01_10 + } + else + { + PIXEL01_70 + } + PIXEL10_20 + PIXEL11_12 + break; + } + case 117: + case 116: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_12 + if (diff(w[6], w[8])) + { + PIXEL11_10 + } + else + { + PIXEL11_70 + } + break; + } + case 189: + { + PIXEL00_12 + PIXEL01_11 + PIXEL10_11 + PIXEL11_12 + break; + } + case 231: + { + PIXEL00_11 + PIXEL01_12 + PIXEL10_12 + PIXEL11_11 + break; + } + case 126: + { + PIXEL00_10 + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 219: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + PIXEL10_10 + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 125: + { + if (diff(w[8], w[4])) + { + PIXEL00_12 + PIXEL10_0 + } + else + { + PIXEL00_61 + PIXEL10_90 + } + PIXEL01_11 + PIXEL11_10 + break; + } + case 221: + { + PIXEL00_12 + if (diff(w[6], w[8])) + { + PIXEL01_11 + PIXEL11_0 + } + else + { + PIXEL01_60 + PIXEL11_90 + } + PIXEL10_10 + break; + } + case 207: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL01_12 + } + else + { + PIXEL00_90 + PIXEL01_61 + } + PIXEL10_10 + PIXEL11_11 + break; + } + case 238: + { + PIXEL00_10 + PIXEL01_12 + if (diff(w[8], w[4])) + { + PIXEL10_0 + PIXEL11_11 + } + else + { + PIXEL10_90 + PIXEL11_60 + } + break; + } + case 190: + { + PIXEL00_10 + if (diff(w[2], w[6])) + { + PIXEL01_0 + PIXEL11_12 + } + else + { + PIXEL01_90 + PIXEL11_61 + } + PIXEL10_11 + break; + } + case 187: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + PIXEL10_11 + } + else + { + PIXEL00_90 + PIXEL10_60 + } + PIXEL01_10 + PIXEL11_12 + break; + } + case 243: + { + PIXEL00_11 + PIXEL01_10 + if (diff(w[6], w[8])) + { + PIXEL10_12 + PIXEL11_0 + } + else + { + PIXEL10_61 + PIXEL11_90 + } + break; + } + case 119: + { + if (diff(w[2], w[6])) + { + PIXEL00_11 + PIXEL01_0 + } + else + { + PIXEL00_60 + PIXEL01_90 + } + PIXEL10_12 + PIXEL11_10 + break; + } + case 237: + case 233: + { + PIXEL00_12 + PIXEL01_20 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + PIXEL11_11 + break; + } + case 175: + case 47: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + PIXEL01_12 + PIXEL10_11 + PIXEL11_20 + break; + } + case 183: + case 151: + { + PIXEL00_11 + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_20 + PIXEL11_12 + break; + } + case 245: + case 244: + { + PIXEL00_20 + PIXEL01_11 + PIXEL10_12 + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 250: + { + PIXEL00_10 + PIXEL01_10 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 123: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 95: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_10 + PIXEL11_10 + break; + } + case 222: + { + PIXEL00_10 + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_10 + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 252: + { + PIXEL00_21 + PIXEL01_11 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 249: + { + PIXEL00_12 + PIXEL01_22 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 235: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_21 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + PIXEL11_11 + break; + } + case 111: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + PIXEL01_12 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_22 + break; + } + case 63: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_11 + PIXEL11_21 + break; + } + case 159: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_22 + PIXEL11_12 + break; + } + case 215: + { + PIXEL00_11 + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_21 + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 246: + { + PIXEL00_22 + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + PIXEL10_12 + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 254: + { + PIXEL00_10 + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 253: + { + PIXEL00_12 + PIXEL01_11 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 251: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + PIXEL01_10 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 239: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + PIXEL01_12 + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + PIXEL11_11 + break; + } + case 127: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_20 + } + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_20 + } + PIXEL11_10 + break; + } + case 191: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_11 + PIXEL11_12 + break; + } + case 223: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_20 + } + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_10 + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_20 + } + break; + } + case 247: + { + PIXEL00_11 + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + PIXEL10_12 + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + case 255: + { + if (diff(w[4], w[2])) + { + PIXEL00_0 + } + else + { + PIXEL00_100 + } + if (diff(w[2], w[6])) + { + PIXEL01_0 + } + else + { + PIXEL01_100 + } + if (diff(w[8], w[4])) + { + PIXEL10_0 + } + else + { + PIXEL10_100 + } + if (diff(w[6], w[8])) + { + PIXEL11_0 + } + else + { + PIXEL11_100 + } + break; + } + } + + src++; + dst += 2 * dst_Bpp; + } + + src = src_temp + src_pitch; + dst = dst_temp + 2 * dst_pitch; + } +} + + +#define PIXEL00_1M interp1((Uint32 *)dst, c[5], c[1]); +#define PIXEL00_1U interp1((Uint32 *)dst, c[5], c[2]); +#define PIXEL00_1L interp1((Uint32 *)dst, c[5], c[4]); +#define PIXEL00_2 interp2((Uint32 *)dst, c[5], c[4], c[2]); +#define PIXEL00_4 interp4((Uint32 *)dst, c[5], c[4], c[2]); +#define PIXEL00_5 interp5((Uint32 *)dst, c[4], c[2]); +#define PIXEL00_C *(Uint32 *)dst = c[5]; + +#define PIXEL01_1 interp1((Uint32 *)(dst + dst_Bpp), c[5], c[2]); +#define PIXEL01_3 interp3((Uint32 *)(dst + dst_Bpp), c[5], c[2]); +#define PIXEL01_6 interp1((Uint32 *)(dst + dst_Bpp), c[2], c[5]); +#define PIXEL01_C *(Uint32 *)(dst + dst_Bpp) = c[5]; + +#define PIXEL02_1M interp1((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[3]); +#define PIXEL02_1U interp1((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[2]); +#define PIXEL02_1R interp1((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[6]); +#define PIXEL02_2 interp2((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[2], c[6]); +#define PIXEL02_4 interp4((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[2], c[6]); +#define PIXEL02_5 interp5((Uint32 *)(dst + 2 * dst_Bpp), c[2], c[6]); +#define PIXEL02_C *(Uint32 *)(dst + 2 * dst_Bpp) = c[5]; + +#define PIXEL10_1 interp1((Uint32 *)(dst + dst_pitch), c[5], c[4]); +#define PIXEL10_3 interp3((Uint32 *)(dst + dst_pitch), c[5], c[4]); +#define PIXEL10_6 interp1((Uint32 *)(dst + dst_pitch), c[4], c[5]); +#define PIXEL10_C *(Uint32 *)(dst + dst_pitch) = c[5]; + +#define PIXEL11 *(Uint32 *)(dst + dst_pitch + dst_Bpp) = c[5]; + +#define PIXEL12_1 interp1((Uint32 *)(dst + dst_pitch + 2 * dst_Bpp), c[5], c[6]); +#define PIXEL12_3 interp3((Uint32 *)(dst + dst_pitch + 2 * dst_Bpp), c[5], c[6]); +#define PIXEL12_6 interp1((Uint32 *)(dst + dst_pitch + 2 * dst_Bpp), c[6], c[5]); +#define PIXEL12_C *(Uint32 *)(dst + dst_pitch + 2 * dst_Bpp) = c[5]; + +#define PIXEL20_1M interp1((Uint32 *)(dst + 2 * dst_pitch), c[5], c[7]); +#define PIXEL20_1D interp1((Uint32 *)(dst + 2 * dst_pitch), c[5], c[8]); +#define PIXEL20_1L interp1((Uint32 *)(dst + 2 * dst_pitch), c[5], c[4]); +#define PIXEL20_2 interp2((Uint32 *)(dst + 2 * dst_pitch), c[5], c[8], c[4]); +#define PIXEL20_4 interp4((Uint32 *)(dst + 2 * dst_pitch), c[5], c[8], c[4]); +#define PIXEL20_5 interp5((Uint32 *)(dst + 2 * dst_pitch), c[8], c[4]); +#define PIXEL20_C *(Uint32 *)(dst + 2 * dst_pitch) = c[5]; + +#define PIXEL21_1 interp1((Uint32 *)(dst + 2 * dst_pitch + dst_Bpp), c[5], c[8]); +#define PIXEL21_3 interp3((Uint32 *)(dst + 2 * dst_pitch + dst_Bpp), c[5], c[8]); +#define PIXEL21_6 interp1((Uint32 *)(dst + 2 * dst_pitch + dst_Bpp), c[8], c[5]); +#define PIXEL21_C *(Uint32 *)(dst + 2 * dst_pitch + dst_Bpp) = c[5]; + +#define PIXEL22_1M interp1((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[9]); +#define PIXEL22_1D interp1((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[8]); +#define PIXEL22_1R interp1((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[6]); +#define PIXEL22_2 interp2((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[6], c[8]); +#define PIXEL22_4 interp4((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[6], c[8]); +#define PIXEL22_5 interp5((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[6], c[8]); +#define PIXEL22_C *(Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp) = c[5]; + +void hq3x_32( SDL_Surface *src_surface, SDL_Surface *dst_surface ) +{ + Uint8 *src = src_surface->pixels, *src_temp, + *dst = dst_surface->pixels, *dst_temp; + int src_pitch = src_surface->pitch, + dst_pitch = dst_surface->pitch; + const int dst_Bpp = 4; // dst_surface->format->BytesPerPixel + + const int height = vga_height, // src_surface->h + width = vga_width; // src_surface->w + + int prevline, nextline; + + Uint32 w[10]; + Uint32 c[10]; + + // +----+----+----+ + // | | | | + // | w1 | w2 | w3 | + // +----+----+----+ + // | | | | + // | w4 | w5 | w6 | + // +----+----+----+ + // | | | | + // | w7 | w8 | w9 | + // +----+----+----+ + + for (int j = 0; j < height; j++) + { + src_temp = src; + dst_temp = dst; + + prevline = (j > 0) ? -width : 0; + nextline = (j < height - 1) ? width : 0; + + for (int i = 0; i < width; i++) + { + w[2] = *(src + prevline); + w[5] = *src; + w[8] = *(src + nextline); + + if (i>0) + { + w[1] = *(src + prevline - 1); + w[4] = *(src - 1); + w[7] = *(src + nextline - 1); + } else { + w[1] = w[2]; + w[4] = w[5]; + w[7] = w[8]; + } + + if (i < width - 1) + { + w[3] = *(src + prevline + 1); + w[6] = *(src + 1); + w[9] = *(src + nextline + 1); + } else { + w[3] = w[2]; + w[6] = w[5]; + w[9] = w[8]; + } + + int pattern = 0; + int flag = 1; + + YUV1 = yuv_palette[w[5]]; + + for (int k=1; k<=9; k++) + { + if (k==5) continue; + + if ( w[k] != w[5] ) + { + YUV2 = yuv_palette[w[k]]; + if ( ( abs((YUV1 & Ymask) - (YUV2 & Ymask)) > trY ) || + ( abs((YUV1 & Umask) - (YUV2 & Umask)) > trU ) || + ( abs((YUV1 & Vmask) - (YUV2 & Vmask)) > trV ) ) + pattern |= flag; + } + flag <<= 1; + } + + for (int k=1; k<=9; k++) + c[k] = rgb_palette[w[k]] & 0xfcfcfcfc; // hq3x has a nasty inability to accept more than 6 bits for each component + + switch (pattern) + { + case 0: + case 1: + case 4: + case 32: + case 128: + case 5: + case 132: + case 160: + case 33: + case 129: + case 36: + case 133: + case 164: + case 161: + case 37: + case 165: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 2: + case 34: + case 130: + case 162: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 16: + case 17: + case 48: + case 49: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 64: + case 65: + case 68: + case 69: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 8: + case 12: + case 136: + case 140: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 3: + case 35: + case 131: + case 163: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 6: + case 38: + case 134: + case 166: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 20: + case 21: + case 52: + case 53: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 144: + case 145: + case 176: + case 177: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } + case 192: + case 193: + case 196: + case 197: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 96: + case 97: + case 100: + case 101: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 40: + case 44: + case 168: + case 172: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } + case 9: + case 13: + case 137: + case 141: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 18: + case 50: + { + PIXEL00_1M + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_1M + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 80: + case 81: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_1M + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 72: + case 76: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_1M + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 10: + case 138: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 66: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 24: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 7: + case 39: + case 135: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 148: + case 149: + case 180: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } + case 224: + case 228: + case 225: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 41: + case 169: + case 45: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } + case 22: + case 54: + { + PIXEL00_1M + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 208: + case 209: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 104: + case 108: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 11: + case 139: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 19: + case 51: + { + if (diff(w[2], w[6])) + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL12_C + } + else + { + PIXEL00_2 + PIXEL01_6 + PIXEL02_5 + PIXEL12_1 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 146: + case 178: + { + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_1M + PIXEL12_C + PIXEL22_1D + } + else + { + PIXEL01_1 + PIXEL02_5 + PIXEL12_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + break; + } + case 84: + case 85: + { + if (diff(w[6], w[8])) + { + PIXEL02_1U + PIXEL12_C + PIXEL21_C + PIXEL22_1M + } + else + { + PIXEL02_2 + PIXEL12_6 + PIXEL21_1 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + break; + } + case 112: + case 113: + { + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + } + else + { + PIXEL12_1 + PIXEL20_2 + PIXEL21_6 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + break; + } + case 200: + case 204: + { + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + } + else + { + PIXEL10_1 + PIXEL20_5 + PIXEL21_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + break; + } + case 73: + case 77: + { + if (diff(w[8], w[4])) + { + PIXEL00_1U + PIXEL10_C + PIXEL20_1M + PIXEL21_C + } + else + { + PIXEL00_2 + PIXEL10_6 + PIXEL20_5 + PIXEL21_1 + } + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + PIXEL22_1M + break; + } + case 42: + case 170: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + PIXEL01_C + PIXEL10_C + PIXEL20_1D + } + else + { + PIXEL00_5 + PIXEL01_1 + PIXEL10_6 + PIXEL20_2 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL21_1 + PIXEL22_2 + break; + } + case 14: + case 142: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_C + } + else + { + PIXEL00_5 + PIXEL01_6 + PIXEL02_2 + PIXEL10_1 + } + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 67: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 70: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 28: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 152: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 194: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 98: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 56: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 25: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 26: + case 31: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 82: + case 214: + { + PIXEL00_1M + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + if (diff(w[6], w[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 88: + case 248: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL11 + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } + case 74: + case 107: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (diff(w[8], w[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 27: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 86: + { + PIXEL00_1M + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 216: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL20_1M + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 106: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL11 + PIXEL12_1 + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 30: + { + PIXEL00_1M + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 210: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 120: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL11 + PIXEL12_C + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 75: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 29: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1M + break; + } + case 198: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 184: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 99: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 57: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 71: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 156: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 226: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 60: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 195: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 102: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 153: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 58: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 83: + { + PIXEL00_1L + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 92: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + if (diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 202: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 78: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1M + break; + } + case 154: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 114: + { + PIXEL00_1M + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 89: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + if (diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 90: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + if (diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 55: + case 23: + { + if (diff(w[2], w[6])) + { + PIXEL00_1L + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL00_2 + PIXEL01_6 + PIXEL02_5 + PIXEL12_1 + } + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + PIXEL22_1M + break; + } + case 182: + case 150: + { + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + PIXEL22_1D + } + else + { + PIXEL01_1 + PIXEL02_5 + PIXEL12_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL10_1 + PIXEL11 + PIXEL20_2 + PIXEL21_1 + break; + } + case 213: + case 212: + { + if (diff(w[6], w[8])) + { + PIXEL02_1U + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL02_2 + PIXEL12_6 + PIXEL21_1 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL10_1 + PIXEL11 + PIXEL20_1M + break; + } + case 241: + case 240: + { + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL20_1L + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_1 + PIXEL20_2 + PIXEL21_6 + PIXEL22_5 + } + PIXEL00_2 + PIXEL01_1 + PIXEL02_1M + PIXEL10_1 + PIXEL11 + break; + } + case 236: + case 232: + { + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + PIXEL22_1R + } + else + { + PIXEL10_1 + PIXEL20_5 + PIXEL21_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + break; + } + case 109: + case 105: + { + if (diff(w[8], w[4])) + { + PIXEL00_1U + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL00_2 + PIXEL10_6 + PIXEL20_5 + PIXEL21_1 + } + PIXEL01_1 + PIXEL02_2 + PIXEL11 + PIXEL12_1 + PIXEL22_1M + break; + } + case 171: + case 43: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + PIXEL20_1D + } + else + { + PIXEL00_5 + PIXEL01_1 + PIXEL10_6 + PIXEL20_2 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL21_1 + PIXEL22_2 + break; + } + case 143: + case 15: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL02_1R + PIXEL10_C + } + else + { + PIXEL00_5 + PIXEL01_6 + PIXEL02_2 + PIXEL10_1 + } + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_1 + PIXEL22_2 + break; + } + case 124: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 203: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 62: + { + PIXEL00_1M + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 211: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL20_1M + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 118: + { + PIXEL00_1M + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 217: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL20_1M + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 110: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL11 + PIXEL12_1 + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 155: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 188: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 185: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 61: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 157: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 103: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 227: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 230: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 199: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 220: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + if (diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 158: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 234: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1M + PIXEL11 + PIXEL12_1 + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1R + break; + } + case 242: + { + PIXEL00_1M + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1L + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 59: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + if (diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 121: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL11 + PIXEL12_C + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + if (diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 87: + { + PIXEL00_1L + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1M + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 79: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1R + PIXEL11 + PIXEL12_1 + if (diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1M + break; + } + case 122: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + if (diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 94: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + if (diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 218: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + if (diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 91: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + if (diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + if (diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 229: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_2 + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 167: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_2 + PIXEL21_1 + PIXEL22_2 + break; + } + case 173: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } + case 181: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } + case 186: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 115: + { + PIXEL00_1L + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 93: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + if (diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 206: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 205: + case 201: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (diff(w[8], w[4])) + { + PIXEL20_1M + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 174: + case 46: + { + if (diff(w[4], w[2])) + { + PIXEL00_1M + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } + case 179: + case 147: + { + PIXEL00_1L + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_1M + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } + case 117: + case 116: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_1M + } + else + { + PIXEL22_2 + } + break; + } + case 189: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 231: + { + PIXEL00_1L + PIXEL01_C + PIXEL02_1R + PIXEL10_1 + PIXEL11 + PIXEL12_1 + PIXEL20_1L + PIXEL21_C + PIXEL22_1R + break; + } + case 126: + { + PIXEL00_1M + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 219: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL01_3 + PIXEL10_3 + } + PIXEL02_1M + PIXEL11 + PIXEL20_1M + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 125: + { + if (diff(w[8], w[4])) + { + PIXEL00_1U + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL00_2 + PIXEL10_6 + PIXEL20_5 + PIXEL21_1 + } + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + PIXEL22_1M + break; + } + case 221: + { + if (diff(w[6], w[8])) + { + PIXEL02_1U + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL02_2 + PIXEL12_6 + PIXEL21_1 + PIXEL22_5 + } + PIXEL00_1U + PIXEL01_1 + PIXEL10_C + PIXEL11 + PIXEL20_1M + break; + } + case 207: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL02_1R + PIXEL10_C + } + else + { + PIXEL00_5 + PIXEL01_6 + PIXEL02_2 + PIXEL10_1 + } + PIXEL11 + PIXEL12_1 + PIXEL20_1M + PIXEL21_C + PIXEL22_1R + break; + } + case 238: + { + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + PIXEL22_1R + } + else + { + PIXEL10_1 + PIXEL20_5 + PIXEL21_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL01_C + PIXEL02_1R + PIXEL11 + PIXEL12_1 + break; + } + case 190: + { + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + PIXEL22_1D + } + else + { + PIXEL01_1 + PIXEL02_5 + PIXEL12_6 + PIXEL22_2 + } + PIXEL00_1M + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + break; + } + case 187: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + PIXEL20_1D + } + else + { + PIXEL00_5 + PIXEL01_1 + PIXEL10_6 + PIXEL20_2 + } + PIXEL02_1M + PIXEL11 + PIXEL12_C + PIXEL21_1 + PIXEL22_1D + break; + } + case 243: + { + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL20_1L + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_1 + PIXEL20_2 + PIXEL21_6 + PIXEL22_5 + } + PIXEL00_1L + PIXEL01_C + PIXEL02_1M + PIXEL10_1 + PIXEL11 + break; + } + case 119: + { + if (diff(w[2], w[6])) + { + PIXEL00_1L + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL00_2 + PIXEL01_6 + PIXEL02_5 + PIXEL12_1 + } + PIXEL10_1 + PIXEL11 + PIXEL20_1L + PIXEL21_C + PIXEL22_1M + break; + } + case 237: + case 233: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_2 + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 175: + case 47: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + PIXEL20_1D + PIXEL21_1 + PIXEL22_2 + break; + } + case 183: + case 151: + { + PIXEL00_1L + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_2 + PIXEL21_1 + PIXEL22_1D + break; + } + case 245: + case 244: + { + PIXEL00_2 + PIXEL01_1 + PIXEL02_1U + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + case 250: + { + PIXEL00_1M + PIXEL01_C + PIXEL02_1M + PIXEL11 + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } + case 123: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_C + if (diff(w[8], w[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 95: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + PIXEL20_1M + PIXEL21_C + PIXEL22_1M + break; + } + case 222: + { + PIXEL00_1M + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1M + if (diff(w[6], w[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 252: + { + PIXEL00_1M + PIXEL01_1 + PIXEL02_1U + PIXEL11 + PIXEL12_C + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + case 249: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1M + PIXEL10_C + PIXEL11 + if (diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } + case 235: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 111: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (diff(w[8], w[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 63: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL10_C + PIXEL11 + PIXEL20_1D + PIXEL21_1 + PIXEL22_1M + break; + } + case 159: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL11 + PIXEL12_C + PIXEL20_1M + PIXEL21_1 + PIXEL22_1D + break; + } + case 215: + { + PIXEL00_1L + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1M + if (diff(w[6], w[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 246: + { + PIXEL00_1M + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + case 254: + { + PIXEL00_1M + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + } + else + { + PIXEL01_3 + PIXEL02_4 + } + PIXEL11 + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + } + else + { + PIXEL10_3 + PIXEL20_4 + } + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL21_3 + PIXEL22_2 + } + break; + } + case 253: + { + PIXEL00_1U + PIXEL01_1 + PIXEL02_1U + PIXEL10_C + PIXEL11 + PIXEL12_C + if (diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + case 251: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + } + else + { + PIXEL00_4 + PIXEL01_3 + } + PIXEL02_1M + PIXEL11 + if (diff(w[8], w[4])) + { + PIXEL10_C + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL10_3 + PIXEL20_2 + PIXEL21_3 + } + if (diff(w[6], w[8])) + { + PIXEL12_C + PIXEL22_C + } + else + { + PIXEL12_3 + PIXEL22_4 + } + break; + } + case 239: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + PIXEL02_1R + PIXEL10_C + PIXEL11 + PIXEL12_1 + if (diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + PIXEL22_1R + break; + } + case 127: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL01_C + PIXEL10_C + } + else + { + PIXEL00_2 + PIXEL01_3 + PIXEL10_3 + } + if (diff(w[2], w[6])) + { + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL02_4 + PIXEL12_3 + } + PIXEL11 + if (diff(w[8], w[4])) + { + PIXEL20_C + PIXEL21_C + } + else + { + PIXEL20_4 + PIXEL21_3 + } + PIXEL22_1M + break; + } + case 191: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + PIXEL20_1D + PIXEL21_1 + PIXEL22_1D + break; + } + case 223: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + PIXEL10_C + } + else + { + PIXEL00_4 + PIXEL10_3 + } + if (diff(w[2], w[6])) + { + PIXEL01_C + PIXEL02_C + PIXEL12_C + } + else + { + PIXEL01_3 + PIXEL02_2 + PIXEL12_3 + } + PIXEL11 + PIXEL20_1M + if (diff(w[6], w[8])) + { + PIXEL21_C + PIXEL22_C + } + else + { + PIXEL21_3 + PIXEL22_4 + } + break; + } + case 247: + { + PIXEL00_1L + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_1 + PIXEL11 + PIXEL12_C + PIXEL20_1L + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + case 255: + { + if (diff(w[4], w[2])) + { + PIXEL00_C + } + else + { + PIXEL00_2 + } + PIXEL01_C + if (diff(w[2], w[6])) + { + PIXEL02_C + } + else + { + PIXEL02_2 + } + PIXEL10_C + PIXEL11 + PIXEL12_C + if (diff(w[8], w[4])) + { + PIXEL20_C + } + else + { + PIXEL20_2 + } + PIXEL21_C + if (diff(w[6], w[8])) + { + PIXEL22_C + } + else + { + PIXEL22_2 + } + break; + } + } + + src++; + dst += 3 * dst_Bpp; + } + + src = src_temp + src_pitch; + dst = dst_temp + 3 * dst_pitch; + } +} + + +#define PIXEL4_00_0 *(Uint32 *)(dst) = c[5]; +#define PIXEL4_00_11 interp1((Uint32 *)(dst), c[5], c[4]); +#define PIXEL4_00_12 interp1((Uint32 *)(dst), c[5], c[2]); +#define PIXEL4_00_20 interp2((Uint32 *)(dst), c[5], c[2], c[4]); +#define PIXEL4_00_50 interp5((Uint32 *)(dst), c[2], c[4]); +#define PIXEL4_00_80 interp8((Uint32 *)(dst), c[5], c[1]); +#define PIXEL4_00_81 interp8((Uint32 *)(dst), c[5], c[4]); +#define PIXEL4_00_82 interp8((Uint32 *)(dst), c[5], c[2]); +#define PIXEL4_01_0 *(Uint32 *)(dst + dst_Bpp) = c[5]; +#define PIXEL4_01_10 interp1((Uint32 *)(dst + dst_Bpp), c[5], c[1]); +#define PIXEL4_01_12 interp1((Uint32 *)(dst + dst_Bpp), c[5], c[2]); +#define PIXEL4_01_14 interp1((Uint32 *)(dst + dst_Bpp), c[2], c[5]); +#define PIXEL4_01_21 interp2((Uint32 *)(dst + dst_Bpp), c[2], c[5], c[4]); +#define PIXEL4_01_31 interp3((Uint32 *)(dst + dst_Bpp), c[5], c[4]); +#define PIXEL4_01_50 interp5((Uint32 *)(dst + dst_Bpp), c[2], c[5]); +#define PIXEL4_01_60 interp6((Uint32 *)(dst + dst_Bpp), c[5], c[2], c[4]); +#define PIXEL4_01_61 interp6((Uint32 *)(dst + dst_Bpp), c[5], c[2], c[1]); +#define PIXEL4_01_82 interp8((Uint32 *)(dst + dst_Bpp), c[5], c[2]); +#define PIXEL4_01_83 interp8((Uint32 *)(dst + dst_Bpp), c[2], c[4]); +#define PIXEL4_02_0 *(Uint32 *)(dst + 2 * dst_Bpp) = c[5]; +#define PIXEL4_02_10 interp1((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[3]); +#define PIXEL4_02_11 interp1((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[2]); +#define PIXEL4_02_13 interp1((Uint32 *)(dst + 2 * dst_Bpp), c[2], c[5]); +#define PIXEL4_02_21 interp2((Uint32 *)(dst + 2 * dst_Bpp), c[2], c[5], c[6]); +#define PIXEL4_02_32 interp3((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[6]); +#define PIXEL4_02_50 interp5((Uint32 *)(dst + 2 * dst_Bpp), c[2], c[5]); +#define PIXEL4_02_60 interp6((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[2], c[6]); +#define PIXEL4_02_61 interp6((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[2], c[3]); +#define PIXEL4_02_81 interp8((Uint32 *)(dst + 2 * dst_Bpp), c[5], c[2]); +#define PIXEL4_02_83 interp8((Uint32 *)(dst + 2 * dst_Bpp), c[2], c[6]); +#define PIXEL4_03_0 *(Uint32 *)(dst + 3 * dst_Bpp) = c[5]; +#define PIXEL4_03_11 interp1((Uint32 *)(dst + 3 * dst_Bpp), c[5], c[2]); +#define PIXEL4_03_12 interp1((Uint32 *)(dst + 3 * dst_Bpp), c[5], c[6]); +#define PIXEL4_03_20 interp2((Uint32 *)(dst + 3 * dst_Bpp), c[5], c[2], c[6]); +#define PIXEL4_03_50 interp5((Uint32 *)(dst + 3 * dst_Bpp), c[2], c[6]); +#define PIXEL4_03_80 interp8((Uint32 *)(dst + 3 * dst_Bpp), c[5], c[3]); +#define PIXEL4_03_81 interp8((Uint32 *)(dst + 3 * dst_Bpp), c[5], c[2]); +#define PIXEL4_03_82 interp8((Uint32 *)(dst + 3 * dst_Bpp), c[5], c[6]); +#define PIXEL4_10_0 *(Uint32 *)(dst + dst_pitch) = c[5]; +#define PIXEL4_10_10 interp1((Uint32 *)(dst + dst_pitch ), c[5], c[1]); +#define PIXEL4_10_11 interp1((Uint32 *)(dst + dst_pitch ), c[5], c[4]); +#define PIXEL4_10_13 interp1((Uint32 *)(dst + dst_pitch ), c[4], c[5]); +#define PIXEL4_10_21 interp2((Uint32 *)(dst + dst_pitch ), c[4], c[5], c[2]); +#define PIXEL4_10_32 interp3((Uint32 *)(dst + dst_pitch ), c[5], c[2]); +#define PIXEL4_10_50 interp5((Uint32 *)(dst + dst_pitch ), c[4], c[5]); +#define PIXEL4_10_60 interp6((Uint32 *)(dst + dst_pitch ), c[5], c[4], c[2]); +#define PIXEL4_10_61 interp6((Uint32 *)(dst + dst_pitch ), c[5], c[4], c[1]); +#define PIXEL4_10_81 interp8((Uint32 *)(dst + dst_pitch ), c[5], c[4]); +#define PIXEL4_10_83 interp8((Uint32 *)(dst + dst_pitch ), c[4], c[2]); +#define PIXEL4_11_0 *(Uint32 *)(dst + dst_pitch + dst_Bpp) = c[5]; +#define PIXEL4_11_30 interp3((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[1]); +#define PIXEL4_11_31 interp3((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[4]); +#define PIXEL4_11_32 interp3((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[2]); +#define PIXEL4_11_70 interp7((Uint32 *)(dst + dst_pitch + dst_Bpp), c[5], c[4], c[2]); +#define PIXEL4_12_0 *(Uint32 *)(dst + dst_pitch + 2 * dst_Bpp) = c[5]; +#define PIXEL4_12_30 interp3((Uint32 *)(dst + dst_pitch + 2 * dst_Bpp), c[5], c[3]); +#define PIXEL4_12_31 interp3((Uint32 *)(dst + dst_pitch + 2 * dst_Bpp), c[5], c[2]); +#define PIXEL4_12_32 interp3((Uint32 *)(dst + dst_pitch + 2 * dst_Bpp), c[5], c[6]); +#define PIXEL4_12_70 interp7((Uint32 *)(dst + dst_pitch + 2 * dst_Bpp), c[5], c[6], c[2]); +#define PIXEL4_13_0 *(Uint32 *)(dst + dst_pitch + 3 * dst_Bpp) = c[5]; +#define PIXEL4_13_10 interp1((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[5], c[3]); +#define PIXEL4_13_12 interp1((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[5], c[6]); +#define PIXEL4_13_14 interp1((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[6], c[5]); +#define PIXEL4_13_21 interp2((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[6], c[5], c[2]); +#define PIXEL4_13_31 interp3((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[5], c[2]); +#define PIXEL4_13_50 interp5((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[6], c[5]); +#define PIXEL4_13_60 interp6((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[5], c[6], c[2]); +#define PIXEL4_13_61 interp6((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[5], c[6], c[3]); +#define PIXEL4_13_82 interp8((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[5], c[6]); +#define PIXEL4_13_83 interp8((Uint32 *)(dst + dst_pitch + 3 * dst_Bpp), c[6], c[2]); +#define PIXEL4_20_0 *(Uint32 *)(dst + 2 * dst_pitch) = c[5]; +#define PIXEL4_20_10 interp1((Uint32 *)(dst + 2 * dst_pitch ), c[5], c[7]); +#define PIXEL4_20_12 interp1((Uint32 *)(dst + 2 * dst_pitch ), c[5], c[4]); +#define PIXEL4_20_14 interp1((Uint32 *)(dst + 2 * dst_pitch ), c[4], c[5]); +#define PIXEL4_20_21 interp2((Uint32 *)(dst + 2 * dst_pitch ), c[4], c[5], c[8]); +#define PIXEL4_20_31 interp3((Uint32 *)(dst + 2 * dst_pitch ), c[5], c[8]); +#define PIXEL4_20_50 interp5((Uint32 *)(dst + 2 * dst_pitch ), c[4], c[5]); +#define PIXEL4_20_60 interp6((Uint32 *)(dst + 2 * dst_pitch ), c[5], c[4], c[8]); +#define PIXEL4_20_61 interp6((Uint32 *)(dst + 2 * dst_pitch ), c[5], c[4], c[7]); +#define PIXEL4_20_82 interp8((Uint32 *)(dst + 2 * dst_pitch ), c[5], c[4]); +#define PIXEL4_20_83 interp8((Uint32 *)(dst + 2 * dst_pitch ), c[4], c[8]); +#define PIXEL4_21_0 *(Uint32 *)(dst + 2 * dst_pitch + dst_Bpp) = c[5]; +#define PIXEL4_21_30 interp3((Uint32 *)(dst + 2 * dst_pitch + dst_Bpp), c[5], c[7]); +#define PIXEL4_21_31 interp3((Uint32 *)(dst + 2 * dst_pitch + dst_Bpp), c[5], c[8]); +#define PIXEL4_21_32 interp3((Uint32 *)(dst + 2 * dst_pitch + dst_Bpp), c[5], c[4]); +#define PIXEL4_21_70 interp7((Uint32 *)(dst + 2 * dst_pitch + dst_Bpp), c[5], c[4], c[8]); +#define PIXEL4_22_0 *(Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp) = c[5]; +#define PIXEL4_22_30 interp3((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[9]); +#define PIXEL4_22_31 interp3((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[6]); +#define PIXEL4_22_32 interp3((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[8]); +#define PIXEL4_22_70 interp7((Uint32 *)(dst + 2 * dst_pitch + 2 * dst_Bpp), c[5], c[6], c[8]); +#define PIXEL4_23_0 *(Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp) = c[5]; +#define PIXEL4_23_10 interp1((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[5], c[9]); +#define PIXEL4_23_11 interp1((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[5], c[6]); +#define PIXEL4_23_13 interp1((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[6], c[5]); +#define PIXEL4_23_21 interp2((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[6], c[5], c[8]); +#define PIXEL4_23_32 interp3((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[5], c[8]); +#define PIXEL4_23_50 interp5((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[6], c[5]); +#define PIXEL4_23_60 interp6((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[5], c[6], c[8]); +#define PIXEL4_23_61 interp6((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[5], c[6], c[9]); +#define PIXEL4_23_81 interp8((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[5], c[6]); +#define PIXEL4_23_83 interp8((Uint32 *)(dst + 2 * dst_pitch + 3 * dst_Bpp), c[6], c[8]); +#define PIXEL4_30_0 *(Uint32 *)(dst + 3 * dst_pitch) = c[5]; +#define PIXEL4_30_11 interp1((Uint32 *)(dst + 3 * dst_pitch ), c[5], c[8]); +#define PIXEL4_30_12 interp1((Uint32 *)(dst + 3 * dst_pitch ), c[5], c[4]); +#define PIXEL4_30_20 interp2((Uint32 *)(dst + 3 * dst_pitch ), c[5], c[8], c[4]); +#define PIXEL4_30_50 interp5((Uint32 *)(dst + 3 * dst_pitch ), c[8], c[4]); +#define PIXEL4_30_80 interp8((Uint32 *)(dst + 3 * dst_pitch ), c[5], c[7]); +#define PIXEL4_30_81 interp8((Uint32 *)(dst + 3 * dst_pitch ), c[5], c[8]); +#define PIXEL4_30_82 interp8((Uint32 *)(dst + 3 * dst_pitch ), c[5], c[4]); +#define PIXEL4_31_0 *(Uint32 *)(dst + 3 * dst_pitch + dst_Bpp) = c[5]; +#define PIXEL4_31_10 interp1((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[5], c[7]); +#define PIXEL4_31_11 interp1((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[5], c[8]); +#define PIXEL4_31_13 interp1((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[8], c[5]); +#define PIXEL4_31_21 interp2((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[8], c[5], c[4]); +#define PIXEL4_31_32 interp3((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[5], c[4]); +#define PIXEL4_31_50 interp5((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[8], c[5]); +#define PIXEL4_31_60 interp6((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[5], c[8], c[4]); +#define PIXEL4_31_61 interp6((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[5], c[8], c[7]); +#define PIXEL4_31_81 interp8((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[5], c[8]); +#define PIXEL4_31_83 interp8((Uint32 *)(dst + 3 * dst_pitch + dst_Bpp), c[8], c[4]); +#define PIXEL4_32_0 *(Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp) = c[5]; +#define PIXEL4_32_10 interp1((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[5], c[9]); +#define PIXEL4_32_12 interp1((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[5], c[8]); +#define PIXEL4_32_14 interp1((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[8], c[5]); +#define PIXEL4_32_21 interp2((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[8], c[5], c[6]); +#define PIXEL4_32_31 interp3((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[5], c[6]); +#define PIXEL4_32_50 interp5((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[8], c[5]); +#define PIXEL4_32_60 interp6((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[5], c[8], c[6]); +#define PIXEL4_32_61 interp6((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[5], c[8], c[9]); +#define PIXEL4_32_82 interp8((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[5], c[8]); +#define PIXEL4_32_83 interp8((Uint32 *)(dst + 3 * dst_pitch + 2 * dst_Bpp), c[8], c[6]); +#define PIXEL4_33_0 *(Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp) = c[5]; +#define PIXEL4_33_11 interp1((Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp), c[5], c[6]); +#define PIXEL4_33_12 interp1((Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp), c[5], c[8]); +#define PIXEL4_33_20 interp2((Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp), c[5], c[8], c[6]); +#define PIXEL4_33_50 interp5((Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp), c[8], c[6]); +#define PIXEL4_33_80 interp8((Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp), c[5], c[9]); +#define PIXEL4_33_81 interp8((Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp), c[5], c[6]); +#define PIXEL4_33_82 interp8((Uint32 *)(dst + 3 * dst_pitch + 3 * dst_Bpp), c[5], c[8]); + +void hq4x_32( SDL_Surface *src_surface, SDL_Surface *dst_surface ) +{ + Uint8 *src = src_surface->pixels, *src_temp, + *dst = dst_surface->pixels, *dst_temp; + int src_pitch = src_surface->pitch, + dst_pitch = dst_surface->pitch; + const int dst_Bpp = 4; // dst_surface->format->BytesPerPixel + + const int height = vga_height, // src_surface->h + width = vga_width; // src_surface->w + + int prevline, nextline; + + Uint32 w[10]; + Uint32 c[10]; + + // +----+----+----+ + // | | | | + // | w1 | w2 | w3 | + // +----+----+----+ + // | | | | + // | w4 | w5 | w6 | + // +----+----+----+ + // | | | | + // | w7 | w8 | w9 | + // +----+----+----+ + + for (int j = 0; j < height; j++) + { + src_temp = src; + dst_temp = dst; + + prevline = (j > 0) ? -width : 0; + nextline = (j < height - 1) ? width : 0; + + for (int i = 0; i < width; i++) + { + w[2] = *(src + prevline); + w[5] = *src; + w[8] = *(src + nextline); + + if (i>0) + { + w[1] = *(src + prevline - 1); + w[4] = *(src - 1); + w[7] = *(src + nextline - 1); + } else { + w[1] = w[2]; + w[4] = w[5]; + w[7] = w[8]; + } + + if (i < width - 1) + { + w[3] = *(src + prevline + 1); + w[6] = *(src + 1); + w[9] = *(src + nextline + 1); + } else { + w[3] = w[2]; + w[6] = w[5]; + w[9] = w[8]; + } + + int pattern = 0; + int flag = 1; + + YUV1 = yuv_palette[w[5]]; + + for (int k=1; k<=9; k++) + { + if (k==5) continue; + + if ( w[k] != w[5] ) + { + YUV2 = yuv_palette[w[k]]; + if ( ( abs((YUV1 & Ymask) - (YUV2 & Ymask)) > trY ) || + ( abs((YUV1 & Umask) - (YUV2 & Umask)) > trU ) || + ( abs((YUV1 & Vmask) - (YUV2 & Vmask)) > trV ) ) + pattern |= flag; + } + flag <<= 1; + } + + for (int k=1; k<=9; k++) + c[k] = rgb_palette[w[k]] & 0xfcfcfcfc; // hq4x has a nasty inability to accept more than 6 bits for each component + + switch (pattern) + { + case 0: + case 1: + case 4: + case 32: + case 128: + case 5: + case 132: + case 160: + case 33: + case 129: + case 36: + case 133: + case 164: + case 161: + case 37: + case 165: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_70 + PIXEL4_13_60 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 2: + case 34: + case 130: + case 162: + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 16: + case 17: + case 48: + case 49: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 64: + case 65: + case 68: + case 69: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_70 + PIXEL4_13_60 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 8: + case 12: + case 136: + case 140: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_70 + PIXEL4_13_60 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 3: + case 35: + case 131: + case 163: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 6: + case 38: + case 134: + case 166: + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 20: + case 21: + case 52: + case 53: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_31 + PIXEL4_13_31 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 144: + case 145: + case 176: + case 177: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 192: + case 193: + case 196: + case 197: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_70 + PIXEL4_13_60 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 96: + case 97: + case 100: + case 101: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_70 + PIXEL4_13_60 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 40: + case 44: + case 168: + case 172: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_70 + PIXEL4_13_60 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 9: + case 13: + case 137: + case 141: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_70 + PIXEL4_13_60 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 18: + case 50: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_12_0 + PIXEL4_13_50 + } + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 80: + case 81: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_61 + PIXEL4_21_30 + if (diff(w[6], w[8])) + { + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_22_0 + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 72: + case 76: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_70 + PIXEL4_13_60 + if (diff(w[8], w[4])) + { + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + } + else + { + PIXEL4_20_50 + PIXEL4_21_0 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 10: + case 138: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_10_10 + PIXEL4_11_30 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + PIXEL4_11_0 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 66: + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 24: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 7: + case 39: + case 135: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 148: + case 149: + case 180: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_31 + PIXEL4_13_31 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 224: + case 228: + case 225: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_70 + PIXEL4_13_60 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 41: + case 169: + case 45: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_70 + PIXEL4_13_60 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 22: + case 54: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_0 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 208: + case 209: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 104: + case 108: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_70 + PIXEL4_13_60 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 11: + case 139: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_11_0 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 19: + case 51: + { + if (diff(w[2], w[6])) + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + } + else + { + PIXEL4_00_12 + PIXEL4_01_14 + PIXEL4_02_83 + PIXEL4_03_50 + PIXEL4_12_70 + PIXEL4_13_21 + } + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 146: + case 178: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_23_32 + PIXEL4_33_82 + } + else + { + PIXEL4_02_21 + PIXEL4_03_50 + PIXEL4_12_70 + PIXEL4_13_83 + PIXEL4_23_13 + PIXEL4_33_11 + } + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_32 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_82 + break; + } + case 84: + case 85: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_81 + if (diff(w[6], w[8])) + { + PIXEL4_03_81 + PIXEL4_13_31 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_03_12 + PIXEL4_13_14 + PIXEL4_22_70 + PIXEL4_23_83 + PIXEL4_32_21 + PIXEL4_33_50 + } + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_31 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 112: + case 113: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_82 + PIXEL4_21_32 + if (diff(w[6], w[8])) + { + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_22_70 + PIXEL4_23_21 + PIXEL4_30_11 + PIXEL4_31_13 + PIXEL4_32_83 + PIXEL4_33_50 + } + break; + } + case 200: + case 204: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_70 + PIXEL4_13_60 + if (diff(w[8], w[4])) + { + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_31 + PIXEL4_33_81 + } + else + { + PIXEL4_20_21 + PIXEL4_21_70 + PIXEL4_30_50 + PIXEL4_31_83 + PIXEL4_32_14 + PIXEL4_33_12 + } + PIXEL4_22_31 + PIXEL4_23_81 + break; + } + case 73: + case 77: + { + if (diff(w[8], w[4])) + { + PIXEL4_00_82 + PIXEL4_10_32 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + } + else + { + PIXEL4_00_11 + PIXEL4_10_13 + PIXEL4_20_83 + PIXEL4_21_70 + PIXEL4_30_50 + PIXEL4_31_21 + } + PIXEL4_01_82 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_11_32 + PIXEL4_12_70 + PIXEL4_13_60 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 42: + case 170: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_20_31 + PIXEL4_30_81 + } + else + { + PIXEL4_00_50 + PIXEL4_01_21 + PIXEL4_10_83 + PIXEL4_11_70 + PIXEL4_20_14 + PIXEL4_30_12 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_21_31 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_31_81 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 14: + case 142: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_10 + PIXEL4_11_30 + } + else + { + PIXEL4_00_50 + PIXEL4_01_83 + PIXEL4_02_13 + PIXEL4_03_11 + PIXEL4_10_21 + PIXEL4_11_70 + } + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 67: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 70: + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 28: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_31 + PIXEL4_13_31 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 152: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 194: + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 98: + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 56: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 25: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 26: + case 31: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_11_0 + PIXEL4_12_0 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 82: + case 214: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_0 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 88: + case 248: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_30 + PIXEL4_13_10 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + break; + } + case 74: + case 107: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_11_0 + PIXEL4_12_30 + PIXEL4_13_61 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 27: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_11_0 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 86: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_0 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 216: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 106: + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_30 + PIXEL4_13_61 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 30: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_0 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 210: + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 120: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_30 + PIXEL4_13_10 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 75: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_11_0 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 29: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_31 + PIXEL4_13_31 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 198: + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 184: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 99: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 57: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 71: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 156: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_31 + PIXEL4_13_31 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 226: + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 60: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_31 + PIXEL4_13_31 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 195: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 102: + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 153: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 58: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_10_10 + PIXEL4_11_30 + } + else + { + PIXEL4_00_20 + PIXEL4_01_12 + PIXEL4_10_11 + PIXEL4_11_0 + } + if (diff(w[2], w[6])) + { + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + } + else + { + PIXEL4_02_11 + PIXEL4_03_20 + PIXEL4_12_0 + PIXEL4_13_12 + } + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 83: + { + PIXEL4_00_81 + PIXEL4_01_31 + if (diff(w[2], w[6])) + { + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + } + else + { + PIXEL4_02_11 + PIXEL4_03_20 + PIXEL4_12_0 + PIXEL4_13_12 + } + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_20_61 + PIXEL4_21_30 + if (diff(w[6], w[8])) + { + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_22_0 + PIXEL4_23_11 + PIXEL4_32_12 + PIXEL4_33_20 + } + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 92: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_31 + PIXEL4_13_31 + if (diff(w[8], w[4])) + { + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + } + else + { + PIXEL4_20_12 + PIXEL4_21_0 + PIXEL4_30_20 + PIXEL4_31_11 + } + if (diff(w[6], w[8])) + { + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_22_0 + PIXEL4_23_11 + PIXEL4_32_12 + PIXEL4_33_20 + } + break; + } + case 202: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_10_10 + PIXEL4_11_30 + } + else + { + PIXEL4_00_20 + PIXEL4_01_12 + PIXEL4_10_11 + PIXEL4_11_0 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_61 + if (diff(w[8], w[4])) + { + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + } + else + { + PIXEL4_20_12 + PIXEL4_21_0 + PIXEL4_30_20 + PIXEL4_31_11 + } + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 78: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_10_10 + PIXEL4_11_30 + } + else + { + PIXEL4_00_20 + PIXEL4_01_12 + PIXEL4_10_11 + PIXEL4_11_0 + } + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_12_32 + PIXEL4_13_82 + if (diff(w[8], w[4])) + { + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + } + else + { + PIXEL4_20_12 + PIXEL4_21_0 + PIXEL4_30_20 + PIXEL4_31_11 + } + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 154: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_10_10 + PIXEL4_11_30 + } + else + { + PIXEL4_00_20 + PIXEL4_01_12 + PIXEL4_10_11 + PIXEL4_11_0 + } + if (diff(w[2], w[6])) + { + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + } + else + { + PIXEL4_02_11 + PIXEL4_03_20 + PIXEL4_12_0 + PIXEL4_13_12 + } + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 114: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + } + else + { + PIXEL4_02_11 + PIXEL4_03_20 + PIXEL4_12_0 + PIXEL4_13_12 + } + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_20_82 + PIXEL4_21_32 + if (diff(w[6], w[8])) + { + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_22_0 + PIXEL4_23_11 + PIXEL4_32_12 + PIXEL4_33_20 + } + PIXEL4_30_82 + PIXEL4_31_32 + break; + } + case 89: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_30 + PIXEL4_13_10 + if (diff(w[8], w[4])) + { + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + } + else + { + PIXEL4_20_12 + PIXEL4_21_0 + PIXEL4_30_20 + PIXEL4_31_11 + } + if (diff(w[6], w[8])) + { + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_22_0 + PIXEL4_23_11 + PIXEL4_32_12 + PIXEL4_33_20 + } + break; + } + case 90: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_10_10 + PIXEL4_11_30 + } + else + { + PIXEL4_00_20 + PIXEL4_01_12 + PIXEL4_10_11 + PIXEL4_11_0 + } + if (diff(w[2], w[6])) + { + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + } + else + { + PIXEL4_02_11 + PIXEL4_03_20 + PIXEL4_12_0 + PIXEL4_13_12 + } + if (diff(w[8], w[4])) + { + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + } + else + { + PIXEL4_20_12 + PIXEL4_21_0 + PIXEL4_30_20 + PIXEL4_31_11 + } + if (diff(w[6], w[8])) + { + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_22_0 + PIXEL4_23_11 + PIXEL4_32_12 + PIXEL4_33_20 + } + break; + } + case 55: + case 23: + { + if (diff(w[2], w[6])) + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_12_0 + PIXEL4_13_0 + } + else + { + PIXEL4_00_12 + PIXEL4_01_14 + PIXEL4_02_83 + PIXEL4_03_50 + PIXEL4_12_70 + PIXEL4_13_21 + } + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 182: + case 150: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_12_0 + PIXEL4_13_0 + PIXEL4_23_32 + PIXEL4_33_82 + } + else + { + PIXEL4_02_21 + PIXEL4_03_50 + PIXEL4_12_70 + PIXEL4_13_83 + PIXEL4_23_13 + PIXEL4_33_11 + } + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_32 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_82 + break; + } + case 213: + case 212: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_81 + if (diff(w[6], w[8])) + { + PIXEL4_03_81 + PIXEL4_13_31 + PIXEL4_22_0 + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_03_12 + PIXEL4_13_14 + PIXEL4_22_70 + PIXEL4_23_83 + PIXEL4_32_21 + PIXEL4_33_50 + } + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_31 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 241: + case 240: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_82 + PIXEL4_21_32 + if (diff(w[6], w[8])) + { + PIXEL4_22_0 + PIXEL4_23_0 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_22_70 + PIXEL4_23_21 + PIXEL4_30_11 + PIXEL4_31_13 + PIXEL4_32_83 + PIXEL4_33_50 + } + break; + } + case 236: + case 232: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_70 + PIXEL4_13_60 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_21_0 + PIXEL4_30_0 + PIXEL4_31_0 + PIXEL4_32_31 + PIXEL4_33_81 + } + else + { + PIXEL4_20_21 + PIXEL4_21_70 + PIXEL4_30_50 + PIXEL4_31_83 + PIXEL4_32_14 + PIXEL4_33_12 + } + PIXEL4_22_31 + PIXEL4_23_81 + break; + } + case 109: + case 105: + { + if (diff(w[8], w[4])) + { + PIXEL4_00_82 + PIXEL4_10_32 + PIXEL4_20_0 + PIXEL4_21_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_00_11 + PIXEL4_10_13 + PIXEL4_20_83 + PIXEL4_21_70 + PIXEL4_30_50 + PIXEL4_31_21 + } + PIXEL4_01_82 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_11_32 + PIXEL4_12_70 + PIXEL4_13_60 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 171: + case 43: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + PIXEL4_11_0 + PIXEL4_20_31 + PIXEL4_30_81 + } + else + { + PIXEL4_00_50 + PIXEL4_01_21 + PIXEL4_10_83 + PIXEL4_11_70 + PIXEL4_20_14 + PIXEL4_30_12 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_21_31 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_31_81 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 143: + case 15: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_0 + PIXEL4_11_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_83 + PIXEL4_02_13 + PIXEL4_03_11 + PIXEL4_10_21 + PIXEL4_11_70 + } + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 124: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_31 + PIXEL4_13_31 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 203: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_11_0 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 62: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_0 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 211: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 118: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_0 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 217: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 110: + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_32 + PIXEL4_13_82 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 155: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_11_0 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 188: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_31 + PIXEL4_13_31 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 185: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 61: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_31 + PIXEL4_13_31 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 157: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_31 + PIXEL4_13_31 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 103: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 227: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 230: + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 199: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 220: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_31 + PIXEL4_13_31 + if (diff(w[8], w[4])) + { + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + } + else + { + PIXEL4_20_12 + PIXEL4_21_0 + PIXEL4_30_20 + PIXEL4_31_11 + } + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + break; + } + case 158: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_10_10 + PIXEL4_11_30 + } + else + { + PIXEL4_00_20 + PIXEL4_01_12 + PIXEL4_10_11 + PIXEL4_11_0 + } + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_12_0 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 234: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_10_10 + PIXEL4_11_30 + } + else + { + PIXEL4_00_20 + PIXEL4_01_12 + PIXEL4_10_11 + PIXEL4_11_0 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_61 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 242: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + } + else + { + PIXEL4_02_11 + PIXEL4_03_20 + PIXEL4_12_0 + PIXEL4_13_12 + } + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + PIXEL4_30_82 + PIXEL4_31_32 + break; + } + case 59: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + if (diff(w[2], w[6])) + { + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + } + else + { + PIXEL4_02_11 + PIXEL4_03_20 + PIXEL4_12_0 + PIXEL4_13_12 + } + PIXEL4_11_0 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 121: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_30 + PIXEL4_13_10 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + if (diff(w[6], w[8])) + { + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_22_0 + PIXEL4_23_11 + PIXEL4_32_12 + PIXEL4_33_20 + } + break; + } + case 87: + { + PIXEL4_00_81 + PIXEL4_01_31 + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_0 + PIXEL4_20_61 + PIXEL4_21_30 + if (diff(w[6], w[8])) + { + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_22_0 + PIXEL4_23_11 + PIXEL4_32_12 + PIXEL4_33_20 + } + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 79: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_11_0 + PIXEL4_12_32 + PIXEL4_13_82 + if (diff(w[8], w[4])) + { + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + } + else + { + PIXEL4_20_12 + PIXEL4_21_0 + PIXEL4_30_20 + PIXEL4_31_11 + } + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 122: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_10_10 + PIXEL4_11_30 + } + else + { + PIXEL4_00_20 + PIXEL4_01_12 + PIXEL4_10_11 + PIXEL4_11_0 + } + if (diff(w[2], w[6])) + { + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + } + else + { + PIXEL4_02_11 + PIXEL4_03_20 + PIXEL4_12_0 + PIXEL4_13_12 + } + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + if (diff(w[6], w[8])) + { + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_22_0 + PIXEL4_23_11 + PIXEL4_32_12 + PIXEL4_33_20 + } + break; + } + case 94: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_10_10 + PIXEL4_11_30 + } + else + { + PIXEL4_00_20 + PIXEL4_01_12 + PIXEL4_10_11 + PIXEL4_11_0 + } + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_12_0 + if (diff(w[8], w[4])) + { + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + } + else + { + PIXEL4_20_12 + PIXEL4_21_0 + PIXEL4_30_20 + PIXEL4_31_11 + } + if (diff(w[6], w[8])) + { + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_22_0 + PIXEL4_23_11 + PIXEL4_32_12 + PIXEL4_33_20 + } + break; + } + case 218: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_10_10 + PIXEL4_11_30 + } + else + { + PIXEL4_00_20 + PIXEL4_01_12 + PIXEL4_10_11 + PIXEL4_11_0 + } + if (diff(w[2], w[6])) + { + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + } + else + { + PIXEL4_02_11 + PIXEL4_03_20 + PIXEL4_12_0 + PIXEL4_13_12 + } + if (diff(w[8], w[4])) + { + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + } + else + { + PIXEL4_20_12 + PIXEL4_21_0 + PIXEL4_30_20 + PIXEL4_31_11 + } + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + break; + } + case 91: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + if (diff(w[2], w[6])) + { + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + } + else + { + PIXEL4_02_11 + PIXEL4_03_20 + PIXEL4_12_0 + PIXEL4_13_12 + } + PIXEL4_11_0 + if (diff(w[8], w[4])) + { + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + } + else + { + PIXEL4_20_12 + PIXEL4_21_0 + PIXEL4_30_20 + PIXEL4_31_11 + } + if (diff(w[6], w[8])) + { + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_22_0 + PIXEL4_23_11 + PIXEL4_32_12 + PIXEL4_33_20 + } + break; + } + case 229: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_70 + PIXEL4_13_60 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 167: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 173: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_70 + PIXEL4_13_60 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 181: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_31 + PIXEL4_13_31 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 186: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_10_10 + PIXEL4_11_30 + } + else + { + PIXEL4_00_20 + PIXEL4_01_12 + PIXEL4_10_11 + PIXEL4_11_0 + } + if (diff(w[2], w[6])) + { + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + } + else + { + PIXEL4_02_11 + PIXEL4_03_20 + PIXEL4_12_0 + PIXEL4_13_12 + } + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 115: + { + PIXEL4_00_81 + PIXEL4_01_31 + if (diff(w[2], w[6])) + { + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + } + else + { + PIXEL4_02_11 + PIXEL4_03_20 + PIXEL4_12_0 + PIXEL4_13_12 + } + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_20_82 + PIXEL4_21_32 + if (diff(w[6], w[8])) + { + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_22_0 + PIXEL4_23_11 + PIXEL4_32_12 + PIXEL4_33_20 + } + PIXEL4_30_82 + PIXEL4_31_32 + break; + } + case 93: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_31 + PIXEL4_13_31 + if (diff(w[8], w[4])) + { + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + } + else + { + PIXEL4_20_12 + PIXEL4_21_0 + PIXEL4_30_20 + PIXEL4_31_11 + } + if (diff(w[6], w[8])) + { + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_22_0 + PIXEL4_23_11 + PIXEL4_32_12 + PIXEL4_33_20 + } + break; + } + case 206: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_10_10 + PIXEL4_11_30 + } + else + { + PIXEL4_00_20 + PIXEL4_01_12 + PIXEL4_10_11 + PIXEL4_11_0 + } + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_12_32 + PIXEL4_13_82 + if (diff(w[8], w[4])) + { + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + } + else + { + PIXEL4_20_12 + PIXEL4_21_0 + PIXEL4_30_20 + PIXEL4_31_11 + } + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 205: + case 201: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_70 + PIXEL4_13_60 + if (diff(w[8], w[4])) + { + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + } + else + { + PIXEL4_20_12 + PIXEL4_21_0 + PIXEL4_30_20 + PIXEL4_31_11 + } + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 174: + case 46: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_10_10 + PIXEL4_11_30 + } + else + { + PIXEL4_00_20 + PIXEL4_01_12 + PIXEL4_10_11 + PIXEL4_11_0 + } + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 179: + case 147: + { + PIXEL4_00_81 + PIXEL4_01_31 + if (diff(w[2], w[6])) + { + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + } + else + { + PIXEL4_02_11 + PIXEL4_03_20 + PIXEL4_12_0 + PIXEL4_13_12 + } + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 117: + case 116: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_31 + PIXEL4_13_31 + PIXEL4_20_82 + PIXEL4_21_32 + if (diff(w[6], w[8])) + { + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + } + else + { + PIXEL4_22_0 + PIXEL4_23_11 + PIXEL4_32_12 + PIXEL4_33_20 + } + PIXEL4_30_82 + PIXEL4_31_32 + break; + } + case 189: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_31 + PIXEL4_13_31 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 231: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 126: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_0 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 219: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_11_0 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 125: + { + if (diff(w[8], w[4])) + { + PIXEL4_00_82 + PIXEL4_10_32 + PIXEL4_20_0 + PIXEL4_21_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_00_11 + PIXEL4_10_13 + PIXEL4_20_83 + PIXEL4_21_70 + PIXEL4_30_50 + PIXEL4_31_21 + } + PIXEL4_01_82 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_11_32 + PIXEL4_12_31 + PIXEL4_13_31 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 221: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_81 + if (diff(w[6], w[8])) + { + PIXEL4_03_81 + PIXEL4_13_31 + PIXEL4_22_0 + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_03_12 + PIXEL4_13_14 + PIXEL4_22_70 + PIXEL4_23_83 + PIXEL4_32_21 + PIXEL4_33_50 + } + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_31 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 207: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_0 + PIXEL4_11_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_83 + PIXEL4_02_13 + PIXEL4_03_11 + PIXEL4_10_21 + PIXEL4_11_70 + } + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_31 + PIXEL4_23_81 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 238: + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_32 + PIXEL4_13_82 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_21_0 + PIXEL4_30_0 + PIXEL4_31_0 + PIXEL4_32_31 + PIXEL4_33_81 + } + else + { + PIXEL4_20_21 + PIXEL4_21_70 + PIXEL4_30_50 + PIXEL4_31_83 + PIXEL4_32_14 + PIXEL4_33_12 + } + PIXEL4_22_31 + PIXEL4_23_81 + break; + } + case 190: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_12_0 + PIXEL4_13_0 + PIXEL4_23_32 + PIXEL4_33_82 + } + else + { + PIXEL4_02_21 + PIXEL4_03_50 + PIXEL4_12_70 + PIXEL4_13_83 + PIXEL4_23_13 + PIXEL4_33_11 + } + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_32 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_82 + break; + } + case 187: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + PIXEL4_11_0 + PIXEL4_20_31 + PIXEL4_30_81 + } + else + { + PIXEL4_00_50 + PIXEL4_01_21 + PIXEL4_10_83 + PIXEL4_11_70 + PIXEL4_20_14 + PIXEL4_30_12 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_21_31 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_31_81 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 243: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_82 + PIXEL4_21_32 + if (diff(w[6], w[8])) + { + PIXEL4_22_0 + PIXEL4_23_0 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_22_70 + PIXEL4_23_21 + PIXEL4_30_11 + PIXEL4_31_13 + PIXEL4_32_83 + PIXEL4_33_50 + } + break; + } + case 119: + { + if (diff(w[2], w[6])) + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_12_0 + PIXEL4_13_0 + } + else + { + PIXEL4_00_12 + PIXEL4_01_14 + PIXEL4_02_83 + PIXEL4_03_50 + PIXEL4_12_70 + PIXEL4_13_21 + } + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 237: + case 233: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_60 + PIXEL4_03_20 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_70 + PIXEL4_13_60 + PIXEL4_20_0 + PIXEL4_21_0 + PIXEL4_22_31 + PIXEL4_23_81 + if (diff(w[8], w[4])) + { + PIXEL4_30_0 + } + else + { + PIXEL4_30_20 + } + PIXEL4_31_0 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 175: + case 47: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + } + else + { + PIXEL4_00_20 + } + PIXEL4_01_0 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_0 + PIXEL4_11_0 + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_70 + PIXEL4_23_60 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_60 + PIXEL4_33_20 + break; + } + case 183: + case 151: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_0 + if (diff(w[2], w[6])) + { + PIXEL4_03_0 + } + else + { + PIXEL4_03_20 + } + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_0 + PIXEL4_13_0 + PIXEL4_20_60 + PIXEL4_21_70 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_20 + PIXEL4_31_60 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 245: + case 244: + { + PIXEL4_00_20 + PIXEL4_01_60 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_60 + PIXEL4_11_70 + PIXEL4_12_31 + PIXEL4_13_31 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_0 + PIXEL4_23_0 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_0 + if (diff(w[6], w[8])) + { + PIXEL4_33_0 + } + else + { + PIXEL4_33_20 + } + break; + } + case 250: + { + PIXEL4_00_80 + PIXEL4_01_10 + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_30 + PIXEL4_13_10 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + break; + } + case 123: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_11_0 + PIXEL4_12_30 + PIXEL4_13_10 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 95: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_11_0 + PIXEL4_12_0 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_80 + PIXEL4_31_10 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 222: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_0 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 252: + { + PIXEL4_00_80 + PIXEL4_01_61 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_31 + PIXEL4_13_31 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + PIXEL4_22_0 + PIXEL4_23_0 + PIXEL4_32_0 + if (diff(w[6], w[8])) + { + PIXEL4_33_0 + } + else + { + PIXEL4_33_20 + } + break; + } + case 249: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_61 + PIXEL4_03_80 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_0 + PIXEL4_21_0 + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + if (diff(w[8], w[4])) + { + PIXEL4_30_0 + } + else + { + PIXEL4_30_20 + } + PIXEL4_31_0 + break; + } + case 235: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_11_0 + PIXEL4_12_30 + PIXEL4_13_61 + PIXEL4_20_0 + PIXEL4_21_0 + PIXEL4_22_31 + PIXEL4_23_81 + if (diff(w[8], w[4])) + { + PIXEL4_30_0 + } + else + { + PIXEL4_30_20 + } + PIXEL4_31_0 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 111: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + } + else + { + PIXEL4_00_20 + } + PIXEL4_01_0 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_0 + PIXEL4_11_0 + PIXEL4_12_32 + PIXEL4_13_82 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + PIXEL4_22_30 + PIXEL4_23_61 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 63: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + } + else + { + PIXEL4_00_20 + } + PIXEL4_01_0 + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_10_0 + PIXEL4_11_0 + PIXEL4_12_0 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_61 + PIXEL4_33_80 + break; + } + case 159: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + PIXEL4_02_0 + if (diff(w[2], w[6])) + { + PIXEL4_03_0 + } + else + { + PIXEL4_03_20 + } + PIXEL4_11_0 + PIXEL4_12_0 + PIXEL4_13_0 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_80 + PIXEL4_31_61 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 215: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_0 + if (diff(w[2], w[6])) + { + PIXEL4_03_0 + } + else + { + PIXEL4_03_20 + } + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_0 + PIXEL4_13_0 + PIXEL4_20_61 + PIXEL4_21_30 + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 246: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_10_61 + PIXEL4_11_30 + PIXEL4_12_0 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_0 + PIXEL4_23_0 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_0 + if (diff(w[6], w[8])) + { + PIXEL4_33_0 + } + else + { + PIXEL4_33_20 + } + break; + } + case 254: + { + PIXEL4_00_80 + PIXEL4_01_10 + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_10_10 + PIXEL4_11_30 + PIXEL4_12_0 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + PIXEL4_22_0 + PIXEL4_23_0 + PIXEL4_32_0 + if (diff(w[6], w[8])) + { + PIXEL4_33_0 + } + else + { + PIXEL4_33_20 + } + break; + } + case 253: + { + PIXEL4_00_82 + PIXEL4_01_82 + PIXEL4_02_81 + PIXEL4_03_81 + PIXEL4_10_32 + PIXEL4_11_32 + PIXEL4_12_31 + PIXEL4_13_31 + PIXEL4_20_0 + PIXEL4_21_0 + PIXEL4_22_0 + PIXEL4_23_0 + if (diff(w[8], w[4])) + { + PIXEL4_30_0 + } + else + { + PIXEL4_30_20 + } + PIXEL4_31_0 + PIXEL4_32_0 + if (diff(w[6], w[8])) + { + PIXEL4_33_0 + } + else + { + PIXEL4_33_20 + } + break; + } + case 251: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + PIXEL4_02_10 + PIXEL4_03_80 + PIXEL4_11_0 + PIXEL4_12_30 + PIXEL4_13_10 + PIXEL4_20_0 + PIXEL4_21_0 + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + if (diff(w[8], w[4])) + { + PIXEL4_30_0 + } + else + { + PIXEL4_30_20 + } + PIXEL4_31_0 + break; + } + case 239: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + } + else + { + PIXEL4_00_20 + } + PIXEL4_01_0 + PIXEL4_02_32 + PIXEL4_03_82 + PIXEL4_10_0 + PIXEL4_11_0 + PIXEL4_12_32 + PIXEL4_13_82 + PIXEL4_20_0 + PIXEL4_21_0 + PIXEL4_22_31 + PIXEL4_23_81 + if (diff(w[8], w[4])) + { + PIXEL4_30_0 + } + else + { + PIXEL4_30_20 + } + PIXEL4_31_0 + PIXEL4_32_31 + PIXEL4_33_81 + break; + } + case 127: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + } + else + { + PIXEL4_00_20 + } + PIXEL4_01_0 + if (diff(w[2], w[6])) + { + PIXEL4_02_0 + PIXEL4_03_0 + PIXEL4_13_0 + } + else + { + PIXEL4_02_50 + PIXEL4_03_50 + PIXEL4_13_50 + } + PIXEL4_10_0 + PIXEL4_11_0 + PIXEL4_12_0 + if (diff(w[8], w[4])) + { + PIXEL4_20_0 + PIXEL4_30_0 + PIXEL4_31_0 + } + else + { + PIXEL4_20_50 + PIXEL4_30_50 + PIXEL4_31_50 + } + PIXEL4_21_0 + PIXEL4_22_30 + PIXEL4_23_10 + PIXEL4_32_10 + PIXEL4_33_80 + break; + } + case 191: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + } + else + { + PIXEL4_00_20 + } + PIXEL4_01_0 + PIXEL4_02_0 + if (diff(w[2], w[6])) + { + PIXEL4_03_0 + } + else + { + PIXEL4_03_20 + } + PIXEL4_10_0 + PIXEL4_11_0 + PIXEL4_12_0 + PIXEL4_13_0 + PIXEL4_20_31 + PIXEL4_21_31 + PIXEL4_22_32 + PIXEL4_23_32 + PIXEL4_30_81 + PIXEL4_31_81 + PIXEL4_32_82 + PIXEL4_33_82 + break; + } + case 223: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + PIXEL4_01_0 + PIXEL4_10_0 + } + else + { + PIXEL4_00_50 + PIXEL4_01_50 + PIXEL4_10_50 + } + PIXEL4_02_0 + if (diff(w[2], w[6])) + { + PIXEL4_03_0 + } + else + { + PIXEL4_03_20 + } + PIXEL4_11_0 + PIXEL4_12_0 + PIXEL4_13_0 + PIXEL4_20_10 + PIXEL4_21_30 + PIXEL4_22_0 + if (diff(w[6], w[8])) + { + PIXEL4_23_0 + PIXEL4_32_0 + PIXEL4_33_0 + } + else + { + PIXEL4_23_50 + PIXEL4_32_50 + PIXEL4_33_50 + } + PIXEL4_30_80 + PIXEL4_31_10 + break; + } + case 247: + { + PIXEL4_00_81 + PIXEL4_01_31 + PIXEL4_02_0 + if (diff(w[2], w[6])) + { + PIXEL4_03_0 + } + else + { + PIXEL4_03_20 + } + PIXEL4_10_81 + PIXEL4_11_31 + PIXEL4_12_0 + PIXEL4_13_0 + PIXEL4_20_82 + PIXEL4_21_32 + PIXEL4_22_0 + PIXEL4_23_0 + PIXEL4_30_82 + PIXEL4_31_32 + PIXEL4_32_0 + if (diff(w[6], w[8])) + { + PIXEL4_33_0 + } + else + { + PIXEL4_33_20 + } + break; + } + case 255: + { + if (diff(w[4], w[2])) + { + PIXEL4_00_0 + } + else + { + PIXEL4_00_20 + } + PIXEL4_01_0 + PIXEL4_02_0 + if (diff(w[2], w[6])) + { + PIXEL4_03_0 + } + else + { + PIXEL4_03_20 + } + PIXEL4_10_0 + PIXEL4_11_0 + PIXEL4_12_0 + PIXEL4_13_0 + PIXEL4_20_0 + PIXEL4_21_0 + PIXEL4_22_0 + PIXEL4_23_0 + if (diff(w[8], w[4])) + { + PIXEL4_30_0 + } + else + { + PIXEL4_30_20 + } + PIXEL4_31_0 + PIXEL4_32_0 + if (diff(w[6], w[8])) + { + PIXEL4_33_0 + } + else + { + PIXEL4_33_20 + } + break; + } + } + + src++; + dst += 4 * dst_Bpp; + } + + src = src_temp + src_pitch; + dst = dst_temp + 4 * dst_pitch; + } +} + +// kate: tab-width 4; vim: set noet: diff --git a/contrib/games/opentyrian/src/xmas.c b/contrib/games/opentyrian/src/xmas.c new file mode 100644 index 000000000..5b65f537a --- /dev/null +++ b/contrib/games/opentyrian/src/xmas.c @@ -0,0 +1,100 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#include "xmas.h" + +#include "font.h" +#include "keyboard.h" +#include "palette.h" +#include "setup.h" +#include "sprite.h" +#include "video.h" + +#include +#include + +bool xmas = false; + +bool xmas_time( void ) +{ + time_t now = time(NULL); + return localtime(&now)->tm_mon == 11; +} + +bool xmas_prompt( void ) +{ + const char *prompt[] = + { + "Christmas has been detected.", + "Activate Christmas?", + }; + const char *choice[] = + { + "Yes", + "No", + }; + + set_palette(palettes[0], 0, 255); + + for (uint i = 0; i < COUNTOF(prompt); ++i) + draw_font_hv(VGAScreen, 320 / 2, 85 + 15 * i, prompt[i], normal_font, centered, (i % 2) ? 2 : 4, -2); + + uint selection = 0; + + bool decided = false, quit = false; + while (!decided) + { + for (uint i = 0; i < COUNTOF(choice); ++i) + draw_font_hv(VGAScreen, 320 / 2 - 20 + 40 * i, 120, choice[i], normal_font, centered, 15, (selection == i) ? -2 : -4); + + JE_showVGA(); + + JE_word temp = 0; + JE_textMenuWait(&temp, false); + + if (newkey) + { + switch (lastkey_sym) + { + case SDLK_LEFT: + if (selection == 0) + selection = 2; + selection--; + break; + case SDLK_RIGHT: + selection++; + selection %= 2; + break; + + case SDLK_RETURN: + decided = true; + break; + case SDLK_ESCAPE: + decided = true; + quit = true; + break; + default: + break; + } + } + } + + fade_black(10); + + return (selection == 0 && quit == false); +} diff --git a/contrib/games/opentyrian/src/xmas.h b/contrib/games/opentyrian/src/xmas.h new file mode 100644 index 000000000..8e19c8823 --- /dev/null +++ b/contrib/games/opentyrian/src/xmas.h @@ -0,0 +1,30 @@ +/* + * OpenTyrian: A modern cross-platform port of Tyrian + * Copyright (C) 2007-2009 The OpenTyrian Development Team + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef XMAS_H +#define XMAS_H + +#include + +extern bool xmas; + +bool xmas_time( void ); +bool xmas_prompt( void ); + +#endif /* XMAS_H */ +