diff --git a/Makefile b/Makefile index d464203..d56dd76 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,5 @@ -SUBDIRS = src/instead src/sdl-instead stead games themes +#SUBDIRS = src/sdl-instead stead games themes icon desktop doc +SUBDIRS = src/sdl-instead stead games themes icon desktop doc all: @for dir in $(SUBDIRS); do \ diff --git a/Makefile.windows b/Makefile.windows new file mode 100644 index 0000000..26ff06e --- /dev/null +++ b/Makefile.windows @@ -0,0 +1,18 @@ +MAKE=mingw32-make + +all: + $(MAKE) -C src\sdl-instead all + $(MAKE) -C themes -f Makefile.windows all +clean: + $(MAKE) -C src\sdl-instead clean + +install: all + if not exist bin mkdir bin + copy src\sdl-instead\sdl-instead.exe bin + $(MAKE) -C themes -f Makefile.windows install + $(MAKE) -C games -f Makefile.windows install + $(MAKE) -C stead -f Makefile.windows install + $(MAKE) -C icon -f Makefile.windows install + $(MAKE) -C doc -f Makefile.windows install + + diff --git a/Rules.make b/Rules.make index ea604df..d0f4e26 120000 --- a/Rules.make +++ b/Rules.make @@ -1 +1 @@ -Rules.make.system \ No newline at end of file +Rules.make.standalone \ No newline at end of file diff --git a/Rules.make.standalone b/Rules.make.standalone index b79aef5..2da57e7 100644 --- a/Rules.make.standalone +++ b/Rules.make.standalone @@ -1,23 +1,29 @@ -VERSION := \"0.7\" +VERSION := \"0.8.5\" DESTDIR= BIN= STEADPATH=./stead THEMESPATH=./themes GAMESPATH=./games +ICONPATH=./icon LUA_CFLAGS=$(shell pkg-config --cflags lua5.1) LUA_LFLAGS=$(shell pkg-config --libs lua5.1) -# for arch linux use this +# for arch linux (may be others) use this # -#LUA_CFLAGS=$(shell pkg-config --cflags lua) -#LUA_LFLAGS=$(shell pkg-config --libs lua) +# LUA_CFLAGS=$(shell pkg-config --cflags lua) +# LUA_LFLAGS=$(shell pkg-config --libs lua) # SDL_CFLAGS=$(shell sdl-config --cflags) SDL_LFLAGS=$(shell sdl-config --libs) -lSDL_ttf -lSDL_mixer -lSDL_image -CFLAGS += -g -Wall -DHAVE_ICONV -DRUSSIAN +CFLAGS += -g -Wall -D_HAVE_ICONV -DRUSSIAN +INSTALLD=echo +INSTALL=echo +EXE= +PLATFORM=unix.c +RESOURCES= diff --git a/Rules.make.system b/Rules.make.system index 3f991cf..5f7ff96 100644 --- a/Rules.make.system +++ b/Rules.make.system @@ -1,10 +1,11 @@ -VERSION := \"0.7.4\" +VERSION := \"0.8.5\" DESTDIR=/usr/local -BIN=$(DESTDIR)/bin +BIN=$(DESTDIR)/bin/ STEADPATH=$(DESTDIR)/share/stead -THEMESPATH=$(DESTDIR)/share/stead/themes -GAMESPATH=$(DESTDIR)/share/stead/games +THEMESPATH=$(STEADPATH)/themes +GAMESPATH=$(STEADPATH)/games +ICONPATH=$(DESTDIR)/share/pixmaps LUA_CFLAGS=$(shell pkg-config --cflags lua5.1) LUA_LFLAGS=$(shell pkg-config --libs lua5.1) @@ -18,6 +19,11 @@ LUA_LFLAGS=$(shell pkg-config --libs lua5.1) SDL_CFLAGS=$(shell sdl-config --cflags) SDL_LFLAGS=$(shell sdl-config --libs) -lSDL_ttf -lSDL_mixer -lSDL_image -CFLAGS += -Wall -DHAVE_ICONV -DRUSSIAN +CFLAGS += -Wall -D_HAVE_ICONV -DRUSSIAN +INSTALLD=install -d -m 0775 +INSTALL=install -m 0775 +EXE= +PLATFORM=unix.c +RESOURCES= diff --git a/Rules.windows b/Rules.windows new file mode 100644 index 0000000..578359b --- /dev/null +++ b/Rules.windows @@ -0,0 +1,30 @@ +VERSION := \"0.8.5\" + +DESTDIR= +BIN=..\..\bin\\ + +STEADPATH=./stead +THEMESPATH=./themes/ +GAMESPATH=./games +ICONPATH=./icon + +LUA_CFLAGS= +LUA_LFLAGS=-llua5.1 + +SDL_CFLAGS=-IC:\MinGW\include\SDL +SDL_LFLAGS=-lSDL -lSDLmain -lSDL_ttf -lSDL_mixer -lSDL_image + +CFLAGS += -Wall -DRUSSIAN -mwindows -D_HAVE_ICONV +LDFLAGS += -liconv + +CC=gcc +RM=del +EXE=.exe + +INSTALLD=echo mkdir +INSTALL=copy +PLATFORM=windows.c +RESOURCES=resources.o + +resources.o: resources.rc + windres -i resources.rc -o resources.o diff --git a/configure.bat b/configure.bat new file mode 100644 index 0000000..672502a --- /dev/null +++ b/configure.bat @@ -0,0 +1 @@ +copy Rules.windows Rules.make diff --git a/debian/README.Debian b/debian/README.Debian new file mode 100644 index 0000000..3b9b9c5 --- /dev/null +++ b/debian/README.Debian @@ -0,0 +1,6 @@ +instead for Debian +------------------ + + + + -- Peter Kosyh Thu, 21 May 2009 11:40:29 +0400 diff --git a/debian/changelog b/debian/changelog new file mode 100644 index 0000000..9f8492a --- /dev/null +++ b/debian/changelog @@ -0,0 +1,99 @@ +instead (0.8.5) unstable; urgency=low + + * possible segfaults in timer functions. + * ways, objs, drop, take - extra parameters + * put - function + * set_music fixes + + -- Peter Kosyh Thu, 24 Aug 2009 08:32:00 +0400 + +instead (0.8.4) unstable; urgency=low + + * bug in savevar + * bug in long strings + + -- Peter Kosyh Thu, 20 Aug 2009 14:09:14 +0400 + +instead (0.8.3) unstable; urgency=low + + * sge forgotten + * set_music with loop argument bug + + -- Peter Kosyh Wed, 19 Aug 2009 14:09:14 +0400 + +instead (0.8.2) unstable; urgency=low + + * gfx_load_img + + -- Peter Kosyh Sat, 15 Aug 2009 14:09:14 +0400 + +instead (0.8) unstable; urgency=low + + * key_name attribute!!!! + * do_ini + * version in stead.lua + * opairs!!! + * vroom fix + * typos + * backtrace + * list_set + + -- Peter Kosyh Fri, 07 Aug 2009 14:09:14 +0400 + +instead (0.7.7-4) unstable; urgency=low + + * Fixes :) + + -- Peter Kosyh Wed, 05 Aug 2009 17:26:55 +0400 + +instead (0.7.7) unstable; urgency=low + + * typos in games + * local games now can be placed in ~/.instead/games + * console version disabled by default + * -- $Name: tag -- full name of games, not only dirname + * fix of link color for title/ways + * bug in text layout logic (last period in line) + * embedded mode (picture and ways inside text!!!) + * float mode (for books) + * faster text renderer (x2-x5 times) + * free motion mode of scrolling + * click sound support in themes (in default theme too) + * new options (click sound, music) + * default theme changes + * fullscreen with Alt-Enter + * options -alsa, -fullscreen, -window + * now italic font used for events + * 48000Hz added in sound preferences + * now scroll position is saved in embedded mode + * themes support, one new theme added + * vway and vroom added + * updated tutorial + * set_music now take an loop parameter + * autosave is now default + * now vars saved if they begins from uppercase letter (undescore too); + * save slots !!! + * a lot of bugs fixed (memory leaks, SDL cavities.. ) + + -- Peter Kosyh Thu, 30 Jul 2009 18:12:19 +0400 + +instead (0.7.6-1) unstable; urgency=low + + * debian file fixes + + -- Peter Kosyh Fri, 29 May 2009 14:17:01 +0400 + +instead (0.7.6) unstable; urgency=low + + * Keyboard scrolling + * Font scaling + * inv.horiz option + + -- Peter Kosyh Mon, 25 May 2009 10:58:29 +0400 + +instead (0.7.5) unstable; urgency=low + + * Initial release + + -- Peter Kosyh Thu, 21 May 2009 11:40:29 +0400 + diff --git a/debian/changelog.txt b/debian/changelog.txt new file mode 100644 index 0000000..3e2e87b --- /dev/null +++ b/debian/changelog.txt @@ -0,0 +1,21 @@ + * опечатки + * 3 режима размещения картинки (в том числе и встроенный в текст) + * механизм выбора тем представлен в меню и настройках + * режим свободного скроллинга (таскание мышкой) + * ускорение отрисовки текста (x4-x5 раз) + * слоты для загрузки/сохранения игр + * звук на реакцию движка + * выделение наклонным шрифтом событий сцены + * изменена тема по умолчанию (картинка теперь не мешает тексту, так как скроллируется с ним), + * добавлена тема 'Книга' -- оптимальная для книг-игр с вертикальными картинками + * позиция скроллирования не сбрасывается при реакции движка + * фильтр событий мыши (случайные двойные клики и т.д.) + * полноэкранный режим по Alt-Enter + * другие улучшения интерфейса + + * обновлена документация (новые объекты, расширения движка) + * игры и темы кроме системного каталога ищутся также в ~/.instead/games ~/.instead/themes (unix версия) + * консольная версия не входит в поставку по умолчанию + * параметры командной строки: -alsa, -fullscreen, -window + * исправлено множество ошибок + diff --git a/debian/compat b/debian/compat new file mode 100644 index 0000000..7f8f011 --- /dev/null +++ b/debian/compat @@ -0,0 +1 @@ +7 diff --git a/debian/control b/debian/control new file mode 100644 index 0000000..28dda17 --- /dev/null +++ b/debian/control @@ -0,0 +1,14 @@ +Source: instead +Section: games +Priority: optional +Maintainer: Peter Kosyh +Build-Depends: debhelper (>= 7), pkg-config, liblua5.1-dev, libsdl1.2-dev, libsdl-ttf2.0-dev, libsdl-image1.2-dev, libsdl-mixer1.2-dev, libreadline5-dev +Standards-Version: 3.7.3 +Homepage: http://instead.googlecode.com + +Package: instead +Architecture: any +Depends: ${shlibs:Depends}, ${misc:Depends} +Description: instead, simply text adventures/visual novels engine and game. + Visual novell/text quest-like game in russian with engine. + diff --git a/debian/copyright b/debian/copyright new file mode 100644 index 0000000..101a5e7 --- /dev/null +++ b/debian/copyright @@ -0,0 +1,24 @@ +This package was debianized by Peter Kosyh on +Thu, 21 May 2009 11:40:29 +0400. + +It was downloaded from + +Upstream Author(s): + + + + +Copyright: + + + + +License: + + + +The Debian packaging is (C) 2009, Peter Kosyh and +is licensed under the GPL, see `/usr/share/common-licenses/GPL'. + +# Please also look if there are files or directories which have a +# different copyright/license attached and list them here. diff --git a/debian/cron.d.ex b/debian/cron.d.ex new file mode 100644 index 0000000..c3bc3de --- /dev/null +++ b/debian/cron.d.ex @@ -0,0 +1,4 @@ +# +# Regular cron jobs for the instead package +# +0 4 * * * root [ -x /usr/bin/instead_maintenance ] && /usr/bin/instead_maintenance diff --git a/debian/docs b/debian/docs new file mode 100644 index 0000000..f3d4ef2 --- /dev/null +++ b/debian/docs @@ -0,0 +1,2 @@ +readme.txt +readme.txt diff --git a/debian/rules b/debian/rules new file mode 100755 index 0000000..b1f4b47 --- /dev/null +++ b/debian/rules @@ -0,0 +1,100 @@ +#!/usr/bin/make -f +# -*- makefile -*- +# Sample debian/rules that uses debhelper. +# This file was originally written by Joey Hess and Craig Small. +# As a special exception, when this file is copied by dh-make into a +# dh-make output file, you may use that output file without restriction. +# This special exception was added by Craig Small in version 0.37 of dh-make. + +# Uncomment this to turn on verbose mode. +#export DH_VERBOSE=1 + + + + + +configure: configure-stamp +configure-stamp: + dh_testdir + # Add here commands to configure the package. + + touch configure-stamp + + +build: build-stamp + +build-stamp: configure-stamp + dh_testdir + + # Add here commands to compile the package. + rm -rf Rules.make + ln -s Rules.make.system Rules.make + $(MAKE) DESTDIR=/usr BIN=/usr/games STEADPATH=/usr/share/games/stead + rm -rf Rules.make + #docbook-to-man debian/instead.sgml > instead.1 + touch $@ + +clean: + dh_testdir + dh_testroot + rm -f build-stamp configure-stamp + + # Add here commands to clean up after the build process. + rm -rf Rules.make + ln -s Rules.make.system Rules.make + $(MAKE) clean + rm -rf Rules.make + ln -s Rules.make.standalone Rules.make + + dh_clean + +install: build + dh_testdir + dh_testroot + dh_clean -k + dh_installdirs + + # Add here commands to install the package into debian/instead. + rm -rf Rules.make + ln -s Rules.make.system Rules.make + $(MAKE) install DESTDIR=$(CURDIR)/debian/instead/usr STEADPATH=$(CURDIR)/debian/instead/usr/share/games/stead BIN=$(CURDIR)/debian/instead/usr/games ICONPATH=$(CURDIR)/debian/instead/usr/share/pixmaps + rm -rf Rules.make + + +# Build architecture-independent files here. +binary-indep: build install +# We have nothing to do by default. + +# Build architecture-dependent files here. +binary-arch: build install + dh_testdir + dh_testroot + dh_installchangelogs + dh_installdocs + dh_installexamples +# dh_install +# dh_installmenu +# dh_installdebconf +# dh_installlogrotate +# dh_installemacsen +# dh_installpam +# dh_installmime +# dh_python +# dh_installinit +# dh_installcron +# dh_installinfo + dh_installman + dh_link + dh_strip + dh_compress + dh_fixperms +# dh_perl +# dh_makeshlibs + dh_installdeb + dh_shlibdeps + dh_gencontrol + dh_md5sums + dh_builddeb + +binary: binary-indep binary-arch +.PHONY: build clean binary-indep binary-arch binary install configure diff --git a/desktop/Makefile b/desktop/Makefile new file mode 100644 index 0000000..454c754 --- /dev/null +++ b/desktop/Makefile @@ -0,0 +1,14 @@ +include ../Rules.make +clean: + rm -rf instead.desktop + +all: instead.desktop + +instead.desktop: instead.desktop.in + cat instead.desktop.in | sed -e "s|@BIN|$(BIN)|g" > instead.desktop + +install: + install -d -m 0755 $(DESTDIR)/share/applications + install -m 0644 instead.desktop $(DESTDIR)/share/applications/instead.desktop + install -m 0644 instead.desktop $(DESTDIR)/share/applications/instead.desktop + diff --git a/desktop/Makefile.windows b/desktop/Makefile.windows new file mode 100644 index 0000000..263836e --- /dev/null +++ b/desktop/Makefile.windows @@ -0,0 +1,3 @@ +clean: +all: +install: diff --git a/desktop/instead.desktop.in b/desktop/instead.desktop.in new file mode 100644 index 0000000..482ce9c --- /dev/null +++ b/desktop/instead.desktop.in @@ -0,0 +1,13 @@ +[Desktop Entry] +Encoding=UTF-8 +Version=0.8.5 +Type=Application +Name=INSTEAD +Name[ru]=INSTEAD +Comment=Siple Text Adventures Interpretor +Comment[ru]=Интерпретатор простых приключений +Exec=@BIN/sdl-instead +Icon=sdl_instead.png +Terminal=false +Categories=Game;LogicGame; + diff --git a/doc/Makefile b/doc/Makefile new file mode 100644 index 0000000..5725b45 --- /dev/null +++ b/doc/Makefile @@ -0,0 +1,7 @@ +include ../Rules.make +clean: +all: +install: + install -d -m0755 $(DESTDIR)/share/doc/instead + install -m0644 *.jpg *.html *.txt $(DESTDIR)/share/doc/instead + diff --git a/doc/Makefile.windows b/doc/Makefile.windows new file mode 100644 index 0000000..5838a1a --- /dev/null +++ b/doc/Makefile.windows @@ -0,0 +1,8 @@ +clean: +all: +install: + if not exist ..\bin\doc mkdir ..\bin\doc + copy /Y *.txt ..\bin\doc + copy /Y *.png ..\bin\doc + copy /Y *.html ..\bin\doc + copy /Y *.jpg ..\bin\doc diff --git a/doc/index.html b/doc/index.html index 4b5e1a1..e25e6b3 100644 --- a/doc/index.html +++ b/doc/index.html @@ -4,7 +4,7 @@
[ Что это такое? ] [ Скриншоты ] [ Скачать ]

-

INSTEAD 0.7 -- интерпретатор простых текстовых приключений для Unix

+

INSTEAD 0.8 -- интерпретатор простых текстовых приключений для Unix

Что это такое?

Интерпретатор STEAD (Simply Text Adventure) позволяет проигрывать игры, которые по жанру являются смесью визуальной новеллы, текстового квеста и классических квестов 90-х. Особенности STEAD игры:

@@ -15,8 +15,7 @@
  • поддержка тем для графического интерпретатора -- конкретная игра может менять вид интерфейса;
  • переносимость (изначально написана для Linux, зависит от SDL и lua). -

    На данный момент автором STEAD написана одна игра: 'Возвращение Квантового Кота', которая входит в состав архива с исходным кодом. -Игра содержит около 70 сцен, графику и oldschool треки.

    +

    В архив с интерпретатором входит игра: 'Возвращение Квантового Кота'. Игра содержит около 70 сцен, графику и oldschool треки.

    Скриншоты

    Работа графического интерпретатора.

    @@ -31,7 +30,7 @@

    Скачать

    -Скачать текущую версию можно здесь: http://sites.google.com/site/sdlinstead/ +Скачать текущую версию и другие игры для INSTEAD можно здесь: http://instead.googlecode.com/
    [ Что это такое? ] [ Скриншоты ] [ Скачать ]
    diff --git a/doc/writing_games.txt b/doc/writing_games.txt new file mode 100644 index 0000000..fcffce1 --- /dev/null +++ b/doc/writing_games.txt @@ -0,0 +1,964 @@ +== 0. Общие сведения == + +Код игр для STEAD пишется на lua (5.1), поэтому, знание этого языка полезно, хотя и не необходимо. Код движка на lua занимает всего ~900 строк и лучшей документацией, конечно, является изучение его кода. + +Главное окно игры содержит информацию о статической и динамической части сцены, активные события и картинку сцены с возможными переходами в другие сцены (в графическом интерпретаторе). + +Динамическая часть сцены составлена из описаний объектов сцены, она отображается всегда. + +Статическая часть сцены отображается только один раз, при показе сцены, или при повторении команды look (в графическом интерпретаторе -- клик на названии сцены). + +Игроку доступны объекты доступные на любой сцене -- инвентарь. Игрок может взаимодействовать с объектами инвентаря и действовать объектами инвентаря на другие объекты сцены или инвентаря. + +Следует отметить, что понятие инвентаря является условным. Например, в "инвентаре" могут находиться такие объекты как "открыть", "осмотреть", "использовать" и т.д. + +Действиями игрока могут быть: + + * осмотр сцены; + * действие на объект сцены; + * действие на объект инвентаря; + * действие объектом инвентаря на объект сцены; + * действие объектом инвентаря на объект инвентаря; + * переход в другую сцену; + +Игра представляет из себя каталог, в котором должен находиться скрипт main.lua. Другие ресурсы игры (скрипты на lua, графика и музыка) должны находиться в рамках этого каталога. Все ссылки на ресурсы делаются относительно текущего каталога -- каталога игры. + +В начале файла main.lua может быть определен заголовок, состоящий из тегов. Теги должны начинаться с символов '--': комментарий с точки зрения lua. На данный момент существует один тег: $Name:, который должен содержать название игры в кодировке UTF8. Пример использования тега: +{{{ +-- $Name: Самая интересная игра!$ +}}} + +Графический интерпретатор ищет доступные игры в каталоге games. Unix версия интерпретатора кроме этого каталога просматривает также игры в каталоге ~/.instead/games. + +== 1. Сцена == + +Сцена -- это единица игры, в рамках которой игрок может изучать все объекты сцены и взаимодействовать с ними. В игре должна быть хотя бы одна сцена с именем main. +{{{ +main = room { + nam = 'главная комната', + dsc = 'Вы в большой комнате.', +}; +}}} +Запись означает создание объекта main типа room. У каждого объекта игры есть атрибуты и обработчики. Например, атрибут nam (имя) является необходимым для любого объекта. + +Атрибут nam для сцены это то, что будет заголовком сцены при ее отображении. Имя сцены также используется для ее идентификации при переходах. + +Атрибут dsc это описание статической части сцены, которое выводится один раз при входе в сцену или явном выполнении команды look. + +Внимание!!! Если для вашего творческого замысла необходимо, чтобы описание статической части сцены выводилось каждый раз, вы можете определить для своей игры параметр forcedsc (в начале игры). +{{{ +game.forcedsc = true; +}}} +Или, аналогично, задать атрибут forcedsc для конкретных сцен. + +Для длинных описаний удобно использовать запись вида: + +{{{dsc = [[ Очень длинное описание... ]],}}} + +При этом переводы строк игнорируются. Если вы хотите, чтобы в выводе описания сцены присутствовали абзацы -- используйте символ ^. + +{{{ +dsc = [[ Первый абзац. ^^ +Второй Абзац.^^ + +Третий абзац.^ +На новой строке.]], +}}} +== 2. Объекты == + +Объекты -- это единицы сцены, с которыми взаимодействует игрок. +{{{ +tabl = obj { + nam = 'стол', + dsc = 'В комнате стоит {стол}.', + act = 'Гм... Просто стол...', +}; +}}} +Имя объекта nam используется при попадании его в инвентарь а также в текстовом интерпретаторе для адресации объекта. + +dsc -- описатель объекта. Он будет выведен в динамической части сцены. Фигурными скобками отображается фрагмент текста, который будет являться ссылкой в графическом интерпретаторе. + +act -- это обработчик, который вызывается при действии пользователя (действие на объект сцены). Его задача -- возвращение строки текста, которая станет частью событий сцены. + +ВНИМАНИЕ: в пространстве имен lua уже существуют некоторые объекты (таблицы), например: table, io, string... Будьте внимательны, при создании объекта. Например, в приведенном примере используется tabl, а не table. + +== 3. Добавляем объекты в сцену == + +Ссылкой на объект называется текстовая строка, содержащая имя объекта при его создании. Например: 'tabl' -- ссылка на объект tabl. + +Для того, чтобы поместить в сцену объекты, нужно определить массив obj, состоящий из ссылок на объекты: +{{{ +main = room { + nam = 'главная комната', + dsc = 'Вы в большой комнате.', + obj = { 'tabl' }, +}; +}}} +Теперь, при отображении сцены мы увидим объект стол в динамической части. + +== 4. Объекты связанные с объектами == + +Объекты тоже могут содержать атрибут obj. При этом, список будет последовательно разворачиваться. Например, поместим на стол яблоко. +{{{ +apple = obj { + nam = 'яблоко', + dsc = 'На столе лежит {яблоко}.', + act = 'Взять что-ли?', +}; + +tabl = obj { + nam = 'стол', + dsc = 'В комнате стоит {стол}.', + act = 'Гм... Просто стол...', + obj = { 'apple' }, +}; +}}} +При этом, в описании сцены мы увидим описание объектов стол и яблоко, так как apple -- связанный с tabl объект. + + +== 5. Атрибуты и обработчики как функции == + +Большинство атрибутов и обработчиков могут быть также функциями. Так, например: +{{{ +nam = function() + return 'яблоко'; +end, +}}} +Это синоним записи: nam = 'яблоко'; + +Функции сильно расширяют возможности STEAD, например: +{{{ +apple = obj { + nam = 'яблоко', + dsc = function(s) + if not s._seen then + return 'На столе {что-то} лежит.'; + else + return 'На столе лежит {яблоко}.'; + end + end, + act = function(s) + if s._seen then + return 'Это яблоко!'; + else + s._seen = true; + return 'Гм... Это же яблоко!'; + end + end, +}; +}}} +Если атрибут или обработчик оформлен как функция, то обычно первый аргумент функции (s) сам объект. В данном примере, при показе сцены будет в динамической части сцены будет текст: 'На столе что-то лежит'. При взаимодействии с 'что-то', переменная `_seen` объекта apple будет установлена в true и мы увидим, что это было яблоко. + +Зная lua, можно упростить запись: +{{{ +function sw(v, a, b) + if v then + return a; + end + return b +end + +apple = obj { + nam = function(s) + return sw(not s._seen, 'нечто','яблоко'); + end, + dsc = function(s) + return sw(not s._seen,'На столе {что-то} лежит.', 'На столе лежит {яблоко}.'); + end, + act = function(s) + if s._seen then + return 'Это яблоко!'; + else + s._seen = true; + return 'Гм... Это же яблоко!'; + end + end, +}; +}}} +И в дальнейшем всегда использовать функцию sw (или какую-либо другую, вспомогательную функцию). + +Запись `s._seen` означает, что переменная `_seen` размещена в объекте s (то-есть apple). Подчеркивание означает, что эта переменная попадет в файл сохранения игры. Начиная с версии интерпретатора 0.7.7 в файл сохранения игры попадают также переменные, название которых начинается с большой буквы. + +Внимание!!! Переменные в любом случае не записываются в файл сохранения, если они не размещены в одном из перечисленных типов объектов: комната, объект, игра, игрок. + +== 6. Инвентарь == + +Простейший вариант сделать объект, который можно брать -- определить обработчик tak. + +Например: +{{{ +apple = obj { + nam = 'яблоко', + dsc = 'На столе лежит {яблоко}.', + inv = function() + inv():del('apple'); + return 'Я съел яблоко.'; + end, + tak = 'Вы взяли яблоко.', +}; +}}} +При этом, при действии игрока на объект яблоко -- яблоко будет убрано из сцены и добавлено в инвентарь. При действии игрока на инвентарь -- вызывается обработчик inv. + +В нашем примере -- при действии игроком на яблоко в инвентаре - яблоко будет съедено. + +== 7. Переходы между сценами == + +Для перехода между сценами используется атрибут сцены -- список way. +{{{ +room2 = room { + nam = 'зал', + dsc = 'Вы в огромном зале.', + way = { 'main' }, +}; + + +main = room { + nam = 'главная комната', + dsc = 'Вы в большой комнате.', + obj = { 'tabl' }, + way = { 'room2' }, +}; +}}} +При этом, вы сможете переходить между сценами main и room2. Как вы помните, nam может быть функцией, и вы можете генерировать имена сцен на лету, например, если вы хотите, чтобы игрок не знал название сцены, пока не попал на нее. + +При переходе между сценами движок вызывает обработчик exit из текущей сцены и enter в той сцены, куда идет игрок. Например: +{{{ +room2 = room { + enter = 'Вы заходите в зал.', + nam = 'зал', + dsc = 'Вы в огромном зале.', + way = { 'main' }, + exit = 'Вы выходите из зала.', +}; +}}} + +exit и enter могут быть функциями. Тогда первый параметр это (как всегда) сам объект, а второй это ссылка на комнату куда игрок хочет идти (для exit) или из которой уходит (для enter). Например: +{{{ +room2 = room { + enter = function(s, f) + if f == 'main' then + return 'Вы пришли из комнаты.'; + end + end, + nam = 'зал', + dsc = 'Вы в огромном зале.', + way = { 'main' }, + exit = function(s, t) + if t == 'main' then + return 'Я не хочу назад!', false + end + end, +}; +}}} +Как видим, обработчики могут возвращать два значения: строку и статус. В нашем примере функция exit вернет false, если игрок попытается уйти из зала в main комнату. false означает, что переход не будет выполнен. Такая же логика работает и для enter. Кроме того, она работает и для обработчика tak. + +== 8. Действие объектов друг на друга == + +Игрок может действовать объектом инвентаря на другие объекты. При этом вызывается обработчик use у объекта которым действуют и used -- на которого действуют. + +Например: +{{{ +knife = obj { + nam = 'нож', + dsc = 'На столе лежит {нож}', + inv = 'Острый!', + tak = 'Я взял нож!', + use = 'Вы пытаетесь использовать нож.', +}; + +tabl = obj { + nam = 'стол', + dsc = 'В комнате стоит {стол}.', + act = 'Гм... Просто стол...', + obj = { 'apple', 'knife' }, + used = 'Вы пытаетесь сделать что-то со столом...', +}; +}}} +Если игрок возьмет нож и использует его на стол -- то увидит текст обработчиков use и used. use и used могут быть функциями. Тогда первый параметр это сам объект, +а второй -- ссылка на объект на который направлено действие в случае use и объект, которым действие осуществляется в случае used. + +use может вернуть статус false, в этом случае обработчик used не вызовется (если он вообще был). Статус обработчика used -- игнорируется. + +Пример: +{{{ +knife = obj { + nam = 'нож', + dsc = 'На столе лежит {нож}', + inv = 'Острый!', + tak = 'Я взял нож!', + use = function(s, w) + if w ~= 'tabl' + return 'Не хочу это резать.', false + else + return 'Вы вырезаете на столе свои инициалы.'; + end +}; +}}} +Нож можно использовать только на стол. + +== 9. Объект игрок == + +Игрок в STEAD представлен объектом pl. Тип объекта -- player. В движке объект создается следующим образом: +{{{ +pl = player { + nam = "Incognito", + where = 'main', + obj = { } +}; +}}} +Атрибут obj представляет собой инвентарь игрока. + +== 10. Объект game == + +Игра также представлена объектом game с типом game. В движке он определяется следующим образом: +{{{ +game = game { + nam = "INSTEAD -- Simple Text Adventure interpreter v"..version.." '2009 by Peter Kosyh", + dsc = [[ +Commands:^ + look(or just enter), act (or just what), use [on what], go ,^ + back, inv, way, obj, quit, save , load .]], + pl ='pl', + showlast = true, +}; +}}} +Как видим, объект хранит в себе указатель на текущего игрока ('pl') и некоторые параметры. Например, вы можете указать в начале своей игры кодировку текста следующим образом: +{{{ game.codepage="UTF-8"; }}} + +Поддержка произвольных кодировок изначально присутствует в UNIX версии интерпретатора, в windows версии -- начиная с 0.7.7. + +Кроме того, объект game может содержать обработчики по умолчанию act, inv, use, которые будут вызваны, если в результате действий пользователя не будут найдены никакие другие обработчики. Например, вы можете написать в начале игры: +{{{ +game.act = 'Не получается.'; +game.inv = 'Гм.. Странная штука..'; +game.use = 'Не сработает...'; +}}} + +== 11. Атрибуты - списки == + +Атрибуты списки (такие как way или obj) позволяют работать с собой, таким образом позволяя реализовать динамически определяемые переходы между сценами, живые объекты и т.д. + +Методы списков: add, del, look, srch. Из них наиболее часто используемые: add и del. + +add - добавляет в список. del -- удаляет из него. srch -- выполняет поиск объекта. Следует отметить, что параметром del и srch может быть не только сам объект или идентификатор объекта, но и имя объекта. + +Начиная с версии 0.8 параметром add может быть сам объект. Кроме того, с этой версии добавляется необязательный второй параметр -- позиция в списке. Начиная с версии 0.8 вы можете также выполнять модификацию списка по индексу с помощью метода set. Например: +{{{ +objs():set('knife',1); +}}} + +Выше, вы уже видели пример со съеденным яблоком, там использовалась конструкция inv():del('apple'); + +inv() -- это функция, которая возвращает список инвентаря. del после ':'-- метод, удаляющий элемент инвентаря. + +Аналогично, собственная реализация tak может быть такой: +{{{ +knife = obj { + nam = 'нож', + dsc = 'На столе лежит {нож}', + inv = 'Острый!', + act = function(s) + objs():del('knife'); + inv():add('knife'); + end, +}; +}}} + +Кроме добавления, удаления объектов из списков вы можете использовать выключение/включение объектов с помощью методов enable() и disable(). Например: knife:disable(). При этом объект knife пропадает из описания сцены, но в последствии может быть опять быть включен, с помощью knife:enable(). + +== 12. Функции, которые возвращают объекты == + +В STEAD определены некоторые функции, которые возвращают наиболее часто используемые объекты. Например: + * inv() возвращает список инвентаря; + * objs() возвращает список объектов текущей сцены; (начиная с 0.8.5 -- необязательный параметр -- сцена, для которой возвращается список); + * ways() возвращает список возможных переходов из текущей сцены; (начиная с 0.8.5 -- необязательный параметр -- сцена, для которой возвращается список); + * me() возвращает объект-игрок; + * here() возвращает объект текущую сцену; (начиная с 0.8.5 -- еще одна функция where() -- возвращает текущую сцену как строку-имя объекта, а не сам объект) + * from() возвращает объект прошлой сцены; + +Комбинируя эти функции с методами add, del можно динамически менять сцену, например: +{{{ +ways:add('nexroom'); -- добавить переход на новую сцену; +}}} +{{{ +objs():add('chair'); -- добавить объект в текущую сцену; +}}} + +Еще одна функция, которая получает объект по ссылке: +ref(). + +Например, мы можем добавить объект в локацию 'home' следующим образом: +{{{ +ref('home').obj:add('chair'); +}}} + +Впрочем, следующая более простая запись тоже является корректной: +{{{ +home.obj:add('chair'); +}}} + +Или, для версии >=0.8.5: +{{{ +objs('home'):add('chair'); +}}} +или, наконец: +{{{ +put('chair', 'home'); +}}} + +Начиная с 0.8.5 -- deref(o), возвращает ссылку-строку для объекта; + +== 13. Некоторые вспомогательные функци. == + +В STEAD определены некоторые высокоуровневые функции, которые могут оказаться полезными при написании игры. + +have() -- проверяет, есть ли объект в инвентаре. По имени объекта или по атрибуту nam объекта. Например: +{{{ +... +act = function(s) + if have('knfie') then + return 'Но у меня же есть нож!'; + end +end +... +}}} +move(o, w) -- переносит объект из текущей сцены в другую: + +{{{ move('mycat','inmycar');}}} + +Если вы хотите перенести объект из произвольной сцены, вам придется удалить его из старой сцены с помощью метода del. Для создания сложно перемещающихся объектов, вам придется написать свой метод, который будет сохранять текущую позицию объекта в самом объекте и делать удаление объекта из старой сцены. Вы можете указать исходную позицию (комнату) объекта в качестве третьего параметра move. + +{{{ move('mycat','inmycar', 'forest'); }}} + +Начиная с версии 0.8 присутствует также функция movef, аналогичная move, но добавляющая объект в начало списка. + +seen(o) -- если объект присутствует в текущей сцене: +{{{ + if seen('mycat') then + move('mycat','inmycar'); + end +}}} +drop(o) -- положить объект из инвентаря на сцену: + + drop('knife'); + +Начиная с версии 0.8 присутствует также функция dropf, аналогичная drop, но добавляющая объект в начало списка. Начиная с версии 0.8.5 второй необязательный параметр -- комната, куда помещается предмет. Кроме того, для версий >=0.8.5 доступна функция put, которая не удаляет предмет из инвентаря. + + +take(o) -- взять объект. + + take('knife'); + +Начиная с версии 0.8.5 второй необязательный параметр -- комната, с которой берется предмет. + +taken(o) -- если объект взят -- вернет true (взят с помощью tak или take()); + +rnd(m) -- случайное значение от 1 до m. + +goto(w) -- перейти в сцену w, при этом обработчику нужно вернуть возвращаемое значение goto, так как goto вернет описание новой сцены или сообщенмие о том, что переход невозможен, например: + +change_pl(p) -- переключиться на другого игрока (со своим инвентарем и позицией). При этом функция возвращает описание сцены нового игрока и это возвращаемое значение должно быть передано из обработчика (см. goto()). + +{{{ +mycar = obj { + nam = 'моя машина', + dsc = 'Перед хижиной стоит мой старенький {пикап} Toyota.', + act = function(s) + return goto('inmycar'); + end +}; +}}} + +back() -- goto в предыдущую сцену. + +time() -- возвращает текущее время игры. Время игры считается в активных действиях. + +cat(...) -- возвращает строку -- склейку строк-аргументов. Если первый аргумент nil, то функция возвращает nil. + +par(...) -- возвращает строку -- склейку строк-аргументов, разбитых строкой-первым параметром. + +== 14. Диалоги == + +Диалоги это сцены, содержащие объекты -- фразы. Например, простейший диалог может выглядеть следующим образом. +{{{ +povardlg = dlg { + nam = 'на кухне', + dsc = 'Передо мной полное лицо женщины - повара в белом колпаке и усталым взглядом...', + obj = { + [1] = phr('Мне вот-этих зелененьких... Ага -- и бобов!', 'На здоровье!'), + [2] = phr('Картошку с салом, пожалуйста!', 'Приятного аппетита!'), + [3] = phr('Две порции чесночного супа!!!', 'Прекрасный выбор!'), + [4] = phr('Мне что-нибудь легонькое, у меня язва...', 'Овсянка!'), + }, +}; +}}} +phr -- создание фразы. Фраза содержит вопрос, ответ и реакцию (реакция в данном примере отсутствует). Когда игрок выбирает одну из фраз, фраза отключается. Когда все фразы отключатся диалог заканчивается. Реакция -- это строка кода на lua который выполнится после отключения фразы. Например: +{{{ +food = obj { + nam = 'еда', + inv = function (s) + inv():del('food'); + return 'Я ем.'; + end +}; + +gotfood = function(w) + inv():add('food'); + food._num = w; + return back(); +end + +povardlg = dlg { + nam = 'на кухне', + dsc = 'Передо мной полное лицо женщины - повара в белом колпаке и усталым взглядом...', + obj = { + [1] = phr('Мне вот-этих зелененьких... Ага -- и бобов!', 'На здоровье!', [[pon(1); return gotfood(1);]]), + [2] = phr('Картошку с салом, пожалуйста!', 'Приятного аппетита!', [[pon(2); return gotfood(2);]]), + [3] = phr('Две порции чесночного супа!!!', 'Прекрасный выбор!', [[pon(3);return gotfood(3);]]), + [4] = phr('Мне что-нибудь легонькое, у меня язва...', 'Овсянка!', [[pon(4); return gotfood(4);]]), + }, +}; +}}} +В данном примере, игрок выбирает еду. Получае ее (запомнив выбор в переменной food._num) и возвращается обратно (в ту сцену откуда попал в диалог). + +В реакции может быть любой lua код, но в STEAD определены наиболее часто используемые функции: + +pon(n..) -- включить фразы диалога с номерами n... (в нашем примере -- чтобы игрок мог повторно взять еду того-же вида). +poff(n...) -- выключить фразы диалога с номерами n... +prem(n...) -- удалить (заблокировать) фразы диалога с номерами n... (удаление означает невозможность включения фраз. pon(n..) не приведет к включению фраз). + +Переход в диалог осуществляется как переход на сцену: +{{{ +povar = obj { + nam = 'повар', + dsc = 'Я вижу {повара}.', + act = function() + return goto('povardlg'); + end, +}; +}}} +Вы можете переходить из одного диалога в другой диалог -- организовывая иерархические диалоги. + +Также, вы можете прятать некоторые фразы при инициализации диалога и показывать их при некоторых условиях. +{{{ +facectrl = dlg { + nam = 'фэйсконтроль', + dsc = 'Я вижу перед собой неприятное лицо полного охранника.', + obj = { + [1] = phr('Я пришел послушать лекцию Белина...', + '-- Я не знаю кто вы -- ухмыляется охранник -- но мне велели пускать сюда только приличных людей.', + [[pon(2);]]), + [2] = _phr('У меня есть приглашение!', + '-- А мне плевать! Посмотри на себя в зеркало!!! Ты пришел слушать самого Белина -- правую руку самого... -- охранник почтительно помолчал -- Так что пошел вон..', [[pon(3,4)]]), + [3] = _phr('Сейчас я дам тебе по роже!', '-- Ну все... Мощные руки выталкивают меня в коридор...', + [[poff(4)]]), + [4] = _phr('Ты, кабан! Я же тебе сказал -- у меня есть приглашение!', + '-- Чтоооооо? Глаза охранника наливаются кровью... Мощный пинок отправляет меня в коридор...', + [[poff(3)]]), + }, + exit = function(s,w) + s:pon(1); + end, +}; +}}} + +`_phr` -- создает выключенную фразу, которую можно включить. Данный пример показывает также возможность использования методов pon, poff, prem для диалога (см. exit). + +Вы можете включать/выключать фразы не только текущего, но и произвольного диалога, с помощью методов объекта диалог pon/poff. Например: shopman:pon(5); + +== 15. Облегченные объекты == + +Иногда, сцену нудно наполнить декорациями, которые обладают ограниченной функциональностью, но делают игру разнообразней. Для этого можно использовать облегченный объект. Например: +{{{ +sside = room { + nam = 'южная сторона', + dsc = [[Я нахожусь у южной стены здания института. ]], + act = function(s, w) + if w == 1 then + ways():add('stolcorridor'); + return "Я подошел к подъезду. На двери подъезда надпись -- 'Столовая'. Хм -- зайти внутрь?"; + end + if w == 2 then + return 'Те, кто выходят, выглядят более довольными...'; + end + end, + obj = { vobj(1, "подъезд", "У восточного угла находится небольшой {подъезд}."), + vobj(2, "люди", "Время от времени дверь подъезда хлопает впуская и выпуская {людей}.")}, +}; +}}} +Как видим, vobj позволяет сделать легкую версию статического объекта, с которым тем не менее можно взаимодействовать (за счет определения обработчика act в сцене и анализа ключа объекта). vobj также вызывает метод used, при этом в качестве третьего параметра передается объект, воздействующий на виртуальный объект. + +Синтаксис vobj: vobj(ключ, имя, описатель); где ключ -- это цифра, которая будет передана обработчикам act/used сцены как второй параметр. + +Существует модификация объекта vobj -- vway. vway реализует ссылку. +Синтаксис vway: vway(имя, описатель, сцена назначения); например: + +{{{ + obj = { vway("дальше", "Нажмите {здесь}.", 'nextroom') } +}}} + +Вы можете динамически заполнять сцену объектами vobj или vway с помощью методов add и del. Например: + +{{{ + home.objs:add(vway("next", "{Дальше}.", 'next_room'); +-- some code here + home.objs:del("next"); +}}} + +Определена также упрощенная сцена vroom. +Синтаксис: vroom(имя перехода, сцена назначения). Например: + +{{{ + home.objs:add(vroom("идти на запад", 'mountains'); +}}} + +== 16. Динамические события == + +Вы можете определять обработчики, которые выполняются каждый раз, когда время игры увеличивается на 1. Например: +{{{ +mycat = obj { + nam = 'Барсик', + lf = { + [1] = 'Барсик шевелится у меня за пазухой.', + [2] = 'Барсик выглядывает из за пазухи.', + [3] = 'Барсик мурлычит у меня за пазухой.', + [4] = 'Барсик дрожит у меня за пазухой.', + [5] = 'Я чувствую тепло Барсика у себя за пазухой.', + [6] = 'Барсик высовывает голову из за пазухи и осматривает местность.', + }, + life = function(s) + local r = rnd(6); + if r > 2 then + return; + end + r = rnd(6); + return s.lf[r]; + end, +.... + +profdlg2 = dlg { + nam = 'Белин', + dsc = 'Белин бледен. Он смотрит на дробовик рассеянным взглядом.', + obj = { + [1] = phr('Я пришел за своим котом.', + 'Я выхватываю Барсика из руки Белина и засовываю себе за пазуху.', + [[inv():add('mycat'); lifeon('mycat')]]), +.... +}}} +Любой объект или сцена могут иметь свой обработчик life, который вызывается каждый раз при смене текущего времени игры, если объект или сцена были добавлены в список живых объектов с помощью lifeon. Не забывайте удалять живые объекты из списка с помощью lifeoff, когда они больше не нужны. Это можно сделать, например, в обработчике exit, или любым другим способом. + +== 17. Графика и музыка == + +Графический интерпретатор анализирует атрибут сцены pic, и воспринимает его как путь к картинке, например: + +{{{ +home = room { + pic = 'gfx/home.png', + nam = 'дома', + dsc = 'Я у себя дома', +}; +}}} + +Конечно, pic может быть функцией, расширяя возможности разработчика. +Если в текущей сцене не определен атрибут pic, то берется атрибут game.pic. Если не определен и он, то картинка не отображается. + +Интерпретатор проигрывает в цикле текущую музыку, которая задается с помощью функции: +set_music(имя музыкального файла). + +Например: +{{{ +street = room { + pic = 'gfx/street.png', + enter = function() + set_music('mus/rain.ogg'); + end, + nam = 'на улице', + dsc = 'На улице идет дождь.', +}; +}}} + +get_music() возвращает текущее имя трека. + +Начиная с версии 0.7.7 в функцию set_music() можно передавать второй параметр -- количество проигрываний. Получить текущий счетчик можно с помощью get_music_loop. 0 - означает вечный цикл. 1..n -- количество проигрываний. -1 -- проигрывание текущего трека закончено. + +== 18. Полезные советы == +=== Статус игрока === +Код движка расположен в файле stead.lua. В своей игре вы можете переопределять любую функцию или объект lua, добиваясь того, что нужно для вашего творческого замысла. Кроме того, полезно знать особенности работы движка. Например, ниже представлена реализация статуса игрока в виде текста, который появляется в инвентаре, но не может быть выбран. + +{{{ +pl.Life = 10; +pl.Power = 10; + +status = obj { + nam = 'Жизнь: '..pl.Life..',Сила: '..pl.Power, +}; +inv():add('status'); +status.object_type = nil +}}} + +=== goto из обработчика exit == + +Если вы выполните goto из обработчика exit, то получите переполнение стека, так как goto снова и снова будет вызывать метод exit. Вы можете избавиться от этого, если вставите проверку, разрушающую рекурсию. Например: +{{{ +exit = function(s, t) + if t == 'dialog' then return; end + return goto('dialog'); +}}} + +Вы можете также делать goto из обработчиков enter. + +=== Динамически создаваемые ссылки. == +Динамически создаваемые ссылки могут быть реализованы разным способом. Ниже приводится пример, основанный на использовании объектов vway. Для добавления ссылки можно использовать запись: +{{{ +home.obj:add(vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest')); +}}} +Для удаления ссылки можно использовать метод del. +{{{ +home.obj:del('Дорога'); +}}} +Для определения наличия ссылки в сцене -- метод srch. +{{{ +if not home.obj:srch('Дорога') then + home.obj:add(vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest')); +end +}}} +Динамические ссылки удобно создавать в обработчике enter, или по мере необходимости в любом месте кода игры. Если ссылки создаются в текущей сцене, то последний пример можно упростить: +{{{ +if not seen('Дорога') then + objs():add(vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest')); +end +}}} +Кроме того, вы можете просто включать и выключать ссылки с помощью enable(), disable(), например: +{{{ + objs()[1]:disable(); +}}} + +Вы можете создавать выключенные vobj и vway следующим образом: +{{{ + obj = {vway('Дорога', 'Я заметил {дорогу}, ведущую в лес...', 'forest'):disable()}, +}}} +И затем включать их по индексу в массиве obj: +{{{ + objs()[1]:enable(); +}}} + +=== Компиляция игр === +Если вы не хотите показывать исходный код своих игр, вы можете откомпилировать исходный код компилятором luac. Правда, при этом главный файл main.lua желательно оставлять текстовым, так его заголовок сканируется интерпретатором при выборе игры. Таким образом схема выглядит следующим образом (game.lua -- скомпилированный lua код): + +main.lua +{{{ +-- $Name: Моя закрытая игра!$ +dofile("game.lua"); +}}} + +Кроме того, компиляция игр может быть использована для поиска ошибок в коде. + +=== Переключение между игроками === +Вы можете создать игру с несколькими персонажами и время от времени переключаться между ними (см. switch_pl). Но вы можете также использовать этот трюк для того, что бы иметь возможность переключаться между разными типами инвентаря. + +=== Использование первого параметра обработчика === +Пример кода. +{{{ +knife = obj { + nam = 'камень', + dsc = 'На краю лежит {камень}.', + act = function() + objs():del('knife'); + return 'Я толкнул камень, он сорвался и улетел вниз...'; + end +}}} + +Обработчик act мог бы выглядеть проще: +{{{ + act = function(s) + objs():del(s); + return 'Я толкнул камень, он сорвался и улетел вниз...'; + end +}}} +=== Использование set_music === +Вы можете использовать set_music для проигрывания звуков, задавая второй параметр -- счетчик циклов проигрывания звукового файла. + +Вы можете написать для игры свой проигрыватель музыки, создав его на основе живого объекта, например: +{{{ +-- играет треки в случайном порядке, начиная со 2-го +tracks = {"mus/astro2.mod", "mus/aws_chas.xm", "mus/dmageofd.xm", "mus/doomsday.s3m"} +mplayer = obj { + nam = 'плеер', + life = function(s) + local n = get_music(); + local v = get_music_loop(); + if not n or not v then + set_music(tracks[2], 1); + elseif v == -1 then + local n = get_music(); + while get_music() == n do + n = tracks[rnd(4)] + end + set_music(n, 1); + end + end, +}; +lifeon('mplayer'); +}}} + +Вы можете использовать функции get_music_loop и get_music, для того, чтобы запоминать прошлую мелодию, и потом восстанавливать ее, например: + +{{{ +function save_music(s) + s.OldMusic = get_music(); + s.OldMusicLoop = get_music_loop(); +end + +function restore_music(s) + set_music(s.OldMusic, s.OldMusicLoop); +end + +-- .... +enter = function(s) + save_music(s); +end, +exit = function(s) + restore_music(s); +end, +-- .... + +}}} + +Начиная с версии 0.8.5 функции save_music и restore_music уже присутствуют в библиотеке. + +=== Живые объекты === +Если вашему герою нужен друг, одним из способов может стать метод life этого персонажа, который всегда переносит объект в локацию игрока: +{{{ +horse = obj { + nam = 'лощадь', + dsc = 'Рядом со мной стоит {лошадь}.', + life = function(s) + if not seen('horse') then + move('horse', here(), s.__where); + s.__where = pl.where; + end + end, +}; +lifeon('horse'); +}}} + +=== Отладка === +Для того, чтобы во время ошибки увидеть стек вызовов функций lua, вы можете запустить sdl-instead с параметром -debug. При этом в windows версии интерпретатора будет создана консоль отладки. + +Вы можете отлаживать свою игру вообще без instead. Например, вы можете создать следующий файл game.lua: +{{{ +dofile("/usr/share/games/stead/stead.lua"); -- путь к stead.lua +dofile("main.lua"); -- ваша игра +game:ini(); +iface:shell(); +}}} +И запустите игру в lua: lua game.lua. +При этом игра будет работать в примитивном shell окружении. Полезные команды: ls, go, act, use.... + +== 19. Темы для sdl-instead == + +Графический интерпретатор поддерживает механизм тем. Тема представляет из себя каталог, с файлом theme.ini внутри. + +Тема, которая является минимально необходимой -- это тема default. Эта тема всегда загружается первой. Все остальные темы наследуются от нее и могут частично или полностью заменять ее параметры. Выбор темы осуществляется пользователем через меню настроек, однако конкретная игра может содержать собственную тему и таким образом влиять на свой внешний вид. В этом случае в каталоге с игрой должен находиться свой файл theme.ini. Тем не-менее пользователь свободен отключить данный механизм, при этом интерпретатор будет предупреждать о нарушении творческого замысла автора игры. + +Синтаксис theme.ini очень прост. + +<параметр> = <значение> + +или + +; комментарий + +Значения могут быть следующих типов: строка, цвет, число. + +Цвет задается в форме #rgb, где r g и b компоненты цвета в шестнадцатеричном виде. Кроме того некоторые основные цвета распознаются по своим именам. Например: yellowgreen, или violet. + +Параметры могут принимать значения: + +scr.w = ширина игрового пространства в пикселях (число) + +scr.h = высота игрового пространства в пикселях (число) + +scr.col.bg = цвет фона + +scr.gfx.bg = путь к картинке фонового изображения (строка) + +scr.gfx.use = путь к картинке-индикатору режима использования (строка) + +scr.gfx.pad = размер отступов к скролл-барам и краям меню (число) + +scr.gfx.x, scr.gfx.y, scr.gfx.w, scr.gfx.h = координаты, ширина и высота окна изображений. Области в которой располагается картинка сцены. Интерпретация зависит от режима расположения (числа) + +win.gfx.h - синоним scr.gfx.h (для совместимости) + +scr.gfx.mode = режим расположения (строка fixed, embedded или float). Задает режим изображения. embedded -- картинка является частью содержимого главного окна, параметры win.x, win.y, win.w игнорируются. float -- картинка расположена по указанным координатам (win.x, win.y) и масштабируется к размеру win.w x win.h если превышает его. fixed -- картинка является частью сцены как в режиме embedded, но не скроллируется вместе с текстом а расположена непосредственно над ним. + +win.x, win.y, win.w, win.h = координаты, ширина и высота главного окна. Области в которой располагается описание сцены (числа) + +win.fnt.name = путь к файлу-шрифту (строка) + +win.fnt.size = размер шрифта главного окна (размер) + +win.gfx.up, win.gfx.down = пути к файлам-изображениям скорллеров вверх/вниз для главного окна (строка) + +win.col.fg = цвет текста главного окна (цвет) + +win.col.link = цвет ссылок главного окна (цвет) + +win.col.alink = цвет активных ссылок главного окна (цвет) + +inv.x, inv.y, inv.w, inv.h = координаты, высота и ширина области инвентаря. (числа) + +inv.mode = строка режима инвентаря (horizontal или vertical). В горизонтальном режиме инвентаря в одной строке могут быть несколько предметов. В вертикальном режиме, в каждой строке инвентаря содержится только один предмет. (число) + +inv.col.fg = цвет текста инвентаря (цвет) + +inv.col.link = цвет ссылок инвентаря (цвет) + +inv.col.alink = цвет активных ссылок инвентаря (цвет) + +inv.fnt.name = путь к файлу-шрифту инвентаря (строка) + +inv.fnt.size = размер шрифта инвентаря (размер) + +inv.gfx.up, inv.gfx.down = пути к файлам-изображениям скорллеров вверх/вниз для инвентаря (строка) + +menu.col.bg = фон меню (цвет) + +menu.col.fg = цвет текста меню (цвет) + +menu.col.link = цвет ссылок меню (цвет) + +menu.col.alink = цвет активных ссылок меню (цвет) + +menu.col.alpha = прозрачность меню 0-255 (число) + +menu.col.border = цвет бордюра меню (цвет) + +menu.bw = толщина бордюра меню (число) + +menu.fnt.name = путь к файлу-шрифту меню (строка) + +menu.fnt.size = размер шрифта меню (размер) + +menu.gfx.button = путь к файлу изображению значка меню (строка) + +menu.button.x, menu.button.y = координаты кнопки меню (числа) + +snd.click = путь к звуковому файлу щелчка (строка) + +include = имя темы (последний компонент в пути каталога) (строка) + +Кроме того, заголовок темы может включать в себя комментарии с тегами. На данный момент существует только один тег: $Name:, содержащий UTF-8 строку с именем темы. Например: +{{{ +; $Name:Новая тема$ +; модификация темы book +include = book +scr.gfx.h = 500 +}}} + +Интерпретатор выполняет поиск тем в каталоге themes. Unix версия кроме этого каталога, просматривает также каталог ~/.instead/themes/ + + +TODO +Полный список объектов и методов. diff --git a/games/Makefile b/games/Makefile index 3a7e598..28b1e40 100644 --- a/games/Makefile +++ b/games/Makefile @@ -5,6 +5,6 @@ install: for f in *; do \ if [ ! -d $$f ]; then continue; fi;\ install -d -m0755 $(GAMESPATH)/$$f;\ - tar -c -C $$f . | tar -x -C $(GAMESPATH)/$$f;\ + tar --exclude=".svn" -c -C $$f . | tar -x -C $(GAMESPATH)/$$f;\ done clean: diff --git a/games/Makefile.windows b/games/Makefile.windows new file mode 100644 index 0000000..5fa58a5 --- /dev/null +++ b/games/Makefile.windows @@ -0,0 +1,11 @@ +all: +install: + if not exist ..\bin\games\cat mkdir ..\bin\games\cat + copy cat ..\bin\games\cat + if not exist ..\bin\games\cat\gfx mkdir ..\bin\games\cat\gfx + copy cat\gfx ..\bin\games\cat\gfx + if not exist ..\bin\games\cat\mus mkdir ..\bin\games\cat\mus + copy cat\mus ..\bin\games\cat\mus + if not exist ..\bin\games\tutorial mkdir ..\bin\games\tutorial + copy tutorial ..\bin\games\tutorial +clean: diff --git a/games/cat/ep1.lua b/games/cat/ep1.lua index bb1b2b6..f85ca7b 100644 --- a/games/cat/ep1.lua +++ b/games/cat/ep1.lua @@ -2,11 +2,11 @@ mywear = obj { nam = 'ватник', dsc = function(s) if here() == stolcorridor then - local st=''; + local st='.'; if not have('gun') then - st = 'Под которым спрятан дробовик.'; + st = ', под которым спрятан дробовик.'; end - return 'А еще на вешалке висит мой {ватник}.'..st; + return 'А еще на вешалке висит мой {ватник}'..st; else return 'На гвоздике, вбитом в сосновую дверь, висит {ватник}.'; end @@ -109,7 +109,7 @@ gun = obj { return 'Я попытался спрятать дробовик в одежду, но он слишком длинный.' else s._hidden = true; - return 'Теперь я могу спрятать обрез в одежде!.'; + return 'Теперь я могу спрятать обрез в одежде!'; end end end @@ -374,7 +374,7 @@ home = room { if seen(mycat) then move('mycat','forest'); return [[ -Когда я выходил из хижины Барсик внезапно проснулся и бросился мне под ноги. +Когда я выходил из хижины, Барсик внезапно проснулся и бросился мне под ноги. Я погладил его за ушами -- Значит едем вместе? ]] end @@ -562,7 +562,7 @@ shop = room { village.obj:del('truck'); village.obj:del('mycat'); return [[ -Когда я заходил в магазин я чуть не с толкнулся с неприятным типом в сером пальто и +Когда я заходил в магазин, я чуть не с толкнулся с неприятным типом в сером пальто и шляпе с длинными полями... Он извинился каким-то шипящим голосом и сделал вид, что приподнимает шляпу... Из под ее полей блеснули белые зубы... Дойдя до прилавка я услышал звук запускающегося двигателя.]]; end @@ -730,8 +730,8 @@ guarddlg = dlg { [2] = phr('Я забыл свой пропуск -- можно мне зайти?','-- Нет...', 'poff(1); pon(3);'), [3] = _phr('Вы знаете Белина? У него мой кот -- мне нужно его забрать...', '-- Нет пропуска?', 'pon(4)'), [4] = _phr('Я просто пришел забрать своего кота! Дайте телефон Белина.', -[[Глаза охранника меняют свой цвет. Уголки губ поднимаются наверх -- вот что, господин хороший -- я так понял, -пропуска у вас нет, идите-ка отсюда пока можете...]], 'pon(5, 6)'), +[[Глаза охранника меняют свой цвет. Уголки губ поднимаются наверх -- вот что, господин хороший, -- я так понял, +пропуска у вас нет, идите-ка отсюда, пока можете...]], 'pon(5, 6)'), [5] = _phr('Ну все, щас я дам по твоей роже...', 'Рука охранника тянется к автомату. ', 'poff(6); return guardreact();'), [6] = _phr('Ладно, я пошел...', '-- Не спеши - охранник уже не скрывает свою ухмылку - ты мне не нравишься...','poff(5); return guardreact()'), [7] = _phr('Щас я вас всех перестреляю из своего дробовика...', 'На этот раз охранник даже не отвечает. Его налитые кровью глаза красноречивей всяких слов.','return guardreact()'), @@ -740,7 +740,7 @@ guarddlg = dlg { guard = obj { nam = 'охрана', dsc = [[ -В будке сидит {охрана}. Кажется она вооружена автоматами калашникова. +В будке сидит {охрана}. Кажется, она вооружена автоматами калашникова. ]], act = function(s) return goto('guarddlg'); @@ -762,13 +762,13 @@ inst = room { учреждение. Позади территории института находятся железнодорожные пути. ]], act = function(s, w) if w == 1 then - return 'Высота стены около 5 метров. Но этого мало -- сверху проходит колючая проволока -- думаю она под напряжением...'; + return 'Высота стены около 5 метров. Но этого мало -- сверху проходит колючая проволока -- думаю, она под напряжением...'; end if w == 2 then return 'Нет, Владимир был прав... Это какой-то военный штаб...'; end if w == 3 then - return 'Да -- это похоже тот самый фургон, в котором человек в сером пальто увез моего Барсика.'; + return 'Да -- это, похоже, тот самый фургон, в котором человек в сером пальто увез моего Барсика.'; end end, used = function(s, w, b) @@ -889,7 +889,7 @@ backwall = room { if f == 'inmycar' then return 'Отлично... Кажется удалось добраться незамеченным...'..' '..st; end - return 'Плутая по снежному полю я добрался до задней стены.'..' '..st; + return 'Плутая по снежному полю, я добрался до задней стены.'..' '..st; end, nam = 'восточная стена института', dsc = 'Я нахожусь у задней стороны института.', diff --git a/games/cat/ep2.lua b/games/cat/ep2.lua index 5c74461..0e20683 100644 --- a/games/cat/ep2.lua +++ b/games/cat/ep2.lua @@ -76,8 +76,8 @@ alienwear = obj { 'Холодновато, но стильно!', 'Очень красиво смотрится на фоне снега!', 'Длиннополое пальто -- это ретро!', - 'Я терминатор!', - 'Я пацифист!', + 'Я -- терминатор!', + 'Я -- пацифист!', 'Пиджачок сидит на мне как влитой!', 'Рокн рол мертв, а я еше нет!', 'Когда-то я занимался альпинизмом!', @@ -88,7 +88,7 @@ alienwear = obj { inv = function(s) if s._num == 7 and not have('card') then inv():add('card'); - return 'Немного покопавшись в карманах косухи я нашел карточку.'; + return 'Немного покопавшись в карманах косухи, я нашел карточку.'; end return s.xinv[s._num]; end, @@ -99,7 +99,7 @@ garderob = obj { dsc = 'На правой стороне коридора висят {вешалки} с одеждой.', act = function(s, w) if have('mywear') or have('alienwear') then - return 'Здесь много людей, я не думаю что я смогу сделать это незаметно.'; + return 'Здесь много людей, я не думаю, что я смогу сделать это незаметно.'; elseif tonumber(w) and tonumber(w) > 0 and tonumber(w) <= 8 then if not me()._walked then return 'Это будет слишком заметно...'; @@ -143,7 +143,7 @@ garderob = obj { portrait = obj { nam = 'портреты', dsc = 'По стенам развешены большие {портреты} в деревянных рамах.', - act = 'Гм... На портретах одно и тоже лицо! Улыбающееся холодной улыбкой лицо человека лет сорока с холодным взглядом.', + act = 'Гм... На портретах одно и тоже лицо! Улыбающееся холодной улыбкой лицо человека, лет сорока, с пустым, ничего не выражающим взглядом.', }; salo = obj { @@ -286,15 +286,15 @@ kitchendlg = dlg { [3] = _phr('Хм... Из отдела искривления пространства...', '-- Старье!', [[pon(6);poff(4,5)]]), [4] = _phr('Ааа... Отдел квантовых скачков..', '-- Хм? Не слышал о таком.', [[pon(6);poff(3,5)]]), - [5] = _phr('ЭЭэ... Отдел изучения квазипространства!', '-- Ого! Прикольно!', [[pon(6);poff(3,4)]]), + [5] = _phr('Эээ... Отдел изучения квазипространства!', '-- Ого! Прикольно!', [[pon(6);poff(3,4)]]), [6] = _phr('Хм... ', '-- А у тебя какой уровень секретности?', [[pon(7,8)]]), [7] = _phr('Супер секретно!', '-- Ух ты! ... ', [[poff(8); pon(9)]]), [8] = _phr('Анонимный.', '-- Да? Не слышал о таком, наверное это более секретный уровень, чем мой...', [[poff(7); pon(9)]]), [9] = _phr('Мэээ...', '-- Меня зовут Илья... Просто Илья -- тянет худую руку парень -- а тебя как?', [[pon(10, 11, 12)]]), [10] = _phr('Пп.. Пк... Пупкин... Василий Пупкин.', '-- Редкая фамилия!', [[poff(11,12); pon(13)]]), - [11] = _phr('Сережка.', '-- Дай пятерню братан!', [[poff(10,12); pon(13)]]), - [12] = _phr('Гоша...', '-- Ну, будем знакомы Гошка!', [[poff(10,11); pon(13)]]), + [11] = _phr('Сережка.', '-- Дай пятерню, братан!', [[poff(10,12); pon(13)]]), + [12] = _phr('Гоша...', '-- Ну, будем знакомы, Гошка!', [[poff(10,11); pon(13)]]), [13] = _phr('Кхмм...', [[-- Какой ты странный... Но это не важно. Мы все здесь -- Илья сделал выразительное лицо -... Мне тут поручили распространить приглашения на закрытую лекцию Белина... Только для друзей... Ты мне нравишься, да и по уровню @@ -405,7 +405,7 @@ sside = room { end, way = {'eside','wside'}, obj = { vobj(1, "подъезд", "У восточного угла находится небольшой {подъезд}."), - vobj(2, "люди", "Время от времени дверь подъезда хлопает впуская и выпуская {людей}.")}, + vobj(2, "люди", "Время от времени дверь подъезда хлопает, впуская и выпуская {людей}.")}, exit = function(s, t) if t == 'eside' then return 'Если я пойду туда, я попаду в зону видимости пулеметных вышек.', false @@ -420,7 +420,7 @@ nside = room { way = {'eside','wside' }, act = function(s, w) if w == 1 then - return 'Да -- водосточная труба... Довольно крепкая. Но вряд-ли я смогу взобраться по ней наверх.'; + return 'Да -- водосточная труба... Довольно крепкая. Но вряд ли я смогу взобраться по ней наверх.'; end end, obj = { vobj(1, 'труба', 'Водосточная {труба} проходит по восточному углу здания.')}, @@ -434,7 +434,7 @@ wside = room { way = {'entrance', 'nside','sside' }, act = function(s, w) if w == 1 then - return 'Тот самый фургон с которого все началось...'; + return 'Тот самый фургон, с которого все началось...'; end if w == 5 then return 'Она начинается слишком высоко, к тому-же снизу она закрыта на замок. Возможно, это будет кому-то полезно при пожаре, хотя я лично сомневаюсь...' @@ -535,7 +535,7 @@ entrance = room { way = { 'wside' }, enter = function(s, f) if have('gun') and f == 'wside' and not gun._hidden then - return 'Мне кажется там внутри возникнет множество вопросов по-поводу моего дробовика... Я должен его как-то спрятать.', false + return 'Мне кажется, там внутри возникнет множество вопросов по-поводу моего дробовика... Я должен его как-то спрятать.', false end end, exit = function(s, t) @@ -744,7 +744,7 @@ stol = obj { if w == 'vent' or w == 'resh' then inv():del('stol'); stoly._moved = true; - return 'Напрягая свои силы я сдвигаю один из столов в центр комнаты.'; + return 'Напрягая свои силы, я сдвигаю один из столов в центр комнаты.'; end end }; @@ -782,7 +782,7 @@ eroom = room { dsc = [[Я нахожусь в небольшой комнате с бежевыми стенами.]], enter = function(s, f) if f == 'cor3' then - return [[Открыв дверь я заглядываю внутрь комнаты. Ухх... Пусто! Можно осмотреться...]]; + return [[Открыв дверь, я заглядываю внутрь комнаты. Ухх... Пусто! Можно осмотреться...]]; end if f == 'toilet' then return 'Ну что же... Я поднимаю железную решетку в полу туалета и лезу в темноту... Через несколько минут я уже спрыгиваю из вентиляционного отверстия на стол, и спускаюсь на пол.'; @@ -872,7 +872,7 @@ switch = obj { if not cor3._locked then return [[Я перевожу выключатель в позицию <<выключено>> и иду вдоль стен, когда одна из дверей за мной вдруг открывается и старческий голос звучит на весь коридор -- Безобразие!!! Включите обратно!!! -- -мне приходиться вернуться к выключателю и перевести его в позицию <<включено>>.]]; +мне приходится вернуться к выключателю и перевести его в позицию <<включено>>.]]; end vent._off = true; return 'Выключаю!'; @@ -890,7 +890,7 @@ cor3 = room { dsc = 'Длинный коридор идет до самой стены здания. На потолке тускло горят лампы дневного света. На полу постелена зеленая ковровая дорожка.', act = function(s, w) if w == 1 then - return 'Я подхожу к одной из дверей и заглядываю в окошко-иллюминатор... Люди в белых костюмах, словно пчелы снуют у причудливых аппаратов. -- Наверное это лаборатории -- догадываюсь я.'; + return 'Я подхожу к одной из дверей и заглядываю в окошко-иллюминатор... Люди в белых костюмах, словно пчелы, снуют у причудливых аппаратов. -- Наверное, это лаборатории -- догадываюсь я.'; end if not tonumber(w) then return nil, false @@ -906,7 +906,7 @@ cor3 = room { return goto('room3x'); end if w == 7 then - return 'Окно выходит на южную сторону... Темно -- ничего не видно кроме снежинок, ударяющихся о стекло...'; + return 'Окно выходит на южную сторону... Темно -- ничего не видно, кроме снежинок, ударяющихся о стекло...'; end if w == 8 then return 'Зайти?'; @@ -967,7 +967,7 @@ umyvalnik = obj { me()._mylo = false; return 'Я смываю мыло со своего лица...'; end - return 'Я пью хлорированную воду жадными глотками... Да -- это не та вода из ручья которую я пью в лесу...'; + return 'Я пью хлорированную воду жадными глотками... Да -- это не та вода из ручья, которую я пью в лесу...'; end, used = function(s, w) if w == 'mylo' then @@ -1016,7 +1016,7 @@ floor3 = room { end if w == 2 then if not s._unlocked then - return 'Металл обитый кожей. Дверь снабжена считывателем электронных карт. Надпись на двери: <<Уровень:3 Категория:Прикладная физика>>'; + return 'Металл обитый кожей. Дверь снабжена считывателем электронных карт. Надпись на двери: <<Уровень:3 Категория: Прикладная физика>>'; end return goto('cor3'); end @@ -1369,13 +1369,13 @@ hall41 = room { pic = 'gfx/hall1.png', act = function(s, w) if w == 1 then - return 'Глядя в ночную темноту я с тоской вспоминаю Барсика...'; + return 'Глядя в ночную темноту, я с тоской вспоминаю Барсика...'; end if w == 2 then return 'Примерно такие были у нас в институте, когда я... Ладно...'; end if w == 3 then - return 'Все, что я мог бы вспомнить я благополучно забыл.'; + return 'Все, что я мог бы вспомнить, я благополучно забыл.'; end end, obj = { @@ -1404,7 +1404,7 @@ cor4 = room { return 'Я тоскливо гляжу в ночную тьму... Я понимаю -- что очень устал... Но я должен идти...'; end if w == 12 then - return 'Эта дверь, как и многие здесь, снабжена считывателем смарткарт. На нем горит красная лампочка.'; + return 'Эта дверь, как и многие здесь, снабжена считывателем смарт-карт. На нем горит красная лампочка.'; end if tonumber(w) >=5 and tonumber(w) <=9 then room4x._num = w - 4; @@ -1517,7 +1517,7 @@ floor5 = room { vobj(4, 'информация', 'Я вижу две двери, ведущие в южный и северный коридоры. На южной {двери} написано: <<уровень 5, категория: информация>>.'), vobj(5, 'красная дверь', 'На северной {двери} нет надписей. Это массивная дверь, обитая красной кожей.'), - vobj(6, 'охранник', 'Между проходами в коридоры установлен стол, за котором сидит {охранник},'); + vobj(6, 'охранник', 'Между проходами в коридоры установлен стол, за которым сидит {охранник},'); }, way = { 'lift' }, }; diff --git a/games/cat/ep3.lua b/games/cat/ep3.lua index 4a01b11..ffe7855 100644 --- a/games/cat/ep3.lua +++ b/games/cat/ep3.lua @@ -17,7 +17,7 @@ lection = room { Согласно квантовой механике, если над ядром не производится наблюдения, то его состояние описывается суперпозицией (смешением) двух состояний — распавшегося ядра и нераспавшегося ядра, следовательно, кот, сидящий в ящике, и жив, и мёртв одновременно. -- Белин повышает голос. -- можно сказать, что это просто игры разума, отвлеченная лирика, но я покажу и докажу, что это не совсем так... ^^ --- Итак, если ящик открыть, то экспериментатор обязан увидеть только какое-нибудь одно конкретное состояние — <<ядро распалось, кот мёртв>> или <<ядро не распалось, кот жив>>. Сам Шредингер думал, что его парадокс доказывает несостоятельность квантовой механики, но мы то с вами знаем, что квантовая механика и есть истинное представление о нашем мире -- снова повышает тон голоса Белин -- и вот независимо друг от друга, что доказывает отчасти истинность предположения -- Ганс Моравек в 1987 и Бруно Маршал в 1988 рассмотрели ситуацию с точки зрения самого кота!^^ +-- Итак, если ящик открыть, то экспериментатор обязан увидеть только какое-нибудь одно конкретное состояние — <<ядро распалось, кот мёртв>> или <<ядро не распалось, кот жив>>. Сам Шредингер думал, что его парадокс доказывает несостоятельность квантовой механики, но мы то с вами знаем, что квантовая механика и есть истинное представление о нашем мире -- снова повышает тон голоса Белин -- и вот, независимо друг от друга, что доказывает отчасти истинность предположения -- Ганс Моравек в 1987 и Бруно Маршал в 1988 рассмотрели ситуацию с точки зрения самого кота!^^ -- Если верна многомировая интерпретация Эверетта, то в результате каждого проведенного эксперимента с котом вселенная расщепляется на две вселенных, в одной из которых кот остается жив, а в другой погибает. В мирах, где кот умирает, он перестает существовать. Напротив, с точки зрения неумершего кота, эксперимент будет продолжаться, не приводя к исчезновению кота. Это происходит потому, что в любом ответвлении кот способен наблюдать результат эксперимента лишь в том мире, в котором он выживает. И если многомировая интерпретация верна, то кот может заметить, что он никогда не погибнет в ходе эксперимента... -- Белин замолкает и осматривает зал... ^^ @@ -27,7 +27,7 @@ lection = room { Мы все с вами тяжело работали этот год, под четким руководством... -- тут Белин бросил взгляд в сторону портретов -- и должен вам сказать, что информации в нашем информационном центре -- Белин посмотрел в потолок -- достаточно, чтобы доказать, я повторяю, научно доказать теоретически и экспериментально истинность многомировой интерпретации... -- Но что это значит для нас? -- Вы не можете этого видеть, но -- Белин смотрит на часы -- уже через несколько минут состав с ураном прибывает к задним воротам института... Урана хватит для того, чтобы обеспечить каждого из вас ядерной бомбой. Так как вы скоро убедитесь в том, что квантовое бессмертие это реальность, то каждый из нас сможет стать непобедимым террористом!!! Вселенная расщепится на множество миров, в каждом из который Вы -- палец Белина указывает в зал -- будете его диктатором и господином!!! -- Белин почти кричал...^^ -Зал не выдержал и взревел. Люди вставали и хлопали... Их глаза горели каким-то бешеным огнем... О Боже, подумал я -- это какое-то наваждение... Мои ноги не слушались меня -- я сидел на своем месте и не мог пошевелиться...^^ +Зал не выдержал и взревел. Люди вставали и хлопали... Их глаза горели каким-то бешеным огнем... О Боже, -- подумал я -- это какое-то наваждение... Мои ноги не слушались меня -- я сидел на своем месте и не мог пошевелиться...^^ Но я отвлекся -- говорит Белин -- продолжим наш опыт. С этими словами он достал из под стола живой комочек... Это был мой Барсик... -- Сейчас я помещу эту кошку в ящик и мы с вами... -- красная пелена застилает мои глаза... ]], @@ -65,14 +65,14 @@ lection = room { end end, obj = { - vobj(1, 'дальше', '{Дальше}'), + vobj(1, 'дальше', '{Дальше}.'), }; }; profdlg = dlg { nam = '!!!', pic = 'gfx/me.png', - dsc = 'Я собираюсь с силами встаю и во всю глотку кричу...', + dsc = 'Я собираюсь с силами, встаю и во всю глотку кричу...', obj = { [1] = phr('Это кот, а не кошка!', '-- Рука Белина останавливается -- его взгляд фокусируется на мне -- он узнает меня!! -- Охрана -- в зале посторонний!!! Убе... Уберите его!!! -- кричит он..', @@ -105,7 +105,7 @@ gdlg1 = dlg { [1] = phr('Положи свое ружье прикладом вперед на стол и толкни его сюда..', 'Охранник неуверенно смотрит на меня..', [[pon(2)]]), - [2] = _phr('Я сказал на стол ружье!!! -- я посильнее надавливаю стволами на Белина -- он близок к обмороку.', 'Охранник оторожно кладет помповое ружье на стол и толкает его ко мне... -- я быстро забираю ружье. Теперь в левой руке у меня обрез, в правом -- помповое ружье.', + [2] = _phr('Я сказал на стол ружье!!! -- я посильнее надавливаю стволами на Белина -- он близок к обмороку.', 'Охранник осторожно кладет помповое ружье на стол и толкает его ко мне... -- я быстро забираю ружье. Теперь в левой руке у меня обрез, в правом -- помповое ружье.', [[pon(3); inv():add('shotgun')]]), [3] = phr('Не нравилось тебе мое лицо да?? Дааа???', 'Охранник молчит, на его лбу выступает пот...', @@ -177,7 +177,7 @@ professor = obj { s._gun = true; objs():add('guardian'); gun._hidden = false; - return 'Я достаю обрез из-под одежды и, перепрыгивая через стол, бросаюсь к Белину.'; + return 'Я достаю обрез из-под одежды и, перепрыгивая через стол, бросаюсь к Белину.'; end end, }; @@ -316,7 +316,7 @@ lest = obj { s._seen = true; return 'Это же пожарная лестница!!!'; end - return 'Прыгать лили нет? Вот в чем вопрос...'; + return 'Прыгать или нет? Вот в чем вопрос...'; end, }; @@ -328,7 +328,7 @@ window = room { return 'в окно'; end, pic = 'gfx/fromwin1.png', - enter = "Это безумие, но все-же я бросаюсь к окну... За спиной я слышу рев толпы..."; + enter = "Это безумие, но все же я бросаюсь к окну... За спиной я слышу рев толпы..."; dsc = 'Я стою на подоконнике и всматриваюсь в ночную пустоту.', obj = { 'lest', @@ -378,7 +378,7 @@ up = room { s._num = s._num + 1; if s._num == 2 then lifeon('ladder'); - return 'Внезапно ночную тьму разрезает луч прожектора и тишину нарушает вой сирены... Похоже, внизу меня заметили...', false; + return 'Внезапно, ночную тьму разрезает луч прожектора и тишину нарушает вой сирены... Похоже, внизу меня заметили...', false; end if s._num > 4 then ladder.way:del('up'); @@ -489,7 +489,7 @@ fire = obj { return 'Я кидаю бумажку на горящий ковер...'; end kover._fire = true; - return 'Я кладу бумагу на ковер... Волоски ковра вспыхивают... Похоже начинается пожар..'; + return 'Я кладу бумагу на ковер... Волоски ковра вспыхивают... Похоже, начинается пожар..'; end }; @@ -701,7 +701,7 @@ escape2 = room { enter = function(s, f) if f == 'room5' then lifeon('escape2'); - return 'В бессилии я бью прикладом в ненавистную дверь. И вдруг, через несколько секунд слышу, как кто-то подходит к двери с внешней стороны... Это охранник!!! Писк срабатывания считывателя и вот дверь открывается. Охранник пятится -- ему в грудь упирается ствол помпового ружья. Мы выходим на площадку пятого этажа.'; + return 'В бессилии я бью прикладом в ненавистную дверь. И вдруг, через несколько секунд слышу, как кто-то подходит к двери с внешней стороны... Это охранник!!! Писк срабатывания считывателя -- и вот, дверь открывается. Охранник пятится -- ему в грудь упирается ствол помпового ружья. Мы выходим на площадку пятого этажа.'; end if f == 'krysha' then lifeon('escape2'); @@ -710,7 +710,7 @@ escape2 = room { life = function(s) s._timer = s._timer + 1; if s._timer == 3 then - return 'Внезапно на этаже раздается звук сирены. -- Внимание!!! На пятом этаже лицо с нулевым уровнем допуска. Повторяю... -- льется голос из невидимых динамиков.'; + return 'Внезапно, на этаже раздается звук сирены. -- Внимание!!! На пятом этаже лицо с нулевым уровнем допуска. Повторяю... -- льется голос из невидимых динамиков.'; end if s._timer > 3 then return '-- На пятом этаже лицо с нулевым уровнем доступа!!! -- от воя сирены меня начинает мутить.'; @@ -756,7 +756,7 @@ escape2 = room { return 'Там пламя!', false end if t == 'lift' then - return 'Я вижу что лампочки вызова всех лифтов горят. Скорее всего это означает, что сюда поднимается охрана... Надо спешить!', false; + return 'Я вижу, что лампочки вызова всех лифтов горят. Скорее всего это означает, что сюда поднимается охрана... Надо спешить!', false; end if t == 'krysha' then lifeoff('escape2'); @@ -809,7 +809,7 @@ hall5 = room { return 'Я провожу ладонью по скользкой поверхности одной из колонн... Мрамор!'; end if w == 3 then - return 'Но уж эта то люстра точно из горного хрусталя.'; + return 'Но уж эта-то люстра точно из горного хрусталя.'; end if w == 4 then return 'Темно... Хотя... Что это? Мне кажется, что у задней стороны института стоит поезд.'; @@ -845,7 +845,7 @@ hall5 = room { vobj(5,'западные окна', 'Разбитое {окно} находится слева от меня.'), vobj(6,'небольшая дверь', 'В конце зала, в его северной части, я вижу небольшую деревянную {дверь}.'), vobj(7,'входная дверь', 'За моей спиной входная {дверь}.'), - vobj(8,'картины', 'По стенам развешены {картины} в красивых рамах.'), + vobj(8,'картины', 'По стенам развешаны {картины} в красивых рамах.'), vobj(9,'диваны', 'У стен расставлены мягкие {диваны}.'), }, way = { 'krysha' }, @@ -893,9 +893,9 @@ gudlg = dlg { enter = function(s) lifeoff('mycat'); inv():del('shotgun'); - return [[Странно... Дверь не закрыта... Я осторожно открываю дверь и вхожу в комнату.^^Внезапно я обнаруживаю, что на меня смотрит дуло револьвера. -- Браво, браво, браво -- говорит мне человек в кресле, владелец револьвера. -- Я уже заждался. Тот самый лесник? Ну что же, подождем охрану. А пока -- брось ружье на пол. Мне ничего не остается, как сделать то - что он сказал.]]; + return [[Странно... Дверь не закрыта... Я осторожно открываю дверь и вхожу в комнату.^^Внезапно я обнаруживаю, что на меня смотрит дуло револьвера. -- Браво, браво, браво -- говорит мне человек в кресле, владелец револьвера. -- Я уже заждался. Тот самый лесник? Ну что же, подождем охрану. А пока -- брось ружье на пол. Мне ничего не остается, как сделать то, что он сказал.]]; end, - dsc = [[Передо мной то самое лицо. Лицо с портретов, которые увешивают почти все комнаты этого здания. Лицо спокойное, ничего не выражающее. Слабая улыбка на губах. Надо тянуть время... И я спрашиваю у него:]], + dsc = [[Передо мной то самое лицо. Лицо с портретов, которыми увешаны почти все комнаты этого здания. Лицо спокойное, ничего не выражающее. Слабая улыбка на губах. Надо тянуть время... И я спрашиваю у него:]], obj = { [1] = phr('Может, поговорим?', 'Гм... Ну о чем нам говорить? О чем мне говорить с лесником?', [[pon(2)]]), @@ -909,10 +909,10 @@ gudlg = dlg { [5] = _phr('Ложь!', 'А что если? Представь, что если?... Ты же хакер да? Любишь продумывать все заранее...', [[pon(6);poff(9)]]), - [6] = _phr('Нет! Это не может быть правдой! Если это правда -- рано или поздно мир будет обречен! Тоггда...', + [6] = _phr('Нет! Это не может быть правдой! Если это правда -- рано или поздно мир будет обречен! Тогда...', '-- Да, ты правильно понял... Тогда есть только ТЫ!!! Послушай себя -- кто дал тебе этот ответ? Не его ли ты жаждешь? Не его ли жаждет твое Я? -- Я проваливаюсь в его бездонный взгляд.', [[pon(7)]]), [7] = _phr('Если... Если... То зачем?', - '-- Правильно... Правильно -- вкрадчиво говорит мне человек с портрета... Новая волна страха оглушает меня я падаю на колени... Сердце бешено стучит и вылетает из груди...', + '-- Правильно... Правильно -- вкрадчиво говорит мне человек с портрета... Новая волна страха оглушает меня, я падаю на колени... Сердце бешено стучит и вылетает из груди...', [[pon(8)]]), [8] = _phr('Не могу... Нет...', '-- И если все так, то тебе нечего бояться -- мурлычет он. -- Сердце бьется еще сильнее. И наконец, моя грудь взрывается, мягкий комочек шерсти отталкивается от нее лапами и летит в лицо человеку с портрета. Раздается выстрел, резкая боль в левом плече отрезвляет меня, я вскакиваю на ноги и бросаюсь вперед...',[[return goto('escape3')]]), @@ -1056,7 +1056,7 @@ escape3 = room { dsc = [[Я нахожусь в небольшой, но уютной комнате. Посреди стоит стол. Рядом опрокинуто кресло. Из небольшой люстры равномерно льется свет. Два небольших книжных шкафа стоят у стен.]], obj = { vobj(1, 'человек', 'На полу, прислонившись к столу, сидит {человек} с портретов. Струйка крови стекает с его губ -- он стонет.'), - vobj(2, 'картины', 'По стенам развешены {картины}.'), + vobj(2, 'картины', 'По стенам развешаны {картины}.'), vobj(3, 'дверь', 'За моей спиной {дверь}.'), 'revol', 'shkaf', @@ -1095,7 +1095,7 @@ nwall = room { way = {'eside2','wside' }, act = function(s, w) if w == 1 then - return 'Да -- водосточная труба... Довольно крепкая. Но вряд-ли я смогу взобраться по ней наверх.'; + return 'Да -- водосточная труба... Довольно крепкая. Но вряд ли я смогу взобраться по ней наверх.'; end end, enter = function(s, f) diff --git a/games/cat/main.lua b/games/cat/main.lua index 3007888..9c07b63 100644 --- a/games/cat/main.lua +++ b/games/cat/main.lua @@ -1,3 +1,4 @@ +-- $Name:Возвращение квантового кота$ game.codepage="UTF-8"; game.act = 'Не получается.'; game.inv = 'Гм.. Странная штука..'; @@ -30,7 +31,7 @@ main = room { начать все по-порядку...^^ В тот холодный февральский день я как всегда собрался ехать в поселок...]], -obj = { vobj(1,'Дальше','{Дальше}') }, +obj = { vobj(1,'Дальше','{Дальше}.') }, act = function() return goto('home'); end, diff --git a/games/tutorial/main.lua b/games/tutorial/main.lua index 6a3c8d5..aa9052f 100644 --- a/games/tutorial/main.lua +++ b/games/tutorial/main.lua @@ -1,10 +1,17 @@ +-- $Name:Обучение game.codepage="UTF-8" +game.act = 'Не получается.'; +game.inv = 'Гм.. Не то..'; +game.use = 'Не сработает...'; game.dsc = [[Команды:^ look(или просто ввод), act <на что> (или просто на что), use <что> [на что], go <куда>,^ back, inv, way, obj, quit, save , load . Работает автодополнение по табуляции.^^]]; +game.pic = 'stead.png'; + +set_music('ramparts.mod'); main = room { - nam = 'туториал', + nam = 'Обучение', act = function() return goto('r1'); end, @@ -12,7 +19,7 @@ main = room { Каждая сцена игры представляет собой описание статической и динамической части.^^ Динамическая часть сцены состоит из объектов, персонажей и т.д. С динамической частью -игрок может взаимодействовать с помощью мыши, с помощью нажатий на подсвеченные ссылки.^^ +игрок может взаимодействовать с помощью мыши, нажимая на подсвеченные ссылки.^^ В данной сцене единственным объектом является объект "Дальше" -- который вы видите внизу текста. Итак, для продолжения обучения вы можете нажать на "Дальше". @@ -69,7 +76,7 @@ r1 = room { }; r2 = room { - nam = 'урок 2', + nam = 'Урок 2', enter = function() lifeon('r2'); end, @@ -112,7 +119,7 @@ vasa = obj { } r3 = room { - nam = 'урок 3', + nam = 'Урок 3', enter = function() lifeon('r3'); end, @@ -131,7 +138,7 @@ r3 = room { }; r4 = room { - nam = 'урок 4', + nam = 'Урок 4', enter = function() apple._knife = false; lifeon('r4'); @@ -148,7 +155,7 @@ r4 = room { }; r5 = room { - nam = 'урок 5', + nam = 'Урок 5', dsc = [[Хорошо. Изучаем переходы. Идите на урок 6. Для этого нажмите мышью на соответствующую ссылку.]], exit = function(s, t) if t ~= 'r6' then @@ -159,7 +166,7 @@ r5 = room { }; r6 = room { - nam = 'урок 6', + nam = 'Урок 6', dsc = [[Идите на последний урок.]], exit = function(s, t) if t ~= 'theend' then @@ -170,9 +177,19 @@ r6 = room { }; theend = room { - nam = 'последний урок', + nam = 'Последний урок', dsc = [[Вы можете сохранять игру, выбрать другую игру или выполнять иные действия с помощью меню. Для вызова меню нажмите клавишу esc или нажмите мышью на символ меню (справа снизу). -Теперь вы готовы к игре. Удачи!!!]], +Теперь вы готовы к игре. Удачи!!!^^ +В обучении использован трек:^Ramparts by Scorpion]], + obj = { vway('клавиши', 'Посмотреть {список клавиш}.', 'help')}, }; +help = room { + nam = 'Список клавиш', + dsc = [[ +esc - меню^ +alt-q - выход^ +alt-enter - полный экран/окно^ +Вверх/Вниз, Пробел/Backspace, PgUp/PgDn - прокрутка главного окна.]], +}; diff --git a/games/tutorial/ramparts.mod b/games/tutorial/ramparts.mod new file mode 100644 index 0000000..623dc7a Binary files /dev/null and b/games/tutorial/ramparts.mod differ diff --git a/games/tutorial/stead.png b/games/tutorial/stead.png new file mode 100644 index 0000000..526195a Binary files /dev/null and b/games/tutorial/stead.png differ diff --git a/icon/Makefile b/icon/Makefile new file mode 100644 index 0000000..dcdf8b7 --- /dev/null +++ b/icon/Makefile @@ -0,0 +1,7 @@ +include ../Rules.make +clean: +all: +install: + install -d -m0755 $(ICONPATH) + install -m0644 sdl_instead.png $(ICONPATH) + diff --git a/icon/Makefile.windows b/icon/Makefile.windows new file mode 100644 index 0000000..8d27c9c --- /dev/null +++ b/icon/Makefile.windows @@ -0,0 +1,5 @@ +clean: +all: +install: + if not exist ..\bin\icon mkdir ..\bin\icon + copy sdl_instead.png ..\bin\icon diff --git a/icon/book16x16.ico b/icon/book16x16.ico new file mode 100644 index 0000000..ccde08f Binary files /dev/null and b/icon/book16x16.ico differ diff --git a/icon/sdl_instead.ico b/icon/sdl_instead.ico new file mode 100644 index 0000000..2226546 Binary files /dev/null and b/icon/sdl_instead.ico differ diff --git a/icon/sdl_instead.png b/icon/sdl_instead.png new file mode 100644 index 0000000..2d6a637 Binary files /dev/null and b/icon/sdl_instead.png differ diff --git a/readme.txt b/readme.txt index 88d9920..73a87a4 100644 --- a/readme.txt +++ b/readme.txt @@ -1,19 +1,28 @@ Deps: -sdl, sdl-mixer, sdl-image, sdl-ttf, lua5.1 - -On debian systems: -apt-get install liblua5.1-dev libreadline-dev libsdl1.2-dev libsdl-ttf2.0-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libreadline5-dev - -make -make install -sdl-instead + sdl, sdl-mixer, sdl-image, sdl-ttf, lua5.1 To run just from build dir use: -rm Rules.make -ln -s Rules.make.standalone Rules.make -make clean -make -./sdl-instead + rm -f Rules.make + ln -s Rules.make.standalone Rules.make + make clean + make + ./sdl-instead + +To install into system: + + # as root + rm -f Rules.make + ln -s Rules.make.system Rules.make + make clean + make install + sdl-instead + +On debian systems: + apt-get install liblua5.1-dev libreadline-dev libsdl1.2-dev libsdl-ttf2.0-dev libsdl-image1.2-dev libsdl-mixer1.2-dev libreadline5-dev + +building deb package: + build-essential + debuild diff --git a/src/instead/Makefile b/src/instead/Makefile index 3a8edc4..ddce230 100644 --- a/src/instead/Makefile +++ b/src/instead/Makefile @@ -2,7 +2,7 @@ include ../../Rules.make all: instead instead: instead.c rline.c - $(CC) $(CFLAGS) -DSTEAD_PATH=\"${STEADPATH}/\" instead.c $(LUA_CFLAGS) rline.c $(LUA_LFLAGS) -lreadline -o instead + $(CC) $(CFLAGS) -DSTEAD_PATH=\"${STEADPATH}/\" instead.c $(LUA_CFLAGS) rline.c $(LUA_LFLAGS) -lncurses -lreadline -o instead clean: rm -rf *.o instead install: diff --git a/src/instead/copying b/src/instead/copying new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/src/instead/copying @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/src/instead/rline.c b/src/instead/rline.c index a8de73c..dba1caf 100644 --- a/src/instead/rline.c +++ b/src/instead/rline.c @@ -12,7 +12,7 @@ #include #include #include -#ifdef HAVE_ICONV +#ifdef _HAVE_ICONV #include #include #endif @@ -241,7 +241,7 @@ char **tquest_completion(char *text, int start, int end) /* Tell the GNU Readline library how to complete. We want to try to complete on command names if this is the first word in the line, or on filenames if not. */ -#ifdef HAVE_ICONV +#ifdef _HAVE_ICONV static char curcp[64]; static char *fromcp; #endif @@ -257,7 +257,7 @@ void initialize_readline(void) rl_parse_and_bind(strdup("Control-g: 'go '")); /* Tell the completer that we want a crack first. */ rl_attempted_completion_function = (CPPFunction *) tquest_completion; -#ifdef HAVE_ICONV +#ifdef _HAVE_ICONV strncpy(curcp, nl_langinfo(CODESET), sizeof(curcp)); fromcp = getstring("return game.codepage;"); #endif @@ -292,7 +292,7 @@ char *string_fromwc(const wchar_t *str) pstr[ wcstombs(pstr, str, mbs_size) ] = 0; return pstr; } -#ifdef HAVE_ICONV +#ifdef _HAVE_ICONV #define CHAR_MAX_LEN 4 static char *decode(iconv_t hiconv, const char *s) { diff --git a/src/instead/tutorial/main.lua b/src/instead/tutorial/main.lua index 74b99f2..41b2d85 100644 --- a/src/instead/tutorial/main.lua +++ b/src/instead/tutorial/main.lua @@ -1,10 +1,11 @@ game.codepage="UTF-8" +game.err = "Ошибка!" game.dsc = [[Команды:^ look(или просто ввод), act <на что> (или просто на что), use <что> [на что], go <куда>,^ back, inv, way, obj, quit, save , load . Работает автодополнение по табуляции.^^]]; main = room { - nam = 'туториал', + nam = 'Обучение', act = function() return goto('r1'); end, diff --git a/src/sdl-instead/Makefile b/src/sdl-instead/Makefile index 8fdb52b..d756552 100644 --- a/src/sdl-instead/Makefile +++ b/src/sdl-instead/Makefile @@ -1,27 +1,24 @@ include ../../Rules.make -CFLAGS += $(SDL_CFLAGS) $(LUA_CFLAGS) -DSTEAD_PATH=\"${STEADPATH}/\" -DGAMES_PATH=\"${GAMESPATH}/\" -DTHEMES_PATH=\"${THEMESPATH}/\" -DVERSION=$(VERSION) +CFLAGS += $(SDL_CFLAGS) $(LUA_CFLAGS) -DSTEAD_PATH=\"${STEADPATH}/\" -DGAMES_PATH=\"${GAMESPATH}/\" -DTHEMES_PATH=\"${THEMESPATH}/\" -DVERSION=$(VERSION) -DICON_PATH=\"${ICONPATH}/\" LDFLAGS += $(SDL_LFLAGS) $(LUA_LFLAGS) -SRC := graphics.c input.c game.c main.c instead.c sound.c +SRC := graphics.c input.c game.c main.c instead.c sound.c SDL_rotozoom.c $(PLATFORM) OBJ := $(patsubst %.c, %.o, $(SRC)) -all: sdl-instead -PREFIX:=/usr/local +all: sdl-instead$(EXE) -$(OBJ): %.o : %.c gui.h +$(OBJ): %.o : %.c gui.h menu.h menu-en.h $(CC) -c $(<) $(I) $(CFLAGS) -sdl-instead: $(OBJ) +sdl-instead$(EXE): $(OBJ) $(RESOURCES) $(CC) $(CFLAGS) $(^) $(LDFLAGS) -o $(@) -# strip sdl-instead install: - install -d -m 0755 $(BIN) - install -m 0755 sdl-instead $(BIN)/sdl-instead + $(INSTALLD) $(BIN) + $(INSTALL) sdl-instead$(EXE) $(BIN)/sdl-instead$(EXE) clean: - rm -f *.o sdl-instead - + $(RM) -f *.o sdl-instead$(EXE) diff --git a/src/sdl-instead/SDL_rotozoom.c b/src/sdl-instead/SDL_rotozoom.c new file mode 100644 index 0000000..d82265c --- /dev/null +++ b/src/sdl-instead/SDL_rotozoom.c @@ -0,0 +1,1355 @@ +/* + + SDL_rotozoom.c - rotozoomer for 32bit or 8bit surfaces + + LGPL (c) A. Schiffler + +*/ + +#ifdef WIN32 +#include +#endif + +#include +#include + +#include "SDL_rotozoom.h" + +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) + + +/* + + 32bit integer-factor averaging Shrinker + + Shrinks 32bit RGBA/ABGR 'src' surface to 'dst' surface. + +*/ + +int shrinkSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int factorx, int factory) +{ + int x, y, dx, dy, sgap, dgap, ra, ga, ba, aa; + int n_average; + tColorRGBA *sp, *osp, *oosp; + tColorRGBA *dp; + + /* + * Averaging integer shrink + */ + + /* Precalculate division factor */ + n_average = factorx*factory; + + /* + * Scan destination + */ + sp = (tColorRGBA *) src->pixels; + sgap = src->pitch - src->w * 4; + + dp = (tColorRGBA *) dst->pixels; + dgap = dst->pitch - dst->w * 4; + + for (y = 0; y < dst->h; y++) { + + osp=sp; + for (x = 0; x < dst->w; x++) { + + /* Trace out source box and accumulate */ + oosp=sp; + ra=ga=ba=aa=0; + for (dy=0; dy < factory; dy++) { + for (dx=0; dx < factorx; dx++) { + ra += sp->r; + ga += sp->g; + ba += sp->b; + aa += sp->a; + + sp++; + } + /* src dx loop */ + sp = (tColorRGBA *)((Uint8*)sp + (src->pitch - 4*factorx)); // next y + } + /* src dy loop */ + + /* next box-x */ + sp = (tColorRGBA *)((Uint8*)oosp + 4*factorx); + + /* Store result in destination */ + dp->r = ra/n_average; + dp->g = ga/n_average; + dp->b = ba/n_average; + dp->a = aa/n_average; + + /* + * Advance destination pointer + */ + dp++; + } + /* dst x loop */ + + /* next box-y */ + sp = (tColorRGBA *)((Uint8*)osp + src->pitch*factory); + + /* + * Advance destination pointers + */ + dp = (tColorRGBA *) ((Uint8 *) dp + dgap); + } + /* dst y loop */ + + return (0); +} + +/* + + 8bit integer-factor averaging Shrinker + + Shrinks 8bit Y 'src' surface to 'dst' surface. + +*/ + +int shrinkSurfaceY(SDL_Surface * src, SDL_Surface * dst, int factorx, int factory) +{ + int x, y, dx, dy, sgap, dgap, a; + int n_average; + Uint8 *sp, *osp, *oosp; + Uint8 *dp; + + /* + * Averaging integer shrink + */ + + /* Precalculate division factor */ + n_average = factorx*factory; + + /* + * Scan destination + */ + sp = (Uint8 *) src->pixels; + sgap = src->pitch - src->w; + + dp = (Uint8 *) dst->pixels; + dgap = dst->pitch - dst->w; + + for (y = 0; y < dst->h; y++) { + + osp=sp; + for (x = 0; x < dst->w; x++) { + + /* Trace out source box and accumulate */ + oosp=sp; + a=0; + for (dy=0; dy < factory; dy++) { + for (dx=0; dx < factorx; dx++) { + a += (*sp); + /* next x */ + sp++; + } + /* end src dx loop */ + /* next y */ + sp = (Uint8 *)((Uint8*)sp + (src->pitch - factorx)); + } + /* end src dy loop */ + + /* next box-x */ + sp = (Uint8 *)((Uint8*)oosp + factorx); + + /* Store result in destination */ + *dp = a/n_average; + + /* + * Advance destination pointer + */ + dp++; + } + /* end dst x loop */ + + /* next box-y */ + sp = (Uint8 *)((Uint8*)osp + src->pitch*factory); + + /* + * Advance destination pointers + */ + dp = (Uint8 *)((Uint8 *)dp + dgap); + } + /* end dst y loop */ + + return (0); +} + +/* + + 32bit Zoomer with optional anti-aliasing by bilinear interpolation. + + Zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface. + +*/ + +int zoomSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int flipx, int flipy, int smooth) +{ + int x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, ex, ey, t1, t2, sstep, lx, ly; + tColorRGBA *c00, *c01, *c10, *c11, *cswap; + tColorRGBA *sp, *csp, *dp; + int dgap; + + /* + * Variable setup + */ + if (smooth) { + /* + * For interpolation: assume source dimension is one pixel + */ + /* + * smaller to avoid overflow on right and bottom edge. + */ + sx = (int) (65536.0 * (float) (src->w - 1) / (float) dst->w); + sy = (int) (65536.0 * (float) (src->h - 1) / (float) dst->h); + } else { + sx = (int) (65536.0 * (float) src->w / (float) dst->w); + sy = (int) (65536.0 * (float) src->h / (float) dst->h); + } + + /* + * Allocate memory for row increments + */ + if ((sax = (int *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) { + return (-1); + } + if ((say = (int *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) { + free(sax); + return (-1); + } + + /* + * Precalculate row increments + */ + sp = csp = (tColorRGBA *) src->pixels; + dp = (tColorRGBA *) dst->pixels; + + if (flipx) csp += (src->w-1); + if (flipy) csp += (src->pitch*(src->h-1)); + + csx = 0; + csax = sax; + for (x = 0; x <= dst->w; x++) { + *csax = csx; + csax++; + csx &= 0xffff; + csx += sx; + } + csy = 0; + csay = say; + for (y = 0; y <= dst->h; y++) { + *csay = csy; + csay++; + csy &= 0xffff; + csy += sy; + } + + dgap = dst->pitch - dst->w * 4; + + /* + * Switch between interpolating and non-interpolating code + */ + if (smooth) { + + /* + * Interpolating Zoom + */ + + /* + * Scan destination + */ + ly = 0; + csay = say; + for (y = 0; y < dst->h; y++) { + /* + * Setup color source pointers + */ + c00 = csp; + c01 = csp; + c01++; + c10 = (tColorRGBA *) ((Uint8 *) csp + src->pitch); + c11 = c10; + c11++; + csax = sax; + if (flipx) { + cswap = c00; c00=c01; c01=cswap; + cswap = c10; c10=c11; c11=cswap; + } + if (flipy) { + cswap = c00; c00=c10; c10=cswap; + cswap = c01; c01=c11; c11=cswap; + } + lx = 0; + for (x = 0; x < dst->w; x++) { + /* + * Interpolate colors + */ + ex = (*csax & 0xffff); + ey = (*csay & 0xffff); + t1 = ((((c01->r - c00->r) * ex) >> 16) + c00->r) & 0xff; + t2 = ((((c11->r - c10->r) * ex) >> 16) + c10->r) & 0xff; + dp->r = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01->g - c00->g) * ex) >> 16) + c00->g) & 0xff; + t2 = ((((c11->g - c10->g) * ex) >> 16) + c10->g) & 0xff; + dp->g = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01->b - c00->b) * ex) >> 16) + c00->b) & 0xff; + t2 = ((((c11->b - c10->b) * ex) >> 16) + c10->b) & 0xff; + dp->b = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01->a - c00->a) * ex) >> 16) + c00->a) & 0xff; + t2 = ((((c11->a - c10->a) * ex) >> 16) + c10->a) & 0xff; + dp->a = (((t2 - t1) * ey) >> 16) + t1; + + /* + * Advance source pointers + */ + csax++; + sstep = (*csax >> 16); + lx += sstep; + if (lx >= src->w) sstep = 0; + if (flipx) sstep = -sstep; + c00 += sstep; + c01 += sstep; + c10 += sstep; + c11 += sstep; + /* + * Advance destination pointer + */ + dp++; + } + /* + * Advance source pointer + */ + csay++; + sstep = (*csay >> 16); + ly += sstep; + if (ly >= src->h) sstep = 0; + sstep *= src->pitch; + if (flipy) sstep = -sstep; + csp = (tColorRGBA *) ((Uint8 *) csp + sstep); + + /* + * Advance destination pointers + */ + dp = (tColorRGBA *) ((Uint8 *) dp + dgap); + } + } else { + + /* + * Non-Interpolating Zoom + */ + + csay = say; + for (y = 0; y < dst->h; y++) { + sp = csp; + csax = sax; + for (x = 0; x < dst->w; x++) { + /* + * Draw + */ + *dp = *sp; + /* + * Advance source pointers + */ + csax++; + sstep = (*csax >> 16); + if (flipx) sstep = -sstep; + sp += sstep; + /* + * Advance destination pointer + */ + dp++; + } + /* + * Advance source pointer + */ + csay++; + sstep = (*csay >> 16) * src->pitch; + if (flipy) sstep = -sstep; + csp = (tColorRGBA *) ((Uint8 *) csp + sstep); + + /* + * Advance destination pointers + */ + dp = (tColorRGBA *) ((Uint8 *) dp + dgap); + } + } + + /* + * Remove temp arrays + */ + free(sax); + free(say); + + return (0); +} + +/* + + 8bit Zoomer without smoothing. + + Zoomes 8bit palette/Y 'src' surface to 'dst' surface. + +*/ + +int zoomSurfaceY(SDL_Surface * src, SDL_Surface * dst, int flipx, int flipy) +{ + Uint32 x, y, sx, sy, *sax, *say, *csax, *csay, csx, csy, sstep; + Uint8 *sp, *dp, *csp; + int dgap; + + /* + * Variable setup + */ + sx = (Uint32) (65536.0 * (float) src->w / (float) dst->w); + sy = (Uint32) (65536.0 * (float) src->h / (float) dst->h); + + + /* + * Allocate memory for row increments + */ + if ((sax = (Uint32 *) malloc((dst->w + 1) * sizeof(Uint32))) == NULL) { + return (-1); + } + if ((say = (Uint32 *) malloc((dst->h + 1) * sizeof(Uint32))) == NULL) { + free(sax); + return (-1); + } + + /* + * Pointer setup + */ + sp = csp = (Uint8 *) src->pixels; + dp = (Uint8 *) dst->pixels; + dgap = dst->pitch - dst->w; + + if (flipx) csp += (src->w-1); + if (flipy) csp = ( (Uint8*)csp + src->pitch*(src->h-1) ); + + /* + * Precalculate row increments + */ + csx = 0; + csax = sax; + for (x = 0; x <= dst->w; x++) { + *csax = csx; + csax++; + csx &= 0xffff; + csx += sx; + } + csy = 0; + csay = say; + for (y = 0; y <= dst->h; y++) { + *csay = csy; + csay++; + csy &= 0xffff; + csy += sy; + } + + + /* + * Draw + */ + csay = say; + for (y = 0; y < dst->h; y++) { + csax = sax; + sp = csp; + for (x = 0; x < dst->w; x++) { + /* + * Draw + */ + *dp = *sp; + /* + * Advance source pointers + */ + csax++; + sstep = (*csax >> 16); + if (flipx) sstep = -sstep; + sp += sstep; + /* + * Advance destination pointer + */ + dp++; + } + /* + * Advance source pointer (for row) + */ + csay++; + sstep = (*csay >> 16) * src->pitch; + if (flipy) sstep = -sstep; + csp = ((Uint8 *) csp + sstep); + + /* + * Advance destination pointers + */ + dp += dgap; + } + + /* + * Remove temp arrays + */ + free(sax); + free(say); + + return (0); +} + +/* + + 32bit Rotozoomer with optional anti-aliasing by bilinear interpolation. + + Rotates and zoomes 32bit RGBA/ABGR 'src' surface to 'dst' surface. + +*/ + +void transformSurfaceRGBA(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy, int smooth) +{ + int x, y, t1, t2, dx, dy, xd, yd, sdx, sdy, ax, ay, ex, ey, sw, sh; + tColorRGBA c00, c01, c10, c11, cswap; + tColorRGBA *pc, *sp; + int gap; + + /* + * Variable setup + */ + xd = ((src->w - dst->w) << 15); + yd = ((src->h - dst->h) << 15); + ax = (cx << 16) - (icos * cx); + ay = (cy << 16) - (isin * cx); + sw = src->w - 1; + sh = src->h - 1; + pc = dst->pixels; + gap = dst->pitch - dst->w * 4; + + /* + * Switch between interpolating and non-interpolating code + */ + if (smooth) { + for (y = 0; y < dst->h; y++) { + dy = cy - y; + sdx = (ax + (isin * dy)) + xd; + sdy = (ay - (icos * dy)) + yd; + for (x = 0; x < dst->w; x++) { + dx = (sdx >> 16); + dy = (sdy >> 16); + if ((dx > -1) && (dy > -1) && (dx < src->w) && (dy < src->h)) { + if (flipx) dx = sw - dx; + if (flipy) dy = sh - dy; + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + sp += dx; + c00 = *sp; + sp += 1; + c01 = *sp; + sp = (tColorRGBA *) ((Uint8 *) sp + src->pitch); + c11 = *sp; + sp -= 1; + c10 = *sp; + if (flipx) { + cswap = c00; c00=c01; c01=cswap; + cswap = c10; c10=c11; c11=cswap; + } + if (flipy) { + cswap = c00; c00=c10; c10=cswap; + cswap = c01; c01=c11; c11=cswap; + } + /* + * Interpolate colors + */ + ex = (sdx & 0xffff); + ey = (sdy & 0xffff); + t1 = ((((c01.r - c00.r) * ex) >> 16) + c00.r) & 0xff; + t2 = ((((c11.r - c10.r) * ex) >> 16) + c10.r) & 0xff; + pc->r = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01.g - c00.g) * ex) >> 16) + c00.g) & 0xff; + t2 = ((((c11.g - c10.g) * ex) >> 16) + c10.g) & 0xff; + pc->g = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01.b - c00.b) * ex) >> 16) + c00.b) & 0xff; + t2 = ((((c11.b - c10.b) * ex) >> 16) + c10.b) & 0xff; + pc->b = (((t2 - t1) * ey) >> 16) + t1; + t1 = ((((c01.a - c00.a) * ex) >> 16) + c00.a) & 0xff; + t2 = ((((c11.a - c10.a) * ex) >> 16) + c10.a) & 0xff; + pc->a = (((t2 - t1) * ey) >> 16) + t1; + } + sdx += icos; + sdy += isin; + pc++; + } + pc = (tColorRGBA *) ((Uint8 *) pc + gap); + } + } else { + for (y = 0; y < dst->h; y++) { + dy = cy - y; + sdx = (ax + (isin * dy)) + xd; + sdy = (ay - (icos * dy)) + yd; + for (x = 0; x < dst->w; x++) { + dx = (short) (sdx >> 16); + dy = (short) (sdy >> 16); + if (flipx) dx = (src->w-1)-dx; + if (flipy) dy = (src->h-1)-dy; + if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) { + sp = (tColorRGBA *) ((Uint8 *) src->pixels + src->pitch * dy); + sp += dx; + *pc = *sp; + } + sdx += icos; + sdy += isin; + pc++; + } + pc = (tColorRGBA *) ((Uint8 *) pc + gap); + } + } +} + +/* + + 8bit Rotozoomer without smoothing + + Rotates and zoomes 8bit palette/Y 'src' surface to 'dst' surface. + +*/ + +void transformSurfaceY(SDL_Surface * src, SDL_Surface * dst, int cx, int cy, int isin, int icos, int flipx, int flipy) +{ + int x, y, dx, dy, xd, yd, sdx, sdy, ax, ay, sw, sh; + tColorY *pc, *sp; + int gap; + + /* + * Variable setup + */ + xd = ((src->w - dst->w) << 15); + yd = ((src->h - dst->h) << 15); + ax = (cx << 16) - (icos * cx); + ay = (cy << 16) - (isin * cx); + sw = src->w - 1; + sh = src->h - 1; + pc = dst->pixels; + gap = dst->pitch - dst->w; + /* + * Clear surface to colorkey + */ + memset(pc, (unsigned char) (src->format->colorkey & 0xff), dst->pitch * dst->h); + /* + * Iterate through destination surface + */ + for (y = 0; y < dst->h; y++) { + dy = cy - y; + sdx = (ax + (isin * dy)) + xd; + sdy = (ay - (icos * dy)) + yd; + for (x = 0; x < dst->w; x++) { + dx = (short) (sdx >> 16); + dy = (short) (sdy >> 16); + if (flipx) dx = (src->w-1)-dx; + if (flipy) dy = (src->h-1)-dy; + if ((dx >= 0) && (dy >= 0) && (dx < src->w) && (dy < src->h)) { + sp = (tColorY *) (src->pixels); + sp += (src->pitch * dy + dx); + *pc = *sp; + } + sdx += icos; + sdy += isin; + pc++; + } + pc += gap; + } +} + +/* + + 32bit specialized 90degree rotator + + Rotates and zooms 'src' surface to 'dst' surface in 90degree increments. + + (contributed by Jeff Schiller) + +*/ +SDL_Surface* rotateSurface90Degrees(SDL_Surface* pSurf, int numClockwiseTurns) +{ + int row, col, newWidth, newHeight; + SDL_Surface* pSurfOut; + + /* Has to be a valid surface pointer and only 32-bit surfaces (for now) */ + if (!pSurf || pSurf->format->BitsPerPixel != 32) { return NULL; } + + /* normalize numClockwiseTurns */ + while(numClockwiseTurns < 0) { numClockwiseTurns += 4; } + numClockwiseTurns = (numClockwiseTurns % 4); + + /* if it's even, our new width will be the same as the source surface */ + newWidth = (numClockwiseTurns % 2) ? (pSurf->h) : (pSurf->w); + newHeight = (numClockwiseTurns % 2) ? (pSurf->w) : (pSurf->h); + pSurfOut = SDL_CreateRGBSurface( pSurf->flags, newWidth, newHeight, pSurf->format->BitsPerPixel, + pSurf->format->Rmask, + pSurf->format->Gmask, + pSurf->format->Bmask, + pSurf->format->Amask); + if(!pSurfOut) { + return NULL; + } + + if(numClockwiseTurns != 0) { + SDL_LockSurface(pSurf); + SDL_LockSurface(pSurfOut); + switch(numClockwiseTurns) { + + /* rotate clockwise */ + case 1: /* rotated 90 degrees clockwise */ + { + Uint32* srcBuf = NULL; + Uint32* dstBuf = NULL; + + for (row = 0; row < pSurf->h; ++row) { + srcBuf = (Uint32*)(pSurf->pixels) + (row*pSurf->pitch/4); + dstBuf = (Uint32*)(pSurfOut->pixels) + (pSurfOut->w - row - 1); + for (col = 0; col < pSurf->w; ++col) { + *dstBuf = *srcBuf; + ++srcBuf; + dstBuf += pSurfOut->pitch/4; + } + /* end for(col) */ + } + /* end for(row) */ + } + break; + + case 2: /* rotated 180 degrees clockwise */ + { + Uint32* srcBuf = NULL; + Uint32* dstBuf = NULL; + + for(row = 0; row < pSurf->h; ++row) { + srcBuf = (Uint32*)(pSurf->pixels) + (row*pSurf->pitch/4); + dstBuf = (Uint32*)(pSurfOut->pixels) + ((pSurfOut->h - row - 1)*pSurfOut->pitch/4) + (pSurfOut->w - 1); + for(col = 0; col < pSurf->w; ++col) { + *dstBuf = *srcBuf; + ++srcBuf; + --dstBuf; + } + } + } + break; + + case 3: + { + Uint32* srcBuf = NULL; + Uint32* dstBuf = NULL; + + for(row = 0; row < pSurf->h; ++row) { + srcBuf = (Uint32*)(pSurf->pixels) + (row*pSurf->pitch/4); + dstBuf = (Uint32*)(pSurfOut->pixels) + row + ((pSurfOut->h - 1)*pSurfOut->pitch/4); + for(col = 0; col < pSurf->w; ++col) { + *dstBuf = *srcBuf; + ++srcBuf; + dstBuf -= pSurfOut->pitch/4; + } + } + } + break; + } + /* end switch */ + + SDL_UnlockSurface(pSurf); + SDL_UnlockSurface(pSurfOut); + } + /* end if numClockwiseTurns > 0 */ + else { + /* simply copy surface to output */ + if(SDL_BlitSurface(pSurf, NULL, pSurfOut, NULL)) { + return NULL; + } + } + return pSurfOut; +} + +/* + + rotozoomSurface() + + Rotates and zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. + 'angle' is the rotation in degrees. 'zoom' a scaling factor. If 'smooth' is 1 + then the destination 32bit surface is anti-aliased. If the surface is not 8bit + or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. + +*/ + +#define VALUE_LIMIT 0.001 + +/* Local rotozoom-size function with trig result return */ + +void rotozoomSurfaceSizeTrig(int width, int height, double angle, double zoomx, double zoomy, int *dstwidth, int *dstheight, + double *canglezoom, double *sanglezoom) +{ + double x, y, cx, cy, sx, sy; + double radangle; + int dstwidthhalf, dstheighthalf; + + /* + * Determine destination width and height by rotating a centered source box + */ + radangle = angle * (M_PI / 180.0); + *sanglezoom = sin(radangle); + *canglezoom = cos(radangle); + *sanglezoom *= zoomx; + *canglezoom *= zoomx; + x = width / 2; + y = height / 2; + cx = *canglezoom * x; + cy = *canglezoom * y; + sx = *sanglezoom * x; + sy = *sanglezoom * y; + + dstwidthhalf = MAX((int) + ceil(MAX(MAX(MAX(fabs(cx + sy), fabs(cx - sy)), fabs(-cx + sy)), fabs(-cx - sy))), 1); + dstheighthalf = MAX((int) + ceil(MAX(MAX(MAX(fabs(sx + cy), fabs(sx - cy)), fabs(-sx + cy)), fabs(-sx - cy))), 1); + *dstwidth = 2 * dstwidthhalf; + *dstheight = 2 * dstheighthalf; +} + + +/* Publically available rotozoom-size function */ + +void rotozoomSurfaceSizeXY(int width, int height, double angle, double zoomx, double zoomy, int *dstwidth, int *dstheight) +{ + double dummy_sanglezoom, dummy_canglezoom; + + rotozoomSurfaceSizeTrig(width, height, angle, zoomx, zoomy, dstwidth, dstheight, &dummy_sanglezoom, &dummy_canglezoom); +} + +/* Publically available rotozoom-size function */ + +void rotozoomSurfaceSize(int width, int height, double angle, double zoom, int *dstwidth, int *dstheight) +{ + double dummy_sanglezoom, dummy_canglezoom; + + rotozoomSurfaceSizeTrig(width, height, angle, zoom, zoom, dstwidth, dstheight, &dummy_sanglezoom, &dummy_canglezoom); +} + +/* Publically available rotozoom function */ + +SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom, int smooth) +{ + return rotozoomSurfaceXY(src, angle, zoom, zoom, smooth); +} + +/* Publically available rotozoom function */ + +SDL_Surface *rotozoomSurfaceXY(SDL_Surface * src, double angle, double zoomx, double zoomy, int smooth) +{ + SDL_Surface *rz_src; + SDL_Surface *rz_dst; + double zoominv; + double sanglezoom, canglezoom, sanglezoominv, canglezoominv; + int dstwidthhalf, dstwidth, dstheighthalf, dstheight; + int is32bit; + int i, src_converted; + int flipx,flipy; + Uint8 r,g,b; + Uint32 colorkey; + int colorKeyAvailable = 0; + + /* + * Sanity check + */ + if (src == NULL) + return (NULL); + + if( src->flags & SDL_SRCCOLORKEY ) + { + colorkey = src->format->colorkey; + SDL_GetRGB(colorkey, src->format, &r, &g, &b); + colorKeyAvailable = 1; + } + /* + * Determine if source surface is 32bit or 8bit + */ + is32bit = (src->format->BitsPerPixel == 32); + if ((is32bit) || (src->format->BitsPerPixel == 8)) { + /* + * Use source surface 'as is' + */ + rz_src = src; + src_converted = 0; + } else { + /* + * New source surface is 32bit with a defined RGBA ordering + */ + rz_src = + SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32, +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 +#else + 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff +#endif + ); + if(colorKeyAvailable) + SDL_SetColorKey(src, 0, 0); + + SDL_BlitSurface(src, NULL, rz_src, NULL); + + if(colorKeyAvailable) + SDL_SetColorKey(src, SDL_SRCCOLORKEY, colorkey); + src_converted = 1; + is32bit = 1; + } + + /* + * Sanity check zoom factor + */ + flipx = (zoomx<0.0); + if (flipx) zoomx=-zoomx; + flipy = (zoomy<0.0); + if (flipy) zoomy=-zoomy; + if (zoomx < VALUE_LIMIT) zoomx = VALUE_LIMIT; + if (zoomy < VALUE_LIMIT) zoomy = VALUE_LIMIT; + zoominv = 65536.0 / (zoomx * zoomx); + + /* + * Check if we have a rotozoom or just a zoom + */ + if (fabs(angle) > VALUE_LIMIT) { + + /* + * Angle!=0: full rotozoom + */ + /* + * ----------------------- + */ + + /* Determine target size */ + rotozoomSurfaceSizeTrig(rz_src->w, rz_src->h, angle, zoomx, zoomy, &dstwidth, &dstheight, &canglezoom, &sanglezoom); + + /* + * Calculate target factors from sin/cos and zoom + */ + sanglezoominv = sanglezoom; + canglezoominv = canglezoom; + sanglezoominv *= zoominv; + canglezoominv *= zoominv; + + /* Calculate half size */ + dstwidthhalf = dstwidth / 2; + dstheighthalf = dstheight / 2; + + /* + * Alloc space to completely contain the rotated surface + */ + rz_dst = NULL; + if (is32bit) { + /* + * Target surface is 32bit with source RGBA/ABGR ordering + */ + rz_dst = + SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32, + rz_src->format->Rmask, rz_src->format->Gmask, + rz_src->format->Bmask, rz_src->format->Amask); + } else { + /* + * Target surface is 8bit + */ + rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0); + } + + if (colorKeyAvailable == 1){ + colorkey = SDL_MapRGB(rz_dst->format, r, g, b); + + SDL_FillRect(rz_dst, NULL, colorkey ); + } + + /* + * Lock source surface + */ + SDL_LockSurface(rz_src); + /* + * Check which kind of surface we have + */ + if (is32bit) { + /* + * Call the 32bit transformation routine to do the rotation (using alpha) + */ + transformSurfaceRGBA(rz_src, rz_dst, dstwidthhalf, dstheighthalf, + (int) (sanglezoominv), (int) (canglezoominv), + flipx, flipy, + smooth); + /* + * Turn on source-alpha support + */ + SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255); + } else { + /* + * Copy palette and colorkey info + */ + for (i = 0; i < rz_src->format->palette->ncolors; i++) { + rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i]; + } + rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors; + /* + * Call the 8bit transformation routine to do the rotation + */ + transformSurfaceY(rz_src, rz_dst, dstwidthhalf, dstheighthalf, + (int) (sanglezoominv), (int) (canglezoominv), + flipx, flipy); + SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, rz_src->format->colorkey); + } + /* + * Unlock source surface + */ + SDL_UnlockSurface(rz_src); + + } else { + + /* + * Angle=0: Just a zoom + */ + /* + * -------------------- + */ + + /* + * Calculate target size + */ + zoomSurfaceSize(rz_src->w, rz_src->h, zoomx, zoomy, &dstwidth, &dstheight); + + /* + * Alloc space to completely contain the zoomed surface + */ + rz_dst = NULL; + if (is32bit) { + /* + * Target surface is 32bit with source RGBA/ABGR ordering + */ + rz_dst = + SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32, + rz_src->format->Rmask, rz_src->format->Gmask, + rz_src->format->Bmask, rz_src->format->Amask); + } else { + /* + * Target surface is 8bit + */ + rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0); + } + + if (colorKeyAvailable == 1){ + colorkey = SDL_MapRGB(rz_dst->format, r, g, b); + + SDL_FillRect(rz_dst, NULL, colorkey ); + } + + /* + * Lock source surface + */ + SDL_LockSurface(rz_src); + /* + * Check which kind of surface we have + */ + if (is32bit) { + /* + * Call the 32bit transformation routine to do the zooming (using alpha) + */ + zoomSurfaceRGBA(rz_src, rz_dst, flipx, flipy, smooth); + /* + * Turn on source-alpha support + */ + SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255); + } else { + /* + * Copy palette and colorkey info + */ + for (i = 0; i < rz_src->format->palette->ncolors; i++) { + rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i]; + } + rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors; + /* + * Call the 8bit transformation routine to do the zooming + */ + zoomSurfaceY(rz_src, rz_dst, flipx, flipy); + SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, rz_src->format->colorkey); + } + /* + * Unlock source surface + */ + SDL_UnlockSurface(rz_src); + } + + /* + * Cleanup temp surface + */ + if (src_converted) { + SDL_FreeSurface(rz_src); + } + + /* + * Return destination surface + */ + return (rz_dst); +} + +/* + + zoomSurface() + + Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. + 'zoomx' and 'zoomy' are scaling factors for width and height. If 'smooth' is 1 + then the destination 32bit surface is anti-aliased. If the surface is not 8bit + or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. + +*/ + +#define VALUE_LIMIT 0.001 + +void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight) +{ + /* + * Sanity check zoom factors + */ + if (zoomx < VALUE_LIMIT) { + zoomx = VALUE_LIMIT; + } + if (zoomy < VALUE_LIMIT) { + zoomy = VALUE_LIMIT; + } + + /* + * Calculate target size + */ + *dstwidth = (int) ((double) width * zoomx); + *dstheight = (int) ((double) height * zoomy); + if (*dstwidth < 1) { + *dstwidth = 1; + } + if (*dstheight < 1) { + *dstheight = 1; + } +} + +SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth) +{ + SDL_Surface *rz_src; + SDL_Surface *rz_dst; + int dstwidth, dstheight; + int is32bit; + int i, src_converted; + int flipx, flipy; + + /* + * Sanity check + */ + if (src == NULL) + return (NULL); + + /* + * Determine if source surface is 32bit or 8bit + */ + is32bit = (src->format->BitsPerPixel == 32); + if ((is32bit) || (src->format->BitsPerPixel == 8)) { + /* + * Use source surface 'as is' + */ + rz_src = src; + src_converted = 0; + } else { + /* + * New source surface is 32bit with a defined RGBA ordering + */ + rz_src = + SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32, +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 +#else + 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff +#endif + ); + SDL_BlitSurface(src, NULL, rz_src, NULL); + src_converted = 1; + is32bit = 1; + } + + flipx = (zoomx<0.0); + if (flipx) zoomx = -zoomx; + flipy = (zoomy<0.0); + if (flipy) zoomy = -zoomy; + + /* Get size if target */ + zoomSurfaceSize(rz_src->w, rz_src->h, zoomx, zoomy, &dstwidth, &dstheight); + + /* + * Alloc space to completely contain the zoomed surface + */ + rz_dst = NULL; + if (is32bit) { + /* + * Target surface is 32bit with source RGBA/ABGR ordering + */ + rz_dst = + SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32, + rz_src->format->Rmask, rz_src->format->Gmask, + rz_src->format->Bmask, rz_src->format->Amask); + } else { + /* + * Target surface is 8bit + */ + rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0); + } + + /* + * Lock source surface + */ + SDL_LockSurface(rz_src); + /* + * Check which kind of surface we have + */ + if (is32bit) { + /* + * Call the 32bit transformation routine to do the zooming (using alpha) + */ + zoomSurfaceRGBA(rz_src, rz_dst, flipx, flipy, smooth); + /* + * Turn on source-alpha support + */ + SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255); + } else { + /* + * Copy palette and colorkey info + */ + for (i = 0; i < rz_src->format->palette->ncolors; i++) { + rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i]; + } + rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors; + /* + * Call the 8bit transformation routine to do the zooming + */ + zoomSurfaceY(rz_src, rz_dst, flipx, flipy); + SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, rz_src->format->colorkey); + } + /* + * Unlock source surface + */ + SDL_UnlockSurface(rz_src); + + /* + * Cleanup temp surface + */ + if (src_converted) { + SDL_FreeSurface(rz_src); + } + + /* + * Return destination surface + */ + return (rz_dst); +} + +SDL_Surface *shrinkSurface(SDL_Surface * src, int factorx, int factory) +{ + SDL_Surface *rz_src; + SDL_Surface *rz_dst; + int dstwidth, dstheight; + int is32bit; + int i, src_converted; + + /* + * Sanity check + */ + if (src == NULL) + return (NULL); + + /* + * Determine if source surface is 32bit or 8bit + */ + is32bit = (src->format->BitsPerPixel == 32); + if ((is32bit) || (src->format->BitsPerPixel == 8)) { + /* + * Use source surface 'as is' + */ + rz_src = src; + src_converted = 0; + } else { + /* + * New source surface is 32bit with a defined RGBA ordering + */ + rz_src = + SDL_CreateRGBSurface(SDL_SWSURFACE, src->w, src->h, 32, +#if SDL_BYTEORDER == SDL_LIL_ENDIAN + 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000 +#else + 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff +#endif + ); + SDL_BlitSurface(src, NULL, rz_src, NULL); + src_converted = 1; + is32bit = 1; + } + + /* Get size for target */ + dstwidth=rz_src->w/factorx; + while (dstwidth*factorx>rz_src->w) { dstwidth--; } + dstheight=rz_src->h/factory; + while (dstheight*factory>rz_src->h) { dstheight--; } + + /* + * Alloc space to completely contain the shrunken surface + */ + rz_dst = NULL; + if (is32bit) { + /* + * Target surface is 32bit with source RGBA/ABGR ordering + */ + rz_dst = + SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 32, + rz_src->format->Rmask, rz_src->format->Gmask, + rz_src->format->Bmask, rz_src->format->Amask); + } else { + /* + * Target surface is 8bit + */ + rz_dst = SDL_CreateRGBSurface(SDL_SWSURFACE, dstwidth, dstheight, 8, 0, 0, 0, 0); + } + + /* + * Lock source surface + */ + SDL_LockSurface(rz_src); + /* + * Check which kind of surface we have + */ + if (is32bit) { + /* + * Call the 32bit transformation routine to do the shrinking (using alpha) + */ + shrinkSurfaceRGBA(rz_src, rz_dst, factorx, factory); + /* + * Turn on source-alpha support + */ + SDL_SetAlpha(rz_dst, SDL_SRCALPHA, 255); + } else { + /* + * Copy palette and colorkey info + */ + for (i = 0; i < rz_src->format->palette->ncolors; i++) { + rz_dst->format->palette->colors[i] = rz_src->format->palette->colors[i]; + } + rz_dst->format->palette->ncolors = rz_src->format->palette->ncolors; + /* + * Call the 8bit transformation routine to do the shrinking + */ + shrinkSurfaceY(rz_src, rz_dst, factorx, factory); + SDL_SetColorKey(rz_dst, SDL_SRCCOLORKEY | SDL_RLEACCEL, rz_src->format->colorkey); + } + /* + * Unlock source surface + */ + SDL_UnlockSurface(rz_src); + + /* + * Cleanup temp surface + */ + if (src_converted) { + SDL_FreeSurface(rz_src); + } + + /* + * Return destination surface + */ + return (rz_dst); +} diff --git a/src/sdl-instead/SDL_rotozoom.h b/src/sdl-instead/SDL_rotozoom.h new file mode 100644 index 0000000..9773866 --- /dev/null +++ b/src/sdl-instead/SDL_rotozoom.h @@ -0,0 +1,117 @@ + +/* + + SDL_rotozoom - rotozoomer + + LGPL (c) A. Schiffler + +*/ + +#ifndef _SDL_rotozoom_h +#define _SDL_rotozoom_h + +#include + +/* Set up for C function definitions, even when using C++ */ +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef M_PI +#define M_PI 3.141592654 +#endif + +#include "SDL.h" + +/* ---- Defines */ + +#define SMOOTHING_OFF 0 +#define SMOOTHING_ON 1 + +/* ---- Structures */ + + typedef struct tColorRGBA { + Uint8 r; + Uint8 g; + Uint8 b; + Uint8 a; + } tColorRGBA; + + typedef struct tColorY { + Uint8 y; + } tColorY; + + +/* ---- Prototypes */ + +#define DLLINTERFACE + +/* + + rotozoomSurface() + + Rotates and zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. + 'angle' is the rotation in degrees. 'zoom' a scaling factor. If 'smooth' is 1 + then the destination 32bit surface is anti-aliased. If the surface is not 8bit + or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. + +*/ + + DLLINTERFACE SDL_Surface *rotozoomSurface(SDL_Surface * src, double angle, double zoom, int smooth); + + DLLINTERFACE SDL_Surface *rotozoomSurfaceXY + (SDL_Surface * src, double angle, double zoomx, double zoomy, int smooth); + +/* Returns the size of the target surface for a rotozoomSurface() call */ + + DLLINTERFACE void rotozoomSurfaceSize(int width, int height, double angle, double zoom, int *dstwidth, + int *dstheight); + + DLLINTERFACE void rotozoomSurfaceSizeXY + (int width, int height, double angle, double zoomx, double zoomy, + int *dstwidth, int *dstheight); + +/* + + zoomSurface() + + Zoomes a 32bit or 8bit 'src' surface to newly created 'dst' surface. + 'zoomx' and 'zoomy' are scaling factors for width and height. If 'smooth' is 1 + then the destination 32bit surface is anti-aliased. If the surface is not 8bit + or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. + +*/ + + DLLINTERFACE SDL_Surface *zoomSurface(SDL_Surface * src, double zoomx, double zoomy, int smooth); + +/* Returns the size of the target surface for a zoomSurface() call */ + + DLLINTERFACE void zoomSurfaceSize(int width, int height, double zoomx, double zoomy, int *dstwidth, int *dstheight); + + +/* + shrinkSurface() + + Shrinks a 32bit or 8bit 'src' surface ti a newly created 'dst' surface. + 'factorx' and 'factory' are the shrinking ratios (i.e. 2=1/2 the size, + 3=1/3 the size, etc.) The destination surface is antialiased by averaging + the source box RGBA or Y information. If the surface is not 8bit + or 32bit RGBA/ABGR it will be converted into a 32bit RGBA format on the fly. +*/ + + DLLINTERFACE SDL_Surface *shrinkSurface(SDL_Surface * src, int factorx, int factory); + +/* + + Other functions + +*/ + + DLLINTERFACE SDL_Surface* rotateSurface90Degrees(SDL_Surface* pSurf, int numClockwiseTurns); + +/* Ends C function definitions when using C++ */ +#ifdef __cplusplus +} +#endif + +#endif /* _SDL_rotozoom_h */ diff --git a/src/sdl-instead/copying b/src/sdl-instead/copying new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/src/sdl-instead/copying @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/src/sdl-instead/game.c b/src/sdl-instead/game.c index 64bdeab..d561a6b 100644 --- a/src/sdl-instead/game.c +++ b/src/sdl-instead/game.c @@ -1,31 +1,72 @@ -#include #include #include #include #include #include +#include #include -#include #include +#include +#include #include "graphics.h" #include "sound.h" #include "game.h" #include "input.h" #include "instead.h" +#ifdef RUSSIAN +#include "menu.h" +#else +#include "menu-en.h" +#endif + +int opt_fsize = 0; int opt_fs = 0; int opt_hl = 1; int opt_hz = 22050; int opt_vol = 127; -int opt_autosave = 0; +int opt_motion = 1; +int opt_click = 1; +int opt_music = 1; +int opt_autosave = 1; +int opt_filter = 1; +int opt_owntheme = 1; char *opt_game = NULL; +char *opt_theme = NULL; +char *err_msg = NULL; + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +#define ERR_MSG_MAX 512 + +void game_err_msg(const char *s) +{ + if (err_msg) + free(err_msg); + if (s) { + err_msg = strdup(s); + if (err_msg && strlen(err_msg) > ERR_MSG_MAX) { + err_msg[ERR_MSG_MAX - 4] = 0; + strcat(err_msg, "..."); + } + } else + err_msg = NULL; +} char game_cwd[PATH_MAX]; char *curgame = NULL; +char *curgame_dir = NULL; +static int own_theme = 0; +char *curtheme = NULL; +char *curtheme_dir = NULL; int cfg_parse(const char *path); -char *game_cfg_path(void); -int game_save(void); +extern char *game_cfg_path(void); +extern char *game_save_path(int rc, int nr); + +int game_save(int nr); int cfg_load(void) { @@ -46,7 +87,14 @@ int cfg_save(void) fp = fopen(p, "w"); if (!fp) return -1; - fprintf(fp, "fs = %d\nhl = %d\nhz = %d\nvol = %d\nautosave = %d\ngame = %s", opt_fs, opt_hl, opt_hz, opt_vol, opt_autosave, curgame?curgame:""); + fprintf(fp, "fs = %d\nhl = %d\nhz = %d\nvol = %d\nautosave = %d\n\ +game = %s\nfscale = %d\nmotion = %d\n\ +click = %d\nmusic = %d\ntheme = %s\n\ +filter = %d\nowntheme = %d", + opt_fs, opt_hl, opt_hz, opt_vol, opt_autosave, + curgame_dir?curgame_dir:"", opt_fsize, opt_motion, + opt_click, opt_music, curtheme_dir?curtheme_dir:DEFAULT_THEME, + opt_filter, opt_owntheme); fclose(fp); return 0; } @@ -55,11 +103,14 @@ void game_menu_box(int show, const char *txt); void game_cursor(int on); -#ifndef THEMES_PATH -#define THEMES_PATH "./themes" -#endif +#define GFX_MODE_FLOAT 0 +#define GFX_MODE_FIXED 1 +#define GFX_MODE_EMBEDDED 2 -struct theme { +#define INV_MODE_VERT 0 +#define INV_MODE_HORIZ 1 + +struct game_theme { int w; int h; color_t bgcol; @@ -77,7 +128,10 @@ struct theme { char *font_name; int font_size; fnt_t font; - + + int gfx_x; + int gfx_y; + int max_scene_w; int max_scene_h; char *a_up_name; @@ -93,6 +147,7 @@ struct theme { int inv_y; int inv_w; int inv_h; + color_t icol; color_t ilcol; color_t iacol; @@ -123,6 +178,10 @@ struct theme { img_t menu_button; int menu_button_x; int menu_button_y; + int gfx_mode; + int inv_mode; + char *click_name; + void *click; } game_theme = { .w = 800, .h = 480, @@ -146,13 +205,17 @@ struct theme { .menu_font = NULL, .menu_button_name = NULL, .menu_button = NULL, + .gfx_mode = GFX_MODE_EMBEDDED, + .inv_mode = INV_MODE_VERT, + .click_name = NULL, + .click = NULL, }; #define FREE(v) do { if ((v)) free((v)); v = NULL; } while(0) void free_theme_strings() { - struct theme *t = &game_theme; + struct game_theme *t = &game_theme; FREE(t->use_name); FREE(t->bg_name); FREE(t->inv_a_up_name); @@ -163,6 +226,7 @@ void free_theme_strings() FREE(t->inv_font_name); FREE(t->menu_font_name); FREE(t->menu_button_name); +/* FREE(t->click_name); must be reloaded, ugly :(*/ } int game_theme_free(void) @@ -194,82 +258,93 @@ int game_theme_free(void) if (game_theme.menu_button) gfx_free_image(game_theme.menu_button); + if (game_theme.click) + snd_free_wav(game_theme.click); + game_theme.font = game_theme.inv_font = game_theme.menu_font = NULL; game_theme.a_up = game_theme.a_down = game_theme.use = NULL; game_theme.inv_a_up = game_theme.inv_a_down = NULL; game_theme.menu_button = NULL; game_theme.bg = NULL; + game_theme.click = NULL; // game_theme.slide = gfx_load_image("slide.png", 1); return 0; } +#define FONT_SZ(v) ((v) * (1.0f + ((0.1f * opt_fsize)))) + int game_theme_init(void) { - struct theme *t = &game_theme; + struct game_theme *t = &game_theme; if (t->font_name) { fnt_free(t->font); - if (!(t->font = fnt_load(t->font_name, t->font_size))) + if (!(t->font = fnt_load(t->font_name, FONT_SZ(t->font_size)))) goto err; } if (t->inv_font_name) { fnt_free(t->inv_font); - if (!(t->inv_font = fnt_load(t->inv_font_name, t->inv_font_size))) - goto err;; + if (!(t->inv_font = fnt_load(t->inv_font_name, FONT_SZ(t->inv_font_size)))) + goto err; } if (t->menu_font_name) { fnt_free(t->menu_font); - if (!(t->menu_font = fnt_load(t->menu_font_name, t->menu_font_size))) + if (!(t->menu_font = fnt_load(t->menu_font_name, t->menu_font_size))) /* do not scale menu!!! */ goto err; } if (t->a_up_name) { gfx_free_image(t->a_up); - if (!(t->a_up = gfx_load_image(t->a_up_name, 1))) + if (!(t->a_up = gfx_load_image(t->a_up_name))) goto err; } if (t->a_down_name) { gfx_free_image(t->a_down); - if (!(t->a_down = gfx_load_image(t->a_down_name, 1))) + if (!(t->a_down = gfx_load_image(t->a_down_name))) goto err; } if (t->inv_a_up_name) { gfx_free_image(t->inv_a_up); - if (!(t->inv_a_up = gfx_load_image(t->inv_a_up_name, 1))) + if (!(t->inv_a_up = gfx_load_image(t->inv_a_up_name))) goto err; } if (t->inv_a_down_name) { gfx_free_image(t->inv_a_down); - if (!(t->inv_a_down = gfx_load_image(t->inv_a_down_name, 1))) + if (!(t->inv_a_down = gfx_load_image(t->inv_a_down_name))) goto err; } if (t->bg_name) { gfx_free_image(t->bg); t->bg = NULL; - if (t->bg_name[0] && !(t->bg = gfx_load_image(t->bg_name, 0))) + if (t->bg_name[0] && !(t->bg = gfx_load_image(t->bg_name))) goto err; } if (t->use_name) { gfx_free_image(t->use); - if (!(t->use = gfx_load_image(t->use_name, 1))) + if (!(t->use = gfx_load_image(t->use_name))) goto err; } if (t->menu_button_name) { gfx_free_image(t->menu_button); - if (!(t->menu_button = gfx_load_image(t->menu_button_name, 1))) + if (!(t->menu_button = gfx_load_image(t->menu_button_name))) goto err; } + + if (t->click_name) { + snd_free_wav(t->click); + t->click = snd_load_wav(t->click_name); + } free_theme_strings(); @@ -280,29 +355,11 @@ int game_theme_init(void) } return 0; err: + fprintf(stderr, "Can not init theme!\n"); game_theme_free(); return -1; } -int theme_load(const char *name); - -int game_theme_load(const char *name) -{ - char cwd[PATH_MAX]; - getcwd(cwd, sizeof(cwd)); - if (chdir(THEMES_PATH) || chdir(name) || theme_load("theme.ini")) { - chdir(cwd); - return -1; - } - chdir(cwd); - return 0; -} - -int game_default_theme(void) -{ - return game_theme_load("default"); -} - typedef int (*parser_fn)(const char *v, void *data); int parse_string(const char *v, void *data) @@ -316,6 +373,48 @@ int parse_string(const char *v, void *data) return 0; } +int parse_gfx_mode(const char *v, void *data) +{ + int *i = (int *)data; + if (!strcmp(v, "fixed")) + *i = GFX_MODE_FIXED; + else if (!strcmp(v, "embedded")) + *i = GFX_MODE_EMBEDDED; + else if (!strcmp(v, "float")) + *i = GFX_MODE_FLOAT; + else + return -1; + return 0; +} + +int parse_inv_mode(const char *v, void *data) +{ + int *i = (int *)data; + if (!strcmp(v, "vertical") || !strcmp(v, "0")) + *i = INV_MODE_VERT; + else if (!strcmp(v, "horizontal") || !strcmp(v, "1")) + *i = INV_MODE_HORIZ; + else + return -1; + return 0; +} + +int parse_full_path(const char *v, void *data) +{ + char cwd[PATH_MAX]; + char **p = ((char **)data); + if (*p) + free(*p); + getcwd(cwd, sizeof(cwd)); + *p = malloc(strlen(v) + strlen(cwd) + 2); + if (!*p) + return -1; + strcpy(*p, cwd); + strcat(*p,"/"); + strcat(*p, v); + return 0; +} + int parse_int(const char *v, void *data) { int *i = (int *)data; @@ -331,14 +430,20 @@ int parse_color(const char *v, void *data) color_t *c = (color_t *)data; return gfx_parse_color(v, c); } +int game_theme_load(const char *name); +int game_theme_select(const char *name); int parse_include(const char *v, void *data) { int rc; char cwd[PATH_MAX]; + if (!strcmp(v, DEFAULT_THEME)) + return 0; getcwd(cwd, sizeof(cwd)); chdir(game_cwd); - rc = game_theme_load(v); + rc = game_theme_load(v); +// if (!rc) +// game_theme_select(v); chdir(cwd); return rc; } @@ -355,7 +460,14 @@ struct parser cfg_parser[] = { { "vol", parse_int, &opt_vol }, { "hl", parse_int, &opt_hl }, { "game", parse_string, &opt_game }, + { "theme", parse_string, &opt_theme }, { "autosave", parse_int, &opt_autosave }, + { "motion", parse_int, &opt_motion }, + { "click", parse_int, &opt_click }, + { "music", parse_int, &opt_music }, + { "fscale", parse_int, &opt_fsize }, + { "filter", parse_int, &opt_filter }, + { "owntheme", parse_int, &opt_owntheme }, { NULL, }, }; @@ -366,6 +478,11 @@ struct parser cmd_parser[] = { { "scr.gfx.bg", parse_string, &game_theme.bg_name }, { "scr.gfx.use", parse_string, &game_theme.use_name }, { "scr.gfx.pad", parse_int, &game_theme.pad }, + { "scr.gfx.x", parse_int, &game_theme.gfx_x }, + { "scr.gfx.y", parse_int, &game_theme.gfx_y }, + { "scr.gfx.w", parse_int, &game_theme.max_scene_w }, + { "scr.gfx.h", parse_int, &game_theme.max_scene_h }, + { "scr.gfx.mode", parse_gfx_mode, &game_theme.gfx_mode }, { "win.x", parse_int, &game_theme.win_x }, { "win.y", parse_int, &game_theme.win_y }, @@ -373,7 +490,9 @@ struct parser cmd_parser[] = { { "win.h", parse_int, &game_theme.win_h }, { "win.fnt.name", parse_string, &game_theme.font_name }, { "win.fnt.size", parse_int, &game_theme.font_size }, +/* compat mode directive */ { "win.gfx.h", parse_int, &game_theme.max_scene_h }, +/* here it was */ { "win.gfx.up", parse_string, &game_theme.a_up_name }, { "win.gfx.down", parse_string, &game_theme.a_down_name }, { "win.col.fg", parse_color, &game_theme.fgcol }, @@ -384,6 +503,9 @@ struct parser cmd_parser[] = { { "inv.y", parse_int, &game_theme.inv_y }, { "inv.w", parse_int, &game_theme.inv_w }, { "inv.h", parse_int, &game_theme.inv_h }, + { "inv.mode", parse_inv_mode, &game_theme.inv_mode }, + { "inv.horiz", parse_inv_mode, &game_theme.inv_mode }, + { "inv.col.fg", parse_color, &game_theme.icol }, { "inv.col.link", parse_color, &game_theme.ilcol }, { "inv.col.alink", parse_color, &game_theme.iacol }, @@ -402,8 +524,13 @@ struct parser cmd_parser[] = { { "menu.fnt.name", parse_string, &game_theme.menu_font_name }, { "menu.fnt.size", parse_int, &game_theme.menu_font_size }, { "menu.gfx.button", parse_string, &game_theme.menu_button_name }, + { "menu.button.x", parse_int, &game_theme.menu_button_x }, + { "menu.button.y", parse_int, &game_theme.menu_button_y }, +/* compat */ { "menu.buttonx", parse_int, &game_theme.menu_button_x }, { "menu.buttony", parse_int, &game_theme.menu_button_y }, + + { "snd.click", parse_full_path, &game_theme.click_name }, { "include", parse_include, NULL }, { NULL, }, }; @@ -454,7 +581,7 @@ int parse_ini(const char *path, struct parser *cmd_parser) if (*p == ';') continue; len = strcspn(p, "="); - if (p[len] != '=') + if (p[len] != '=') /* just ignore it */ continue; p[len] = 0; val = p + len + 1; @@ -462,7 +589,7 @@ int parse_ini(const char *path, struct parser *cmd_parser) p[len] = 0; // printf("%s\n", p); val += strspn(val, " \t"); - val[strcspn(val, "\n")] = 0; + val[strcspn(val, ";\n")] = 0; if (process_cmd(p, val, cmd_parser)) { rc = -1; fprintf(stderr, "Can't process cmd '%s' on line %d : %s\n", p, line_nr, strerror(errno)); @@ -475,7 +602,8 @@ int parse_ini(const char *path, struct parser *cmd_parser) int theme_parse(const char *path) { if (parse_ini(path, cmd_parser)) { - game_theme_free(); + fprintf(stderr, "Theme parsed with errors!\n"); +// game_theme_free(); return -1; } return 0; @@ -484,7 +612,7 @@ int theme_parse(const char *path) int theme_load(const char *name) { if (theme_parse(name)) - return -1; + return 0; /* no theme loaded if error in parsing */ if (game_theme_init()) return -1; return 0; @@ -495,11 +623,7 @@ int cfg_parse(const char *path) return parse_ini(path, cfg_parser); } -#ifndef GAMES_PATH -#define GAMES_PATH "./games" -#endif - -char *getpath(const char *d, const char *n) +char *getfilepath(const char *d, const char *n) { int i = strlen(d) + strlen(n) + 3; char *p = malloc(i); @@ -507,17 +631,22 @@ char *getpath(const char *d, const char *n) strcpy(p, d); strcat(p, "/"); strcat(p, n); - strcat(p, "/"); } return p; } -#define MAIN_FILE "main.lua" +char *getpath(const char *d, const char *n) +{ + char *p = getfilepath(d, n); + strcat(p, "/"); + return p; +} -int is_game(const char *n) + +static int is_game(const char *path, const char *n) { int rc = 0; - char *p = getpath(GAMES_PATH, n); + char *p = getpath(path, n); char *pp; if (!p) return 0; @@ -536,23 +665,26 @@ int is_game(const char *n) struct game { char *path; char *name; + char *dir; }; -struct game *games; +struct game *games = NULL; int games_nr = 0; + + int game_select(const char *name) { int i; if (!name || !*name) { if (games_nr == 1) - name = games[0].name; + name = games[0].dir; else return 0; } chdir(game_cwd); for (i = 0; ipw_dir); - return save_path; - -} -char *game_save_path(int cr) -{ - struct passwd *pw; - if (!curgame) + } + l += strlen(comm); l += strspn(l, " \t"); + if (strncmp(l, tag, strlen(tag))) return NULL; - pw = getpwuid(getuid()); - if (!pw) - return NULL; - snprintf(save_path, sizeof(save_path) - 1 , "%s/.instead/", pw->pw_dir); - if (cr && mkdir(save_path, S_IRWXU) && errno != EEXIST) - return NULL; - snprintf(save_path, sizeof(save_path) - 1 , "%s/.instead/saves", pw->pw_dir); - if (cr && mkdir(save_path, S_IRWXU) && errno != EEXIST) - return NULL; - snprintf(save_path, sizeof(save_path) - 1, "%s/.instead/saves/%s/", pw->pw_dir, curgame); - if (cr && mkdir(save_path, S_IRWXU) && errno != EEXIST) - return NULL; - snprintf(save_path, sizeof(save_path) - 1, "%s/.instead/saves/%s/autosave", pw->pw_dir, curgame); - return save_path; + l += strlen(tag); + l += strspn(l, " \t"); + l[strcspn(l, "$\n\r")] = 0; + return strdup(l); } -int games_lookup(void) +static char *game_name(const char *path, const char *d_name) +{ + int brk = 0; + char *p = getfilepath(path, MAIN_FILE); + if (p) { + char *l; char line[1024]; + FILE *fd = fopen(p, "r"); + free(p); + if (!fd) + goto err; + + while ((l = fgets(line, sizeof(line), fd)) && !brk) { + l = parse_tag(l, "$Name:", "--", &brk); + if (l) + return l; + } + fclose(fd); + } +err: + return strdup(d_name); +} + +int games_lookup(const char *path) { char *p; - int n = 0; + int n = 0, i = 0; DIR *d; struct dirent *de; - d = opendir(GAMES_PATH); + + if (!path) + return 0; + + d = opendir(path); if (!d) return -1; while ((de = readdir(d))) { /*if (de->d_type != DT_DIR) continue;*/ - if (!is_game(de->d_name)) + if (!is_game(path, de->d_name)) continue; n ++; } + rewinddir(d); if (!n) return 0; - games = malloc(sizeof(struct game) * n); - while ((de = readdir(d)) && games_nr < n) { + games = realloc(games, sizeof(struct game) * (n + games_nr)); + while ((de = readdir(d)) && i < n) { /*if (de->d_type != DT_DIR) continue;*/ - if (!is_game(de->d_name)) + if (!is_game(path, de->d_name)) continue; - p = getpath(GAMES_PATH, de->d_name); + p = getpath(path, de->d_name); games[games_nr].path = p; - games[games_nr].name = strdup(de->d_name); + games[games_nr].dir = strdup(de->d_name); + games[games_nr].name = game_name(p, de->d_name); games_nr ++; + i ++; } closedir(d); return 0; } +struct theme *themes = NULL; +int themes_nr = 0; + +struct theme { + char *path; + char *name; + char *dir; +}; + +static int is_theme(const char *path, const char *n) +{ + int rc = 0; + char *p = getpath(path, n); + char *pp; + if (!p) + return 0; + pp = malloc(strlen(p) + strlen(THEME_FILE) + 1); + if (pp) { + strcpy(pp, p); + strcat(pp, THEME_FILE); + if (!access(pp, R_OK)) + rc = 1; + free(pp); + } + free(p); + return rc; +} + +static char *theme_name(const char *path, const char *d_name) +{ + int brk = 0; + char *p = getfilepath(path, THEME_FILE); + if (p) { + char *l; char line[1024]; + FILE *fd = fopen(p, "r"); + free(p); + if (!fd) + goto err; + + while ((l = fgets(line, sizeof(line), fd)) && !brk) { + l = parse_tag(l, "$Name:", ";", &brk); + if (l) + return l; + } + fclose(fd); + } +err: + return strdup(d_name); +} + +int themes_lookup(const char *path) +{ + char *p; + int n = 0, i = 0; + DIR *d; + struct dirent *de; + + if (!path) + return 0; + + d = opendir(path); + if (!d) + return -1; + while ((de = readdir(d))) { + if (!is_theme(path, de->d_name)) + continue; + n ++; + } + + rewinddir(d); + if (!n) + return 0; + themes = realloc(themes, sizeof(struct theme) * (n + themes_nr)); + while ((de = readdir(d)) && i < n) { + /*if (de->d_type != DT_DIR) + continue;*/ + if (!is_theme(path, de->d_name)) + continue; + p = getpath(path, de->d_name); + themes[themes_nr].path = p; + themes[themes_nr].dir = strdup(de->d_name); + themes[themes_nr].name = theme_name(p, de->d_name); + themes_nr ++; + i ++; + } + closedir(d); + return 0; +} + +int theme_load(const char *name); + +struct theme *theme_lookup(const char *name) +{ + int i; + if (!name || !*name) { + if (themes_nr == 1) + return &themes[0]; + } + for (i = 0; ipath) || theme_load(THEME_FILE)) { + chdir(cwd); + return -1; + } + chdir(cwd); + return 0; +} + +int game_theme_select(const char *name) +{ + struct theme *theme; + theme = theme_lookup(name); + if (!theme) + return -1; + curtheme = theme->name; + curtheme_dir = theme->dir; + return 0; +} + +int game_default_theme(void) +{ + return game_theme_load(DEFAULT_THEME); +} + +static int motion_mode = 0; +static int motion_id = 0; +static int motion_y = 0; static char *last_pict = NULL; static char *last_title = NULL; @@ -650,7 +937,15 @@ enum { menu_askquit, menu_saved, menu_games, + menu_themes, + menu_own_theme, + menu_custom_theme, + menu_load, + menu_save, + menu_error, + menu_warning, }; + int game_cmd(char *cmd); int change_vol(int d, int val); @@ -750,37 +1045,67 @@ img_t el_img(int num) { return objs[num].p.img; } + char *game_menu_gen(void); +void game_menu(int nr) +{ + cur_menu = nr; + menu_shown = 1; + game_menu_box(menu_shown, game_menu_gen()); +} + +int game_error(const char *name) +{ + game_done(); + if (game_init(NULL)) { + fprintf(stderr,"Fatal error! Can't init anything!!!\n"); + exit(1); + } + game_menu(menu_error); + return 0; +} void el_draw(int n); -int game_init(const char *name) + +static void custom_theme_warn(void) +{ + if (own_theme && !opt_owntheme) { + game_menu(menu_custom_theme); + } +} + +int window_sw = 0; +int fullscreen_sw = 0; + +static int game_load(int nr) { char *s; + s = game_save_path(0, nr); + + if (s && !access(s, R_OK)) { + char cmd[PATH_MAX]; + snprintf(cmd, sizeof(cmd) - 1, "load %s", s); + game_cmd(cmd); + if (nr == -1) + unlink(s); + return 0; + } + return -1; +} + +int game_apply_theme(void) +{ layout_t lay; textbox_t box; - getcwd(game_cwd, sizeof(game_cwd)); - cfg_load(); - if (!name && opt_game) - name = opt_game; - if (gfx_init(game_theme.w, game_theme.h, opt_fs)) + + memset(objs, 0, sizeof(struct el) * el_max); + + if (gfx_setmode(game_theme.w, game_theme.h, opt_fs)) return -1; - snd_init(opt_hz); - change_vol(0, opt_vol); - if (game_default_theme()) - return -1; - if (game_select(name)) { - name = NULL; - } - if (!access("theme.ini", R_OK)) { - if (theme_load("theme.ini")) - return -1; - } gfx_bg(game_theme.bgcol); game_clear(0, 0, game_theme.w, game_theme.h); -// if (instead_init("cat.lua")) -// return -1; -// gfx_update(0, 0, game_theme.w, game_theme.h); + gfx_flip(); lay = txt_layout(game_theme.font, ALIGN_JUSTIFY, game_theme.win_w, game_theme.win_h); if (!lay) return -1; @@ -790,17 +1115,17 @@ int game_init(const char *name) txt_layout_color(lay, game_theme.fgcol); txt_layout_link_color(lay, game_theme.lcol); txt_layout_active_color(lay, game_theme.acol); -// txt_layout_link_style(lay, game_theme.lstyle); txt_box_set(box, lay); el_set(el_scene, elt_box, game_theme.win_x, 0, box); - lay = txt_layout(game_theme.inv_font, ALIGN_LEFT, game_theme.inv_w, game_theme.inv_h); + lay = txt_layout(game_theme.inv_font, (game_theme.inv_mode == INV_MODE_HORIZ)? + ALIGN_CENTER:ALIGN_LEFT, game_theme.inv_w, game_theme.inv_h); if (!lay) return -1; + txt_layout_color(lay, game_theme.icol); txt_layout_link_color(lay, game_theme.ilcol); txt_layout_active_color(lay, game_theme.iacol); -// txt_layout_link_style(lay, game_theme.ilstyle); box = txt_box(game_theme.inv_w, game_theme.inv_h); if (!box) return -1; @@ -812,65 +1137,117 @@ int game_init(const char *name) if (!lay) return -1; + txt_layout_color(lay, game_theme.fgcol); + txt_layout_link_color(lay, game_theme.lcol); + txt_layout_active_color(lay, game_theme.acol); + el_set(el_title, elt_layout, game_theme.win_x, game_theme.win_y, lay); lay = txt_layout(game_theme.font, ALIGN_CENTER, game_theme.win_w, 0); if (!lay) return -1; + + txt_layout_color(lay, game_theme.fgcol); + txt_layout_link_color(lay, game_theme.lcol); + txt_layout_active_color(lay, game_theme.acol); + el_set(el_ways, elt_layout, game_theme.win_x, 0, lay); el_set(el_sdown, elt_image, 0, 0, game_theme.a_down); el_set(el_sup, elt_image, 0, 0, game_theme.a_up); -// el_set(el_sslide, elt_image, 0, 0, game_theme.slide); el_set(el_idown, elt_image, 0, 0, game_theme.inv_a_down); el_set(el_iup, elt_image, 0, 0, game_theme.inv_a_up); -// el_set(el_islide, elt_image, 0, 0, game_theme.slide); el_set(el_spic, elt_image, game_theme.win_x, game_theme.win_y, NULL); el_set(el_menu, elt_layout, 0, 0, NULL); el_set(el_menu_button, elt_image, game_theme.menu_button_x, game_theme.menu_button_y, game_theme.menu_button); el_draw(el_menu_button); - - s = game_save_path(0); - - if (s && !access(s, R_OK)) { - char cmd[256]; - snprintf(cmd, sizeof(cmd) - 1, "load %s", s); - game_cmd(cmd); - return 0; - } - if (!curgame) { - menu_shown = 1; - cur_menu = menu_games; - game_menu_box(menu_shown, game_menu_gen()); - gfx_flip(); - } else - game_cmd("look"); return 0; } +int game_init(const char *name) +{ + getcwd(game_cwd, sizeof(game_cwd)); + if (name) + game_err_msg(NULL); + + if (gfx_init() || input_init()) + return -1; + + snd_init(opt_hz); + change_vol(0, opt_vol); + + if (game_default_theme()) { + fprintf(stderr, "Can't load default theme.\n"); + return -1; + } + + if (game_select(name)) + return -1; + + if (curgame && !access(THEME_FILE, R_OK)) { + own_theme = 1; + } + + if (own_theme && opt_owntheme) { + if (theme_load(THEME_FILE)) + return -1; + } else if (curtheme_dir && strcmp(DEFAULT_THEME, curtheme_dir)) { + game_theme_load(curtheme_dir); + } + + if (game_apply_theme()) + return -1; + + if (!curgame) { + game_menu(menu_games); + } else { + if (!game_load(-1)) /* tmp save */ + return 0; + if (opt_autosave && !game_load(0)) /* autosave */ + return 0; + instead_eval("game:ini()"); + game_cmd("look"); + custom_theme_warn(); + if (opt_autosave) + game_save(0); + } + return 0; +} + +void free_last_music(void) +{ + if (last_music) + free(last_music); + last_music = NULL; +} + void free_last(void) { if (last_pict) free(last_pict); if (last_title) free(last_title); - if (last_music) - free(last_music); - last_pict = last_title = last_music = NULL; + free_last_music(); + last_pict = last_title = NULL; + snd_stop_mus(500); } void game_done(void) { int i; - if (opt_autosave) - game_save(); + if (opt_autosave && curgame) + game_save(0); chdir(game_cwd); - cfg_save(); - menu_shown = 0; - game_menu_box(0, NULL); +// cfg_save(); + + if (menu_shown) { + menu_shown = 0; + game_menu_box(0, NULL); + } cur_menu = menu_main; + if (el_img(el_spic)) gfx_free_image(el_img(el_spic)); @@ -897,10 +1274,14 @@ void game_done(void) gfx_free_image(menubg); menu = menubg = NULL; game_theme_free(); - instead_done(); + input_clear(); snd_done(); + instead_done(); gfx_done(); curgame = NULL; + curgame_dir = NULL; + own_theme = 0; +// SDL_Quit(); } void el_size(int i, int *w, int *h) @@ -950,8 +1331,8 @@ void el_update(int n) void box_update_scrollbar(int n) { - struct el *elup; - struct el *eldown; + struct el *elup = NULL; + struct el *eldown = NULL; // struct el *elslide; layout_t l; @@ -1002,7 +1383,7 @@ void box_update_scrollbar(int n) el_clear(elup->id); el_clear(eldown->id); - if (hh - off >= h) + if (hh - off > h) el_draw(eldown->id); if (off) el_draw(elup->id); @@ -1034,15 +1415,32 @@ void el_draw(int n) img_t game_pict_scale(img_t img, int ww, int hh) { img_t img2 = img; - int w, h; - float scale1, scale2, scale; + int w, h, www, hhh; + float scale1, scale2, scale = 1.0f; w = gfx_img_w(img); h = gfx_img_h(img); + + if (ww == -1) + ww = w; + if (hh == -1) + hh = h; + if (w <= ww && h <= hh) return img; - scale1 = (float)ww / (float)w; - scale2 = (float)hh / (float)h; - scale = (scale1 ww || scale * (float)h > hh) { + scale1 = (float)(www - 2) / (float)(w); + scale2 = (float)(hhh - 2) / (float)(h); + scale = (scale1 FONT_MIN_SZ) { + restart_needed = 1; + } else + opt_fsize ++; + game_menu_box(menu_shown, game_menu_gen()); + } else if (!strcmp(a, "/fs++")) { + opt_fsize ++; + if (FONT_SZ(game_theme.font_size) < FONT_MAX_SZ) { + restart_needed = 1; + } else + opt_fsize --; + game_menu_box(menu_shown, game_menu_gen()); } else if (!strcmp(a, "/hl")) { opt_hl ^= 1; + game_menu_box(menu_shown, game_menu_gen()); } else if (!strcmp(a, "/fs")) { - char *og = curgame; + restart_needed = 1; opt_fs ^= 1; - game_save(); - game_done(); - game_init(og); + game_menu_box(menu_shown, game_menu_gen()); } else if (!strcmp(a, "/games_prev")) { games_menu_from -= MENU_GAMES_MAX; if (games_menu_from < 0) games_menu_from = 0; + game_menu_box(menu_shown, game_menu_gen()); } else if (!strcmp(a, "/games_next")) { if (games_menu_from + MENU_GAMES_MAX < games_nr) games_menu_from += MENU_GAMES_MAX; + game_menu_box(menu_shown, game_menu_gen()); + } else if (!strcmp(a, "/themes_prev")) { + themes_menu_from -= MENU_THEMES_MAX; + if (themes_menu_from < 0) + themes_menu_from = 0; + game_menu_box(menu_shown, game_menu_gen()); + } else if (!strcmp(a, "/themes_next")) { + if (themes_menu_from + MENU_THEMES_MAX < themes_nr) + themes_menu_from += MENU_THEMES_MAX; + game_menu_box(menu_shown, game_menu_gen()); } else if (!strcmp(a, "/select")) { - cur_menu = menu_games; - } else if (!strcmp(a, "/save")) { - if (!game_save()) { - cur_menu = menu_saved; + game_menu(menu_games); + } else if (!strcmp(a, "/themes")) { + game_menu(menu_themes); + } else if (!strcmp(a, "/save_menu")) { + if (curgame) + game_menu(menu_save); + } else if (!strncmp(a, "/save", 5)) { + if (!game_save(atoi(a + 5))) { + game_menu(menu_saved); } - } - else if (!strcmp(a, "/new")) { - if (!curgame) + } else if (!strcmp(a, "/load_menu")) { + if (curgame) + game_menu(menu_load); + } else if (!strncmp(a, "/load", 5)) { + int nr = atoi(a + 5); + if (!curgame_dir) return 0; + free_last(); - game_select(curgame); -// instead_done(); -// instead_init(); -// instead_load(MAIN_FILE); + game_select(curgame_dir); menu_shown = 0; game_menu_box(0, NULL); - game_cmd("look"); - game_save(); -// game_done(); -// game_init(); -// game_cmd("look"); - } - else if (!strcmp(a,"/main")) { + game_load(nr); cur_menu = menu_main; - } - else if (!strcmp(a,"/ask_quit")) { - cur_menu = menu_askquit; - } - else if (!strcmp(a,"/about")) { - cur_menu = menu_about; - } - else if (!strcmp(a,"/mtoggle")) { +// game_menu_box(0, NULL); + } else if (!strcmp(a, "/new")) { + char *s; + if (!curgame_dir) + return 0; + free_last(); + game_select(curgame_dir); + menu_shown = 0; + game_menu_box(0, NULL); + instead_eval("game:ini()"); + game_cmd("look"); + s = game_save_path(0, 0); + if (s && !access(s, R_OK) && opt_autosave) + unlink (s); + custom_theme_warn(); + } else if (!strcmp(a,"/main")) { + game_restart(); + game_menu(menu_main); + } else if (!strcmp(a,"/ask_quit")) { + game_menu(menu_askquit); + } else if (!strcmp(a,"/about")) { + game_menu(menu_about); + } else if (!strcmp(a,"/mtoggle")) { if (!old_vol) { old_vol = snd_volume_mus(-1); change_vol(0, 0); @@ -1186,102 +1655,175 @@ int game_menu_act(const char *a) change_vol(0, old_vol); old_vol = 0; } - } - else if (!strcmp(a,"/resume")) { + } else if (!strcmp(a,"/music")) { + opt_music ^= 1; + if (!opt_music) { + snd_stop_mus(0); + free_last_music(); + } else + music_player(); + game_menu_box(menu_shown, game_menu_gen()); + } else if (!strcmp(a,"/resume")) { menu_shown = 0; + cur_menu = menu_main; game_menu_box(0, NULL); - } - else if (!strcmp(a, "/settings")) { - cur_menu = menu_settings; - } - else if (!strcmp(a, "/vol--")) { + } else if (!strcmp(a, "/settings")) { + game_menu(menu_settings); + } else if (!strcmp(a, "/vol--")) { change_vol(-10, 0); - } - else if (!strcmp(a, "/vol++")) { + game_menu_box(menu_shown, game_menu_gen()); + } else if (!strcmp(a, "/vol++")) { change_vol(+10, 0); - } - else if (!strcmp(a, "/vol-")) { + game_menu_box(menu_shown, game_menu_gen()); + } else if (!strcmp(a, "/vol-")) { change_vol(-1, 0); - } - else if (!strcmp(a, "/vol+")) { + game_menu_box(menu_shown, game_menu_gen()); + } else if (!strcmp(a, "/vol+")) { change_vol(+1, 0); - } - else if (!strcmp(a, "/hz-")) { + game_menu_box(menu_shown, game_menu_gen()); + } else if (!strcmp(a, "/hz-")) { int hz = snd_hz(); - if (hz == 44100) + if (hz == 48000) + hz = 44100; + else if (hz == 44100) hz = 22050; else if (hz == 22050) hz = 11025; else hz = 0; change_hz(hz); - } - else if (!strcmp(a, "/hz+")) { + game_menu_box(menu_shown, game_menu_gen()); + } else if (!strcmp(a, "/hz+")) { int hz = snd_hz(); if (hz == 11025) hz = 22050; else if (hz == 22050) hz = 44100; + else if (hz == 44100) + hz = 48000; else hz = 0; change_hz(hz); - } - else if (!strcmp(a,"/quit")) { + game_menu_box(menu_shown, game_menu_gen()); + } else if (!strcmp(a,"/quit")) { return -1; } else if (cur_menu == menu_games) { char *p; p = strdup(a); if (p) { - char *og = curgame; game_done(); if (game_init(p)) { - fprintf(stderr, "Can't init game:%s:%s\n", p, strerror(errno)); - // exit(1); - game_done(); - game_init(og); + game_error(p); + } + free(p); + } + } else if (cur_menu == menu_themes) { + char *p; + p = strdup(a); + if (p) { + if (game_theme_select(p)) + fprintf(stderr, "Can't select theme:%s:%s\n", p, strerror(errno)); + char *og = curgame_dir; + game_save(-1); + game_done(); + if (game_init(og)) + game_error(og); + else if (curgame && own_theme && opt_owntheme) { + game_menu(menu_own_theme); } free(p); } } return 0; } -#ifdef RUSSIAN -#define MAIN_MENU "Вернуться в игру\nВыбор игры\nНовая игра\nСохранить игру\nИнформация\nНастройки\nВыход" -#define ABOUT_MENU "IN S.T.E.A.D SDL - "VERSION"\n\nИнтерпретатор простых\nтекстовых приключений.\n\nКосых П.А. '2009\n\nНазад" - -#define BACK_MENU "Назад" -#define ON "Да" -#define OFF "Нет" - -#define SETTINGS_MENU "Громкость\n<<< %d%% >>>\n\n\ -Качество звука\n<< %dГц >>\n\nПодсветка ссылок: %s\n\nПолный экран: %s\n\n\ -Автосохранение: %s\n\n\ -Назад" - -#define QUIT_MENU "На самом деле выйти?\n\nДа | Нет" -#define SELECT_GAME_MENU "Выбор игры\n\n" -#define SAVED_MENU "Игра сохранена!\n\nОк" -#define NOGAMES_MENU "Не найдена ни одна игра. \nПожалуйста, скопируйте хотя бы одну игру в каталог:\n'%s'" -#else -#define MAIN_MENU "Resume\nSelect Game\nNew\nSave\nAbout\nSettings\nQuit" -#define ABOUT_MENU "Written by Peter Kosyh '2009\n\nBack" -#define BACK_MENU "Back" - -#define ON "on" -#define OFF "off" -#define SELECT_GAME_MENU "Select game to play\n\n" -#define SETTINGS_MENU "Volume\n<<< %d%% >>>\n\n\ -Quality\n<< %dHz >>Refs highlighting: %s\n\nFull Screen: %s\n\n\ -Autosave: %s\n\n\ -Back" - -#define QUIT_MENU "Really quit?\n\nYes | No" - -#define SAVED_MENU "Current Game saved!\n\nOk" -#define NOGAMES_MENU "No games found. \nPlease, write any game in the this directory:\n'%s'" -#endif char menu_buff[4096]; + +char *slot_name(const char *path) +{ + struct stat st; + int brk = 0; + char *l; char line[1024]; + FILE *fd = fopen(path, "r"); + if (!fd) + return NULL; + + while ((l = fgets(line, sizeof(line), fd)) && !brk) { + l = parse_tag(l, "$Name:", "--", &brk); + if (l) { + char *s = fromgame(l); + free(l); + return s; + } + } + fclose(fd); + if (stat(path, &st)) + return NULL; + l = ctime(&st.st_ctime); + if (!l) + return NULL; + l[strcspn(l,"\n")] = 0; + return strdup(l); +} + +void load_menu(void) +{ + int i; + *menu_buff = 0; + sprintf(menu_buff, SELECT_LOAD_MENU); + for (i = 0; i < MAX_SAVE_SLOTS; i ++) { + char tmp[PATH_MAX]; + char *s = game_save_path(0, i); + if (!s || access(s, R_OK)) { + if (!i) + continue; + snprintf(tmp, sizeof(tmp), "%d - "SAVE_SLOT_EMPTY"\n", i); + } else { + char *name; + if (!i) + name = strdup(AUTOSAVE_SLOT); + else + name = slot_name(s); + if (!name) + snprintf(tmp, sizeof(tmp), "%d - "BROKEN_SLOT"\n", i); + else { + snprintf(tmp, sizeof(tmp), "%d - %s\n", i, i, name); + free(name); + } + } + strcat(menu_buff, tmp); + } + strcat(menu_buff,"\nОтмена"); +} + +void save_menu(void) +{ + int i; + *menu_buff = 0; + sprintf(menu_buff, SELECT_SAVE_MENU); + for (i = 1; i < MAX_SAVE_SLOTS; i ++) { + char tmp[PATH_MAX]; + char *s = game_save_path(0, i); + if (!s || access(s, R_OK)) + snprintf(tmp, sizeof(tmp), "%d - "SAVE_SLOT_EMPTY"\n", i, i); + else { + char *name; + if (!i) + name = strdup(AUTOSAVE_SLOT); + else + name = slot_name(s); + if (!name) + snprintf(tmp, sizeof(tmp), "%d - "BROKEN_SLOT"\n", i, i); + else { + snprintf(tmp, sizeof(tmp), "%d - %s\n", i, i, name); + free(name); + } + } + strcat(menu_buff, tmp); + } + strcat(menu_buff,"\nОтмена"); +} + void games_menu(void) { int i; @@ -1292,7 +1834,7 @@ void games_menu(void) if (curgame && !strcmp(games[i].name, curgame)) snprintf(tmp, sizeof(tmp), "%s\n", games[i].name); else - snprintf(tmp, sizeof(tmp), "%s\n", games[i].name, games[i].name); + snprintf(tmp, sizeof(tmp), "%s\n", games[i].dir, games[i].name); strcat(menu_buff, tmp); } if (!games_nr) @@ -1305,6 +1847,29 @@ void games_menu(void) strcat(menu_buff," >>"); } +void themes_menu(void) +{ + int i; + *menu_buff = 0; + sprintf(menu_buff, SELECT_THEME_MENU); + for (i = themes_menu_from; i < themes_nr && i - themes_menu_from < MENU_THEMES_MAX; i ++) { + char tmp[PATH_MAX]; + if (curtheme && !strcmp(themes[i].name, curtheme)) + snprintf(tmp, sizeof(tmp), "%s\n", themes[i].name); + else + snprintf(tmp, sizeof(tmp), "%s\n", themes[i].dir, themes[i].name); + strcat(menu_buff, tmp); + } + if (!themes_nr) + sprintf(menu_buff, NOTHEMES_MENU, THEMES_PATH); + strcat(menu_buff,"\n"); + if (themes_menu_from) + strcat(menu_buff,"<< "); + strcat(menu_buff, BACK_MENU); + if (themes_menu_from + MENU_THEMES_MAX < themes_nr) + strcat(menu_buff," >>"); +} + char *game_menu_gen(void) { if (cur_menu == menu_main) { @@ -1312,8 +1877,10 @@ char *game_menu_gen(void) } else if (cur_menu == menu_about) { snprintf(menu_buff, sizeof(menu_buff), ABOUT_MENU); } else if (cur_menu == menu_settings) { - snprintf(menu_buff, sizeof(menu_buff), - SETTINGS_MENU, vol_to_pcn(snd_volume_mus(-1)), snd_hz(), opt_hl?ON:OFF, opt_fs?ON:OFF, opt_autosave?ON:OFF); + snprintf(menu_buff, sizeof(menu_buff), SETTINGS_MENU, + vol_to_pcn(snd_volume_mus(-1)), snd_hz(), opt_music?ON:OFF, opt_click?ON:OFF, + opt_fs?ON:OFF, opt_fsize, opt_hl?ON:OFF, opt_motion?ON:OFF, opt_filter?ON:OFF, + opt_owntheme?ON:OFF, opt_autosave?ON:OFF); } else if (cur_menu == menu_askquit) { snprintf(menu_buff, sizeof(menu_buff), QUIT_MENU); } else if (cur_menu == menu_saved) { @@ -1321,6 +1888,26 @@ char *game_menu_gen(void) SAVED_MENU); } else if (cur_menu == menu_games) { games_menu(); + } else if (cur_menu == menu_themes) { + themes_menu(); + } else if (cur_menu == menu_own_theme) { + snprintf(menu_buff, sizeof(menu_buff), + OWN_THEME_MENU); + } else if (cur_menu == menu_custom_theme) { + snprintf(menu_buff, sizeof(menu_buff), + CUSTOM_THEME_MENU); + } else if (cur_menu == menu_load) { + load_menu(); + } else if (cur_menu == menu_save) { + save_menu(); + } else if (cur_menu == menu_error) { + snprintf(menu_buff, sizeof(menu_buff), + ERROR_MENU, err_msg?err_msg:UNKNOWN_ERROR); + game_err_msg(NULL); + } else if (cur_menu == menu_warning) { + snprintf(menu_buff, sizeof(menu_buff), + WARNING_MENU, err_msg?err_msg:UNKNOWN_ERROR); + game_err_msg(NULL); } return menu_buff; } @@ -1355,7 +1942,16 @@ void game_menu_box(int show, const char *txt) gfx_flip(); return; } - lay = txt_layout(game_theme.menu_font, ALIGN_CENTER, game_theme.win_w, 0); + lay = txt_layout(game_theme.menu_font, ALIGN_CENTER, game_theme.win_w - 2 * (b + pad), 0); + txt_layout_set(lay, (char*)txt); + txt_layout_real_size(lay, &w, &h); + txt_layout_free(lay); + + lay = txt_layout(game_theme.menu_font, ALIGN_CENTER, w, 0); + + txt_layout_set(lay, (char*)txt); + txt_layout_real_size(lay, &w, &h); + txt_layout_color(lay, game_theme.menu_fg); txt_layout_link_color(lay, game_theme.menu_link); txt_layout_active_color(lay, game_theme.menu_alink); @@ -1369,7 +1965,7 @@ void game_menu_box(int show, const char *txt) gfx_img_fill(menu, 0, 0, w + (b + pad)*2, h + (b + pad)*2, game_theme.border_col); gfx_img_fill(menu, b, b, w + pad*2, h + pad*2, game_theme.menu_bg); gfx_set_alpha(menu, game_theme.menu_alpha); - x = (game_theme.win_w - w)/2 + game_theme.win_x; // (game_theme.w - w)/2; + x = (game_theme.win_w - w)/2 + game_theme.win_x; //(game_theme.w - w)/2; y = (game_theme.win_h - h)/2 + game_theme.win_y; //(game_theme.h - h)/2; mx = x - b - pad; my = y - b - pad; @@ -1377,7 +1973,7 @@ void game_menu_box(int show, const char *txt) mh = h + (b + pad) * 2; menubg = gfx_grab_screen(mx, my, mw, mh); gfx_draw(menu, mx, my); - el_set(el_menu, elt_layout, game_theme.win_x, y, lay); + el_set(el_menu, elt_layout, /*game_theme.win_x*/ x, y, lay); el_draw(el_menu); gfx_flip(); } @@ -1436,45 +2032,128 @@ void scene_scrollbar(void) el_draw(el_sup); } + +static void dec_music(void *data) +{ + char *mus; + if (!curgame) + return; + mus = instead_eval("return dec_music_loop()"); + if (!mus) + return; + if (atoi(mus) == -1) + free_last_music(); + free(mus); +} + +void game_music_finished(void) +{ + push_user_event(&dec_music, NULL); +} + +void unix_path(char *path) +{ + char *p = path; + if (!path) + return; + while (*p) { /* bad bad Windows!!! */ + if (*p == '\\') + *p = '/'; + p ++; + } + return; +} + void music_player(void) { + int loop; char *mus; if (!snd_volume_mus(-1)) return; - mus = instead_eval("return get_music()"); + if (!opt_music) + return; + + mus = instead_eval("return get_music_loop()"); + if (mus) { - if (!last_music && mus) { - last_music = mus; - snd_stop_mus(500); - snd_play_mus(mus, 0); - } else if (strcmp(last_music, mus)) { - free(last_music); - last_music = mus; - snd_stop_mus(500); - snd_play_mus(mus, 0); - } else - free(mus); + loop = atoi(mus); + free(mus); + } else + loop = -1; + + mus = instead_eval("return get_music()"); + unix_path(mus); + + if (mus && loop == -1) { /* disabled, 0 - forever, 1-n - loops */ + free(mus); + mus = NULL; } + + if (!mus) { + if (last_music) { + free(last_music); + snd_stop_mus(500); + last_music = NULL; + } + } else if (!last_music && mus) { + last_music = mus; + snd_stop_mus(500); + snd_play_mus(mus, 0, loop - 1); + } else if (strcmp(last_music, mus)) { + free(last_music); + last_music = mus; + snd_stop_mus(500); + snd_play_mus(mus, 0, loop - 1); + } else + free(mus); +} + +char *horiz_inv(char *invstr) +{ + char *p = invstr; + char *ns = malloc(strlen(p) * 3); + char *np = ns; + if (!np) + return invstr; + while (*p) { + if (*p == '\n') { + if (p[strspn(p, " \n\t")]) { + *(np++) = ' '; + *(np++) = '|'; + *(np) = ' '; + } else + break; + } else + *np = *p; + p ++; + np ++; + } + *(np++) = '\n'; + *np = 0; + free(invstr); + invstr = ns; + return invstr; } int game_cmd(char *cmd) { int new_pict = 0, new_place = 0; int title_h = 0, ways_h = 0, pict_h = 0; - char buf[256]; + char buf[512]; char *cmdstr; char *invstr; char *waystr; char *title; char *pict; - img_t oldscreen; + img_t oldscreen = NULL; cmdstr = instead_cmd(cmd); - if (!cmdstr) { - return -1; - } + if (!cmdstr) + goto err; music_player(); +// sound_player(); /* TODO */ title = instead_eval("return get_title();"); + unix_path(title); if (title) { snprintf(buf, sizeof(buf), "%s", title); txt_layout_set(el_layout(el_title), buf); @@ -1484,7 +2163,7 @@ int game_cmd(char *cmd) new_place = check_new_place(title); txt_layout_size(el_layout(el_title), NULL, &title_h); -// title_h += 12; // todo + title_h += game_theme.font_size / 2; // todo? pict = instead_eval("return get_picture();"); new_pict = check_new_pict(pict); @@ -1494,23 +2173,33 @@ int game_cmd(char *cmd) img_t img; if (new_pict) { - img = gfx_load_image(pict, 1); + img = gfx_load_image(pict); if (el_img(el_spic)) gfx_free_image(el_img(el_spic)); el(el_spic)->p.p = NULL; - img = game_pict_scale(img, game_theme.win_w, game_theme.max_scene_h); + if (game_theme.gfx_mode != GFX_MODE_FLOAT) + img = game_pict_scale(img, game_theme.win_w, game_theme.max_scene_h); + else + img = game_pict_scale(img, game_theme.max_scene_w, game_theme.max_scene_h); } else img = el_img(el_spic); if (img) { w = gfx_img_w(img); h = gfx_img_h(img); - x = (game_theme.win_w - w)/2 + game_theme.win_x; - el_set(el_spic, elt_image, x, game_theme.win_y + title_h, img); + if (game_theme.gfx_mode != GFX_MODE_FLOAT) { + x = (game_theme.win_w - w)/2 + game_theme.win_x; + el_set(el_spic, elt_image, x, game_theme.win_y + title_h, img); + } else { + x = (game_theme.max_scene_w - w)/2 + game_theme.gfx_x; + el_set(el_spic, elt_image, x, game_theme.gfx_y/* + (game_theme.max_scene_h - h)/2*/, img); + } +// if (!game_theme.emb_gfx) pict_h = h; } } else if (el_img(el_spic)) { - el_clear(el_spic); + if (game_theme.gfx_mode != GFX_MODE_EMBEDDED) + el_clear(el_spic); gfx_free_image(el_img(el_spic)); el(el_spic)->p.p = NULL; } @@ -1518,18 +2207,49 @@ int game_cmd(char *cmd) waystr = instead_cmd("way"); invstr = instead_cmd("inv"); + if (invstr && game_theme.inv_mode == INV_MODE_HORIZ) { + invstr = horiz_inv(invstr); + } + if (waystr) { waystr[strcspn(waystr,"\n")] = 0; } - txt_layout_set(el_layout(el_ways), waystr); + if (game_theme.gfx_mode != GFX_MODE_EMBEDDED) { + txt_layout_set(el_layout(el_ways), waystr); + txt_layout_size(el_layout(el_ways), NULL, &ways_h); + } + + + if (game_theme.gfx_mode == GFX_MODE_EMBEDDED) { + int off = 0; + if (!new_pict && !new_place) { + off = txt_box_off(el_box(el_scene)); + if (off > pict_h) + off = pict_h; + } + pict_h = 0; /* to fake code bellow */ + txt_layout_set(txt_box_layout(el_box(el_scene)), ""); /* hack, to null layout, but not images */ + if (el_img(el_spic)) { + txt_layout_add_img(txt_box_layout(el_box(el_scene)),"scene", el_img(el_spic)); + txt_layout_add(txt_box_layout(el_box(el_scene)), "\n"); + } + txt_layout_add(txt_box_layout(el_box(el_scene)), waystr); + txt_layout_add(txt_box_layout(el_box(el_scene)), "\n"); /* small hack */ + txt_layout_add(txt_box_layout(el_box(el_scene)), cmdstr); + txt_box_set(el_box(el_scene), txt_box_layout(el_box(el_scene))); + if (!new_pict && !new_place) + txt_box_scroll(el_box(el_scene), off); + } else { + if (game_theme.gfx_mode == GFX_MODE_FLOAT) + pict_h = 0; + txt_layout_set(txt_box_layout(el_box(el_scene)), cmdstr); + txt_box_set(el_box(el_scene), txt_box_layout(el_box(el_scene))); + } + free(cmdstr); + el(el_ways)->y = el(el_title)->y + title_h + pict_h; if (waystr) free(waystr); - txt_layout_size(el_layout(el_ways), NULL, &ways_h); - el(el_ways)->y = el(el_title)->y + title_h + pict_h; - txt_layout_set(txt_box_layout(el_box(el_scene)), cmdstr); - txt_box_set(el_box(el_scene), txt_box_layout(el_box(el_scene))); - free(cmdstr); el(el_scene)->y = el(el_ways)->y + ways_h; /* draw title and ways */ @@ -1540,6 +2260,9 @@ int game_cmd(char *cmd) } if (new_pict || new_place) { game_clear(game_theme.win_x, game_theme.win_y, game_theme.win_w, game_theme.win_h); + if (game_theme.gfx_mode == GFX_MODE_FLOAT) { + game_clear(game_theme.gfx_x, game_theme.gfx_y, game_theme.max_scene_w, game_theme.max_scene_h); + } // el_draw(el_title); } else { game_clear(game_theme.win_x, game_theme.win_y + pict_h + title_h, @@ -1548,15 +2271,22 @@ int game_cmd(char *cmd) el_clear(el_title); el_draw(el_title); - el_draw(el_ways); - if (new_pict || new_place) - el_draw(el_spic); + if (game_theme.gfx_mode != GFX_MODE_EMBEDDED) { + el_draw(el_ways); + if ((new_pict || new_place)) + el_draw(el_spic); + } txt_box_resize(el_box(el_scene), game_theme.win_w, game_theme.win_h - title_h - ways_h - pict_h); el_draw(el_scene); - txt_layout_set(txt_box_layout(el_box(el_inv)), invstr); - txt_box_set(el_box(el_inv), txt_box_layout(el_box(el_inv))); + do { + int off = txt_box_off(el_box(el_inv)); + txt_layout_set(txt_box_layout(el_box(el_inv)), invstr); + txt_box_set(el_box(el_inv), txt_box_layout(el_box(el_inv))); + txt_box_scroll(el_box(el_inv), off); + } while(0); + if (invstr) free(invstr); @@ -1569,9 +2299,16 @@ int game_cmd(char *cmd) offscreen = gfx_screen(oldscreen); gfx_change_screen(offscreen); gfx_free_image(offscreen); - return 0; +// input_clear(); + goto err; + } + gfx_flip(); +// input_clear(); +err: + if (err_msg) { + game_menu(menu_warning); + return -1; } - gfx_flip(); return 0; } @@ -1675,20 +2412,6 @@ int game_highlight(int x, int y, int on) hxref = NULL; } hxref = xref; -// if (!up || !elem) -// return 0; -// xref_update(hxref, elem->x, elem->y, game_clear); -// game_cursor(2); -// game_cursor(0); - -// if (elem->id == el_menu) { -// menu_update(elem); -// } -// else if (elem->type == elt_layout) -// txt_layout_update_links(elem->p.lay, elem->x, elem->y, game_clear); -// else if (elem->type == elt_box) -// txt_box_update_links(elem->p.box, elem->x, elem->y, game_clear); -// el_update(elem->id); oel = elem; return 0; } @@ -1699,86 +2422,128 @@ void menu_toggle(void) game_highlight(-1, -1, 0); disable_inv(); menu_shown ^= 1; + motion_mode = 0; + old_xref = old_el = NULL; if (!menu_shown) cur_menu = menu_main; game_menu_box(menu_shown, game_menu_gen()); } +static void scroll_pup(int id) +{ + game_cursor(0); + game_highlight(-1, -1, 0); + if (game_theme.gfx_mode == GFX_MODE_EMBEDDED) { + int hh; + el_size(el_scene, NULL, &hh); + txt_box_scroll(el_box(id), -hh); + } else + txt_box_prev(el_box(id)); + el_clear(id); + el_draw(id); + el_update(id); +} + +static void scroll_pdown(int id) +{ + game_cursor(0); + game_highlight(-1, -1, 0); + if (game_theme.gfx_mode == GFX_MODE_EMBEDDED) { + int hh; + el_size(el_scene, NULL, &hh); + txt_box_scroll(el_box(id), hh); + } else + txt_box_next(el_box(id)); + el_clear(id); + el_draw(id); + el_update(id); +} + +static unsigned int old_counter = 0; +extern unsigned int timer_counter; +int mouse_filter(void) +{ + if (!opt_filter) + return 0; + if (abs(old_counter - timer_counter) <= 4) /* 400 ms */ + return -1; + old_counter = timer_counter; + return 0; +} + int game_click(int x, int y, int action) { struct el *elem = NULL; - char buf[256]; - xref_t xref; - - if (old_xref) { + char buf[512]; + xref_t xref = NULL; + + if (action) + motion_mode = 0; + + if (opt_filter && action) { + xref_t new_xref; + struct el *new_elem; + new_xref = look_xref(x, y, &new_elem); + if (new_xref != old_xref || new_elem != old_el) { + old_el = NULL; + if (old_xref) { + old_xref = NULL; + return 0; /* just filtered */ + } + old_xref = NULL; + } + } + if (action) { xref = old_xref; elem = old_el; - } else { + old_xref = NULL; + old_el = NULL; + } else { /* just press */ xref = look_xref(x, y, &elem); + if (xref) { + xref_set_active(xref, 1); + xref_update(xref, elem->x, elem->y, game_clear); + } else if (elem && elem->type == elt_box && opt_motion) { + motion_mode = 1; + motion_id = elem->id; + motion_y =y; + return 0; + } + old_xref = xref; + old_el = elem; + return 0; } + /* now look only second press */ if (!xref) { - if (action && elem) { + if (elem) { if (elem->id == el_menu_button) { menu_toggle(); } else if (elem->id == el_sdown) { - txt_box_next(el_box(el_scene)); - el_clear(el_scene); - el_draw(el_scene); - el_update(el_scene); - } - else if (elem->id == el_sup) { - txt_box_prev(el_box(el_scene)); - el_clear(el_scene); - el_draw(el_scene); - el_update(el_scene); - } - else if (elem->id == el_idown) { - txt_box_next(el_box(el_inv)); - el_clear(el_inv); - el_draw(el_inv); + scroll_pdown(el_scene); + } else if (elem->id == el_sup) { + scroll_pup(el_scene); + } else if (elem->id == el_idown) { + scroll_pdown(el_inv); + } else if (elem->id == el_iup) { + scroll_pup(el_inv); + } else if (disable_inv()) el_update(el_inv); - } - else if (elem->id == el_iup) { - txt_box_prev(el_box(el_inv)); - el_clear(el_inv); - el_draw(el_inv); - el_update(el_inv); - } - } - if (disable_inv()) { + motion_mode = 0; + } else if (disable_inv()) { el_update(el_inv); // gfx_flip(); } return 0; } - - if (!action) { - xref_set_active(xref, 1); - xref_update(xref, elem->x, elem->y, game_clear); -/* - if (elem->id == el_menu) { - menu_update(elem); - } else if (elem->type == elt_layout) - txt_layout_update_links(elem->p.lay, elem->x, elem->y, game_clear); - else if (elem->type == elt_box) - txt_box_update_links(elem->p.box, elem->x, elem->y, game_clear); - el_update(elem->id); */ - old_xref = xref; - old_el = elem; - return 0; - } - - old_el = NULL; - old_xref = NULL; - +/* second xref */ if (elem->id == el_menu) { // xref_set_active(xref, 0); // txt_layout_update_links(elem->p.lay, elem->x, elem->y, game_clear); if (game_menu_act(xref_get_text(xref))) { return -1; } - game_menu_box(menu_shown, game_menu_gen()); +// game_menu_box(menu_shown, game_menu_gen()); // gfx_flip(); return 1; } @@ -1786,7 +2551,14 @@ int game_click(int x, int y, int action) if (elem->id == el_ways || elem->id == el_title) { strcpy(buf, xref_get_text(xref)); - disable_inv(); + if (mouse_filter()) + return 0; + if (opt_click) + snd_play(game_theme.click); + if (disable_inv()) { + el_update(el_inv); + return 0; + } game_cmd(buf); return 1; } @@ -1797,6 +2569,10 @@ int game_click(int x, int y, int action) disable_inv(); } else strcpy(buf, xref_get_text(xref)); + if (mouse_filter()) + return 0; + if (opt_click) + snd_play(game_theme.click); game_cmd(buf); return 1; } @@ -1812,6 +2588,10 @@ int game_click(int x, int y, int action) else snprintf(buf,sizeof(buf), "use %s,%s", xref_get_text(inv_xref), xref_get_text(xref)); disable_inv(); + if (mouse_filter()) + return 0; + if (opt_click) + snd_play(game_theme.click); game_cmd(buf); return 1; } @@ -1822,15 +2602,17 @@ void game_cursor(int on) { static img_t grab = NULL; static int xc, yc, w, h; + if (grab) { gfx_draw(grab, xc, yc); gfx_free_image(grab); grab = NULL; + if (!on) { + gfx_update(xc, yc, gfx_img_w(game_theme.use), gfx_img_h(game_theme.use)); + return; + } } - if (!on) { - gfx_update(xc, yc, gfx_img_w(game_theme.use), gfx_img_h(game_theme.use)); - return; - } + if (on == -1) return; if (inv_xref) { @@ -1846,15 +2628,88 @@ void game_cursor(int on) } } + +static void scroll_up(int id, int count) +{ + int i; + game_cursor(0); + game_highlight(-1, -1, 0); + if (game_theme.gfx_mode == GFX_MODE_EMBEDDED) + txt_box_scroll(el_box(id), -(FONT_SZ(game_theme.font_size)) * count); + else + for (i = 0; i < count; i++) + txt_box_prev_line(el_box(id)); + el_clear(id); + el_draw(id); + el_update(id); +} + +static void scroll_down(int id, int count) +{ + int i; + game_cursor(0); + game_highlight(-1, -1, 0); + if (game_theme.gfx_mode == GFX_MODE_EMBEDDED) + txt_box_scroll(el_box(id), (FONT_SZ(game_theme.font_size)) * count); + else + for (i = 0; i < count; i++) + txt_box_next_line(el_box(id)); + el_clear(id); + el_draw(id); + el_update(id); +} + +static void scroll_motion(int id, int off) +{ + game_cursor(0); + game_highlight(-1, -1, 0); + txt_box_scroll(el_box(id), off); + el_clear(id); + el_draw(id); + el_update(id); +} + +static int alt_pressed = 0; int game_loop(void) { static int x = 0, y = 0; struct inp_event ev; while (1) { - if (input(&ev, 1) == -1) /* close */ + int rc; + ev.x = -1; + while ((rc = input(&ev, 1)) == AGAIN); + if (rc == -1) /* close */ break; - if (ev.type == KEY_DOWN && ev.sym && !strcmp(ev.sym,"escape")) { - menu_toggle(); + else if (((ev.type == KEY_DOWN) || (ev.type == KEY_UP)) && ev.sym && + (!strcmp(ev.sym,"left alt") || !strcmp(ev.sym, "right alt"))) { + alt_pressed = (ev.type == KEY_DOWN) ? 1:0; + } else if (ev.type == KEY_DOWN && ev.sym) { + if (!strcmp(ev.sym,"escape")) { + menu_toggle(); + } else if (!strcmp(ev.sym,"up") && !menu_shown) { + scroll_up(el_scene, 1); + } else if (!strcmp(ev.sym,"down") && !menu_shown) { + scroll_down(el_scene, 1); + } else if ((!strcmp(ev.sym,"page up") || !strcmp(ev.sym, "backspace")) && !menu_shown) { + scroll_pup(el_scene); + } else if ((!strcmp(ev.sym,"page down") || !strcmp(ev.sym, "space")) && !menu_shown) { + scroll_pdown(el_scene); + } else if (alt_pressed && !strcmp(ev.sym, "q")) { + break; + } else if (alt_pressed && + (!strcmp(ev.sym,"enter") || !strcmp(ev.sym, "return"))) { + int old_menu = -1; + game_menu_act("/fs"); + game_highlight(-1, -1, 0); + disable_inv(); + old_xref = old_el = NULL; + if (menu_shown) + old_menu = cur_menu; + game_restart(); + if (old_menu != -1) + game_menu(old_menu); +// game_menu_act("/main"); + } } else if (ev.type == MOUSE_DOWN) { game_cursor(0); game_highlight(-1, -1, 0); @@ -1864,7 +2719,7 @@ int game_loop(void) } else if (ev.type == MOUSE_UP) { game_cursor(0); game_highlight(-1, -1, 0); - if (game_click(x, y, 1) == -1) + if (game_click(ev.x, ev.y, 1) == -1) break; } else if (ev.type == MOUSE_WHEEL_UP && !menu_shown) { int xm, ym; @@ -1872,12 +2727,7 @@ int game_loop(void) gfx_cursor(&xm, &ym, NULL, NULL); o = look_obj(xm, ym); if (o && (o->id == el_scene || o->id == el_inv)) { - game_cursor(0); - game_highlight(-1, -1, 0); - txt_box_prev_line(el_box(o->id)); - el_clear(o->id); - el_draw(o->id); - el_update(o->id); + scroll_up(o->id, ev.count); } } else if (ev.type == MOUSE_WHEEL_DOWN && !menu_shown) { int xm, ym; @@ -1885,17 +2735,19 @@ int game_loop(void) gfx_cursor(&xm, &ym, NULL, NULL); o = look_obj(xm, ym); if (o && (o->id == el_scene || o->id == el_inv)) { - game_cursor(0); - game_highlight(-1, -1, 0); - txt_box_next_line(el_box(o->id)); - el_clear(o->id); - el_draw(o->id); - el_update(o->id); + scroll_down(o->id, ev.count); } } else if (ev.type == MOUSE_MOTION) { + if (motion_mode) { + scroll_motion(motion_id, motion_y - ev.y); + motion_y = ev.y; + } // game_highlight(ev.x, ev.y, 1); } - game_highlight(ev.x, ev.y, 1); + if (old_xref) + game_highlight(x, y, 1); + else if (ev.x >= 0) + game_highlight(ev.x, ev.y, 1); game_cursor(1); } return 0; diff --git a/src/sdl-instead/game.h b/src/sdl-instead/game.h index 020e016..a85ccab 100644 --- a/src/sdl-instead/game.h +++ b/src/sdl-instead/game.h @@ -1,9 +1,50 @@ #ifndef __GAME_H__ #define __GAME_H__ + +#ifndef GAMES_PATH +#define GAMES_PATH "./games" +#endif + +#define DEFAULT_THEME "default" +#ifndef THEMES_PATH +#define THEMES_PATH "./themes" +#endif + +#define MAIN_FILE "main.lua" +#define THEME_FILE "theme.ini" + +#define MENU_GAMES_MAX 8 +#define MENU_THEMES_MAX 8 + +#define FONT_MIN_SZ 8 +#define FONT_MAX_SZ 64 + +#define MAX_SAVE_SLOTS 6 +extern char *game_local_games_path(void); +extern char *game_local_themes_path(void); +extern int cfg_load(void); +extern int cfg_save(void); +extern char *opt_game; + +extern int nosound_sw; +extern int alsa_sw; +extern int fullscreen_sw; +extern int window_sw; + +extern int opt_fs; +extern char *opt_theme; +extern char *curtheme; +extern int game_theme_select(const char *name); + + extern int game_load_theme(const char *path); extern int game_init(const char *game); extern void game_done(void); extern int game_loop(void); -extern int games_lookup(void); + +extern int games_lookup(const char *path); +extern int themes_lookup(const char *path); +extern void game_err_msg(const char *s); +extern int game_error(const char *name); #endif diff --git a/src/sdl-instead/graphics.c b/src/sdl-instead/graphics.c index 7fb0e85..8570f49 100644 --- a/src/sdl-instead/graphics.c +++ b/src/sdl-instead/graphics.c @@ -2,7 +2,8 @@ #include #include #include -#include /* for usleep */ +#include "SDL_rotozoom.h" +#include "input.h" #include "graphics.h" #include "math.h" @@ -352,35 +353,16 @@ img_t gfx_combine(img_t src, img_t dst) return new; } -img_t gfx_load_image(char *filename, int transparent) +img_t gfx_load_image(char *filename) { - SDL_Surface *img, *img2; + SDL_Surface *img; img = IMG_Load(filename); if (!img) { fprintf(stderr, "File not found: '%s'\n", filename); return NULL; } - if (transparent && transparent != 2) { - SDL_SetColorKey(img, SDL_RLEACCEL, img->format->colorkey); - return img; - } - // Create hardware surface - img2 = SDL_CreateRGBSurface(SDL_HWSURFACE | (transparent)?SDL_SRCCOLORKEY:0, - img->w, img->h, 16, 0xF800, 0x7E0, 0x1F, 0); - if (!img2) { - SDL_FreeSurface(img); - fprintf(stderr, "Error creating surface!\n"); - return NULL; - } - - if (transparent) - SDL_SetColorKey(img2, SDL_SRCCOLORKEY | SDL_RLEACCEL, 0xF81F); - - SDL_SetAlpha(img2, 0, 0); - SDL_BlitSurface(img, NULL, img2, NULL); - SDL_FreeSurface(img); - return img2; + return img; } void gfx_draw_bg(img_t p, int x, int y, int width, int height) @@ -458,10 +440,38 @@ void gfx_clear(int x, int y, int w, int h) int gfx_width; int gfx_height; -int gfx_init(int width, int height, int fs) + +int gfx_setmode(int w, int h, int fs) { - gfx_width = width; - gfx_height = height; + gfx_width = w; + gfx_height = h; + + screen = SDL_SetVideoMode(gfx_width, gfx_height, 32, SDL_DOUBLEBUF | SDL_HWSURFACE | ( ( fs ) ? SDL_FULLSCREEN : 0 ) ); + if (screen == NULL) { + fprintf(stderr, "Unable to set %dx%d video: %s\n", w, h, SDL_GetError()); + return -1; + } + + gfx_clear(0, 0, gfx_width, gfx_height); + return 0; +} + +unsigned int timer_counter = 0; + +SDL_TimerID timer_han = NULL; +static SDL_Surface *icon = NULL; +Uint32 counter_fn(Uint32 interval, void *p) +{ + timer_counter ++; + return interval; +} + +int gfx_init(void) +{ + char title[4096]; + + strcpy( title, "INSTEAD SDL - " ); + strcat( title, VERSION ); if (TTF_Init()) { fprintf(stderr, "Can't init TTF subsystem.\n"); @@ -469,21 +479,22 @@ int gfx_init(int width, int height, int fs) } // Initialize SDL - if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO) < 0) { + if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER /*| SDL_INIT_AUDIO*/) < 0) { fprintf(stderr, "Couldn't initialize SDL: %s\n", SDL_GetError()); return -1; } - screen = SDL_SetVideoMode(gfx_width, gfx_height, 32, SDL_DOUBLEBUF | SDL_HWSURFACE | ((fs)?SDL_FULLSCREEN:0)); - SDL_WM_SetCaption("IN S.T.E.A.D SDL - "VERSION, NULL); -// icon = IMG_Load( GAMEDATADIR"icon/lines.png" ); -// if (icon) { -// SDL_WM_SetIcon( icon, NULL ); -// } - if (screen == NULL) { - fprintf(stderr, "Unable to set 800x480 video: %s\n", SDL_GetError()); - return -1; + + SDL_WM_SetCaption( title, title ); + +#ifndef ICON_PATH +#define ICON_PATH "./icon" +#endif + + icon = IMG_Load( ICON_PATH"/sdl_instead.png" ); + if ( icon ) { + SDL_WM_SetIcon( icon, NULL ); } - gfx_clear(0, 0, gfx_width, gfx_height); + timer_han = SDL_AddTimer(100, counter_fn, NULL); return 0; } void gfx_flip(void) @@ -516,339 +527,18 @@ void gfx_update(int x, int y, int w, int h) { void gfx_done(void) { + if (icon) + SDL_FreeSurface(icon); + if (timer_han) + SDL_RemoveTimer(timer_han); + timer_han = NULL; TTF_Quit(); - SDL_Quit(); -} - -/* code from sge */ -#ifndef PI - #define PI 3.1414926535 -#endif - -void _PutPixel24(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color) -{ - Uint8 *pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3; - - /* Gack - slow, but endian correct */ - *(pix+surface->format->Rshift/8) = color>>surface->format->Rshift; - *(pix+surface->format->Gshift/8) = color>>surface->format->Gshift; - *(pix+surface->format->Bshift/8) = color>>surface->format->Bshift; - *(pix+surface->format->Ashift/8) = color>>surface->format->Ashift; -} -void _PutPixel32(SDL_Surface *surface, Sint16 x, Sint16 y, Uint32 color) -{ - *((Uint32 *)surface->pixels + y*surface->pitch/4 + x) = color; -} - -void _PutPixelX(SDL_Surface *dest,Sint16 x,Sint16 y,Uint32 color) -{ - switch ( dest->format->BytesPerPixel ) { - case 1: - *((Uint8 *)dest->pixels + y*dest->pitch + x) = color; - break; - case 2: - *((Uint16 *)dest->pixels + y*dest->pitch/2 + x) = color; - break; - case 3: - _PutPixel24(dest,x,y,color); - break; - case 4: - *((Uint32 *)dest->pixels + y*dest->pitch/4 + x) = color; - break; - } -} - -Uint32 sge_GetPixel(SDL_Surface *surface, Sint16 x, Sint16 y) -{ - if(x<0 || x>=surface->w || y<0 || y>=surface->h) - return 0; - - switch (surface->format->BytesPerPixel) { - case 1: { /* Assuming 8-bpp */ - return *((Uint8 *)surface->pixels + y*surface->pitch + x); - } - break; - - case 2: { /* Probably 15-bpp or 16-bpp */ - return *((Uint16 *)surface->pixels + y*surface->pitch/2 + x); - } - break; - - case 3: { /* Slow 24-bpp mode, usually not used */ - Uint8 *pix; - int shift; - Uint32 color=0; - - pix = (Uint8 *)surface->pixels + y * surface->pitch + x*3; - shift = surface->format->Rshift; - color = *(pix+shift/8)<format->Gshift; - color|= *(pix+shift/8)<format->Bshift; - color|= *(pix+shift/8)<format->Ashift; - color|= *(pix+shift/8)<pixels + y*surface->pitch/4 + x); - } - break; - } - return 0; -} - -/* -* Macro to get clipping -*/ -#if SDL_VERSIONNUM(SDL_MAJOR_VERSION, SDL_MINOR_VERSION, SDL_PATCHLEVEL) >= \ - SDL_VERSIONNUM(1, 1, 5) - #define sge_clip_xmin(pnt) pnt->clip_rect.x - #define sge_clip_xmax(pnt) pnt->clip_rect.x + pnt->clip_rect.w-1 - #define sge_clip_ymin(pnt) pnt->clip_rect.y - #define sge_clip_ymax(pnt) pnt->clip_rect.y + pnt->clip_rect.h-1 -#else - #define sge_clip_xmin(pnt) pnt->clip_minx - #define sge_clip_xmax(pnt) pnt->clip_maxx - #define sge_clip_ymin(pnt) pnt->clip_miny - #define sge_clip_ymax(pnt) pnt->clip_maxy -#endif - -#define SWAP(x,y,temp) temp=x;x=y;y=temp -//================================================================================== -// Helper function to sge_transform() -// Returns the bounding box -//================================================================================== -void _calcRect(SDL_Surface *src, SDL_Surface *dst, float theta, float xscale, float yscale, Uint16 px, Uint16 py, Uint16 qx, Uint16 qy, Sint16 *xmin, Sint16 *ymin, Sint16 *xmax, Sint16 *ymax) -{ - int i; - Sint16 x, y, rx, ry; - Sint32 istx, ictx, isty, icty; - // Clip to src surface - Sint16 sxmin = sge_clip_xmin(src); - Sint16 sxmax = sge_clip_xmax(src); - Sint16 symin = sge_clip_ymin(src); - Sint16 symax = sge_clip_ymax(src); - Sint16 sx[]={sxmin, sxmax, sxmin, sxmax}; - Sint16 sy[]={symin, symax, symax, symin}; - - // We don't really need fixed-point here - // but why not? - istx = (Sint32)((sin(theta)*xscale) * 8192.0); /* Inverse transform */ - ictx = (Sint32)((cos(theta)*xscale) * 8192.2); - isty = (Sint32)((sin(theta)*yscale) * 8192.0); - icty = (Sint32)((cos(theta)*yscale) * 8192.2); - - //Calculate the four corner points - for(i=0; i<4; i++){ - rx = sx[i] - px; - ry = sy[i] - py; - - x = (Sint16)(((ictx*rx - isty*ry) >> 13) + qx); - y = (Sint16)(((icty*ry + istx*rx) >> 13) + qy); - - - if(i==0){ - *xmax = *xmin = x; - *ymax = *ymin = y; - }else{ - if(x>*xmax) - *xmax=x; - else if(x<*xmin) - *xmin=x; - - if(y>*ymax) - *ymax=y; - else if(y<*ymin) - *ymin=y; - } - } - - //Better safe than sorry... - *xmin -= 1; - *ymin -= 1; - *xmax += 1; - *ymax += 1; - - //Clip to dst surface - if( !dst ) - return; - if( *xmin < sge_clip_xmin(dst) ) - *xmin = sge_clip_xmin(dst); - if( *xmax > sge_clip_xmax(dst) ) - *xmax = sge_clip_xmax(dst); - if( *ymin < sge_clip_ymin(dst) ) - *ymin = sge_clip_ymin(dst); - if( *ymax > sge_clip_ymax(dst) ) - *ymax = sge_clip_ymax(dst); -} - -#define TRANSFORM_GENERIC_AA \ - Uint8 R, G, B, A, R1, G1, B1, A1=0, R2, G2, B2, A2=0, R3, G3, B3, A3=0, R4, G4, B4, A4=0; \ - Sint32 wx, wy, p1, p2, p3, p4;\ -\ - Sint32 const one = 2048; /* 1 in Fixed-point */ \ - Sint32 const two = 2*2048; /* 2 in Fixed-point */ \ -\ - for (y=ymin; y> 13); /* Convert from fixed-point */ \ - ry=(Sint16)(sy >> 13); \ -\ - /* Make sure the source pixel is actually in the source image. */ \ - if( (rx>=sxmin) && (rx+1<=sxmax) && (ry>=symin) && (ry+1<=symax) ){ \ - wx = (sx & 0x00001FFF) >> 2; /* (float(x) - int(x)) / 4 */ \ - wy = (sy & 0x00001FFF) >> 2;\ -\ - p4 = wx+wy;\ - p3 = one-wx+wy;\ - p2 = wx+one-wy;\ - p1 = two-wx-wy;\ -\ - SDL_GetRGBA(sge_GetPixel(src,rx, ry), src->format, &R1, &G1, &B1, &A1);\ - SDL_GetRGBA(sge_GetPixel(src,rx+1,ry), src->format, &R2, &G2, &B2, &A2);\ - SDL_GetRGBA(sge_GetPixel(src,rx, ry+1), src->format, &R3, &G3, &B3, &A3);\ - SDL_GetRGBA(sge_GetPixel(src,rx+1,ry+1), src->format, &R4, &G4, &B4, &A4);\ -\ - /* Calculate the average */\ - R = (p1*R1 + p2*R2 + p3*R3 + p4*R4)>>13;\ - G = (p1*G1 + p2*G2 + p3*G3 + p4*G4)>>13;\ - B = (p1*B1 + p2*B2 + p3*B3 + p4*B4)>>13;\ - A = (p1*A1 + p2*A2 + p3*A3 + p4*A4)>>13;\ -\ - _PutPixelX(dst,x,y,SDL_MapRGBA(dst->format, R, G, B, A)); \ - \ - } \ - sx += ctx; /* Incremental transformations */ \ - sy -= sty; \ - } \ - } - -Uint8 _sge_lock=1; - -SDL_Rect sge_transformAA(SDL_Surface *src, SDL_Surface *dst, float angle, float xscale, float yscale ,Uint16 px, Uint16 py, Uint16 qx, Uint16 qy, Uint8 flags) -{ - Sint32 dy, sx, sy; - Sint16 x, y, rx, ry; - SDL_Rect r; - r.x = r.y = r.w = r.h = 0; - - float theta = (float)(angle*PI/180.0); /* Convert to radians. */ - - - // Here we use 18.13 fixed point integer math - // Sint32 should have 31 usable bits and one for sign - // 2^13 = 8192 - - // Check scales - Sint32 maxint = (Sint32)(pow(2, sizeof(Sint32)*8 - 1 - 13)); // 2^(31-13) - - if( xscale == 0 || yscale == 0) - return r; - - if( 8192.0/xscale > maxint ) - xscale = (float)(8192.0/maxint); - else if( 8192.0/xscale < -maxint ) - xscale = (float)(-8192.0/maxint); - - if( 8192.0/yscale > maxint ) - yscale = (float)(8192.0/maxint); - else if( 8192.0/yscale < -maxint ) - yscale = (float)(-8192.0/maxint); - - - // Fixed-point equivalents - Sint32 const stx = (Sint32)((sin(theta)/xscale) * 8192.0); - Sint32 const ctx = (Sint32)((cos(theta)/xscale) * 8192.0); - Sint32 const sty = (Sint32)((sin(theta)/yscale) * 8192.0); - Sint32 const cty = (Sint32)((cos(theta)/yscale) * 8192.0); - Sint32 const mx = (Sint32)(px*8192.0); - Sint32 const my = (Sint32)(py*8192.0); - - // Compute a bounding rectangle - Sint16 xmin=0, xmax=dst->w, ymin=0, ymax=dst->h; - _calcRect(src, dst, theta, xscale, yscale, px, py, qx, qy, &xmin,&ymin, &xmax,&ymax); - - // Clip to src surface - Sint16 sxmin = sge_clip_xmin(src); - Sint16 sxmax = sge_clip_xmax(src); - Sint16 symin = sge_clip_ymin(src); - Sint16 symax = sge_clip_ymax(src); - - // Some terms in the transform are constant - Sint32 const dx = xmin - qx; - Sint32 const ctdx = ctx*dx; - Sint32 const stdx = sty*dx; - - // Lock surfaces... hopfully less than two needs locking! - if ( SDL_MUSTLOCK(src) && _sge_lock ) - if ( SDL_LockSurface(src) < 0 ) - return r; - if ( SDL_MUSTLOCK(dst) && _sge_lock ){ - if ( SDL_LockSurface(dst) < 0 ){ - if ( SDL_MUSTLOCK(src) && _sge_lock ) - SDL_UnlockSurface(src); - return r; - } - } - - - TRANSFORM_GENERIC_AA - - - // Unlock surfaces - if ( SDL_MUSTLOCK(src) && _sge_lock ) - SDL_UnlockSurface(src); - if ( SDL_MUSTLOCK(dst) && _sge_lock ) - SDL_UnlockSurface(dst); - - //Return the bounding rectangle - r.x=xmin; r.y=ymin; r.w=xmax-xmin; r.h=ymax-ymin; - return r; -} - - -SDL_Rect sge_transform(SDL_Surface *src, SDL_Surface *dst, float angle, float xscale, float yscale, Uint16 px, Uint16 py, Uint16 qx, Uint16 qy, Uint8 flags) -{ - return sge_transformAA(src, dst, angle, xscale, yscale, px, py, qx, qy, flags); -} - -SDL_Surface *sge_transform_surface(SDL_Surface *src, Uint32 bcol, float angle, float xscale, float yscale, Uint8 flags) -{ - float theta = (float)(angle*PI/180.0); /* Convert to radians. */ - - // Compute a bounding rectangle - Sint16 xmin=0, xmax=0, ymin=0, ymax=0; - _calcRect(src, NULL, theta, xscale, yscale, 0, 0, 0, 0, &xmin,&ymin, &xmax,&ymax); - - Sint16 w = xmax-xmin+1; - Sint16 h = ymax-ymin+1; - - Sint16 qx = -xmin; - Sint16 qy = -ymin; - - SDL_Surface *dest; - dest = SDL_CreateRGBSurface(SDL_SWSURFACE, w, h, src->format->BitsPerPixel, src->format->Rmask, src->format->Gmask, src->format->Bmask, src->format->Amask); - if(!dest) - return NULL; - -// sge_ClearSurface(dest,bcol); //Set background color - - sge_transform(src, dest, angle, xscale, yscale, 0, 0, qx, qy, flags); - - return dest; + SDL_QuitSubSystem(SDL_INIT_VIDEO | SDL_INIT_TIMER); } img_t gfx_scale(img_t src, float xscale, float yscale) { - return (img_t)sge_transform_surface((SDL_Surface *)src, 0, 0, xscale, yscale, 0); + return (img_t)zoomSurface((SDL_Surface *)src, xscale, yscale, 1); } fnt_t fnt_load(const char *fname, int size) @@ -897,6 +587,7 @@ struct word { struct word *next; /* in line */ struct line *line; struct xref *xref; + SDL_Surface *prerend; }; struct word *word_new(const char *str) @@ -914,6 +605,7 @@ struct word *word_new(const char *str) w->style = 0; w->img = NULL; w->unbrake = 0; + w->prerend = NULL; return w; } @@ -925,6 +617,9 @@ void word_free(struct word *word) // gfx_free_image(word->img); if (word->word) free(word->word); + if (word->prerend) + SDL_FreeSurface(word->prerend); + word->prerend = NULL; free(word); } @@ -1172,8 +867,8 @@ void layout_add_line(struct layout *layout, struct line *line) { struct line *l = layout->lines; line->layout = layout; - if (line->w > layout->w) - layout->w = line->w; +// if (line->w > layout->w) +// layout->w = line->w; if (!l) { layout->lines = line; line->prev = NULL; @@ -1186,6 +881,16 @@ void layout_add_line(struct layout *layout, struct line *line) return; } +struct image *_layout_lookup_image(struct layout *layout, const char *name) +{ + struct image *g = layout->images; + for (g = layout->images; g; g = g->next) { + if (!strcmp(g->name, name)) + return g; + } + return NULL; +} + void layout_add_image(struct layout *layout, struct image *image) { struct image *g = layout->images; @@ -1201,12 +906,8 @@ void layout_add_image(struct layout *layout, struct image *image) img_t layout_lookup_image(struct layout *layout, char *name) { - struct image *g = layout->images; - for (g = layout->images; g; g = g->next) { - if (!strcmp(g->name, name)) - return g->image; - } - return NULL; + struct image *g = _layout_lookup_image(layout, name); + return (g)?g->image: NULL; } void layout_add_xref(struct layout *layout, struct xref *xref) @@ -1257,6 +958,11 @@ int txt_layout_add_img(layout_t lay, const char *name, img_t img) { struct layout *layout = (struct layout *)lay; struct image *image; + image = _layout_lookup_image(layout, name); + if (image) { /* overwrite */ + image->image = img; + return 0; + } image = image_new(name, img); if (!image) return -1; @@ -1573,7 +1279,6 @@ void xref_update(xref_t pxref, int x, int y, clear_fn clear) gfx_noclip(); } - void txt_layout_draw_ex(layout_t lay, struct line *line, int x, int y, int off, int height, clear_fn clear) { struct layout *layout = (struct layout*)lay; @@ -1618,11 +1323,15 @@ void txt_layout_draw_ex(layout_t lay, struct line *line, int x, int y, int off, col = acol; else col = lcol; - s = TTF_RenderUTF8_Blended((TTF_Font *)(layout->fn), - word->word, col); + s = word->prerend; + if (!s) { + s = TTF_RenderUTF8_Blended((TTF_Font *)(layout->fn), + word->word, col); + word->prerend = s; + } yy = (line->h - TTF_FontHeight((TTF_Font *)(layout->fn))) / 2; // TODO gfx_draw(s, x + word->x, y + line->y + yy); - SDL_FreeSurface(s); +// SDL_FreeSurface(s); } } // gfx_noclip(); @@ -1693,33 +1402,67 @@ void txt_box_resize(textbox_t tbox, int w, int h) void txt_box_size(textbox_t tbox, int *w, int *h) { struct textbox *box = (struct textbox *)tbox; + if (!tbox) + return; if (w) *w = box->w; if (h) *h = box->h; } -void txt_box_scroll(textbox_t tbox, int disp) +void txt_box_scroll_next(textbox_t tbox, int disp) { - int ld; + int off; struct textbox *box = (struct textbox *)tbox; struct line *line = box->line; if (!line) return; - ld = box->off - line->y + disp; - while (line && ld > line->h) { - ld -= line->h; + + if (box->lay->h - box->off < box->h) + return; + + off = box->lay->h - box->off - box->h; + if (disp > off) + disp = off; + + off = box->off - line->y; /* offset from cur line */ + off += disp; /* needed offset */ + while (line->next && off > line->h) { + off -= line->h; line = line->next; } - if (line) { - box->line = line; - box->off = line->y + ld; + box->line = line; + box->off = line->y + off; +} + +void txt_box_scroll_prev(textbox_t tbox, int disp) +{ + int off; + struct textbox *box = (struct textbox *)tbox; + struct line *line = box->line; + if (!line) + return; + off = box->off - line->y; /* offset from cur line */ + off -= disp; /* offset from current line */ + + while (line->prev && off < 0) { + line = line->prev; + off += line->h; } -// line = line->next; -// if (line) { -// box->off += (line->y - box->off); -// box->line = line; -// } + + box->line = line; + box->off = line->y + off; + + if (box->off <0) + box->off = 0; +} + +void txt_box_scroll(textbox_t tbox, int disp) +{ + if (disp >0) + return txt_box_scroll_next(tbox, disp); + else if (disp <0) + return txt_box_scroll_prev(tbox, -disp); } void txt_box_next_line(textbox_t tbox) @@ -1885,6 +1628,9 @@ img_t get_img(struct layout *layout, char *p) { int len; img_t img; + if (!p) + return NULL; + if (p[0] != '<' || p[1] != 'g' || p[2] != ':') return NULL; p += 3; @@ -1964,7 +1710,7 @@ int get_unbrakable_len(struct layout *layout, const char *ptr) int w = 0; int ww = 0; char *p, *eptr; - while (*ptr) { + while (ptr && *ptr) { int sp, sp2 = 0; while(get_token(ptr, &eptr, NULL, &sp)) { if (sp) @@ -1976,7 +1722,8 @@ int get_unbrakable_len(struct layout *layout, const char *ptr) p = get_word(ptr, &eptr, &sp); if (!p) return w; - if (sp) { + + if (sp || !*p) { free(p); return w; } @@ -2002,7 +1749,7 @@ void _txt_layout_add(layout_t lay, char *txt) int w, h, nl = 0; int spw; img_t img = NULL; - if (!layout) + if (!layout || !layout->fn) return; saved_style = TTF_GetFontStyle((TTF_Font *)(layout->fn)); @@ -2041,18 +1788,20 @@ void _txt_layout_add(layout_t lay, char *txt) sp = 1; } else p = get_word(ptr, &eptr, &sp); + + if (!p) + break; + addlen = get_unbrakable_len(layout, eptr); img = get_img(layout, p); if (img) { w = gfx_img_w(img); h = gfx_img_h(img); } else { -// fprintf(stderr,"AAA:'%s'\n", p); TTF_SizeUTF8((TTF_Font *)(layout->fn), p, &w, &h); } nl = !*p; - if ((line->num && (line->w + ((line->w)?spw:0) + w + addlen) >= layout->w) || nl) { -// if ((line->num && (line->w + ((sp)?spw:0) + w) >= layout->w) || nl) { + if ((line->num && (line->w + ((sp && line->w)?spw:0) + w + addlen) > layout->w) || nl) { struct line *ol = line; if ((layout->h) && (line->y + line->h) >= layout->h) break; @@ -2253,30 +2002,36 @@ void gfx_cursor(int *xp, int *yp, int *w, int *h) #define ALPHA_STEPS 5 volatile int step_nr = -1; -SDL_mutex *sem; -static Uint32 update(Uint32 interval, void *aux) +static void update_gfx(void *aux) { img_t img = (img_t) aux; + if (step_nr == -1) + return; gfx_set_alpha(img, (255 * (step_nr + 1)) / ALPHA_STEPS); gfx_draw(img, 0, 0); gfx_flip(); step_nr ++; if (step_nr == ALPHA_STEPS) { step_nr = -1; - return 0; } +} + +static Uint32 update(Uint32 interval, void *aux) +{ + push_user_event(update_gfx, aux); return interval; } +extern void nsleep(int delay); + void gfx_change_screen(img_t src) { - sem = SDL_CreateMutex(); + struct inp_event ev; + memset(&ev, 0, sizeof(ev)); + SDL_TimerID han; step_nr = 0; - SDL_AddTimer(60, update, src); - while (step_nr != -1) { - usleep(100); - } - SDL_DestroyMutex(sem); + han = SDL_AddTimer(60, update, src); + while (input(&ev, 1) >=0 && step_nr != -1); /* just wait for change */ + SDL_RemoveTimer(han); } - diff --git a/src/sdl-instead/graphics.h b/src/sdl-instead/graphics.h index 67d4701..dffa2a0 100644 --- a/src/sdl-instead/graphics.h +++ b/src/sdl-instead/graphics.h @@ -36,7 +36,8 @@ extern void gfx_noclip(void); extern void gfx_clip(int x, int y, int w, int h); extern int gfx_width; extern int gfx_height; -extern int gfx_init(int w, int h, int fs); +extern int gfx_init(void); +extern int gfx_setmode(int w, int h, int fs); extern void gfx_update(int x, int y, int w, int h); extern void gfx_done(void); extern void gfx_clear(int x, int y, int w, int h); @@ -44,7 +45,7 @@ extern void gfx_draw(img_t pixmap, int x, int y); extern void gfx_draw_wh(img_t p, int x, int y, int w, int h); extern img_t gfx_grab_screen(int x, int y, int w, int h); extern img_t gfx_new(int w, int h); -extern img_t gfx_load_image(char *filename, int transparent); +extern img_t gfx_load_image(char *filename); extern void gfx_free_image(img_t pixmap); extern int gfx_img_w(img_t pixmap); extern int gfx_img_h(img_t pixmap); @@ -86,6 +87,7 @@ extern void txt_box_next(textbox_t tbox); extern void txt_box_prev(textbox_t tbox); extern void txt_box_next_line(textbox_t tbox); extern void txt_box_prev_line(textbox_t tbox); +extern void txt_box_scroll(textbox_t tbox, int disp); extern xref_t txt_box_xref(textbox_t tbox, int x, int y); extern int txt_box_off(textbox_t tbox); extern void txt_box_size(textbox_t tbox, int *w, int *h); diff --git a/src/sdl-instead/gui.h b/src/sdl-instead/gui.h index 6184beb..24f36d2 100644 --- a/src/sdl-instead/gui.h +++ b/src/sdl-instead/gui.h @@ -13,15 +13,18 @@ iface.xref = function(self, str, obj)\n\ if not o then\n\ o = ref(inv():srch(obj));\n\ end\n\ - if not o or not o.id then\n\ + if not isObject(o) or not o.id then\n\ return str;\n\ --- error ('Xref to nowhere:'..str);\n\ end\n\ - return cat('',str,'');\n\ + return cat('',str,'');\n\ end;\n\ iface.title = function(self, str)\n\ return nil\n\ end;\n\ +iface.em = function(self, str)\n\ + if str == nil then return nil; end; \n\ + return cat('',str,'');\n\ +end;\n\ \n\ iface.inv = function(self, str)\n\ if str then\n\ diff --git a/src/sdl-instead/input.c b/src/sdl-instead/input.c index fe58e77..9dff92b 100644 --- a/src/sdl-instead/input.c +++ b/src/sdl-instead/input.c @@ -1,18 +1,56 @@ #include #include "input.h" +#include +#include + +void push_user_event(void (*p) (void*), void *data) +{ + SDL_Event event; + SDL_UserEvent uevent; + uevent.type = SDL_USEREVENT; + uevent.code = 0; + event.type = SDL_USEREVENT; + uevent.data1 = p; + uevent.data2 = data; + event.user = uevent; + SDL_PushEvent(&event); +} + +int input_init(void) +{ + SDL_EnableKeyRepeat(500, 30); + return 0; +} + +void input_clear(void) +{ + SDL_Event event; + while (SDL_PollEvent(&event)); + return; +} + int input(struct inp_event *inp, int wait) { int rc; SDL_Event event; - if (wait) + SDL_Event peek; + memset(&event, 0, sizeof(event)); + memset(&peek, 0, sizeof(peek)); + if (wait) { rc = SDL_WaitEvent(&event); - else + } else rc = SDL_PollEvent(&event); if (!rc) return 0; inp->sym = NULL; inp->type = 0; + inp->count = 1; switch(event.type){ + case SDL_USEREVENT: { + void (*p) (void*) = event.user.data1; + p(event.user.data2); + return AGAIN; + } case SDL_QUIT: return -1; case SDL_KEYDOWN: //A key has been pressed @@ -29,6 +67,10 @@ int input(struct inp_event *inp, int wait) inp->type = MOUSE_MOTION; inp->x = event.button.x; inp->y = event.button.y; + while (SDL_PeepEvents(&peek, 1, SDL_GETEVENT, SDL_EVENTMASK (SDL_MOUSEMOTION)) > 0) { + inp->x = peek.button.x; + inp->y = peek.button.y; + } break; case SDL_MOUSEBUTTONUP: inp->type = MOUSE_UP; @@ -47,6 +89,16 @@ int input(struct inp_event *inp, int wait) inp->type = MOUSE_WHEEL_UP; else if (event.button.button == 5) inp->type = MOUSE_WHEEL_DOWN; + + while (SDL_PeepEvents(&peek, 1, SDL_GETEVENT, SDL_EVENTMASK (SDL_MOUSEBUTTONDOWN)) > 0) { + if (!((event.button.button == 4 && + inp->type == MOUSE_WHEEL_UP) || + (event.button.button == 5 && + inp->type == MOUSE_WHEEL_DOWN))) + break; + inp->count ++; + } + break; } return 1; diff --git a/src/sdl-instead/input.h b/src/sdl-instead/input.h index 4388380..28b42fc 100644 --- a/src/sdl-instead/input.h +++ b/src/sdl-instead/input.h @@ -8,15 +8,21 @@ #define MOUSE_WHEEL_UP 5 #define MOUSE_WHEEL_DOWN 6 #define MOUSE_MOTION 7 +#define USER_EVENT 8 +#define AGAIN 2 struct inp_event { int type; int code; char *sym; int x; int y; + int count; }; int input(struct inp_event *ev, int wait); +int input_init(void); +void input_clear(void); +void push_user_event(void (*p) (void*), void *data); #endif diff --git a/src/sdl-instead/instead.c b/src/sdl-instead/instead.c index 8a37615..e65346c 100644 --- a/src/sdl-instead/instead.c +++ b/src/sdl-instead/instead.c @@ -1,3 +1,7 @@ +#ifndef STEAD_PATH +#define STEAD_PATH "./stead" +#endif + #include #include #include @@ -5,39 +9,99 @@ #include #include #include -#include +// #include #include #include "gui.h" -#ifdef HAVE_ICONV +#ifdef _HAVE_ICONV #include #endif +#include "game.h" /* the Lua interpreter */ char *fromgame(const char *s); char *togame(const char *s); lua_State *L = NULL; +static int report (lua_State *L, int status) +{ + if (status && !lua_isnil(L, -1)) { + const char *msg = lua_tostring(L, -1); + if (msg == NULL) + msg = "(error object is not a string)"; + fprintf(stderr,"Error: %s\n", msg); + game_err_msg(msg); + lua_pop(L, 1); + status = -1; + } + return status; +} + +static int traceback (lua_State *L) +{ + lua_getfield(L, LUA_GLOBALSINDEX, "debug"); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return 1; + } + lua_getfield(L, -1, "traceback"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 2); + return 1; + } + lua_pushvalue(L, 1); /* pass error message */ + lua_pushinteger(L, 2); /* skip this function and traceback */ + lua_call(L, 2, 1); /* call debug.traceback */ + return 1; +} + +extern int debug_sw; + +static int docall (lua_State *L) +{ + int status; + int base = 0; + if (debug_sw) { + base = lua_gettop(L); /* function index */ + lua_pushcfunction(L, traceback); /* push traceback function */ + lua_insert(L, base); /* put it under chunk and args */ + } + status = lua_pcall(L, 0, LUA_MULTRET, base); + if (debug_sw) + lua_remove(L, base); /* remove traceback function */ + /* force a complete garbage collection in case of errors */ + if (status != 0) + lua_gc(L, LUA_GCCOLLECT, 0); + return status; +} + +static int dofile (lua_State *L, const char *name) { + int status = luaL_loadfile(L, name) || docall(L); + return report(L, status); +} + +static int dostring (lua_State *L, const char *s) { + int status = luaL_loadstring(L, s) || docall(L); + return report(L, status); +} + char *getstring(char *cmd) { char *s; if (!L) return NULL; - if (luaL_dostring(L, cmd)) { - fprintf(stderr,"Error: %s\n", lua_tostring(L, -1)); - exit(1); - } + if (dostring(L, cmd)) + return NULL; s = (char*)lua_tostring(L, -1); if (s) s = fromgame(s); return s; } + char *instead_eval(char *s) { char *p; -// s = togame(s); p = getstring(s); -// free(s); return p; } @@ -62,20 +126,19 @@ int luacall(char *cmd) int rc; if (!L) return -1; - if (luaL_dostring(L, cmd)) { - fprintf(stderr,"Error: %s\n", lua_tostring(L, -1)); - exit(1); + if (dostring(L, cmd)) { + return -1; } rc = lua_tonumber(L, -1); return rc; } -#ifdef HAVE_ICONV +#ifdef _HAVE_ICONV static char *curcp = "UTF-8"; static char *fromcp = NULL; #endif -#ifdef HAVE_ICONV +#ifdef _HAVE_ICONV #define CHAR_MAX_LEN 4 static char *decode(iconv_t hiconv, const char *s) { @@ -162,11 +225,10 @@ char *togame(const char *s) int instead_load(char *game) { - if (luaL_loadfile(L, game) || lua_pcall(L, 0, 0, 0)) { - fprintf(stderr,"Error:%s\n", lua_tostring(L, -1)); + if (dofile(L, game)) { return -1; } -#ifdef HAVE_ICONV +#ifdef _HAVE_ICONV if (fromcp) free(fromcp); fromcp = getstring("return game.codepage;"); @@ -181,34 +243,27 @@ int instead_init(void) /* initialize Lua */ L = lua_open(); luaL_openlibs(L); - if (luaL_loadfile(L,STEAD_PATH"stead.lua") || lua_pcall(L, 0, 0, 0)) { - fprintf(stderr,"Error:%s\n", lua_tostring(L, -1)); + if (dofile(L,STEAD_PATH"/stead.lua")) { return -1; } if (luacall(instead_gui_lua)) { - fprintf(stderr,"Error while registering lua gui functions\n"); - return -1; - - } -#if 0 - if (luaL_loadfile(L,"gui.lua") || lua_pcall(L, 0, 0, 0)) { - fprintf(stderr,"Error:%s\n", lua_tostring(L, -1)); return -1; } -#endif /* cleanup Lua */ return 0; } void instead_done(void) { -#ifdef HAVE_ICONV +#ifdef _HAVE_ICONV if (fromcp) free(fromcp); #endif if (L) lua_close(L); L = NULL; +#ifdef _HAVE_ICONV fromcp = NULL; +#endif } diff --git a/src/sdl-instead/instead.h b/src/sdl-instead/instead.h index 8b62135..de28cac 100644 --- a/src/sdl-instead/instead.h +++ b/src/sdl-instead/instead.h @@ -6,5 +6,6 @@ extern int instead_load(char *game); extern void instead_done(void); extern char *instead_cmd(char *s); extern char *instead_eval(char *s); +char *fromgame(const char *s); #endif diff --git a/src/sdl-instead/main.c b/src/sdl-instead/main.c index 9b29b41..f7a92d8 100644 --- a/src/sdl-instead/main.c +++ b/src/sdl-instead/main.c @@ -1,20 +1,65 @@ +#include #include #include "graphics.h" #include "game.h" #include #include +#include + +extern int debug_init(void); +extern void debug_done(void); + +int debug_sw = 0; int main(int argc, char **argv) { - if (games_lookup()) { - fprintf(stderr, "No games found.\n"); - // exit(1); + int i; + for (i = 1; i < argc; i++) { + if (!strcmp(argv[i],"-alsa")) + alsa_sw = 1; + else if (!strcmp(argv[i], "-nosound")) + nosound_sw = 1; + else if (!strcmp(argv[i], "-fullscreen")) + fullscreen_sw = 1; + else if (!strcmp(argv[i], "-window")) + window_sw = 1; + else if (!strcmp(argv[i], "-debug")) + debug_sw = 1; + } + + if (debug_sw) { + debug_init(); } - if (game_init(NULL)) { - exit(1); + if (window_sw) + opt_fs = 0; + if (fullscreen_sw) + opt_fs = 1; + + if (games_lookup(GAMES_PATH)) { + fprintf(stderr, "No games found.\n"); + } + + themes_lookup(THEMES_PATH); + themes_lookup(game_local_themes_path()); + + games_lookup(game_local_games_path()); + + cfg_load(); + + if (opt_theme) + game_theme_select(opt_theme); + if (!curtheme) + game_theme_select(DEFAULT_THEME); + + if (game_init(opt_game)) { + game_error(opt_game); } game_loop(); + cfg_save(); game_done(); + if (debug_sw) + debug_done(); return 0; } + diff --git a/src/sdl-instead/menu-en.h b/src/sdl-instead/menu-en.h new file mode 100644 index 0000000..ce8aa19 --- /dev/null +++ b/src/sdl-instead/menu-en.h @@ -0,0 +1,103 @@ +#define UNKNOWN_ERROR "Unknown error." +#define ERROR_MENU "Error while loading game:\n\ +'%s'\n\ +\n\ +Ok" + +#define WARNING_MENU "Error while processing game:\n\ +'%s'\n\ +\n\ +Ok" + +#define SAVE_SLOT_EMPTY "empty" +#define SELECT_LOAD_MENU "Load game\n\n" +#define AUTOSAVE_SLOT "Autosave" +#define BROKEN_SLOT "error" +#define SELECT_SAVE_MENU "Save game\n\n" + +#define MAIN_MENU \ +"Resume game\n\ +Select game\n\ +Select theme\n\ +Restart game\n\ +Load game\n\ +Save game\n\ +About\n\ +Settings\n\ +Quit" + +#define ABOUT_MENU \ +"Written by Peter Kosyh '2009\n\ +Ported to Windows by Ilja Ryndin\n\ +\n\ +Homepage:\n\ +http://instead.googlecode.com\n\ +\n\ +Back" + +#define BACK_MENU "Back" +#define ON "on" +#define OFF "off" + +#define SELECT_GAME_MENU "Select game to play\n\n" +#define SELECT_THEME_MENU "Select theme\n\n" + +#define SETTINGS_MENU \ +"Volume\n\ +<<< %d%% >>>\n\ +\n\ +Quality\n\ +<< %dHz >>\n\ +\n\ +Music: %s\n\ +Click sound: %s\n\ +\n\ +Full Screen: %s\n\ +Font scaling: <<%d>>\n\ +Refs highlighting: %s\n\ +Motion mode: %s\n\ +Mouse filter: %s\n\ +\n\ +Custom game themes: %s\n\ +Аutosave: %s\n\ +\n\ +Apply" + +#define OWN_THEME_MENU \ +"Warning!!!\n\ +\n\ +Current game have custom theme.\n\ +So, changes will be ignored.\n\ +\n\ +You can disable custom themes\n\ +in settings menu.\n\ +\n\ +Ok" + +#define CUSTOM_THEME_MENU \ +"Warning!!!\n\ +\n\ +This game have custom theme, but custom theme setting is disabled. \ +Game may look differ than author's design.\n\ +\n\ +You can enable custom theme support in settings menu.\n\ +\n\ +Ok" + +#define QUIT_MENU \ +"Really quit?\n\ +\n\ +Yes | No" + +#define SAVED_MENU \ +"Current Game saved!\n\ +\n\ +Ok" + +#define NOGAMES_MENU \ +"No games found. \n\ +Please, put any game in the this directory:\n'%s'" + +#define NOTHEMES_MENU \ +"No themes found.\n\ +Please, write any theme in this directory:\n'%s'" diff --git a/src/sdl-instead/menu.h b/src/sdl-instead/menu.h new file mode 100644 index 0000000..3c56e12 --- /dev/null +++ b/src/sdl-instead/menu.h @@ -0,0 +1,105 @@ +#define UNKNOWN_ERROR "Неизвестная ошибка." +#define ERROR_MENU "Во время инициализации игры произошла ошибка:\n\ +'%s'\n\ +\n\ +Да" + +#define WARNING_MENU "Во время работы игры произошла ошибка:\n\ +'%s'\n\ +\n\ +Да" + +#define SAVE_SLOT_EMPTY "пусто" +#define SELECT_LOAD_MENU "Загрузите игру\n\n" +#define AUTOSAVE_SLOT "Автосохранение" +#define BROKEN_SLOT "ошибка" +#define SELECT_SAVE_MENU "Сохраните игру\n\n" + +#define MAIN_MENU \ +"Вернуться в игру\n\ +Выбор игры\n\ +Выбор темы\n\ +Начать заново\n\ +Загрузить игру\n\ +Сохранить игру\n\ +Информация\n\ +Настройки\n\ +Выход" + +#define ABOUT_MENU \ +"INSTEAD SDL - "VERSION"\n\ +\n\ +Интерпретатор простых\n\ +текстовых приключений.\n\ +Косых П.А. '2009\n\ +\n\ +Версия для Windows\n\ +Рындин И.В. '2009\n\ +\n\ +Сайт проекта:\n\ +http://instead.googlecode.com\n\ +\n\ +Назад" + +#define BACK_MENU "Назад" +#define ON "Да" +#define OFF "Нет" + +#define SETTINGS_MENU \ +"Громкость\n\ +<<< %d%% >>>\n\ +\n\ +Качество звука\n<< %dГц >>\n\ +\n\ +Музыка: %s\n\ +Звук щелчка: %s\n\ +\n\ +Полный экран: %s\n\ +Масштаб шрифта: <<%d>>\n\ +Подсветка ссылок: %s\n\ +Режим прокрутки: %s\n\ +Фильтр мышки: %s\n\ +\n\ +Собственные темы игр: %s\n\ +Автосохранение: %s\n\ +\n\ +Применить" + +#define CUSTOM_THEME_MENU \ +"Внимание!!!\n\ +\n\ +Игра содержит собственную тему, но поддержка собственных тем отключена в настройках. \ +Игра может выглядеть не так, как задумывал ее автор.\n\ +\n\ +Вы можете разрешить возможность переопределения тем в настройках.\n\ +\n\ +Да" + +#define OWN_THEME_MENU \ +"Внимание!!!\n\ +\n\ +Выбранная игра переопределяет тему. Изменения не вступят в силу.\n\ +\n\ +Вы можете запретить возможность переопределения тем в настройках.\n\ +\n\ +Да" + +#define QUIT_MENU \ +"На самом деле выйти?\n\ +\n\ +Да | Нет" + +#define SELECT_GAME_MENU "Выбор игры\n\n" +#define SELECT_THEME_MENU "Выбор темы\n\n" +#define SAVED_MENU \ +"Игра сохранена!\n\ +\n\ +Да" + +#define NOGAMES_MENU \ +"Не найдена ни одна игра.\n\ +Пожалуйста, скопируйте хотя бы одну игру в каталог:\n'%s'" + +#define NOTHEMES_MENU \ +"Не найдена ни одна тема.\n\ +Пожалуйста, скопируйте хотя бы одну тему в каталог:\n'%s'" diff --git a/src/sdl-instead/resources.rc b/src/sdl-instead/resources.rc new file mode 100644 index 0000000..2ec878c --- /dev/null +++ b/src/sdl-instead/resources.rc @@ -0,0 +1 @@ +IDI_ICON1 ICON DISCARDABLE "../../icon/sdl_instead.ico" diff --git a/src/sdl-instead/sound.c b/src/sdl-instead/sound.c index ec10ad1..0c7e36e 100644 --- a/src/sdl-instead/sound.c +++ b/src/sdl-instead/sound.c @@ -1,4 +1,6 @@ #include "sound.h" +#include "input.h" + #include #include @@ -15,67 +17,123 @@ int audio_buffers = 8192; static Mix_Music *mus; static char *next_mus = NULL; static int next_fadein = 0; +static int next_loop = -1; static SDL_TimerID timer_id = NULL; +static int sound_on = 0; + +static void mus_callback(void *aux) +{ + if (!timer_id) + return; + if (snd_playing_mus()) + return; + if (mus) + snd_free_mus(mus); + mus = NULL; + if (next_mus) { + snd_play_mus(next_mus, next_fadein, next_loop); + free(next_mus); + next_mus = NULL; + } + SDL_RemoveTimer(timer_id); + timer_id = NULL; +} + static Uint32 callback(Uint32 interval, void *aux) { - if (!snd_playing_mus()) { - if (mus) - snd_free_mus(mus); - mus = NULL; - if (next_mus) { - snd_play_mus(next_mus, next_fadein); - free(next_mus); - next_mus = NULL; - } - timer_id = NULL; - return 0; - } - return interval; + push_user_event(mus_callback, aux); + return interval; } int snd_hz(void) { int freq = 0; - Mix_QuerySpec(&freq, NULL, NULL); + if (sound_on) + Mix_QuerySpec(&freq, NULL, NULL); return freq; } +int alsa_sw = 0; +int nosound_sw = 0; + int snd_init(int hz) { + if (nosound_sw) + return -1; if (!hz) hz = audio_rate; else audio_rate = hz; + + audio_buffers = (audio_rate / 11025) * 2048; + if (!audio_buffers) /* wrong parameter? */ + audio_buffers = 8192; + + if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0) { + fprintf(stderr, "Unable to init audio!\n"); + return -1; + } + + if (alsa_sw) { + SDL_AudioInit("alsa"); + } + if (Mix_OpenAudio(hz, audio_format, audio_channels, audio_buffers)) { fprintf(stderr, "Unable to open audio!\n"); return -1; } + sound_on = 1; return 0; } int snd_volume_mus(int vol) { + if (!sound_on) + return 0; Mix_Volume(-1, vol); return Mix_VolumeMusic(vol); } +wav_t snd_load_wav(const char *fname) +{ + if (!sound_on) + return NULL; + if (!fname) + return NULL; + return (wav_t)Mix_LoadWAV(fname); +} + +void snd_free_wav(wav_t w) +{ + if (!w) + return; + Mix_HaltChannel(-1); + Mix_FreeChunk((Mix_Chunk*)w); +} + Mix_Music *snd_load_mus(const char *fname) { Mix_Music *mus; + if (!sound_on) + return NULL; mus = Mix_LoadMUS(fname); return mus; } +extern void game_music_finished(void); -int snd_play_mus(char *fname, int ms) +int snd_play_mus(char *fname, int ms, int loop) { + if (!sound_on) + return 0; if (snd_playing_mus()) { if (next_mus) { free(next_mus); } next_mus = strdup(fname); next_fadein = ms; + next_loop = loop; if (!timer_id) timer_id = SDL_AddTimer(200, callback, NULL); return 1; @@ -84,19 +142,27 @@ int snd_play_mus(char *fname, int ms) snd_free_mus(mus); mus = snd_load_mus(fname); - if (!mus) + if (!mus) { + fprintf(stderr,"Can't load '%s'.\n", fname); return 0; - - if (ms) - Mix_FadeInMusic((Mix_Music*)mus, -1, ms); + } + if (loop >= 0) + Mix_HookMusicFinished(game_music_finished); else - Mix_PlayMusic((Mix_Music*)mus, -1); - snd_volume_mus(snd_volume_mus(-1)); // hack? + Mix_HookMusicFinished(NULL); + if (ms) + Mix_FadeInMusic((Mix_Music*)mus, loop, ms); + else + Mix_PlayMusic((Mix_Music*)mus, loop); + snd_volume_mus(snd_volume_mus(-1)); // SDL hack? return 0; } void snd_stop_mus(int ms) { + if (!sound_on) + return; + Mix_HookMusicFinished(NULL); if (ms) Mix_FadeOutMusic(ms); else @@ -105,6 +171,8 @@ void snd_stop_mus(int ms) int snd_playing_mus(void) { + if (!sound_on) + return 0; if (Mix_PlayingMusic() | Mix_FadingMusic()) return 1; return 0; @@ -112,15 +180,29 @@ int snd_playing_mus(void) void snd_free_mus(mus_t mus) { + if (!sound_on) + return; Mix_HaltMusic(); Mix_FreeMusic((Mix_Music*) mus); } +void snd_play(void *chunk) +{ + if (!sound_on) + return; + if (!chunk) + return; + Mix_PlayChannel(-1, (Mix_Chunk*)chunk, 0); +} + void snd_done(void) { - Mix_HaltMusic(); + if (!sound_on) + return; if (timer_id) - SDL_RemoveTimer(timer_id); + + Mix_HaltChannel(-1); + Mix_HaltMusic(); timer_id = NULL; if (mus) Mix_FreeMusic((Mix_Music*) mus); @@ -129,5 +211,5 @@ void snd_done(void) free(next_mus); next_mus = NULL; Mix_CloseAudio(); + SDL_QuitSubSystem(SDL_INIT_AUDIO); } - diff --git a/src/sdl-instead/sound.h b/src/sdl-instead/sound.h index c005f76..a2158a7 100644 --- a/src/sdl-instead/sound.h +++ b/src/sdl-instead/sound.h @@ -8,12 +8,18 @@ #define SND_BOOM 4 #define SND_FADEOUT 5 #define SND_PAINT 6 + +typedef void* wav_t; typedef void* mus_t; + //extern mus_t snd_load_mus(const char *path); extern void snd_free_mus(mus_t mus); extern int snd_init(int hz); extern int snd_hz(void); -extern int snd_play_mus(char *music, int ms); +extern void snd_play(wav_t chunk); +extern void snd_free_wav(wav_t chunk); +extern wav_t snd_load_wav(const char *fname); +extern int snd_play_mus(char *music, int ms, int loop); extern int snd_playing_mus(); extern void snd_stop_mus(int ms); extern int snd_volume_mus(int vol); diff --git a/src/sdl-instead/unix.c b/src/sdl-instead/unix.c new file mode 100644 index 0000000..aac9779 --- /dev/null +++ b/src/sdl-instead/unix.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include +#include +#include +#include + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +extern char *curgame; +extern char *curgame_dir; + +static char save_path[PATH_MAX]; +static char local_games_path[PATH_MAX]; +static char local_themes_path[PATH_MAX]; + + +void nsleep(int u) +{ + usleep(u); +} + +char *game_local_games_path(void) +{ + struct passwd *pw; + pw = getpwuid(getuid()); + if (!pw) + return NULL; + snprintf(local_games_path, sizeof(local_games_path) - 1 , "%s/.instead/games/", pw->pw_dir); + return local_games_path; + +} + +char *game_local_themes_path(void) +{ + struct passwd *pw; + pw = getpwuid(getuid()); + if (!pw) + return NULL; + snprintf(local_themes_path, sizeof(local_themes_path) - 1 , "%s/.instead/themes/", pw->pw_dir); + return local_themes_path; + +} + +char *game_cfg_path(void) +{ + struct passwd *pw; + pw = getpwuid(getuid()); + if (!pw) + return NULL; + snprintf(save_path, sizeof(save_path) - 1 , "%s/.insteadrc", pw->pw_dir); + return save_path; + +} +char *game_save_path(int cr, int nr) +{ + struct passwd *pw; + if (!curgame) + return NULL; + pw = getpwuid(getuid()); + if (!pw) + return NULL; + snprintf(save_path, sizeof(save_path) - 1 , "%s/.instead/", pw->pw_dir); + if (cr && mkdir(save_path, S_IRWXU) && errno != EEXIST) + return NULL; + snprintf(save_path, sizeof(save_path) - 1 , "%s/.instead/saves", pw->pw_dir); + if (cr && mkdir(save_path, S_IRWXU) && errno != EEXIST) + return NULL; + snprintf(save_path, sizeof(save_path) - 1, "%s/.instead/saves/%s/", pw->pw_dir, curgame_dir); + if (cr && mkdir(save_path, S_IRWXU) && errno != EEXIST) + return NULL; + if (nr) + snprintf(save_path, sizeof(save_path) - 1, "%s/.instead/saves/%s/save%d", pw->pw_dir, curgame_dir, nr); + else + snprintf(save_path, sizeof(save_path) - 1, "%s/.instead/saves/%s/autosave", pw->pw_dir, curgame_dir); + return save_path; +} + +int debug_init(void) +{ + return 0; +} + +void debug_done() +{ + +} + diff --git a/src/sdl-instead/windows.c b/src/sdl-instead/windows.c new file mode 100644 index 0000000..17ba7a7 --- /dev/null +++ b/src/sdl-instead/windows.c @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef PATH_MAX +#define PATH_MAX 4096 +#endif + +extern char *curgame; +extern char *curgame_dir; + +static char save_path[PATH_MAX]; + +void nsleep(int u) +{ + Sleep(u); +} + +char *game_local_games_path(void) +{ + return NULL; /* TODO ? */ +} + +char *game_local_themes_path(void) +{ + return NULL; /* TODO ? */ +} + +extern void unix_path(char *); + +char *app_dir( void ) +{ + static char appdir[PATH_MAX]; + SHGetFolderPath( NULL, + CSIDL_FLAG_CREATE | CSIDL_LOCAL_APPDATA, + NULL, + 0, + (LPTSTR)appdir ); + unix_path(appdir); + return appdir; +} + +char *game_cfg_path( void ) +{ + snprintf(save_path, sizeof(save_path) - 1 , "%s\\insteadrc", app_dir()); + return save_path; + +} +char *game_save_path( int cr, int nr ) +{ + char appdir[PATH_MAX]; + strcpy( appdir, app_dir() ); + + snprintf(save_path, sizeof(save_path) - 1 , "%s/instead", appdir); + if (cr && mkdir(save_path) && errno != EEXIST) + return NULL; + snprintf(save_path, sizeof(save_path) - 1 , "%s/instead/saves", appdir); + if (cr && mkdir(save_path) && errno != EEXIST) + return NULL; + snprintf(save_path, sizeof(save_path) - 1, "%s/instead/saves/%s", appdir, curgame_dir); + if (cr && mkdir(save_path) && errno != EEXIST) + return NULL; + if (nr) + snprintf(save_path, sizeof(save_path) - 1, "%s/instead/saves/%s/save%d", appdir, curgame_dir, nr); + else + snprintf(save_path, sizeof(save_path) - 1, "%s/instead/saves/%s/autosave", appdir, curgame_dir); + return save_path; +} + +int debug_init(void) +{ + if (!AllocConsole()) + return -1; + SetConsoleTitle("Debug"); + freopen("CON", "w", stdout); //Map stdout + freopen("CON", "w", stderr); //Map stderr + return 0; +} + +void debug_done() +{ + FreeConsole(); +} + diff --git a/stead/Makefile.windows b/stead/Makefile.windows new file mode 100644 index 0000000..48fd5ee --- /dev/null +++ b/stead/Makefile.windows @@ -0,0 +1,6 @@ +clean: +all: stead.lua + +install: + if not exist ..\bin\stead mkdir ..\bin\stead + copy stead.lua ..\bin\stead diff --git a/stead/copying b/stead/copying new file mode 100644 index 0000000..d60c31a --- /dev/null +++ b/stead/copying @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/stead/stead.lua b/stead/stead.lua index a1a4b0e..1846a68 100644 --- a/stead/stead.lua +++ b/stead/stead.lua @@ -50,6 +50,53 @@ function fmt(...) end return res end +-- integer lists +function inext(t, k) + local v + k, v = next(t, k); + while k and not tonumber(k) do + k, v = next(t, k); + end + if not tonumber(k) then + return nil + end + return k, v +end + +function ilist(s, var) + return inext, s, nil; +end + +function ordered_i(t) + local ordered = {}; + local i,v, max; + max = 0; + for i,v in ilist(t) do + table.insert(ordered, i); + max = max + 1; + end + table.sort(ordered); + ordered.i = 1; + ordered.max = max; + return ordered; +end + +function onext(t, k) + local v + if not k then + k = ordered_i(t); + end + if k.i > k.max then + return nil + end + v = k[k.i] + k.i = k.i + 1 + return k, t[v], v; +end + +function opairs(s, var) + return onext, s, nil; +end function isPlayer(v) if type(v) ~= 'table' then @@ -124,7 +171,7 @@ end function obj_xref(self,str) function xrefrep(str) local s = string.gsub(str,'[{}]',''); - return iface:xref(s, self); + return xref(s, self); end if not str then return @@ -137,7 +184,7 @@ function obj_xref(self,str) end function obj_look(self) - local i, vv + local i, vv, o local v = call(self,'dsc'); if isDisabled(self) then return @@ -147,10 +194,12 @@ function obj_look(self) elseif v then v = string.gsub(v, '[{}]',''); end - for i=1, table.maxn(self.obj) do - local o = ref(self.obj[i]); - vv = obj_look(o); - v = par(' ',v, vv); + for i,o in opairs(self.obj) do + o = ref(o); + if isObject(o) then + vv = obj_look(o); + v = par(' ',v, vv); + end end return v; -- iface:xref(v,self.nam); @@ -159,10 +208,12 @@ end function obj_remove(self) self._disabled = -1; + return self end function obj_disable(self) self._disabled = true; + return self end function obj_enable(self) @@ -170,6 +221,7 @@ function obj_enable(self) -- return -- end self._disabled = false; + return self end function obj_save(self, name, h, need) @@ -181,18 +233,18 @@ function obj_save(self, name, h, need) end function obj_str(self) - local i, v, vv; + local i, v, vv, o; if not isObject(self) then return end if isDisabled(self) then return end - for i = 1,table.maxn(self.obj) do - local o = ref(self.obj[i]); - if not isDisabled(o) and isObject(o) then + for i,o in opairs(self.obj) do + o = ref(o); + if isObject(o) and not isDisabled(o) then vv = call(o, 'nam'); - vv = iface:xref(vv, o); + vv = xref(vv, o); -- vv = cat(vv,'(',tostring(ref(self[i]).id),')'); v = par(',', v, vv, obj_str(o)); -- obj_str(o); @@ -203,7 +255,7 @@ end function obj(v) if v.nam == nil then - error "No object name in constructor."; + error ("No object name in constructor."); end if v.look == nil then v.look = obj_look; @@ -252,27 +304,39 @@ function ref(n) -- ref object by name return nil end +function deref(n) + if type(n) == 'string' then + return n + end + + if type(n) == 'table' and type(n.key_name) == 'string' then + return n.key_name + end + return n +end + function list_check(self) - local i; - for i = 1,table.maxn(self) do - local o = ref(self[i]); - if not o then + local i, v, ii; + for i,v,ii in opairs(self) do + local o = ref(v); + if not o then -- isObject(o) then -- compat return false end - if not isObject(o) then - return false + if deref(v) then + self[ii] = deref(v); end end return true; end function list_str(self) - local i, v, vv; - for i = 1,table.maxn(self) do - local o = ref(self[i]); - if not isDisabled(o) then + local i, v, vv, o; + for i,o in opairs(self) do + o = ref(o); +-- if isObject(o) and not isDisabled(o) then + if o~= nil and not isDisabled(o) then vv = call(o, 'nam'); - vv = iface:xref(vv, o); + vv = xref(vv, o); -- vv = cat(vv,'(',tostring(ref(self[i]).id),')'); v = par(',', v, vv); end @@ -281,33 +345,51 @@ function list_str(self) end -function list_add(self, name) +function list_add(self, name, pos) + local nam -- if type(name) ~= 'string' then -- error "No string adding to list." -- end - if list_find(self, name) then + nam = deref(name); + if list_find(self, nam) then return nil end - self.__modifyed__ = true - table.insert(self, name); + self.__modifyed__ = true; + if tonumber(pos) then + table.insert(self, tonumber(pos), nam); + self[tonumber(pos)] = nam; -- for spare lists + else + table.insert(self, nam); + end + return true +end + +function list_set(self, name, pos) + local nam + local i = tonumber(pos); + if not i then + return nil + end + nam = deref(name); + self.__modifyed__ = true; + self[i] = nam; -- for spare lists return true end function list_find(self, name) - local n + local n, v, ii -- if type(name) ~= 'string' then -- error "No string finding in list." -- end - for n=1,table.maxn(self) do - if ref(self[n]) == ref(name) then - return n; + for n,v,ii in opairs(self) do + if ref(v) == ref(name) then + return ii; end end return nil end function list_save(self, name, h, need) - local k,v; if self.__modifyed__ then h:write(name.." = list({});\n"); need = true; @@ -316,25 +398,27 @@ function list_save(self, name, h, need) end function list_name(self, name) - local n + local n, o, ii -- if type(name) ~= 'string' then -- error "No string finding in list." -- end - for n=1,table.maxn(self) do - local o = ref(self[n]); - local nam = call(o,'nam') ; - if not isDisabled(o) and name == tostring(nam) then - return n; + for n,o,ii in opairs(self) do + o = ref(o); + if isObject(o) then + local nam = call(o,'nam') ; + if not isDisabled(o) and name == tostring(nam) then + return ii; + end end end return nil end function list_id(self, id) - local n - for n=1,table.maxn(self) do - local o = ref(self[n]); - if not isDisabled(o) and id == o.id then - return n; + local n,o,ii + for n,o,ii in opairs(self) do + o = ref(o); + if isObject(o) and not isDisabled(o) and id == o.id then + return ii; end end end @@ -364,11 +448,17 @@ function list_del(self, name) return nil; end self.__modifyed__ = true - return table.remove(self, n); + v = table.remove(self, n); + if not v then + v = self[n]; + self[n] = nil -- for spare lists + end + return v end function list(v) v.add = list_add; + v.set = list_set; v.del = list_del; v.look = list_find; v.name = list_name; @@ -404,10 +494,12 @@ function room_scene(self) end function room_look(self) - local i, vv; - for i = 1,table.maxn(self.obj) do - local o = ref(self.obj[i]); - vv = par(' ',vv, obj_look(o)); + local i, vv, o; + for i,o in opairs(self.obj) do + o = ref(o); + if isObject(o) then + vv = par(' ',vv, obj_look(o)); + end end return cat(vv,' '); end @@ -422,11 +514,13 @@ function obj_search(v, n) if o then return o, v; end - for i=1, table.maxn(v.obj) do - o = ref(v.obj[i]); - local r,rr = obj_search(o, n); - if r then - return r, rr; + for i,o in opairs(v.obj) do + o = ref(o); + if isObject(o) then + local r,rr = obj_search(o, n); + if r then + return r, rr; + end end end return; @@ -452,7 +546,6 @@ function room(v) --constructor end function dialog_enter(self) - local i -- self._last = nil; if not dialog_rescan(self) then return nil, false @@ -461,7 +554,7 @@ function dialog_enter(self) end function dialog_scene(self) - local i,n,v + local v v = iface:title(call(self,'nam')); v = par('^^', v, call(self, 'dsc')); --obj_look(self)); -- if self._last then @@ -473,13 +566,13 @@ function dialog_scene(self) end function dialog_look(self) - local i,n,v - n=1 - for i=1,table.maxn(self.obj) do - local ph = ref(self.obj[i]); - if not isDisabled(ph) then + local i,n,v,ph + n = 1 + for i,ph in opairs(self.obj) do + ph = ref(ph); + if isPhrase(ph) and not isDisabled(ph) then if game.hinting then - v = par('^', v, n..' - '..iface:xref(call(ph,'dsc'),ph)); + v = par('^', v, n..' - '..xref(call(ph,'dsc'),ph)); else v = par('^', v, n..' - '..string.gsub(call(ph,'dsc'),'[{}]','')); end @@ -490,11 +583,10 @@ function dialog_look(self) end function dialog_rescan(self) - local i,k + local i,k,ph k = 1 - - for i=1,table.maxn(self.obj) do - local ph = ref(self.obj[i]); + for i,ph in opairs(self.obj) do + ph = ref(ph); if isPhrase(ph) and not isDisabled(ph) then ph.nam = tostring(k); k = k + 1; @@ -520,15 +612,13 @@ function ponoff(self, on, ...) local i, ph for i=1,table.maxn(arg) do ph = dialog_phrase(self, arg[i]); - if ph ~= nil then - if not isRemoved(ph) then - if on then - ph:enable(); - else - ph:disable(); - end --- ph.__changed__ = true; + if isPhrase(ph) and not isRemoved(ph) then + if on then + ph:enable(); + else + ph:disable(); end +-- ph.__changed__ = true; end end end @@ -537,7 +627,7 @@ function dialog_prem(self, ...) local i, ph for i=1,table.maxn(arg) do ph = dialog_phrase(self, arg[i]); - if ph ~= nil then + if isPhrase(ph) then ph:remove(); -- ph.__changed__=true end @@ -599,11 +689,11 @@ function phrase_action(self) elseif type(ph.do_act) == 'function' then ret = ph.do_act(self, nam); end - r._last = call(ph, 'ans'); + local last = call(ph, 'ans'); if isDialog(here()) and not dialog_rescan(here()) then ret = par(' ', ret, me():back()); end - return par("^^",r._last, ret); + return par("^^", last, ret); -- if dialog_getn(self, 1) == nil then -- me():back(); -- end @@ -637,7 +727,7 @@ end function phr(ask, answ, act) local p = phrase ( { dsc = ask, ans = answ, do_act = act }); - p:enable(); +-- p:enable(); return p; end @@ -664,33 +754,36 @@ function obj_tag(self, id) if isDisabled(self) then return id end - - for k,v in ipairs(self.obj) do - if ref(v) and not isDisabled(ref(v)) then + + for k,v in opairs(self.obj) do + v = ref(v); + if isObject(v) and not isDisabled(v) then id = id + 1; - ref(v).id = id; - id = obj_tag(ref(v), id); + v.id = id; + id = obj_tag(v, id); end end return id; end function player_tagall(self) - local id = 0; - local k, v; - + local id, k, v; + id = 0; + id = obj_tag(here(), id); - for k,v in ipairs(inv()) do - if ref(v) and not isDisabled(ref(v)) then + for k,v in opairs(inv()) do + v = ref(v); + if isObject(v) and not isDisabled(v) then id = id + 1; - ref(v).id = id; + v.id = id; end end - for k,v in ipairs(ways()) do - if isRoom(ref(v)) then + for k,v in opairs(ways()) do + v = ref(v); + if isRoom(v) and not isDisabled(v) then id = id + 1; - ref(v).id = id; + v.id = id; end end end @@ -739,7 +832,7 @@ function player_use(self, what, onwhat) local obj, obj2, v, vv, r; obj = self.obj:srch(what); if not obj then - return nil, false; + return game.err, false; end if onwhat == nil then v, r = call(ref(obj),'inv'); @@ -753,7 +846,7 @@ function player_use(self, what, onwhat) obj2 = self.obj:srch(onwhat); end if not obj2 or obj2 == obj then - return nil, false; + return game.err, false; end v, r = call(ref(obj), 'use', obj2); if r ~= false then @@ -774,58 +867,71 @@ function player_back(self) end function go(self, where, back) + local was = self.where; local need_scene = false; if where == nil then return nil,false end if not isRoom(ref(where)) then - error ("Trying to go nowhere."..where); + error ("Trying to go nowhere: "..where); end if not isRoom(ref(self.where)) then - error "Trying to go from nowhere." + error ("Trying to go from nowhere: "..self.where); end local v, r; -- if not isDialog(ref(self.where)) then v,r = call(ref(self.where), 'exit', where); if r == false then - return v + return v, r end + +-- if ref(was) ~= ref(self.where) then -- jump !!! +-- where = self.where; +-- was = where; +-- end + -- end local res = v; v = nil; if not back or not isDialog(ref(self.where)) or isDialog(ref(where)) then v, r = call(ref(where), 'enter', self.where); if r == false then - return v + return v, r end need_scene = true; + if ref(was) ~= ref(self.where) then -- jump !!! + where = deref(self.where); + need_scene = false; + end end res = par('^^',res,v); if not back then - ref(where).__from__ = self.where; + ref(where).__from__ = deref(self.where); end if need_scene then - self.where = where; + self.where = deref(where); return par('^^',res,ref(where):scene()); end - self.where = where; + self.where = deref(where); return res; end function player_goto(self, where) - return go(self, where, false); + local v = go(self, where, false); + return v; end function player_go(self, where) local w = ref(self.where).way:srch(where); if not w then return nil,false end - return go(self, w, false); + local v = go(self, w, false); + return v; end function player_save(self, name, h) - h:write(tostring(name)..'.where = "'..self.where..'";\n'); + h:write(tostring(name)..'.where = "'..deref(self.where)..'";\n'); savemembers(h, self, name, false); end @@ -879,14 +985,24 @@ end function game_life(self) local i, v; - for i=1, table.maxn(self.lifes) do - if not isDisabled(ref(self.lifes[i])) then + for i,v in ipairs(self.lifes) do + v = ref(v); + if not isDisabled(v) then v = par(' ',v,call(ref(self.lifes[i]),'life')); end end return v; end +function check_object(k, v) + if not v.nam then + error ("No name in "..k); + end + if v.obj and not v.obj:check() then + error ("error in object (obj): "..k); + end +end + function check_room(k, v) if not v.nam then error ("No name in "..k); @@ -897,24 +1013,31 @@ function check_room(k, v) if v.way == nil then error("no way in room:"..k); end - if not v.obj:check() then - error ("error in room (obj): "..k); - end if not v.way:check() then error ("error in room (way): "..k); end end +function check_player(k, v) + if v.obj and not v.obj:check() then + error ("error in player (obj): "..k); + end +end + function do_ini(self) - local v,vv + local v='',vv local function call_ini(k, v) + v.key_name = k; v = call(v, 'ini'); v = cat(v, "^^"); end math.randomseed(tonumber(os.date("%m%d%H%M%S"))) for_each_object(call_ini); + for_each_player(call_ini); -- for_each_room(call_ini); + for_each_object(check_object); for_each_room(check_room); + for_each_player(check_player); me():tag(); if not self.showlast then self._lastdisp = nil; @@ -975,7 +1098,7 @@ function for_each_object(f,...) local k,v for k,v in pairs(_G) do if isObject(v) then - f(k,v, unpack(arg)); + f(k, v, unpack(arg)); end end end @@ -1007,7 +1130,7 @@ function savemembers(h, self, name, need) local need2 if k ~= "__visited__" then need2 = false - if string.find(k, '_') == 1 then + if string.find(k, '_') == 1 or string.match(k,'^%u') then need2 = true; end @@ -1022,14 +1145,15 @@ end function savevar (h, v, n, need) local r,f + if v == nil or type(v)=="userdata" or type(v)=="function" then return end - if string.find(n, '_') == 1 then - need = true; - end +-- if string.find(n, '_') == 1 or string.match(n,'^%u') then +-- need = true; +-- end if type(v) == "string" then if not need then @@ -1083,6 +1207,7 @@ function game_save(self, name, file) if not h then return nil, false end + h:write("-- $Name: "..call(here(),'nam').."$\n"); -- for_each_room(save_object, h); for_each_object(save_object, h); for_each_player(save_object, h); @@ -1108,9 +1233,10 @@ function game_load(self, name) end function game_life(self) - local i,v - for i=1,table.maxn(self.lifes) do - vv = call(ref(self.lifes[i]),'life'); + local i,v,o + for i,o in ipairs(self.lifes) do + o = ref(o); + vv = call(o,'life'); v = par(' ',v, vv); end return v; @@ -1121,10 +1247,10 @@ function game_step(self) return game_life(self); end -version = "0.4.3"; +version = "0.8"; game = game { - nam = "INSTEAD -- Simple Text Adventure interpreter v"..version.." '2008 by Peter Kosyh", + nam = "INSTEAD -- Simple Text Adventure interpreter v"..version.." '2009 by Peter Kosyh", dsc = [[ Commands:^ look(or just enter), act (or just what), use [on what], go ,^ @@ -1139,6 +1265,9 @@ function strip(s) return s; end iface = { + em = function(self, str) + return str; + end, xref = function(self, str, obj) -- return "@"..str.."{"..obj.."}"; local o = ref(here():srch(obj)); @@ -1173,17 +1302,19 @@ iface = { end end, cmd = function(self, inp) - local r, v, vv, i, k , cmd, n; + local r, v, vv, i, k, cmd; local scene = false; local st = false; + local l; + i,k = string.find(inp,'[a-zA-Z0-9_]+', k); if not i or not k then cmd = inp else cmd = string.sub(inp, i, k); end - a = { }; - n = 1; + local a = { }; + local n = 1; while i do k = k + 1; i,k = string.find(inp,'[^,]+', k); @@ -1194,10 +1325,13 @@ iface = { n = n + 1; end v = false --- me():tag(); +-- me():tag(); + local oldloc = here(); + local look = false; if cmd == 'look' or cmd == '' then r,v = me():look(); st = true; + look = true; elseif cmd == 'obj' then r,v = me():objs(); elseif cmd == 'inv' then @@ -1215,7 +1349,7 @@ iface = { st = true; elseif cmd == 'act' then r,v = me():action(unpack(a)); - st = true; + st = true; elseif cmd == 'use' then r,v = me():use(unpack(a)); st = true; @@ -1232,17 +1366,30 @@ iface = { end -- self:text(r); if st and v ~= false then - me():tag(); vv = par(" ",vv, game:step()); - vv = par("^^",here():look(), vv); + me():tag(); + if oldloc == here() and not look then + if here().forcedsc == true then + l,v = me():look(); + elseif game.forcedsc == true and here().forcedsc ~= false then + l,v = me():look(); + end + r = iface:em(r); + vv = par("^^",here():look(), iface:em(vv)); + else + vv = par("^^",here():look(), vv); + end end if v == false then - return fmt(par("^^", vv, "Error.^")); + return fmt(r); end - vv = fmt(cat(par("^^",r,vv),'^')); + vv = fmt(cat(par("^^",l,r,vv),'^')); if st then game._lastdisp = vv end + if vv == nil then -- nil is error + return '' + end return vv; end, shell = function(self) @@ -1268,12 +1415,16 @@ function me() return ref(game.pl); end +function where() + return me().where; +end + function here() - return ref(me().where); + return ref(where()); end function from() - return ref(ref(me().where).__from__); + return ref(here().__from__); end function time() @@ -1284,11 +1435,21 @@ function inv() return me().obj; end -function objs() - return here().obj; +function objs(w) + if not w then + w = here(); + else + w = ref(w); + end + return w.obj; end -function ways() +function ways(w) + if not w then + w = here(); + else + w = ref(w); + end return here().way; end @@ -1324,19 +1485,30 @@ end function vobj_save(self, name, h, need) local dsc; + local w if type(self.dsc) ~= 'string' then dsc = 'nil'; else dsc = "[["..self.dsc.."]]"; end + + if type(deref(self.where)) ~= 'string' then + w = 'nil'; + else + w = "'"..deref(self.where).."'"; + end + if need then - h:write(name.." = vobj("..tostring(self.key)..",[["..self.nam.."]],"..dsc..");\n"); + h:write(name.." = vobj("..tostring(self.key)..",[["..self.nam.."]],"..dsc..","..w..");\n"); end savemembers(h, self, name,false); end function vobj_act(self, ...) - local o, r = here():srch(self.nam); + local o, r = here():srch(self); -- self.nam + if ref(o) and ref(o).where then + return goto(ref(o).where); + end return call(ref(r),'act', self.key, unpack(arg)); end @@ -1345,19 +1517,38 @@ function vobj_used(self, ...) return call(ref(r),'used', self.key, unpack(arg)); end -function vobj(key, name, dsc) - local o = { key = key, nam = name, dsc = dsc, act = vobj_act, used = vobj_used, save = vobj_save, obj = list({}), }; +function vobj(key, name, dsc, w) if not tonumber(key) then error ("vobj key must be number!"); end --- o.object_type = true; - o = obj(o); - return o; + return obj{ key = key, nam = name, dsc = dsc, where = deref(w), act = vobj_act, used = vobj_used, save = vobj_save, obj = list({}) }; end + +function vway(name, dsc, w) +-- o.object_type = true; + return obj{ key = -1, nam = name, dsc = dsc, act = vobj_act, where = deref(w), used = vobj_used, save = vobj_save, obj = list({}), }; +end + +function vroom_save(self, name, h, need) + if need then + h:write(name.." = vroom('"..self.nam.."','"..deref(self.where).."');\n"); + end + savemembers(h, self, name,false); +end + +function vroom_enter(self, ...) + return go(me(), self.where, false); +end + +function vroom(name, w) + return room { nam = name, where = deref(w), enter = vroom_enter, save = vroom_save, }; +end + + function goto(what) - local v=me():goto(what); + local v,r=me():goto(what); me():tag(); - return v; + return v,r; end function back() return me():back(); @@ -1373,48 +1564,113 @@ function taken(obj) return false; end -function take(obj) - local o,w = here():srch(obj); +function take(obj, wh) + if not wh then + wh = here(); + else + wh = ref(wh); + end + local o,w = wh:srch(obj); if w then ref(w).obj:del(obj); end + o = ref(o); + if not isObject(o) then + o = ref(obj); + end + + if not isObject(o) then + error "Trying to take wrong object."; + end me().obj:add(obj); - ref(obj)._taken = true + o._taken = true + return o end -function drop(obj) +function put(obj, w) + local o = ref(obj); + if not isObject(o) then + error "Trying to put wrong object."; + end + if not w then + w = here(); + else + w = ref(w); + end + w.obj:add(obj); + return o; +end + +function drop(obj, w) + local o = put(obj, w); + if not o then + return o; + end me().obj:del(obj); - here().obj:add(obj); - ref(obj)._taken = false + o._taken = false + return o; +end + +function dropf(obj) + local o = ref(obj); + if not isObject(o) then + error "Trying to dropf wrong object:"; + end + me().obj:del(obj); + here().obj:add(obj, 1); + o._taken = false + return o; end function seen(obj) - if here().obj:srch(obj) then - return true + local o,w = here().obj:srch(obj); + o = ref(o); + if isObject(o) then + return o,w end - return false + return nil end function have(obj) - if me().obj:srch(obj) then - return true + local o = me().obj:srch(obj); + o = ref(o); + if isObject(o) then + return o end - return false + return nil end -function move(obj, there) - local o,w = here():srch(obj); +function moveto(obj, there, from, pos) + local o + local w + + if ref(from) then + o,w = ref(from):srch(obj); + else + o,w = here():srch(obj); + end + if w then ref(w).obj:del(obj); end --- if not isRoom(ref(there)) then --- error ("move error"); --- end - ref(there).obj:add(obj); + ref(there).obj:add(obj, pos); + return ref(obj); +end + + +function move(obj, there, from) + return moveto(obj, there, from); +end + +function movef(obj, there, from) + return moveto(obj, there, from, 1); end function get_picture() local s = call(here(),'pic'); + if not s then + s = call(game, 'pic'); + end return s; end @@ -1422,13 +1678,51 @@ function get_title() local s = call(here(),'nam'); return s; end + function get_music() return game._music; end -function set_music(s) - game._music = s; +function get_music_loop() + return game._music_loop; end +function save_music(s) + s.__old_music__ = get_music(); + s.__old_loop__ = get_music_loop(); +end + +function restore_music(s) + set_music(s.__old_music__, s.__old_loop__); +end + +function dec_music_loop() + if game._music_loop == 0 then + return 0 + end + game._music_loop = game._music_loop - 1; + if game._music_loop == 0 then + game._music_loop = -1; + end + return game._music_loop; +end + +function set_music(s, count) + game._music = s; + if not tonumber(count) then + game._music_loop = 0; + else + game._music_loop = tonumber(count); + end +end + +function change_pl(p) + local o = ref(p); + if type(p) ~= 'string' or not o then + error "Wrong player name in change_pl..."; + end + game.pl = p; + return goto(o.where); +end -- here the game begins diff --git a/themes/Makefile b/themes/Makefile index e055de5..ce1216f 100644 --- a/themes/Makefile +++ b/themes/Makefile @@ -6,6 +6,6 @@ install: for f in *; do \ if [ ! -d $$f ]; then continue; fi;\ install -d -m0755 $(THEMESPATH)/$$f;\ - tar -c -C $$f . | tar -x -C $(THEMESPATH)/$$f;\ + tar --exclude=".svn" -c -C $$f . | tar -x -C $(THEMESPATH)/$$f;\ done diff --git a/themes/Makefile.windows b/themes/Makefile.windows new file mode 100644 index 0000000..b0d02b4 --- /dev/null +++ b/themes/Makefile.windows @@ -0,0 +1,7 @@ +include ../Rules.make + +clean: +all: +install: + for /D %%f in (*) do ( if not exist "..\bin\themes\%%f" mkdir "..\bin\themes\%%f" ) + for /D %%f in (*) do ( copy /Y %%f ..\bin\themes\%%f ) diff --git a/themes/book/book.png b/themes/book/book.png new file mode 100644 index 0000000..3b0f556 Binary files /dev/null and b/themes/book/book.png differ diff --git a/themes/book/theme.ini b/themes/book/theme.ini new file mode 100644 index 0000000..e9f7bab --- /dev/null +++ b/themes/book/theme.ini @@ -0,0 +1,33 @@ +; $Name:Книга (800x600)$ +scr.w = 800 +scr.h = 600 +scr.gfx.bg = book.png +scr.col.bg = white + +scr.gfx.x = 18 +scr.gfx.y = 36 +scr.gfx.w = 344 +scr.gfx.h = 410 +scr.gfx.mode = float + +win.x = 400 +win.y = 18 +win.w = 350 +win.h = 550 + +win.col.fg = #000000 +win.col.link = #a02c00 +win.col.alink = #606060 + +menu.button.y = 576 + +inv.x = 30 +inv.y = 460 +inv.w = 320 +inv.h = 100 + +inv.col.fg = #000000 +inv.col.link = #902c00 +inv.col.alink = #606060 + +inv.mode = horizontal diff --git a/themes/default-large/theme.ini b/themes/default-large/theme.ini new file mode 100644 index 0000000..1db8e63 --- /dev/null +++ b/themes/default-large/theme.ini @@ -0,0 +1,8 @@ +; $Name: Стандартная (800x600)$ +scr.w = 800 +scr.h = 600 +win.h = 568 +inv.h = 568 +scr.gfx.h = -1 +menu.button.y = 576 + diff --git a/themes/default/click.ogg b/themes/default/click.ogg new file mode 100644 index 0000000..e87eb91 Binary files /dev/null and b/themes/default/click.ogg differ diff --git a/themes/default/click.wav b/themes/default/click.wav new file mode 100644 index 0000000..af3e8d4 Binary files /dev/null and b/themes/default/click.wav differ diff --git a/themes/default/theme.ini b/themes/default/theme.ini index ba0e726..13c6989 100644 --- a/themes/default/theme.ini +++ b/themes/default/theme.ini @@ -1,9 +1,12 @@ +; $Name: Стандартная (800x480)$ scr.w = 800 scr.h = 480 scr.gfx.bg = bg.png scr.col.bg = white scr.gfx.use = use.png scr.gfx.pad = 16 +scr.gfx.mode = embedded +win.gfx.h = -1 ;unlim win.x = 48 win.y = 8 @@ -11,12 +14,11 @@ win.w = 500 win.h = 448 win.fnt.name = sans.ttf win.fnt.size = 16 -win.gfx.h = 200 win.gfx.up = aup.png win.gfx.down = adown.png -win.col.fg = #000000 -win.col.link = #0000FF -win.col.alink = #FF0000 +win.col.fg = black +win.col.link = #b02c00 +win.col.alink = #606060 inv.x = 620 inv.y = 8 @@ -26,19 +28,22 @@ inv.fnt.name = sans.ttf inv.fnt.size = 16 inv.gfx.up = aup.png inv.gfx.down = adown.png -inv.col.fg = #FFFFFF +inv.col.fg = #CCCCCC inv.col.link = #CCCCCC inv.col.alink = goldenrod +inv.mode = vertical -menu.col.bg = #FFFFFF -menu.col.fg = #000000 -menu.col.link = #0000FF -menu.col.alink = #FF0000 -menu.col.alpha = 180 -menu.col.border = #000000 +menu.col.bg = white +menu.col.fg = black +menu.col.link = blue +menu.col.alink = red +menu.col.alpha = 220 +menu.col.border = black menu.bw = 1 menu.fnt.name = sans.ttf menu.fnt.size = 15 menu.gfx.button = menu.png -menu.buttonx = 776 -menu.buttony = 456 +menu.button.x = 776 +menu.button.y = 456 + +snd.click = click.wav ; click.ogg diff --git a/themes/original/theme.ini b/themes/original/theme.ini new file mode 100644 index 0000000..1d7000a --- /dev/null +++ b/themes/original/theme.ini @@ -0,0 +1,7 @@ +; $Name:Старая (800x480) +win.col.fg = black +win.col.link = blue +win.col.alink = red +scr.gfx.mode = fixed +scr.gfx.h = 200 +