1
0
mirror of https://git.zap.org.au/git/trader.git synced 2025-01-03 14:57:41 -05:00

Merge branch master into with-autogenerated

This commit is contained in:
John Zaitseff 2011-08-29 11:11:53 +10:00
commit b528bbc9f0
42 changed files with 11789 additions and 2100 deletions

7
.gitignore vendored
View File

@ -1,7 +1,9 @@
*.o
*.a
*.gmo
.deps/
Makefile
TAGS
/autom4te.cache
/config.h
@ -9,4 +11,9 @@ Makefile
/config.status
/stamp-h1
/po/POTFILES
/po/en@quot.insert-header
/po/messages.mo
/po/remove-potcdate.sed
/src/trader

32
INSTALL
View File

@ -34,11 +34,22 @@ and installation:
In actual fact, Star Traders uses the GNU Portability Library, so many
older systems may also work without modification.
3. A working X/Open Curses-compatible library, such as NCurses.
3. A working X/Open Curses-compatible library, such as Ncurses. Ncurses
is preferred over system-native libraries, if present. Locales with
multibyte character sequences (such as UTF-8) require a wide-character
version of Curses, such as NcursesW, to work correctly.
4. Development libraries and header files for all of the above. On many
4. The GNU Gettext library, version 0.18.1 or later, to allow the game to
use languages other than English; this is also called Native Language
Support. If you do not have this library (and do not wish to install
it), you may pass "--disable-nls" to the configure script.
5. Development libraries and header files for all of the above. On many
systems, these files are part of XXX-dev packages.
6. The GNU Perfect Hash Function Generator, gperf. This utility program
is required for parts of the GNU Portability Library.
Installation
============
@ -58,14 +69,15 @@ manual:
This version of the configure script understands the following additional
command line options:
--disable-nls Do not use Native Language Support
--disable-assert Turn off all debugging assert() statements
--with-ncurses Force the use of NCurses over the system's Curses
--with-ncurses Force the use of Ncurses over the system's Curses
library
--with-ncursesw Force the use of the NCursesW library with wide-
--with-ncursesw Force the use of the NcursesW library with wide-
character support
--without-ncursesw Don't use the NCursesW library with wide-character
--without-ncursesw Don't use the NcursesW library with wide-character
support
--without-ncurses Don't use the NCurses library: use the system's
--without-ncurses Don't use the Ncurses library: use the system's
normal Curses library
By default, configure uses "/usr/local" as the top-level (prefix) install
@ -90,12 +102,12 @@ You can also run configure in a separate build-only directory tree. This
feature requires GNU Make and allows you to keep the source code tree from
being modified by the compilation process. To use this option, create a
separate build directory, then run configure. For example, if you placed
the Star Traders source code tree in $HOME/src/trader-7.0, you could run
the Star Traders source code tree in $HOME/src/trader-7.2, you could run
something like:
mkdir /tmp/trader-build-7.0
cd /tmp/trader-build-7.0
$HOME/src/trader-7.0/configure
mkdir $HOME/build/trader-build-7.2
cd $HOME/build/trader-build-7.2
$HOME/src/trader-7.2/configure
Once again, the Autoconf manual describes these options (and many others):

View File

@ -31,7 +31,7 @@
ACLOCAL_AMFLAGS = -I m4
# Subdirectories to recurse into
SUBDIRS = lib src doc m4
SUBDIRS = lib src doc po m4
# Additional files to distribute
EXTRA_DIST = build-aux/bootstrap

25
NEWS
View File

@ -15,6 +15,25 @@ consult the Subversion repository for "trader" on The ZAP Group web server
at http://www.zap.org.au/services/svn/.
Version 7.2 (29th August, 2011)
-------------------------------
Star Traders has been internationalised! As part of this update, all
input and output routines have been rewritten to handle multibyte strings.
English (Australian, British, Canadian and US) translations have been
included. Translations for other languages (and corrections to existing
languages) are more than welcome!
Game files are now stored in UTF-8 format (once decrypted!) and can be
loaded under any locale with automatic character set translation. This
does mean, however, that game files from versions 7.0 and 7.1 of Star
Traders will not load under this release.
The program now better handles terminal resizing events (for versions of
Curses supporting such events). It also tries to restore the terminal
environment correctly when receiving a terminating signal.
Version 7.1 (29th July, 2011)
-----------------------------
@ -34,10 +53,10 @@ exercise for a number of software tools and libraries; the algorithms in
the original Pascal and Visual Basic versions are reused for the game
logic.
Note that the current version of Star Traders does NOT handle locales with
Note that versions 7.0 and 7.1 of Star Traders did NOT handle locales with
multibyte character sequences (such as UTF-8) correctly. Each byte in a
multibyte sequence is treated as a separate character. Eight-bit locales
(such as US-ASCII, ISO8859-1, etc.) work correctly.
such a sequence was treated as a separate character. Eight-bit locales
(such as US-ASCII, ISO8859-1, etc.) worked correctly.
Early history

View File

@ -6,4 +6,4 @@
set -e
gnulib-tool --update
autoreconf --install --verbose
env AUTOPOINT=true autoreconf --install --verbose

View File

@ -26,7 +26,7 @@ dnl You should have received a copy of the GNU General Public License
dnl along with this program. If not, see http://www.gnu.org/licenses/.
AC_INIT([Star Traders], [7.1], [J.Zaitseff@zap.org.au], [trader], [http://www.zap.org.au/software/trader/])
AC_INIT([Star Traders], [7.2], [J.Zaitseff@zap.org.au], [trader], [http://www.zap.org.au/software/trader/])
AC_DEFINE([PACKAGE_AUTHOR], ["John Zaitseff"], [Package author])
AC_PREREQ([2.67])
@ -50,6 +50,9 @@ AX_C___ATTRIBUTE__
AC_TYPE_SIZE_T
AC_TYPE_SSIZE_T
AM_GNU_GETTEXT([external])
AM_GNU_GETTEXT_VERSION([0.18.1])
gl_INIT
AX_WITH_CURSES
@ -62,6 +65,7 @@ AC_CONFIG_FILES([
lib/Makefile
src/Makefile
doc/Makefile
po/Makefile.in
m4/Makefile
])
AC_OUTPUT

View File

@ -41,7 +41,7 @@
.if \n[.g] .mso www.tmac
.\"
.\" *********************************************************************
.TH TRADER 6 "22nd July, 2011" "Unix-like systems"
.TH TRADER 6 "29th August, 2011" "Unix-like systems"
.SH NAME
trader \- a game of interstellar trading
.\" *********************************************************************
@ -154,9 +154,11 @@ manual page for in-depth details). It requires a text console or window
of at least 80\(mu24 in size.
.TP
.BR LANG ", " LC_ALL ", etc."
This version of Star Traders has rudimentary support for locales and will
use appropriate settings. In particular, numeric quantities will be
displayed using \fBLC_NUMERIC\fR and monetary quantities will use
This version of Star Traders has full support for locales and will use
appropriate settings. In particular, messages will be displayed using
\fBLC_MESSAGES\fR and \fBLANGUAGE\fR (if Star Traders has been translated
into that language). In addition, numeric quantities will be displayed
using \fBLC_NUMERIC\fR and monetary quantities will use
\fBLC_MONETARY\fR. See the
.BR locale (7)
or
@ -172,10 +174,7 @@ inclusive. The game file is scrambled to prevent you or others from
casually cheating!
.\" *********************************************************************
.SH BUGS
The current version of Star Traders does \fInot\fR handle locales with
multibyte character sequences (such as UTF-8) correctly. Each byte in a
multibyte sequence is treated as a separate character. Eight-bit locales
(such as US-ASCII, ISO8859-1, etc.) work correctly.
None yet known...
.\" *********************************************************************
.SH FEEDBACK
Your comments, suggestions, corrections and enhancements are always
@ -262,8 +261,9 @@ on 15th September, 1995.
Star Traders was then to languish until almost 16 years later... when the
game was rewritten once again, this time in the C programming language.
Version 7.0 was released on 25th July, 2011 for Unix-like operating
systems such as Linux. Now you, too, can run this small piece of
computing history!
systems such as Linux, with subsequent releases to add features and
correct bugs. Now you, too, can run this small piece of computing
history!
.\" *********************************************************************
.SH "SEE ALSO"
.URL http://www.zap.org.au/software/trader/ "Star Traders home page"

19
lib/.gitignore vendored
View File

@ -1,15 +1,4 @@
alloca.h
arg-nonnull.h
c++defs.h
ctype.h
getopt.h
locale.h
math.h
stdio.h
stdlib.h
string.h
sys/
time.h
unistd.h
warn-on-use.h
wchar.h
unistr/.dirstamp
unistr/u8-mbtoucr.c
unistr/u8-uctomb-aux.c
unistr/u8-uctomb.c

1
m4/.gitignore vendored
View File

@ -1 +0,0 @@
largefile.m4

View File

@ -15,33 +15,34 @@
# Specification in the form of a command-line invocation:
# gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --no-conditional-dependencies --no-libtool --macro-prefix=gl assert config-h ctype fprintf-posix getopt-gnu gettimeofday locale printf-posix snprintf-posix stat stdarg stdbool stdio strdup-posix string strncat strstr sys_stat sys_time unistd vfprintf-posix vsnprintf-posix
# gnulib-tool --import --dir=. --lib=libgnu --source-base=lib --m4-base=m4 --doc-base=doc --tests-base=tests --aux-dir=build-aux --no-conditional-dependencies --no-libtool --macro-prefix=gl assert btowc config-h getopt-gnu gettext gettext-h gettimeofday locale mbrtowc mbsrtowcs stdbool stdio striconv string strstr sys_stat sys_time unistd wchar wcrtomb wcsdup wctob wctype-h
# Specification in the form of a few gnulib-tool.m4 macro invocations:
gl_LOCAL_DIR([])
gl_MODULES([
assert
btowc
config-h
ctype
fprintf-posix
getopt-gnu
gettext
gettext-h
gettimeofday
locale
printf-posix
snprintf-posix
stat
stdarg
mbrtowc
mbsrtowcs
stdbool
stdio
strdup-posix
striconv
string
strncat
strstr
sys_stat
sys_time
unistd
vfprintf-posix
vsnprintf-posix
wchar
wcrtomb
wcsdup
wctob
wctype-h
])
gl_AVOID([])
gl_SOURCE_BASE([lib])

33
po/LINGUAS Normal file
View File

@ -0,0 +1,33 @@
#########################################################################
# #
# Star Traders: A Game of Interstellar Trading #
# Copyright (C) 1990-2011, John Zaitseff #
# #
#########################################################################
# Author: John Zaitseff <J.Zaitseff@zap.org.au>
# $Id$
#
# This file, po/LINGUAS, contains a list of the available languages in
# the po directory.
#
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see http://www.gnu.org/licenses/.
en@quot
en_AU
en_CA
en_GB
en_US

444
po/Makefile.in.in Normal file
View File

@ -0,0 +1,444 @@
# Makefile for PO directory in any package using GNU gettext.
# Copyright (C) 1995-1997, 2000-2007, 2009-2010 by Ulrich Drepper <drepper@gnu.ai.mit.edu>
#
# This file can be copied and used freely without restrictions. It can
# be used in projects which are not available under the GNU General Public
# License but which still want to provide support for the GNU gettext
# functionality.
# Please note that the actual code of GNU gettext is covered by the GNU
# General Public License and is *not* in the public domain.
#
# Origin: gettext-0.18
GETTEXT_MACRO_VERSION = 0.18
PACKAGE = @PACKAGE@
VERSION = @VERSION@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
SHELL = /bin/sh
@SET_MAKE@
srcdir = @srcdir@
top_srcdir = @top_srcdir@
VPATH = @srcdir@
prefix = @prefix@
exec_prefix = @exec_prefix@
datarootdir = @datarootdir@
datadir = @datadir@
localedir = @localedir@
gettextsrcdir = $(datadir)/gettext/po
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
# We use $(mkdir_p).
# In automake <= 1.9.x, $(mkdir_p) is defined either as "mkdir -p --" or as
# "$(mkinstalldirs)" or as "$(install_sh) -d". For these automake versions,
# @install_sh@ does not start with $(SHELL), so we add it.
# In automake >= 1.10, @mkdir_p@ is derived from ${MKDIR_P}, which is defined
# either as "/path/to/mkdir -p" or ".../install-sh -c -d". For these automake
# versions, $(mkinstalldirs) and $(install_sh) are unused.
mkinstalldirs = $(SHELL) @install_sh@ -d
install_sh = $(SHELL) @install_sh@
MKDIR_P = @MKDIR_P@
mkdir_p = @mkdir_p@
GMSGFMT_ = @GMSGFMT@
GMSGFMT_no = @GMSGFMT@
GMSGFMT_yes = @GMSGFMT_015@
GMSGFMT = $(GMSGFMT_$(USE_MSGCTXT))
MSGFMT_ = @MSGFMT@
MSGFMT_no = @MSGFMT@
MSGFMT_yes = @MSGFMT_015@
MSGFMT = $(MSGFMT_$(USE_MSGCTXT))
XGETTEXT_ = @XGETTEXT@
XGETTEXT_no = @XGETTEXT@
XGETTEXT_yes = @XGETTEXT_015@
XGETTEXT = $(XGETTEXT_$(USE_MSGCTXT))
MSGMERGE = msgmerge
MSGMERGE_UPDATE = @MSGMERGE@ --update
MSGINIT = msginit
MSGCONV = msgconv
MSGFILTER = msgfilter
POFILES = @POFILES@
GMOFILES = @GMOFILES@
UPDATEPOFILES = @UPDATEPOFILES@
DUMMYPOFILES = @DUMMYPOFILES@
DISTFILES.common = Makefile.in.in remove-potcdate.sin \
$(DISTFILES.common.extra1) $(DISTFILES.common.extra2) $(DISTFILES.common.extra3)
DISTFILES = $(DISTFILES.common) Makevars POTFILES.in \
$(POFILES) $(GMOFILES) \
$(DISTFILES.extra1) $(DISTFILES.extra2) $(DISTFILES.extra3)
POTFILES = \
CATALOGS = @CATALOGS@
# Makevars gets inserted here. (Don't remove this line!)
.SUFFIXES:
.SUFFIXES: .po .gmo .mo .sed .sin .nop .po-create .po-update
.po.mo:
@echo "$(MSGFMT) -c -o $@ $<"; \
$(MSGFMT) -c -o t-$@ $< && mv t-$@ $@
.po.gmo:
@lang=`echo $* | sed -e 's,.*/,,'`; \
test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
echo "$${cdcmd}rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics --verbose -o $${lang}.gmo $${lang}.po"; \
cd $(srcdir) && rm -f $${lang}.gmo && $(GMSGFMT) -c --statistics --verbose -o t-$${lang}.gmo $${lang}.po && mv t-$${lang}.gmo $${lang}.gmo
.sin.sed:
sed -e '/^#/d' $< > t-$@
mv t-$@ $@
all: check-macro-version all-@USE_NLS@
all-yes: stamp-po
all-no:
# Ensure that the gettext macros and this Makefile.in.in are in sync.
check-macro-version:
@test "$(GETTEXT_MACRO_VERSION)" = "@GETTEXT_MACRO_VERSION@" \
|| { echo "*** error: gettext infrastructure mismatch: using a Makefile.in.in from gettext version $(GETTEXT_MACRO_VERSION) but the autoconf macros are from gettext version @GETTEXT_MACRO_VERSION@" 1>&2; \
exit 1; \
}
# $(srcdir)/$(DOMAIN).pot is only created when needed. When xgettext finds no
# internationalized messages, no $(srcdir)/$(DOMAIN).pot is created (because
# we don't want to bother translators with empty POT files). We assume that
# LINGUAS is empty in this case, i.e. $(POFILES) and $(GMOFILES) are empty.
# In this case, stamp-po is a nop (i.e. a phony target).
# stamp-po is a timestamp denoting the last time at which the CATALOGS have
# been loosely updated. Its purpose is that when a developer or translator
# checks out the package via CVS, and the $(DOMAIN).pot file is not in CVS,
# "make" will update the $(DOMAIN).pot and the $(CATALOGS), but subsequent
# invocations of "make" will do nothing. This timestamp would not be necessary
# if updating the $(CATALOGS) would always touch them; however, the rule for
# $(POFILES) has been designed to not touch files that don't need to be
# changed.
stamp-po: $(srcdir)/$(DOMAIN).pot
test ! -f $(srcdir)/$(DOMAIN).pot || \
test -z "$(GMOFILES)" || $(MAKE) $(GMOFILES)
@test ! -f $(srcdir)/$(DOMAIN).pot || { \
echo "touch stamp-po" && \
echo timestamp > stamp-poT && \
mv stamp-poT stamp-po; \
}
# Note: Target 'all' must not depend on target '$(DOMAIN).pot-update',
# otherwise packages like GCC can not be built if only parts of the source
# have been downloaded.
# This target rebuilds $(DOMAIN).pot; it is an expensive operation.
# Note that $(DOMAIN).pot is not touched if it doesn't need to be changed.
$(DOMAIN).pot-update: $(POTFILES) $(srcdir)/POTFILES.in remove-potcdate.sed
if LC_ALL=C grep 'GNU @PACKAGE@' $(top_srcdir)/* 2>/dev/null | grep -v 'libtool:' >/dev/null; then \
package_gnu='GNU '; \
else \
package_gnu=''; \
fi; \
if test -n '$(MSGID_BUGS_ADDRESS)' || test '$(PACKAGE_BUGREPORT)' = '@'PACKAGE_BUGREPORT'@'; then \
msgid_bugs_address='$(MSGID_BUGS_ADDRESS)'; \
else \
msgid_bugs_address='$(PACKAGE_BUGREPORT)'; \
fi; \
case `$(XGETTEXT) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \
'' | 0.[0-9] | 0.[0-9].* | 0.1[0-5] | 0.1[0-5].* | 0.16 | 0.16.[0-1]*) \
$(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \
--add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) @XGETTEXT_EXTRA_OPTIONS@ \
--files-from=$(srcdir)/POTFILES.in \
--copyright-holder='$(COPYRIGHT_HOLDER)' \
--msgid-bugs-address="$$msgid_bugs_address" \
;; \
*) \
$(XGETTEXT) --default-domain=$(DOMAIN) --directory=$(top_srcdir) \
--add-comments=TRANSLATORS: $(XGETTEXT_OPTIONS) @XGETTEXT_EXTRA_OPTIONS@ \
--files-from=$(srcdir)/POTFILES.in \
--copyright-holder='$(COPYRIGHT_HOLDER)' \
--package-name="$${package_gnu}@PACKAGE@" \
--package-version='@VERSION@' \
--msgid-bugs-address="$$msgid_bugs_address" \
;; \
esac
test ! -f $(DOMAIN).po || { \
if test -f $(srcdir)/$(DOMAIN).pot; then \
sed -f remove-potcdate.sed < $(srcdir)/$(DOMAIN).pot > $(DOMAIN).1po && \
sed -f remove-potcdate.sed < $(DOMAIN).po > $(DOMAIN).2po && \
if cmp $(DOMAIN).1po $(DOMAIN).2po >/dev/null 2>&1; then \
rm -f $(DOMAIN).1po $(DOMAIN).2po $(DOMAIN).po; \
else \
rm -f $(DOMAIN).1po $(DOMAIN).2po $(srcdir)/$(DOMAIN).pot && \
mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \
fi; \
else \
mv $(DOMAIN).po $(srcdir)/$(DOMAIN).pot; \
fi; \
}
# This rule has no dependencies: we don't need to update $(DOMAIN).pot at
# every "make" invocation, only create it when it is missing.
# Only "make $(DOMAIN).pot-update" or "make dist" will force an update.
$(srcdir)/$(DOMAIN).pot:
$(MAKE) $(DOMAIN).pot-update
# This target rebuilds a PO file if $(DOMAIN).pot has changed.
# Note that a PO file is not touched if it doesn't need to be changed.
$(POFILES): $(srcdir)/$(DOMAIN).pot
@lang=`echo $@ | sed -e 's,.*/,,' -e 's/\.po$$//'`; \
if test -f "$(srcdir)/$${lang}.po"; then \
test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
echo "$${cdcmd}$(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) --lang=$${lang} $${lang}.po $(DOMAIN).pot"; \
cd $(srcdir) \
&& { case `$(MSGMERGE_UPDATE) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \
'' | 0.[0-9] | 0.[0-9].* | 0.1[0-7] | 0.1[0-7].*) \
$(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) $${lang}.po $(DOMAIN).pot;; \
*) \
$(MSGMERGE_UPDATE) $(MSGMERGE_OPTIONS) --lang=$${lang} $${lang}.po $(DOMAIN).pot;; \
esac; \
}; \
else \
$(MAKE) $${lang}.po-create; \
fi
install: install-exec install-data
install-exec:
install-data: install-data-@USE_NLS@
if test "$(PACKAGE)" = "gettext-tools"; then \
$(mkdir_p) $(DESTDIR)$(gettextsrcdir); \
for file in $(DISTFILES.common) Makevars.template; do \
$(INSTALL_DATA) $(srcdir)/$$file \
$(DESTDIR)$(gettextsrcdir)/$$file; \
done; \
for file in Makevars; do \
rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \
done; \
else \
: ; \
fi
install-data-no: all
install-data-yes: all
@catalogs='$(CATALOGS)'; \
for cat in $$catalogs; do \
cat=`basename $$cat`; \
lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
dir=$(localedir)/$$lang/LC_MESSAGES; \
$(mkdir_p) $(DESTDIR)$$dir; \
if test -r $$cat; then realcat=$$cat; else realcat=$(srcdir)/$$cat; fi; \
$(INSTALL_DATA) $$realcat $(DESTDIR)$$dir/$(DOMAIN).mo; \
echo "installing $$realcat as $(DESTDIR)$$dir/$(DOMAIN).mo"; \
for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \
if test -n "$$lc"; then \
if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \
link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \
mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
(cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \
for file in *; do \
if test -f $$file; then \
ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \
fi; \
done); \
rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
else \
if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \
:; \
else \
rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \
mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
fi; \
fi; \
rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
ln -s ../LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \
ln $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo 2>/dev/null || \
cp -p $(DESTDIR)$(localedir)/$$lang/LC_MESSAGES/$(DOMAIN).mo $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
echo "installing $$realcat link as $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo"; \
fi; \
done; \
done
install-strip: install
installdirs: installdirs-exec installdirs-data
installdirs-exec:
installdirs-data: installdirs-data-@USE_NLS@
if test "$(PACKAGE)" = "gettext-tools"; then \
$(mkdir_p) $(DESTDIR)$(gettextsrcdir); \
else \
: ; \
fi
installdirs-data-no:
installdirs-data-yes:
@catalogs='$(CATALOGS)'; \
for cat in $$catalogs; do \
cat=`basename $$cat`; \
lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
dir=$(localedir)/$$lang/LC_MESSAGES; \
$(mkdir_p) $(DESTDIR)$$dir; \
for lc in '' $(EXTRA_LOCALE_CATEGORIES); do \
if test -n "$$lc"; then \
if (cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc 2>/dev/null) | grep ' -> ' >/dev/null; then \
link=`cd $(DESTDIR)$(localedir)/$$lang && LC_ALL=C ls -l -d $$lc | sed -e 's/^.* -> //'`; \
mv $(DESTDIR)$(localedir)/$$lang/$$lc $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
(cd $(DESTDIR)$(localedir)/$$lang/$$lc.old && \
for file in *; do \
if test -f $$file; then \
ln -s ../$$link/$$file $(DESTDIR)$(localedir)/$$lang/$$lc/$$file; \
fi; \
done); \
rm -f $(DESTDIR)$(localedir)/$$lang/$$lc.old; \
else \
if test -d $(DESTDIR)$(localedir)/$$lang/$$lc; then \
:; \
else \
rm -f $(DESTDIR)$(localedir)/$$lang/$$lc; \
mkdir $(DESTDIR)$(localedir)/$$lang/$$lc; \
fi; \
fi; \
fi; \
done; \
done
# Define this as empty until I found a useful application.
installcheck:
uninstall: uninstall-exec uninstall-data
uninstall-exec:
uninstall-data: uninstall-data-@USE_NLS@
if test "$(PACKAGE)" = "gettext-tools"; then \
for file in $(DISTFILES.common) Makevars.template; do \
rm -f $(DESTDIR)$(gettextsrcdir)/$$file; \
done; \
else \
: ; \
fi
uninstall-data-no:
uninstall-data-yes:
catalogs='$(CATALOGS)'; \
for cat in $$catalogs; do \
cat=`basename $$cat`; \
lang=`echo $$cat | sed -e 's/\.gmo$$//'`; \
for lc in LC_MESSAGES $(EXTRA_LOCALE_CATEGORIES); do \
rm -f $(DESTDIR)$(localedir)/$$lang/$$lc/$(DOMAIN).mo; \
done; \
done
check: all
info dvi ps pdf html tags TAGS ctags CTAGS ID:
mostlyclean:
rm -f remove-potcdate.sed
rm -f stamp-poT
rm -f core core.* $(DOMAIN).po $(DOMAIN).1po $(DOMAIN).2po *.new.po
rm -fr *.o
clean: mostlyclean
distclean: clean
rm -f Makefile Makefile.in POTFILES *.mo
maintainer-clean: distclean
@echo "This command is intended for maintainers to use;"
@echo "it deletes files that may require special tools to rebuild."
rm -f stamp-po $(GMOFILES)
distdir = $(top_builddir)/$(PACKAGE)-$(VERSION)/$(subdir)
dist distdir:
$(MAKE) update-po
@$(MAKE) dist2
# This is a separate target because 'update-po' must be executed before.
dist2: stamp-po $(DISTFILES)
dists="$(DISTFILES)"; \
if test "$(PACKAGE)" = "gettext-tools"; then \
dists="$$dists Makevars.template"; \
fi; \
if test -f $(srcdir)/$(DOMAIN).pot; then \
dists="$$dists $(DOMAIN).pot stamp-po"; \
fi; \
if test -f $(srcdir)/ChangeLog; then \
dists="$$dists ChangeLog"; \
fi; \
for i in 0 1 2 3 4 5 6 7 8 9; do \
if test -f $(srcdir)/ChangeLog.$$i; then \
dists="$$dists ChangeLog.$$i"; \
fi; \
done; \
if test -f $(srcdir)/LINGUAS; then dists="$$dists LINGUAS"; fi; \
for file in $$dists; do \
if test -f $$file; then \
cp -p $$file $(distdir) || exit 1; \
else \
cp -p $(srcdir)/$$file $(distdir) || exit 1; \
fi; \
done
update-po: Makefile
$(MAKE) $(DOMAIN).pot-update
test -z "$(UPDATEPOFILES)" || $(MAKE) $(UPDATEPOFILES)
$(MAKE) update-gmo
# General rule for creating PO files.
.nop.po-create:
@lang=`echo $@ | sed -e 's/\.po-create$$//'`; \
echo "File $$lang.po does not exist. If you are a translator, you can create it through 'msginit'." 1>&2; \
exit 1
# General rule for updating PO files.
.nop.po-update:
@lang=`echo $@ | sed -e 's/\.po-update$$//'`; \
if test "$(PACKAGE)" = "gettext-tools"; then PATH=`pwd`/../src:$$PATH; fi; \
tmpdir=`pwd`; \
echo "$$lang:"; \
test "$(srcdir)" = . && cdcmd="" || cdcmd="cd $(srcdir) && "; \
echo "$${cdcmd}$(MSGMERGE) $(MSGMERGE_OPTIONS) --lang=$$lang $$lang.po $(DOMAIN).pot -o $$lang.new.po"; \
cd $(srcdir); \
if { case `$(MSGMERGE) --version | sed 1q | sed -e 's,^[^0-9]*,,'` in \
'' | 0.[0-9] | 0.[0-9].* | 0.1[0-7] | 0.1[0-7].*) \
$(MSGMERGE) $(MSGMERGE_OPTIONS) -o $$tmpdir/$$lang.new.po $$lang.po $(DOMAIN).pot;; \
*) \
$(MSGMERGE) $(MSGMERGE_OPTIONS) --lang=$$lang -o $$tmpdir/$$lang.new.po $$lang.po $(DOMAIN).pot;; \
esac; \
}; then \
if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \
rm -f $$tmpdir/$$lang.new.po; \
else \
if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \
:; \
else \
echo "msgmerge for $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \
exit 1; \
fi; \
fi; \
else \
echo "msgmerge for $$lang.po failed!" 1>&2; \
rm -f $$tmpdir/$$lang.new.po; \
fi
$(DUMMYPOFILES):
update-gmo: Makefile $(GMOFILES)
@:
# Recreate Makefile by invoking config.status. Explicitly invoke the shell,
# because execution permission bits may not work on the current file system.
# Use @SHELL@, which is the shell determined by autoconf for the use by its
# scripts, not $(SHELL) which is hardwired to /bin/sh and may be deficient.
Makefile: Makefile.in.in Makevars $(top_builddir)/config.status @POMAKEFILEDEPS@
cd $(top_builddir) \
&& @SHELL@ ./config.status $(subdir)/$@.in po-directories
force:
# Tell versions [3.59,3.63) of GNU make not to export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:

55
po/Makevars Normal file
View File

@ -0,0 +1,55 @@
#########################################################################
# #
# Star Traders: A Game of Interstellar Trading #
# Copyright (C) 1990-2011, John Zaitseff #
# #
#########################################################################
# Author: John Zaitseff <J.Zaitseff@zap.org.au>
# $Id$
#
# This file, po/Makevars, contains variables that are substituted into
# po/Makefile for use with GNU gettext.
#
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see http://www.gnu.org/licenses/.
# The message domain is the same as the package name
DOMAIN = $(PACKAGE)
# These two variables depend on the location of this directory
subdir = po
top_builddir = ..
# These options get passed to xgettext
XGETTEXT_OPTIONS = --from-code=UTF-8 --keyword=_ --keyword=N_ --width=132
# These options get passed to msgmerge
MSGMERGE_OPTIONS = --width=132
# Copyright holder that gets inserted into the header of the
# $(DOMAIN).pot file.
COPYRIGHT_HOLDER = John Zaitseff
# E-mail address or URL used by translators to report bugs in the
# untranslated (original) strings
MSGID_BUGS_ADDRESS = J.Zaitseff@zap.org.au
# List of locale categories, beyond LC_MESSAGES, for which the message
# catalogs shall be used. It is usually empty.
EXTRA_LOCALE_CATEGORIES =
# Additional files to distribute
DISTFILES += README

42
po/POTFILES.in Normal file
View File

@ -0,0 +1,42 @@
#########################################################################
# #
# Star Traders: A Game of Interstellar Trading #
# Copyright (C) 1990-2011, John Zaitseff #
# #
#########################################################################
# Author: John Zaitseff <J.Zaitseff@zap.org.au>
# $Id$
#
# This file, po/POTFILES.in, contains a list of source files which
# contain translatable strings.
#
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU General Public License as published by the
# Free Software Foundation, either version 3 of the License, or (at your
# option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see http://www.gnu.org/licenses/.
# Source files from Star Traders
src/trader.c
src/globals.c
src/game.c
src/move.c
src/exch.c
src/fileio.c
src/help.c
src/intf.c
src/utils.c
# Source files from the Gnulib GNU Portability Library
lib/getopt.c

9
po/README Normal file
View File

@ -0,0 +1,9 @@
**************************************************************************
* *
* Star Traders: A Game of Interstellar Trading *
* Copyright (C) 1990-2011, John Zaitseff *
* *
**************************************************************************
This directory, po, contains translations for text strings used in Star
Traders. The GNU Gettext project is used for this purpose.

47
po/Rules-quot Normal file
View File

@ -0,0 +1,47 @@
# Special Makefile rules for English message catalogs with quotation marks.
DISTFILES.common.extra1 = quot.sed boldquot.sed en@quot.header en@boldquot.header insert-header.sin Rules-quot
.SUFFIXES: .insert-header .po-update-en
en@quot.po-create:
$(MAKE) en@quot.po-update
en@boldquot.po-create:
$(MAKE) en@boldquot.po-update
en@quot.po-update: en@quot.po-update-en
en@boldquot.po-update: en@boldquot.po-update-en
.insert-header.po-update-en:
@lang=`echo $@ | sed -e 's/\.po-update-en$$//'`; \
if test "$(PACKAGE)" = "gettext"; then PATH=`pwd`/../src:$$PATH; GETTEXTLIBDIR=`cd $(top_srcdir)/src && pwd`; export GETTEXTLIBDIR; fi; \
tmpdir=`pwd`; \
echo "$$lang:"; \
ll=`echo $$lang | sed -e 's/@.*//'`; \
LC_ALL=C; export LC_ALL; \
cd $(srcdir); \
if $(MSGINIT) -i $(DOMAIN).pot --no-translator -l $$lang -o - 2>/dev/null | sed -f $$tmpdir/$$lang.insert-header | $(MSGCONV) -t UTF-8 | $(MSGFILTER) sed -f `echo $$lang | sed -e 's/.*@//'`.sed 2>/dev/null > $$tmpdir/$$lang.new.po; then \
if cmp $$lang.po $$tmpdir/$$lang.new.po >/dev/null 2>&1; then \
rm -f $$tmpdir/$$lang.new.po; \
else \
if mv -f $$tmpdir/$$lang.new.po $$lang.po; then \
:; \
else \
echo "creation of $$lang.po failed: cannot move $$tmpdir/$$lang.new.po to $$lang.po" 1>&2; \
exit 1; \
fi; \
fi; \
else \
echo "creation of $$lang.po failed!" 1>&2; \
rm -f $$tmpdir/$$lang.new.po; \
fi
en@quot.insert-header: insert-header.sin
sed -e '/^#/d' -e 's/HEADER/en@quot.header/g' $(srcdir)/insert-header.sin > en@quot.insert-header
en@boldquot.insert-header: insert-header.sin
sed -e '/^#/d' -e 's/HEADER/en@boldquot.header/g' $(srcdir)/insert-header.sin > en@boldquot.insert-header
mostlyclean: mostlyclean-quot
mostlyclean-quot:
rm -f *.insert-header

10
po/boldquot.sed Normal file
View File

@ -0,0 +1,10 @@
s/"\([^"]*\)"/“\1”/g
s/`\([^`']*\)'/\1/g
s/ '\([^`']*\)' / \1 /g
s/ '\([^`']*\)'$/ \1/g
s/^'\([^`']*\)' /\1 /g
s/“”/""/g
s///g
s//”/g
s///g
s///g

25
po/en@boldquot.header Normal file
View File

@ -0,0 +1,25 @@
# All this catalog "translates" are quotation characters.
# The msgids must be ASCII and therefore cannot contain real quotation
# characters, only substitutes like grave accent (0x60), apostrophe (0x27)
# and double quote (0x22). These substitutes look strange; see
# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html
#
# This catalog translates grave accent (0x60) and apostrophe (0x27) to
# left single quotation mark (U+2018) and right single quotation mark (U+2019).
# It also translates pairs of apostrophe (0x27) to
# left single quotation mark (U+2018) and right single quotation mark (U+2019)
# and pairs of quotation mark (0x22) to
# left double quotation mark (U+201C) and right double quotation mark (U+201D).
#
# When output to an UTF-8 terminal, the quotation characters appear perfectly.
# When output to an ISO-8859-1 terminal, the single quotation marks are
# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to
# grave/acute accent (by libiconv), and the double quotation marks are
# transliterated to 0x22.
# When output to an ASCII terminal, the single quotation marks are
# transliterated to apostrophes, and the double quotation marks are
# transliterated to 0x22.
#
# This catalog furthermore displays the text between the quotation marks in
# bold face, assuming the VT100/XTerm escape sequences.
#

22
po/en@quot.header Normal file
View File

@ -0,0 +1,22 @@
# All this catalog "translates" are quotation characters.
# The msgids must be ASCII and therefore cannot contain real quotation
# characters, only substitutes like grave accent (0x60), apostrophe (0x27)
# and double quote (0x22). These substitutes look strange; see
# http://www.cl.cam.ac.uk/~mgk25/ucs/quotes.html
#
# This catalog translates grave accent (0x60) and apostrophe (0x27) to
# left single quotation mark (U+2018) and right single quotation mark (U+2019).
# It also translates pairs of apostrophe (0x27) to
# left single quotation mark (U+2018) and right single quotation mark (U+2019)
# and pairs of quotation mark (0x22) to
# left double quotation mark (U+201C) and right double quotation mark (U+201D).
#
# When output to an UTF-8 terminal, the quotation characters appear perfectly.
# When output to an ISO-8859-1 terminal, the single quotation marks are
# transliterated to apostrophes (by iconv in glibc 2.2 or newer) or to
# grave/acute accent (by libiconv), and the double quotation marks are
# transliterated to 0x22.
# When output to an ASCII terminal, the single quotation marks are
# transliterated to apostrophes, and the double quotation marks are
# transliterated to 0x22.
#

1566
po/en_AU.po Normal file

File diff suppressed because it is too large Load Diff

1566
po/en_CA.po Normal file

File diff suppressed because it is too large Load Diff

1566
po/en_GB.po Normal file

File diff suppressed because it is too large Load Diff

1566
po/en_US.po Normal file

File diff suppressed because it is too large Load Diff

23
po/insert-header.sin Normal file
View File

@ -0,0 +1,23 @@
# Sed script that inserts the file called HEADER before the header entry.
#
# At each occurrence of a line starting with "msgid ", we execute the following
# commands. At the first occurrence, insert the file. At the following
# occurrences, do nothing. The distinction between the first and the following
# occurrences is achieved by looking at the hold space.
/^msgid /{
x
# Test if the hold space is empty.
s/m/m/
ta
# Yes it was empty. First occurrence. Read the file.
r HEADER
# Output the file's contents by reading the next line. But don't lose the
# current line while doing this.
g
N
bb
:a
# The hold space was nonempty. Following occurrences. Do nothing.
x
:b
}

6
po/quot.sed Normal file
View File

@ -0,0 +1,6 @@
s/"\([^"]*\)"/“\1”/g
s/`\([^`']*\)'/\1/g
s/ '\([^`']*\)' / \1 /g
s/ '\([^`']*\)'$/ \1/g
s/^'\([^`']*\)' /\1 /g
s/“”/""/g

19
po/remove-potcdate.sin Normal file
View File

@ -0,0 +1,19 @@
# Sed script that remove the POT-Creation-Date line in the header entry
# from a POT file.
#
# The distinction between the first and the following occurrences of the
# pattern is achieved by looking at the hold space.
/^"POT-Creation-Date: .*"$/{
x
# Test if the hold space is empty.
s/P/P/
ta
# Yes it was empty. First occurrence. Remove the line.
g
d
bb
:a
# The hold space was nonempty. Following occurrences. Do nothing.
x
:b
}

View File

@ -41,7 +41,8 @@ trader_SOURCES = \
utils.c utils.h \
system.h
AM_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib
trader_LDADD = @LIBINTL@ @CURSES_LIB@ $(top_builddir)/lib/libgnu.a
trader_CPPFLAGS = -I$(top_builddir)/lib -I$(top_srcdir)/lib \
-DLOCALEDIR=\"$(localedir)\"
trader_LDADD = @CURSES_LIB@ @LIBICONV@ $(top_builddir)/lib/libgnu.a @LIBINTL@
EXTRA_DIST = README

View File

@ -76,7 +76,7 @@ void exchange_stock (void)
selection_t selection = SEL_NONE;
bool bid_used = false;
bool all_off_map;
int i, line;
int w, i, line;
if (quit_selected || abort_game || ! player[current_player].in_game) {
@ -84,18 +84,20 @@ void exchange_stock (void)
}
newtxwin(17, WIN_COLS, 1, WCENTER, false, 0);
w = getmaxx(curwin);
while (selection != SEL_EXIT) {
selection = SEL_NONE;
// Display (or refresh) the Stock Exchange window
wbkgd(curwin, attr_normal_window);
wbkgdset(curwin, attr_normal_window);
werase(curwin);
box(curwin, 0, 0);
center(curwin, 1, attr_title, " Interstellar Stock Exchange ");
center2(curwin, 2, attr_normal, attr_highlight, "Player: ", "%s",
player[current_player].name);
center(curwin, 1, 0, attr_title, 0, 0, 1,
_(" Interstellar Stock Exchange "));
center(curwin, 2, 0, attr_normal, attr_highlight, 0, 1,
_("Player: ^{%ls^}"), player[current_player].name);
all_off_map = true;
for (i = 0; i < MAX_COMPANIES; i++) {
@ -106,37 +108,70 @@ void exchange_stock (void)
}
if (all_off_map) {
center(curwin, 8, attr_normal, "No companies on the map");
center(curwin, 8, 0, attr_normal, attr_highlight, 0, 1,
_("No companies on the map"));
} else {
char *buf = malloc(BUFSIZE);
if (buf == NULL) {
err_exit_nomem();
}
mvwhline(curwin, 4, 2, ' ' | attr_subtitle, w - 4);
mvwhline(curwin, 5, 2, ' ' | attr_subtitle, w - 4);
// Handle the locale's currency symbol
snprintf(buf, BUFSIZE, "share (%s)", lconvinfo.currency_symbol);
wattrset(curwin, attr_subtitle);
mvwprintw(curwin, 4, 2, " %-22s %12s %10s %10s %10s ",
"", "Price per", "", "Shares", "Shares");
mvwprintw(curwin, 5, 2, " %-22s %12s %10s %10s %10s ",
"Company", buf, "Return (%)", "issued", "left");
wattrset(curwin, attr_normal);
left(curwin, 4, 4, attr_subtitle, 0, 0, 2,
/* TRANSLATORS: "Company" is a two-line column label in
a table containing a list of companies. */
pgettext("subtitle", "\nCompany"));
right(curwin, 4, w - 4, attr_subtitle, 0, 0, 2,
/* TRANSLATORS: "Shares left" is a two-line column
label in a table containing the number of shares
left to be purchased in any given company. The
maximum column width is 10 characters (see
STOCK_LEFT_COLS in src/intf.h). */
pgettext("subtitle", "Shares\nleft"));
right(curwin, 4, w - 6 - STOCK_LEFT_COLS, attr_subtitle, 0, 0, 2,
/* TRANSLATORS: "Shares issued" is a two-line column
label in a table containing the number of shares
already sold (ie, bought by all players) in any
given company. The maximum column width is 10
characters (see STOCK_ISSUED_COLS in src/intf.h). */
pgettext("subtitle", "Shares\nissued"));
right(curwin, 4, w - 8 - STOCK_LEFT_COLS - STOCK_ISSUED_COLS,
attr_subtitle, 0, 0, 2,
/* TRANSLATORS: "Return" is a two-line column label in
a table containing the share return as a percentage
in any given company. The maximum column width is
10 characters (see SHARE_RETURN_COLS in src/intf.h). */
pgettext("subtitle", "Return\n(%%)"));
right(curwin, 4, w - 10 - STOCK_LEFT_COLS - STOCK_ISSUED_COLS
- SHARE_RETURN_COLS, attr_subtitle, 0, 0, 2,
/* TRANSLATORS: "Price per share" is a two-line column
label in a table containing the price per share in
any given company. %ls is the currency symbol in
the current locale. The maximum column width is 12
characters INCLUDING the currency symbol (see
SHARE_PRICE_COLS in src/intf.h). */
pgettext("subtitle", "Price per\nshare (%ls)"),
currency_symbol);
for (line = 6, i = 0; i < MAX_COMPANIES; i++) {
if (company[i].on_map) {
mvwaddch(curwin, line, 2, PRINTABLE_MAP_VAL(COMPANY_TO_MAP(i))
| attr_choice);
l_strfmon(buf, BUFSIZE, "%!12n", company[i].share_price);
mvwprintw(curwin, line, 4, "%-22s %12s %10.2f %'10ld %'10ld ",
company[i].name, buf, company[i].share_return
* 100.0, company[i].stock_issued,
company[i].max_stock - company[i].stock_issued);
left(curwin, line, 4, attr_normal, 0, 0, 1, "%ls",
company[i].name);
right(curwin, line, w - 2, attr_normal, 0, 0, 1, "%'ld ",
company[i].max_stock - company[i].stock_issued);
right(curwin, line, w - 4 - STOCK_LEFT_COLS, attr_normal,
0, 0, 1, "%'ld ", company[i].stock_issued);
right(curwin, line, w - 6 - STOCK_LEFT_COLS
- STOCK_ISSUED_COLS, attr_normal, 0, 0, 1, "%.2f ",
company[i].share_return * 100.0);
right(curwin, line, w - 8 - STOCK_LEFT_COLS
- STOCK_ISSUED_COLS - SHARE_RETURN_COLS, attr_normal,
0, 0, 1, " %!N ", company[i].share_price);
line++;
}
}
free(buf);
}
wrefresh(curwin);
@ -144,64 +179,78 @@ void exchange_stock (void)
// Show menu of choices for the player
newtxwin(6, WIN_COLS, 18, WCENTER, true, attr_normal_window);
wmove(curwin, 3, 2);
attrpr(curwin, attr_keycode, "<1>");
waddstr(curwin, " Display stock portfolio");
left(curwin, 3, 2, attr_normal, attr_keycode, 0, 1,
_("^{<1>^} Display stock portfolio"));
left(curwin, 4, 2, attr_normal, attr_keycode, 0, 1,
_("^{<2>^} Display galaxy map"));
left(curwin, 3, getmaxx(curwin) / 2, attr_normal, attr_keycode, 0, 1,
_("^{<3>^} Visit the Trading Bank"));
left(curwin, 4, getmaxx(curwin) / 2, attr_normal, attr_keycode, 0, 1,
_("^{<4>^} Exit the Stock Exchange"));
wmove(curwin, 4, 2);
attrpr(curwin, attr_keycode, "<2>");
waddstr(curwin, " Display galaxy map");
wmove(curwin, 3, 40);
attrpr(curwin, attr_keycode, "<3>");
waddstr(curwin, " Visit the Trading Bank");
wmove(curwin, 4, 40);
attrpr(curwin, attr_keycode, "<4>");
waddstr(curwin, " Exit the Stock Exchange");
mvwaddstr(curwin, 1, 18, "Enter selection ");
waddstr(curwin, "[");
attrpr(curwin, attr_highlight, "Company letter");
waddstr(curwin, "/");
attrpr(curwin, attr_keycode, "1");
waddstr(curwin, "-");
attrpr(curwin, attr_keycode, "4");
waddstr(curwin, "]: ");
center(curwin, 1, -1, attr_normal, attr_keycode, attr_highlight, 1,
_("Enter selection [^[Company letter^]/^{1^}-^{4^}]: "));
curs_set(CURS_ON);
wrefresh(curwin);
// Get the actual selection made by the player
while (selection == SEL_NONE) {
int key = toupper(gettxchar(curwin));
wint_t key;
if (IS_COMPANY_KEY(key)) {
if (company[KEY_TO_COMPANY(key)].on_map) {
selection = KEY_TO_COMPANY(key);
} else {
beep();
if (gettxchar(curwin, &key) == OK) {
// Ordinary wide character
bool found;
if (iswupper(*keycode_company)) {
key = towupper(key);
} else if (iswlower(*keycode_company)) {
key = towlower(key);
}
for (i = 0, found = false; keycode_company[i] != L'\0'; i++) {
if (keycode_company[i] == key) {
found = true;
if (company[i].on_map) {
selection = i;
} else {
beep();
}
break;
}
}
if (! found) {
switch (key) {
case L'1':
curs_set(CURS_OFF);
show_status(current_player);
curs_set(CURS_ON);
break;
case L'2':
curs_set(CURS_OFF);
show_map(true);
curs_set(CURS_ON);
break;
case L'3':
selection = SEL_BANK;
break;
case L'4':
case L' ':
selection = SEL_EXIT;
break;
default:
beep();
}
}
} else {
// Function or control key
switch (key) {
case '1':
curs_set(CURS_OFF);
show_status(current_player);
curs_set(CURS_ON);
break;
case '2':
curs_set(CURS_OFF);
show_map(true);
curs_set(CURS_ON);
break;
case '3':
selection = SEL_BANK;
break;
case '4':
case ' ':
case KEY_ESC:
case KEY_CANCEL:
case KEY_EXIT:
case KEY_CTRL('C'):
@ -251,15 +300,12 @@ void visit_bank (void)
{
double credit_limit;
double val, max;
int key;
wint_t key;
bool done;
char *buf;
chtype *chbuf = xmalloc(BUFSIZE * sizeof(chtype));
int x, width;
buf = malloc(BUFSIZE);
if (buf == NULL) {
err_exit_nomem();
}
credit_limit = (total_value(current_player) - player[current_player].debt)
* CREDIT_LIMIT_RATE;
@ -270,179 +316,209 @@ void visit_bank (void)
// Show the informational part of the Bank
newtxwin(10, WIN_COLS - 4, 5, WCENTER, true, attr_normal_window);
center(curwin, 1, attr_title, " Interstellar Trading Bank ");
center(curwin, 1, 0, attr_title, 0, 0, 1,
_(" Interstellar Trading Bank "));
l_strfmon(buf, BUFSIZE, "%18n", player[current_player].cash);
center2(curwin, 3, attr_normal, attr_highlight, "Current cash: ",
" %s ", buf);
mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, getmaxx(curwin) - 4, &width,
1, pgettext("label", "Current cash: "));
x = (getmaxx(curwin) + width - (BANK_VALUE_COLS + 2)) / 2;
l_strfmon(buf, BUFSIZE, "%18n", player[current_player].debt);
center2(curwin, 4, attr_normal, attr_highlight, "Current debt: ",
" %s ", buf);
rightch(curwin, 3, x, chbuf, 1, &width);
right(curwin, 3, x + BANK_VALUE_COLS + 2, attr_normal, attr_highlight, 0,
1, " ^{%N^} ", player[current_player].cash);
center2(curwin, 5, attr_normal, attr_highlight, "Interest rate: ",
" %17.2f%% ", interest_rate * 100.0);
right(curwin, 4, x, attr_normal, 0, 0, 1,
pgettext("label", "Current debt: "));
right(curwin, 4, x + BANK_VALUE_COLS + 2, attr_normal, attr_highlight, 0,
1, " ^{%N^} ", player[current_player].debt);
l_strfmon(buf, BUFSIZE, "%18n", credit_limit);
center2(curwin, 7, attr_highlight, attr_title, "Credit limit: ",
" %s ", buf);
right(curwin, 5, x, attr_normal, 0, 0, 1,
pgettext("label", "Interest rate: "));
right(curwin, 5, x + BANK_VALUE_COLS + 2, attr_normal, attr_highlight, 0,
1, " ^{%.2f%%^} ", interest_rate * 100.0);
right(curwin, 7, x, attr_highlight, 0, 0, 1,
/* TRANSLATORS: The "Total value", "Current cash", "Current
debt", "Interest rate" and "Credit limit" labels MUST all be
the same length (ie, right-padded with spaces as needed) and
must have at least one trailing space so that the display
routines work correctly. The maximum length of each label
is 36 characters.
Note that some of these labels are used for both the Player
Status window and the Trading Bank window. */
pgettext("label", "Credit limit: "));
whline(curwin, ' ' | attr_title, BANK_VALUE_COLS + 2);
right(curwin, 7, x + BANK_VALUE_COLS + 2, attr_title, 0, 0, 1,
" %N ", credit_limit);
wrefresh(curwin);
// Show menu of choices for the player
newtxwin(7, WIN_COLS - 4, 15, WCENTER, true, attr_normal_window);
center2(curwin, 3, attr_keycode, attr_normal, "<1>", " Borrow money ");
center2(curwin, 4, attr_keycode, attr_normal, "<2>", " Repay debt ");
center2(curwin, 5, attr_keycode, attr_normal, "<3>", " Exit from the Bank");
center(curwin, 3, 0, attr_normal, attr_keycode, 0, 1,
/* TRANSLATORS: The "Borrow money", "Repay debt" and "Exit
from the Bank" menu options must all be the same length
(ie, padded with trailing spaces as required). The maximum
length is 72 characters. */
_("^{<1>^} Borrow money "));
center(curwin, 4, 0, attr_normal, attr_keycode, 0, 1,
_("^{<2>^} Repay debt "));
center(curwin, 5, 0, attr_normal, attr_keycode, 0, 1,
_("^{<3>^} Exit from the Bank"));
mvwaddstr(curwin, 1, 24, "Enter selection ");
waddstr(curwin, "[");
attrpr(curwin, attr_keycode, "1");
waddstr(curwin, "-");
attrpr(curwin, attr_keycode, "3");
waddstr(curwin, "]: ");
center(curwin, 1, 0, attr_normal, attr_keycode, 0, 1,
_("Enter selection [^{1^}-^{3^}]: "));
curs_set(CURS_ON);
wrefresh(curwin);
done = false;
while (! done) {
key = gettxchar(curwin);
if (gettxchar(curwin, &key) == OK) {
// Ordinary wide character
switch (key) {
case L'1':
case L'2':
case L'3':
left(curwin, getcury(curwin), getcurx(curwin), A_BOLD,
0, 0, 1, "%lc", key);
wrefresh(curwin);
switch (key) {
case '1':
case '2':
case '3':
wechochar(curwin, key | A_BOLD);
done = true;
break;
done = true;
break;
case ' ':
case KEY_CANCEL:
case KEY_EXIT:
case KEY_CTRL('C'):
case KEY_CTRL('G'):
case KEY_CTRL('\\'):
done = true;
break;
case L' ':
done = true;
break;
default:
beep();
default:
beep();
}
} else {
// Function or control key
switch (key) {
case KEY_ESC:
case KEY_CANCEL:
case KEY_EXIT:
case KEY_CTRL('C'):
case KEY_CTRL('G'):
case KEY_CTRL('\\'):
done = true;
break;
default:
beep();
}
}
}
curs_set(CURS_OFF);
switch (key) {
case '1':
case L'1':
// Borrow money from the Bank
if (credit_limit == 0.0) {
newtxwin(7, 50, 8, WCENTER, true, attr_error_window);
center(curwin, 1, attr_error_title, " Insufficient Credit Limit ");
center(curwin, 3, attr_error_highlight,
"The Bank will not lend you any more money");
wait_for_key(curwin, 5, attr_error_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight, 0, 0,
attr_error_waitforkey, _(" Insufficient Credit Limit "),
_("The Bank will not lend you any more money."));
} else {
int x, y, n;
int ret;
chtype *chbuf_cursym;
int width_cursym;
int n, ret;
wbkgd(curwin, attr_normal_window);
wbkgdset(curwin, attr_normal_window);
werase(curwin);
box(curwin, 0, 0);
mvwprintw(curwin, 3, 10, "How much do you wish to borrow? ");
n = (lconvinfo.p_sep_by_space == 1) ? 1 : 0;
mkchstr(chbuf, BUFSIZE, attr_normal, attr_normal | A_BOLD, 0, 1,
getmaxx(curwin) / 2, &width_cursym, 1, "^{%ls^}",
currency_symbol);
chbuf_cursym = xchstrdup(chbuf);
mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, getmaxx(curwin)
- BANK_INPUT_COLS - width_cursym - 6, &width, 1,
_("How much do you wish to borrow? "));
x = (getmaxx(curwin) + width - BANK_INPUT_COLS - width_cursym
- n) / 2;
rightch(curwin, 3, x, chbuf, 1, &width);
// Show the currency symbol before or after the input field
wattron(curwin, A_BOLD);
if (lconvinfo.p_cs_precedes == 1) {
wprintw(curwin, "%s%s", lconvinfo.currency_symbol,
(lconvinfo.p_sep_by_space == 1) ? " " : "");
n = 10;
leftch(curwin, 3, x, chbuf_cursym, 1, &width_cursym);
x += width_cursym + n;
} else {
getyx(curwin, y, x);
n = strlen(lconvinfo.currency_symbol) + 10
+ (lconvinfo.p_sep_by_space == 1);
mvwprintw(curwin, y, getmaxx(curwin) - n, "%s%s",
(lconvinfo.p_sep_by_space == 1) ? " " : "",
lconvinfo.currency_symbol);
wmove(curwin, y, x);
leftch(curwin, 3, x + BANK_INPUT_COLS + n, chbuf_cursym, 1,
&width_cursym);
}
wattroff(curwin, A_BOLD);
x = getcurx(curwin);
ret = gettxdouble(curwin, &val, 0.0, credit_limit + ROUNDING_AMOUNT,
0.0, credit_limit, 3, x, getmaxx(curwin) - x - n,
0.0, credit_limit, 3, x, BANK_INPUT_COLS,
attr_input_field);
if (ret == OK && val > ROUNDING_AMOUNT) {
player[current_player].cash += val;
player[current_player].debt += val * (interest_rate + 1.0);
}
free(chbuf_cursym);
}
break;
case '2':
case L'2':
// Repay a debt
if (player[current_player].debt == 0.0) {
newtxwin(7, 50, 8, WCENTER, true, attr_error_window);
center(curwin, 1, attr_error_title, " No Debt ");
center(curwin, 3, attr_error_highlight,
"You have no debt to repay");
wait_for_key(curwin, 5, attr_error_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight, 0, 0,
attr_error_waitforkey, _(" No Debt "),
_("You have no debt to repay."));
} else if (player[current_player].cash == 0.0) {
newtxwin(7, 60, 8, WCENTER, true, attr_error_window);
center(curwin, 1, attr_error_title, " No Cash ");
center(curwin, 3, attr_error_highlight,
"You have no cash with which to repay the debt!");
wait_for_key(curwin, 5, attr_error_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight, 0, 0,
attr_error_waitforkey, _(" No Cash "),
_("You have no cash with which to repay the debt!"));
} else {
int x, y, n;
int ret;
chtype *chbuf_cursym;
int width_cursym;
int n, ret;
wbkgd(curwin, attr_normal_window);
wbkgdset(curwin, attr_normal_window);
werase(curwin);
box(curwin, 0, 0);
mvwprintw(curwin, 3, 10, "How much do you wish to repay? ");
n = (lconvinfo.p_sep_by_space == 1) ? 1 : 0;
mkchstr(chbuf, BUFSIZE, attr_normal, attr_normal | A_BOLD, 0, 1,
getmaxx(curwin) / 2, &width_cursym, 1, "^{%ls^}",
currency_symbol);
chbuf_cursym = xchstrdup(chbuf);
mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, getmaxx(curwin)
- BANK_INPUT_COLS - width_cursym - 6, &width, 1,
_("How much do you wish to repay? "));
x = (getmaxx(curwin) + width - BANK_INPUT_COLS - width_cursym
- n) / 2;
rightch(curwin, 3, x, chbuf, 1, &width);
// Show the currency symbol before or after the input field
wattron(curwin, A_BOLD);
if (lconvinfo.p_cs_precedes == 1) {
wprintw(curwin, "%s%s", lconvinfo.currency_symbol,
(lconvinfo.p_sep_by_space == 1) ? " " : "");
n = 10;
leftch(curwin, 3, x, chbuf_cursym, 1, &width_cursym);
x += width_cursym + n;
} else {
getyx(curwin, y, x);
n = strlen(lconvinfo.currency_symbol) + 10
+ (lconvinfo.p_sep_by_space == 1);
mvwprintw(curwin, y, getmaxx(curwin) - n, "%s%s",
(lconvinfo.p_sep_by_space == 1) ? " " : "",
lconvinfo.currency_symbol);
wmove(curwin, y, x);
leftch(curwin, 3, x + BANK_INPUT_COLS + n, chbuf_cursym, 1,
&width_cursym);
}
wattroff(curwin, A_BOLD);
x = getcurx(curwin);
max = MIN(player[current_player].cash, player[current_player].debt);
ret = gettxdouble(curwin, &val, 0.0, max + ROUNDING_AMOUNT, 0.0,
max, 3, x, getmaxx(curwin) - x - n,
attr_input_field);
max, 3, x, BANK_INPUT_COLS, attr_input_field);
if (ret == OK) {
player[current_player].cash -= val;
@ -455,6 +531,8 @@ void visit_bank (void)
player[current_player].debt = 0.0;
}
}
free(chbuf_cursym);
}
break;
@ -466,7 +544,7 @@ void visit_bank (void)
deltxwin(); // Trading Bank window
txrefresh();
free(buf);
free(chbuf);
}
@ -476,19 +554,18 @@ void visit_bank (void)
void trade_shares (int num, bool *bid_used)
{
bool done;
int key, ret, x;
int ret, w, x;
long int maxshares, val;
double ownership;
char *buf;
chtype *chbuf;
int width;
wint_t key;
assert(num >= 0 && num < MAX_COMPANIES);
assert(company[num].on_map);
buf = malloc(BUFSIZE);
if (buf == NULL) {
err_exit_nomem();
}
chbuf = xmalloc(BUFSIZE * sizeof(chtype));
ownership = (company[num].stock_issued == 0) ? 0.0 :
((double) player[current_player].stock_owned[num]
@ -496,138 +573,172 @@ void trade_shares (int num, bool *bid_used)
// Show the informational part of the trade window
newtxwin(9, WIN_COLS - 4, 5, WCENTER, true, attr_normal_window);
w = getmaxx(curwin);
center(curwin, 1, attr_title, " Stock Transaction in %s ",
company[num].name);
center(curwin, 1, 0, attr_title, 0, 0, 1,
/* TRANSLATORS: %ls represents the company name. */
_(" Stock Transaction in %ls "), company[num].name);
mvwaddstr(curwin, 3, 2, "Shares issued: ");
attrpr(curwin, attr_highlight, "%'12ld", company[num].stock_issued);
mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, w / 2, &width, 1,
/* TRANSLATORS: "Shares issued" represents the number of
shares already sold by the company to all players.
mvwaddstr(curwin, 4, 2, "Shares left: ");
attrpr(curwin, attr_highlight, "%'12ld",
company[num].max_stock - company[num].stock_issued);
Note that the labels "Shares issued", "Shares left",
"Price per share" and "Return" must all be the same length
and must have at least one trailing space for the output
routines to work correctly. The maximum length of each
label is 22 characters. */
pgettext("label|Stock A", "Shares issued: "));
leftch(curwin, 3, 2, chbuf, 1, &width);
right(curwin, 3, width + SHARE_PRICE_COLS + 2, attr_normal, attr_highlight,
0, 1, "^{%'ld^}", company[num].stock_issued);
mvwaddstr(curwin, 5, 2, "Price per share: ");
l_strfmon(buf, BUFSIZE, "%12n", company[num].share_price);
attrpr(curwin, attr_highlight, "%12s", buf);
left(curwin, 4, 2, attr_normal, 0, 0, 1,
/* TRANSLATORS: "Shares left" is the number of shares that are
left to be purchased in the current company. */
pgettext("label|Stock A", "Shares left: "));
right(curwin, 4, width + SHARE_PRICE_COLS + 2, attr_normal, attr_highlight,
0, 1, "^{%'ld^}", company[num].max_stock - company[num].stock_issued);
mvwaddstr(curwin, 6, 2, "Return: ");
attrpr(curwin, attr_highlight, "%11.2f%%",
company[num].share_return * 100.0);
left(curwin, 5, 2, attr_normal, 0, 0, 1,
/* TRANSLATORS: "Price per share" is the cost of each share in
the current company. */
pgettext("label|Stock A", "Price per share: "));
right(curwin, 5, width + SHARE_PRICE_COLS + 2, attr_normal, attr_highlight,
0, 1, "^{%N^}", company[num].share_price);
mvwaddstr(curwin, 3, 38, "Current holdings: ");
attrpr(curwin, attr_highlight, " %'16ld ",
player[current_player].stock_owned[num]);
left(curwin, 6, 2, attr_normal, 0, 0, 1,
/* TRANSLATORS: "Return" is the share return as a percentage. */
pgettext("label|Stock A", "Return: "));
right(curwin, 6, width + SHARE_PRICE_COLS + 2, attr_normal, attr_highlight,
0, 1, "^{%.2f%%^}", company[num].share_return * 100.0);
mvwaddstr(curwin, 4, 38, "Percentage owned: ");
attrpr(curwin, attr_highlight, " %'15.2f%% ", ownership * 100.0);
left(curwin, 3, w / 2, attr_normal, 0, 0, 1,
/* TRANSLATORS: "Current holdings" is the number of shares the
current player owns in this particular company.
wmove(curwin, 6, 38);
attrpr(curwin, attr_highlight, "Current cash: ");
l_strfmon(buf, BUFSIZE, "%16n", player[current_player].cash);
attrpr(curwin, attr_title, " %16s ", buf);
Note that the labels "Current holdings", "Percentage owned"
and "Current cash" MUST all be the same length and contain at
least one trailing space for the display routines to work
correctly. The maximum length of each label is 18
characters. */
pgettext("label|Stock B", "Current holdings: "));
right(curwin, 3, w - 2, attr_normal, attr_highlight, 0, 1, " ^{%'ld^} ",
player[current_player].stock_owned[num]);
left(curwin, 4, w / 2, attr_normal, 0, 0, 1,
/* TRANSLATORS: "Percentage owned" is the current player's
percentage ownership in this particular company. */
pgettext("label|Stock B", "Percentage owned: "));
right(curwin, 4, w - 2, attr_normal, attr_highlight, 0, 1, " ^{%.2f%%^} ",
ownership * 100.0);
left(curwin, 6, w / 2, attr_highlight, 0, 0, 1,
pgettext("label|Stock B", "Current cash: "));
whline(curwin, ' ' | attr_title, TRADE_VALUE_COLS + 2);
right(curwin, 6, w - 2, attr_title, 0, 0, 1, " %N ",
player[current_player].cash);
wrefresh(curwin);
// Show menu of choices for the player
newtxwin(7, WIN_COLS - 4, 14, WCENTER, true, attr_normal_window);
wmove(curwin, 3, 2);
attrpr(curwin, attr_keycode, "<1>");
waddstr(curwin, " Buy stock from company");
left(curwin, 3, 2, attr_normal, attr_keycode, 0, 1,
_("^{<1>^} Buy stock from company"));
left(curwin, 4, 2, attr_normal, attr_keycode, 0, 1,
_("^{<2>^} Sell stock back to company"));
left(curwin, 3, getmaxx(curwin) / 2, attr_normal, attr_keycode, 0, 1,
_("^{<3>^} Bid company to issue more shares"));
left(curwin, 4, getmaxx(curwin) / 2, attr_normal, attr_keycode, 0, 1,
_("^{<4>^} Exit to the Stock Exchange"));
wmove(curwin, 4, 2);
attrpr(curwin, attr_keycode, "<2>");
waddstr(curwin, " Sell stock back to company");
wmove(curwin, 3, 38);
attrpr(curwin, attr_keycode, "<3>");
waddstr(curwin, " Bid company to issue more shares");
wmove(curwin, 4, 38);
attrpr(curwin, attr_keycode, "<4>");
waddstr(curwin, " Exit to the Stock Exchange");
mvwaddstr(curwin, 1, 24, "Enter selection ");
waddstr(curwin, "[");
attrpr(curwin, attr_keycode, "1");
waddstr(curwin, "-");
attrpr(curwin, attr_keycode, "4");
waddstr(curwin, "]: ");
center(curwin, 1, 0, attr_normal, attr_keycode, 0, 1,
_("Enter selection [^{1^}-^{4^}]: "));
curs_set(CURS_ON);
wrefresh(curwin);
done = false;
while (! done) {
key = gettxchar(curwin);
if (gettxchar(curwin, &key) == OK) {
// Ordinary wide character
switch (key) {
case L'1':
case L'2':
case L'3':
case L'4':
left(curwin, getcury(curwin), getcurx(curwin), A_BOLD,
0, 0, 1, "%lc", key);
wrefresh(curwin);
switch (key) {
case '1':
case '2':
case '3':
case '4':
wechochar(curwin, key | A_BOLD);
done = true;
break;
done = true;
break;
case ' ':
case KEY_CANCEL:
case KEY_EXIT:
case KEY_CTRL('C'):
case KEY_CTRL('G'):
case KEY_CTRL('\\'):
done = true;
break;
case L' ':
done = true;
break;
default:
beep();
default:
beep();
}
} else {
// Function or control key
switch (key) {
case KEY_ESC:
case KEY_CANCEL:
case KEY_EXIT:
case KEY_CTRL('C'):
case KEY_CTRL('G'):
case KEY_CTRL('\\'):
done = true;
break;
default:
beep();
}
}
}
curs_set(CURS_OFF);
switch (key) {
case '1':
case L'1':
// Buy stock in company
maxshares = player[current_player].cash / company[num].share_price;
if (company[num].max_stock - company[num].stock_issued == 0) {
newtxwin(7, 50, 8, WCENTER, true, attr_error_window);
center(curwin, 1, attr_error_title, " No Shares Available ");
center(curwin, 3, attr_error_highlight,
"No more shares are available for purchase");
wait_for_key(curwin, 5, attr_error_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight, 0, 0,
attr_error_waitforkey, _(" No Shares Available "),
_("No more shares are available for purchase."));
} else if (maxshares <= 0) {
newtxwin(7, 50, 8, WCENTER, true, attr_error_window);
center(curwin, 1, attr_error_title, " Insufficient Cash ");
center(curwin, 3, attr_error_highlight,
"Not enough cash to purchase shares");
wait_for_key(curwin, 5, attr_error_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight, 0, 0,
attr_error_waitforkey, _(" Insufficient Cash "),
_("You do not have enough cash\n"
"to purchase additional shares."));
} else {
maxshares = MIN(maxshares, company[num].max_stock -
company[num].stock_issued);
wbkgd(curwin, attr_normal_window);
wbkgdset(curwin, attr_normal_window);
werase(curwin);
box(curwin, 0, 0);
center3(curwin, 2, attr_normal, attr_normal, attr_highlight,
"You can purchase up to ", " shares.", "%'ld", maxshares);
center(curwin, 2, 0, attr_normal, attr_highlight, 0, 1,
ngettext("You can purchase ^{one^} share.",
"You can purchase up to ^{%'ld^} shares.",
maxshares), maxshares);
mvwprintw(curwin, 4, 10, "How many shares do you wish to purchase? ");
x = getcurx(curwin);
mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1,
getmaxx(curwin) - TRADE_INPUT_COLS - 4, &width, 1,
_("How many shares do you wish to purchase? "));
x = (getmaxx(curwin) + width - TRADE_INPUT_COLS) / 2;
rightch(curwin, 4, x, chbuf, 1, &width);
ret = gettxlong(curwin, &val, 0, maxshares, 0, maxshares, 4, x,
getmaxx(curwin) - x - 10, attr_input_field);
TRADE_INPUT_COLS, attr_input_field);
if (ret == OK) {
player[current_player].cash -= val * company[num].share_price;
@ -637,32 +748,32 @@ void trade_shares (int num, bool *bid_used)
}
break;
case '2':
case L'2':
// Sell stock back to company
maxshares = player[current_player].stock_owned[num];
if (maxshares == 0) {
newtxwin(7, 50, 8, WCENTER, true, attr_error_window);
center(curwin, 1, attr_error_title, " No Shares ");
center(curwin, 3, attr_error_highlight,
"You do not have any shares to sell");
wait_for_key(curwin, 5, attr_error_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight, 0, 0,
attr_error_waitforkey, _(" No Shares "),
_("You do not have any shares to sell."));
} else {
wbkgd(curwin, attr_normal_window);
wbkgdset(curwin, attr_normal_window);
werase(curwin);
box(curwin, 0, 0);
center3(curwin, 2, attr_normal, attr_normal, attr_highlight,
"You can sell up to ", " shares.", "%'ld", maxshares);
center(curwin, 2, 0, attr_normal, attr_highlight, 0, 1,
ngettext("You can sell ^{one^} share.",
"You can sell up to ^{%'ld^} shares.",
maxshares), maxshares);
mvwprintw(curwin, 4, 10, "How many shares do you wish to sell? ");
x = getcurx(curwin);
mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1,
getmaxx(curwin) - TRADE_INPUT_COLS - 4, &width, 1,
_("How many shares do you wish to sell? "));
x = (getmaxx(curwin) + width - TRADE_INPUT_COLS) / 2;
rightch(curwin, 4, x, chbuf, 1, &width);
ret = gettxlong(curwin, &val, 0, maxshares, 0, maxshares, 4, x,
getmaxx(curwin) - x - 10, attr_input_field);
TRADE_INPUT_COLS, attr_input_field);
if (ret == OK) {
company[num].stock_issued -= val;
@ -672,7 +783,7 @@ void trade_shares (int num, bool *bid_used)
}
break;
case '3':
case L'3':
// Bid company to issue more shares
maxshares = 0;
if (! *bid_used && randf() < ownership && randf() < BID_CHANCE) {
@ -683,25 +794,20 @@ void trade_shares (int num, bool *bid_used)
*bid_used = true;
if (maxshares == 0) {
newtxwin(8, 50, 8, WCENTER, true, attr_error_window);
center(curwin, 1, attr_error_title, " No Shares Issued ");
center(curwin, 3, attr_error_highlight, "%s", company[num].name);
center(curwin, 4, attr_error_highlight,
"has refused to issue more shares");
wait_for_key(curwin, 6, attr_error_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight, 0, 0,
attr_error_waitforkey, _(" No Shares Issued "),
/* TRANSLATORS: %ls represents the company name. */
_("%ls has refused\nto issue more shares."),
company[num].name);
} else {
newtxwin(8, 50, 8, WCENTER, true, attr_normal_window);
center(curwin, 1, attr_title, " Shares Issued ");
center(curwin, 3, attr_highlight, "%s", company[num].name);
center(curwin, 4, attr_highlight, "has issued %'ld more shares",
maxshares);
wait_for_key(curwin, 6, attr_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_normal_window,
attr_title, attr_normal, attr_highlight, 0,
attr_waitforkey, _(" Shares Issued "),
/* TRANSLATORS: %ls represents the company name. */
ngettext("%ls has issued\n^{one^} more share.",
"%ls has issued\n^{%'ld^} more shares.",
maxshares), company[num].name, maxshares);
}
break;
@ -713,7 +819,7 @@ void trade_shares (int num, bool *bid_used)
deltxwin(); // Stock Transaction window
txrefresh();
free(buf);
free(chbuf);
}

View File

@ -52,15 +52,16 @@ static const unsigned char game_file_crypt_key[] = {
#define load_game_scanf(_fmt, _var, _cond) \
do { \
if (fgets(buf, BUFSIZE, file) == NULL) { \
err_exit("%s: missing field on line %d", filename, lineno); \
err_exit(_("%s: missing field on line %d"), \
filename, lineno); \
} \
if (sscanf(unscramble(crypt_key, buf, BUFSIZE), _fmt "\n", \
&(_var)) != 1) { \
err_exit("%s: illegal field on line %d: `%s'", \
err_exit(_("%s: illegal field on line %d: `%s'"), \
filename, lineno, buf); \
} \
if (! (_cond)) { \
err_exit("%s: illegal value on line %d: `%s'", \
err_exit(_("%s: illegal value on line %d: `%s'"), \
filename, lineno, buf); \
} \
lineno++; \
@ -81,32 +82,74 @@ static const unsigned char game_file_crypt_key[] = {
(_var) = b; \
} while (0)
#define load_game_read_string(_var) \
#ifdef USE_UTF8_GAME_FILE
# define load_game_read_string(_var, _var_utf8) \
do { \
char *s; \
int len; \
\
if (fgets(buf, BUFSIZE, file) == NULL) { \
err_exit("%s: missing field on line %d", filename, lineno); \
err_exit(_("%s: missing field on line %d"), \
filename, lineno); \
} \
if (strlen(unscramble(crypt_key, buf, BUFSIZE)) == 0) { \
err_exit("%s: illegal value on line %d", filename, lineno); \
err_exit(_("%s: illegal value on line %d"), \
filename, lineno); \
} \
lineno++; \
\
s = malloc(strlen(buf) + 1); \
if (s == NULL) { \
err_exit_nomem(); \
if (need_icd) { \
s = str_cd_iconv(buf, icd); \
if (s == NULL) { \
if (errno == EILSEQ) { \
err_exit(_("%s: illegal characters on line %d"), \
filename, lineno); \
} else { \
errno_exit("str_cd_iconv"); \
} \
} \
} else { \
s = xstrdup(buf); \
} \
\
strcpy(s, buf); \
len = strlen(s); \
if (len > 0 && s[len - 1] == '\n') { \
s[len - 1] = '\0'; \
} \
\
(_var) = s; \
xmbstowcs(wcbuf, s, BUFSIZE); \
(_var) = xwcsdup(wcbuf); \
(_var_utf8) = s; \
\
lineno++; \
} while (0)
#else // ! USE_UTF8_GAME_FILE
# define load_game_read_string(_var, _var_utf8) \
do { \
char *s; \
int len; \
\
if (fgets(buf, BUFSIZE, file) == NULL) { \
err_exit(_("%s: missing field on line %d"), \
filename, lineno); \
} \
if (strlen(unscramble(crypt_key, buf, BUFSIZE)) == 0) { \
err_exit(_("%s: illegal value on line %d"), \
filename, lineno); \
} \
\
s = xstrdup(buf); \
\
len = strlen(s); \
if (len > 0 && s[len - 1] == '\n') { \
s[len - 1] = '\0'; \
} \
\
xmbstowcs(wcbuf, s, BUFSIZE); \
(_var) = xwcsdup(wcbuf); \
(_var_utf8) = s; \
\
lineno++; \
} while (0)
#endif // ! USE_UTF8_GAME_FILE
// Macros used in save_game()
@ -126,8 +169,41 @@ static const unsigned char game_file_crypt_key[] = {
save_game_printf("%2.20e", _var)
#define save_game_write_bool(_var) \
save_game_printf("%d", (int) _var)
#define save_game_write_string(_var) \
save_game_printf("%s", _var)
#ifdef USE_UTF8_GAME_FILE
# define save_game_write_string(_var, _var_utf8) \
do { \
if ((_var_utf8) != NULL) { \
save_game_printf("%s", _var_utf8); \
} else { \
if (need_icd) { \
snprintf(buf, BUFSIZE, "%ls", _var); \
char *s = str_cd_iconv(buf, icd); \
if (s == NULL) { \
if (errno == EILSEQ) { \
err_exit(_("%s: could not convert string"), \
filename); \
} else { \
errno_exit("str_cd_iconv"); \
} \
} \
save_game_printf("%s", s); \
free(s); \
} else { \
save_game_printf("%ls", _var); \
} \
} \
} while (0)
#else // ! USE_UTF8_GAME_FILE
# define save_game_write_string(_var, _var_utf8) \
do { \
if ((_var_utf8) != NULL) { \
save_game_printf("%s", _var_utf8); \
} else { \
save_game_printf("%ls", _var); \
} \
} while (0)
#endif // ! USE_UTF8_GAME_FILE
/************************************************************************
@ -142,21 +218,28 @@ static const unsigned char game_file_crypt_key[] = {
bool load_game (int num)
{
char *buf, *filename;
char *filename;
FILE *file;
char *codeset, *codeset_nl;
int saved_errno, lineno;
char *prev_locale;
char *buf;
wchar_t *wcbuf;
int crypt_key;
int n, i, j;
#ifdef USE_UTF8_GAME_FILE
iconv_t icd;
bool need_icd;
#endif
assert(num >= 1 && num <= 9);
buf = malloc(BUFSIZE);
if (buf == NULL) {
err_exit_nomem();
}
buf = xmalloc(BUFSIZE);
wcbuf = xmalloc(BUFSIZE * sizeof(wchar_t));
filename = game_filename(num);
assert(filename != NULL);
@ -167,62 +250,94 @@ bool load_game (int num)
if (errno == ENOENT) {
// File not found
newtxwin(7, 40, 9, WCENTER, true, attr_error_window);
center(curwin, 1, attr_error_title, " Game Not Found ");
center(curwin, 3, attr_error_highlight,
"Game %d has not been saved to disk", num);
wait_for_key(curwin, 5, attr_error_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 50, 9, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight, 0, 0,
attr_error_waitforkey, _(" Game Not Found "),
_("Game %d has not been saved to disk."), num);
} else {
// Some other file error
saved_errno = errno;
newtxwin(9, 70, 9, WCENTER, true, attr_error_window);
center(curwin, 1, attr_error_title, " Game Not Loaded ");
center(curwin, 3, attr_error_highlight,
"Game %d could not be loaded from disk", num);
center(curwin, 5, attr_error_normal, "File %s: %s", filename,
strerror(saved_errno));
wait_for_key(curwin, 7, attr_error_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 60, 9, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight,
attr_error_normal, 0, attr_error_waitforkey,
_(" Game Not Loaded "),
_("Game %d could not be loaded from disk.\n\n"
"^{File %s: %s^}"), num, filename,
strerror(saved_errno));
}
free(buf);
free(wcbuf);
free(filename);
return false;
}
// Change the formatting of numbers to the POSIX locale for consistency
prev_locale = strdup(setlocale(LC_NUMERIC, NULL));
if (prev_locale == NULL) {
err_exit_nomem();
#ifdef USE_UTF8_GAME_FILE
// Make sure all strings are read in UTF-8 format for consistency
codeset = nl_langinfo(CODESET);
if (codeset == NULL) {
errno_exit("nl_langinfo(CODESET)");
}
need_icd = (strcmp(codeset, GAME_FILE_CHARSET) != 0);
if (need_icd) {
// Try using the GNU libiconv "//TRANSLIT" option
strcpy(buf, codeset);
strcat(buf, GAME_FILE_TRANSLIT);
icd = iconv_open(buf, GAME_FILE_CHARSET);
if (icd == (iconv_t) -1) {
// Try iconv_open() without "//TRANSLIT"
icd = iconv_open(codeset, GAME_FILE_CHARSET);
if (icd == (iconv_t) -1) {
errno_exit("iconv_open");
}
}
} else {
icd = (iconv_t) -1;
}
codeset_nl = xstrdup(GAME_FILE_CHARSET "\n");
#else // ! USE_UTF8_GAME_FILE
// Make sure all strings are read in the correct codeset
codeset = nl_langinfo(CODESET);
if (codeset == NULL) {
errno_exit("nl_langinfo(CODESET)");
}
codeset_nl = xmalloc(strlen(codeset) + 2);
strcpy(codeset_nl, codeset);
strcat(codeset_nl, "\n");
#endif // ! USE_UTF8_GAME_FILE
// Change the formatting of numbers to the POSIX locale for consistency
prev_locale = xstrdup(setlocale(LC_NUMERIC, NULL));
setlocale(LC_NUMERIC, "C");
// Read the game file header
if (fgets(buf, BUFSIZE, file) == NULL) {
err_exit("%s: missing header in game file", filename);
err_exit(_("%s: missing header in game file"), filename);
}
if (strcmp(buf, GAME_FILE_HEADER "\n") != 0) {
err_exit("%s: not a valid game file", filename);
err_exit(_("%s: not a valid game file"), filename);
}
if (fgets(buf, BUFSIZE, file) == NULL) {
err_exit("%s: missing subheader in game file", filename);
err_exit(_("%s: missing subheader in game file"), filename);
}
if (strcmp(buf, GAME_FILE_API_VERSION "\n") != 0) {
err_exit("%s: saved under a different version of Star Traders",
err_exit(_("%s: saved under a different version of Star Traders"),
filename);
}
if (fgets(buf, BUFSIZE, file) == NULL) {
err_exit(_("%s: missing subheader in game file"), filename);
}
if (strcmp(buf, codeset_nl) != 0) {
err_exit(_("%s: saved under an incompatible character encoding"),
filename);
}
lineno = 3;
lineno = 4;
// Read in the game file encryption key
if (fscanf(file, "%i\n", &crypt_key) != 1) {
err_exit("%s: illegal or missing field on line %d", filename, lineno);
err_exit(_("%s: illegal or missing field on line %d"), filename, lineno);
}
lineno++;
@ -239,7 +354,7 @@ bool load_game (int num)
// Read in player data
for (i = 0; i < number_players; i++) {
load_game_read_string(player[i].name);
load_game_read_string(player[i].name, player[i].name_utf8);
load_game_read_double(player[i].cash, player[i].cash >= 0.0);
load_game_read_double(player[i].debt, player[i].debt >= 0.0);
load_game_read_bool(player[i].in_game);
@ -251,7 +366,8 @@ bool load_game (int num)
// Read in company data
for (i = 0; i < MAX_COMPANIES; i++) {
company[i].name = company_name[i];
xmbstowcs(wcbuf, gettext(company_name[i]), BUFSIZE);
company[i].name = xwcsdup(wcbuf);
load_game_read_double(company[i].share_price, company[i].share_price >= 0.0);
load_game_read_double(company[i].share_return, true);
load_game_read_long(company[i].stock_issued, company[i].stock_issued >= 0);
@ -262,10 +378,10 @@ bool load_game (int num)
// Read in galaxy map
for (int x = 0; x < MAX_X; x++) {
if (fgets(buf, BUFSIZE, file) == NULL) {
err_exit("%s: missing field on line %d", filename, lineno);
err_exit(_("%s: missing field on line %d"), filename, lineno);
}
if (strlen(unscramble(crypt_key, buf, BUFSIZE)) != MAX_Y + 1) {
err_exit("%s: illegal field on line %d", filename, lineno);
err_exit(_("%s: illegal field on line %d"), filename, lineno);
}
for (int y = 0; y < MAX_Y; y++) {
@ -274,7 +390,7 @@ bool load_game (int num)
|| (c >= MAP_A && c <= MAP_LAST)) {
galaxy_map[x][y] = (map_val_t) c;
} else {
err_exit("%s: illegal value on line %d", filename, lineno);
err_exit(_("%s: illegal value on line %d"), filename, lineno);
}
}
@ -291,9 +407,17 @@ bool load_game (int num)
// Change the formatting of numbers back to the user-supplied locale
setlocale(LC_NUMERIC, prev_locale);
#ifdef USE_UTF8_GAME_FILE
if (need_icd) {
iconv_close(icd);
}
#endif
free(buf);
free(wcbuf);
free(filename);
free(prev_locale);
free(codeset_nl);
return true;
}
@ -306,19 +430,22 @@ bool save_game (int num)
const char *data_dir;
char *buf, *filename;
FILE *file;
char *codeset;
int saved_errno;
char *prev_locale;
struct stat statbuf;
int crypt_key;
int i, j, x, y;
#ifdef USE_UTF8_GAME_FILE
iconv_t icd;
bool need_icd;
#endif
assert(num >= 1 && num <= 9);
buf = malloc(BUFSIZE);
if (buf == NULL) {
err_exit_nomem();
}
buf = xmalloc(BUFSIZE);
crypt_key = option_dont_encrypt ? 0 :
game_file_crypt_key[randi(GAME_FILE_CRYPT_KEY_SIZE)];
@ -333,17 +460,13 @@ bool save_game (int num)
; // Do nothing: directory already exists
} else {
// Data directory could not be created
newtxwin(9, 70, 7, WCENTER, true, attr_error_window);
center(curwin, 1, attr_error_title, " Game Not Saved ");
center(curwin, 3, attr_error_highlight,
"Game %d could not be saved to disk", num);
center(curwin, 5, attr_error_normal, "Directory %s: %s",
data_dir, strerror(saved_errno));
wait_for_key(curwin, 7, attr_error_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 60, 7, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight,
attr_error_normal, 0, attr_error_waitforkey,
_(" Game Not Saved "),
_("Game %d could not be saved to disk.\n\n"
"^{Directory %s: %s^}"), num, data_dir,
strerror(saved_errno));
free(buf);
return false;
@ -358,33 +481,49 @@ bool save_game (int num)
if (file == NULL) {
// File could not be opened for writing
saved_errno = errno;
newtxwin(9, 70, 7, WCENTER, true, attr_error_window);
center(curwin, 1, attr_error_title, " Game Not Saved ");
center(curwin, 3, attr_error_highlight,
"Game %d could not be saved to disk", num);
center(curwin, 5, attr_error_normal, "File %s: %s", filename,
strerror(saved_errno));
wait_for_key(curwin, 7, attr_error_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 60, 7, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight,
attr_error_normal, 0, attr_error_waitforkey,
_(" Game Not Saved "),
_("Game %d could not be saved to disk.\n\n"
"^{File %s: %s^}"), num, filename, strerror(saved_errno));
free(buf);
free(filename);
return false;
}
// Change the formatting of numbers to the POSIX locale for consistency
prev_locale = strdup(setlocale(LC_NUMERIC, NULL));
if (prev_locale == NULL) {
err_exit_nomem();
#ifdef USE_UTF8_GAME_FILE
// Make sure all strings are output in UTF-8 format for consistency
codeset = nl_langinfo(CODESET);
if (codeset == NULL) {
errno_exit("nl_langinfo(CODESET)");
}
need_icd = (strcmp(codeset, GAME_FILE_CHARSET) != 0);
if (need_icd) {
icd = iconv_open(GAME_FILE_CHARSET, codeset);
if (icd == (iconv_t) -1) {
errno_exit("iconv_open");
}
} else {
icd = (iconv_t) -1;
}
codeset = GAME_FILE_CHARSET; // Now contains output codeset
#else // ! USE_UTF8_GAME_FILE
// Make sure all strings are output in the correct codeset
codeset = nl_langinfo(CODESET);
if (codeset == NULL) {
errno_exit("nl_langinfo(CODESET)");
}
#endif // ! USE_UTF8_GAME_FILE
// Change the formatting of numbers to the POSIX locale for consistency
prev_locale = xstrdup(setlocale(LC_NUMERIC, NULL));
setlocale(LC_NUMERIC, "C");
// Write out the game file header and encryption key
fprintf(file, "%s\n" "%s\n", GAME_FILE_HEADER, GAME_FILE_API_VERSION);
fprintf(file, "%d\n", crypt_key);
fprintf(file, "%s\n" "%d\n", codeset, crypt_key);
// Write out various game variables
save_game_write_int(MAX_X);
@ -399,7 +538,7 @@ bool save_game (int num)
// Write out player data
for (i = 0; i < number_players; i++) {
save_game_write_string(player[i].name);
save_game_write_string(player[i].name, player[i].name_utf8);
save_game_write_double(player[i].cash);
save_game_write_double(player[i].debt);
save_game_write_bool(player[i].in_game);
@ -443,6 +582,12 @@ bool save_game (int num)
// Change the formatting of numbers back to the user-supplied locale
setlocale(LC_NUMERIC, prev_locale);
#ifdef USE_UTF8_GAME_FILE
if (need_icd) {
iconv_close(icd);
}
#endif
free(buf);
free(filename);
free(prev_locale);

View File

@ -110,14 +110,20 @@ void init_game (void)
{
// Try to load an old game, if possible
if (game_num != 0) {
newtxwin(5, 30, 6, WCENTER, true, attr_status_window);
center(curwin, 2, attr_status_window, "Loading game %d... ", game_num);
chtype *chbuf = xmalloc(BUFSIZE * sizeof(chtype));
int width;
mkchstr(chbuf, BUFSIZE, attr_status_window, 0, 0, 1, WIN_COLS - 7,
&width, 1, _("Loading game %d... "), game_num);
newtxwin(5, width + 5, 6, WCENTER, true, attr_status_window);
centerch(curwin, 2, 0, chbuf, 1, &width);
wrefresh(curwin);
game_loaded = load_game(game_num);
deltxwin();
txrefresh();
free(chbuf);
}
// Initialise game data, if not already loaded
@ -134,18 +140,25 @@ void init_game (void)
choice = ask_game_number();
if (choice != ERR) {
// Try to load the game, if possible
chtype *chbuf = xmalloc(BUFSIZE * sizeof(chtype));
int width;
game_num = choice;
// Try to load the game, if possible
newtxwin(5, 30, 9, WCENTER, true, attr_status_window);
center(curwin, 2, attr_status_window,
"Loading game %d... ", game_num);
mkchstr(chbuf, BUFSIZE, attr_status_window, 0, 0, 1,
WIN_COLS - 7, &width, 1,
_("Loading game %d... "), game_num);
newtxwin(5, width + 5, 9, WCENTER, true, attr_status_window);
centerch(curwin, 2, 0, chbuf, 1, &width);
wrefresh(curwin);
game_loaded = load_game(game_num);
deltxwin();
txrefresh();
free(chbuf);
}
deltxwin(); // "Enter game number" window
@ -158,7 +171,7 @@ void init_game (void)
}
if (! game_loaded) {
int i, j, x, y;
wchar_t *buf = xmalloc(BUFSIZE * sizeof(wchar_t));
ask_player_names();
@ -166,19 +179,20 @@ void init_game (void)
txrefresh();
// Initialise player data (other than names)
for (i = 0; i < number_players; i++) {
for (int i = 0; i < number_players; i++) {
player[i].cash = INITIAL_CASH;
player[i].debt = 0.0;
player[i].in_game = true;
for (j = 0; j < MAX_COMPANIES; j++) {
for (int j = 0; j < MAX_COMPANIES; j++) {
player[i].stock_owned[j] = 0;
}
}
// Initialise company data
for (i = 0; i < MAX_COMPANIES; i++) {
company[i].name = company_name[i];
for (int i = 0; i < MAX_COMPANIES; i++) {
xmbstowcs(buf, gettext(company_name[i]), BUFSIZE);
company[i].name = xwcsdup(buf);
company[i].share_price = 0.0;
company[i].share_return = INITIAL_RETURN;
company[i].stock_issued = 0;
@ -187,8 +201,8 @@ void init_game (void)
}
// Initialise galaxy map
for (x = 0; x < MAX_X; x++) {
for (y = 0; y < MAX_Y; y++) {
for (int x = 0; x < MAX_X; x++) {
for (int y = 0; y < MAX_Y; y++) {
galaxy_map[x][y] = (randf() < STAR_RATIO) ?
MAP_STAR : MAP_EMPTY;
}
@ -207,16 +221,15 @@ void init_game (void)
first_player = randi(number_players);
current_player = first_player;
newtxwin(7, 50, 8, WCENTER, true, attr_normal_window);
center(curwin, 2, attr_normal, "The first player to go is");
center(curwin, 3, attr_highlight, "%s",
player[first_player].name);
wait_for_key(curwin, 5, attr_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 50, 8, WCENTER, attr_normal_window,
attr_title, attr_normal, attr_highlight, 0,
attr_waitforkey, _(" First Player "),
_("The first player to go is ^{%ls^}."),
player[first_player].name);
txrefresh();
}
free(buf);
}
}
@ -230,35 +243,63 @@ void init_game (void)
static int ask_number_players (void)
{
int key, ret;
wchar_t *keycode_contgame = xmalloc(BUFSIZE * sizeof(wchar_t));
chtype *chbuf = xmalloc(BUFSIZE * sizeof(chtype));
int lines, maxwidth;
int widthbuf[2];
int ret;
bool done;
// Ask for the number of players
newtxwin(5, 62, 3, WCENTER, true, attr_normal_window);
lines = mkchstr(chbuf, BUFSIZE, attr_normal, attr_keycode, 0, 2,
WIN_COLS - 7, widthbuf, 2,
/* TRANSLATORS: The keycode <C> should be modified to
match that (or those) specified with msgctxt
"input|ContinueGame". */
_("Enter number of players [^{1^}-^{%d^}] "
"or ^{<C>^} to continue a game: "), MAX_PLAYERS);
assert(lines == 1 || lines == 2);
maxwidth = (lines == 1 ? widthbuf[0] : MAX(widthbuf[0], widthbuf[1])) + 5;
mvwaddstr(curwin, 2, 2, "Enter number of players ");
waddstr(curwin, "[");
attrpr(curwin, attr_keycode, "1");
waddstr(curwin, "-");
attrpr(curwin, attr_keycode, "%d", MAX_PLAYERS);
waddstr(curwin, "]");
waddstr(curwin, " or ");
attrpr(curwin, attr_keycode, "<C>");
waddstr(curwin, " to continue a game: ");
newtxwin(lines + 4, maxwidth, 3, WCENTER, true, attr_normal_window);
leftch(curwin, 2, 2, chbuf, lines, widthbuf);
free(chbuf);
curs_set(CURS_ON);
wrefresh(curwin);
/* TRANSLATORS: This string specifies the keycodes used to continue a
game; these must NOT contain any numeric digit from 1 to 9. The
first character (keyboard input code) is used to print the user's
response if one of those keys is pressed. Both upper and
lower-case versions should be present. */
xmbstowcs(keycode_contgame, pgettext("input|ContinueGame", "Cc"), BUFSIZE);
done = false;
while (! done) {
key = toupper(gettxchar(curwin));
wint_t key;
if (key >= '1' && key <= MAX_PLAYERS + '0') {
wechochar(curwin, key | A_BOLD);
ret = key - '0';
done = true;
if (gettxchar(curwin, &key) == OK) {
// Ordinary wide character
if (key >= L'1' && key <= MAX_PLAYERS + L'0') {
left(curwin, getcury(curwin), getcurx(curwin), A_BOLD,
0, 0, 1, "%lc", key);
wrefresh(curwin);
ret = key - L'0';
done = true;
} else if (wcschr(keycode_contgame, key) != NULL) {
left(curwin, getcury(curwin), getcurx(curwin), A_BOLD,
0, 0, 1, "%lc", *keycode_contgame);
wrefresh(curwin);
ret = 0;
done = true;
} else {
beep();
}
} else {
// Function or control key
switch (key) {
case KEY_ESC:
case KEY_CANCEL:
@ -270,12 +311,6 @@ static int ask_number_players (void)
done = true;
break;
case 'C':
wechochar(curwin, key | A_BOLD);
ret = 0;
done = true;
break;
default:
beep();
}
@ -283,6 +318,7 @@ static int ask_number_players (void)
}
curs_set(CURS_OFF);
free(keycode_contgame);
return ret;
}
@ -292,35 +328,46 @@ static int ask_number_players (void)
int ask_game_number (void)
{
int key, ret;
chtype *chbuf;
int lines, maxwidth;
int widthbuf[2];
int ret;
bool done;
// Ask which game to load
newtxwin(5, 54, 6, WCENTER, true, attr_normal_window);
chbuf = xmalloc(BUFSIZE * sizeof(chtype));
lines = mkchstr(chbuf, BUFSIZE, attr_normal, attr_keycode, 0, 2,
WIN_COLS - 7, widthbuf, 2,
_("Enter game number [^{1^}-^{9^}] "
"or ^{<CTRL><C>^} to cancel: "));
assert(lines == 1 || lines == 2);
maxwidth = (lines == 1 ? widthbuf[0] : MAX(widthbuf[0], widthbuf[1])) + 5;
mvwaddstr(curwin, 2, 2, "Enter game number ");
waddstr(curwin, "[");
attrpr(curwin, attr_keycode, "1");
waddstr(curwin, "-");
attrpr(curwin, attr_keycode, "9");
waddstr(curwin, "]");
waddstr(curwin, " or ");
attrpr(curwin, attr_keycode, "<CTRL><C>");
waddstr(curwin, " to cancel: ");
newtxwin(lines + 4, maxwidth, 6, WCENTER, true, attr_normal_window);
leftch(curwin, 2, 2, chbuf, lines, widthbuf);
free(chbuf);
curs_set(CURS_ON);
wrefresh(curwin);
done = false;
while (! done) {
key = gettxchar(curwin);
wint_t key;
if (key >= '1' && key <= '9') {
wechochar(curwin, key | A_BOLD);
ret = key - '0';
done = true;
if (gettxchar(curwin, &key) == OK) {
// Ordinary wide character
if (key >= L'1' && key <= L'9') {
left(curwin, getcury(curwin), getcurx(curwin), A_BOLD,
0, 0, 1, "%lc", key);
wrefresh(curwin);
ret = key - L'0';
done = true;
} else {
beep();
}
} else {
// Function or control key
switch (key) {
case KEY_ESC:
case KEY_CANCEL:
@ -348,30 +395,39 @@ int ask_game_number (void)
void ask_player_names (void)
{
chtype *chbuf = xmalloc(BUFSIZE * sizeof(chtype));
int width;
if (number_players == 1) {
// Ask for the player's name
newtxwin(5, WIN_COLS - 4, 9, WCENTER, true, attr_normal_window);
mvwaddstr(curwin, 2, 2, "Please enter your name: ");
left(curwin, 2, 2, attr_normal, 0, 0, 1, _("Please enter your name: "));
int x = getcurx(curwin);
int w = getmaxx(curwin) - x - 2;
player[0].name = NULL;
player[0].name_utf8 = NULL;
while (true) {
int ret = gettxstr(curwin, &player[0].name, NULL, false,
2, x, w, attr_input_field);
if (ret == OK && strlen(player[0].name) != 0) {
if (ret == OK && wcslen(player[0].name) != 0) {
break;
} else {
beep();
}
}
newtxwin(5, 44, 6, WCENTER, true, attr_normal_window);
mvwaddstr(curwin, 2, 2, "Do you need any instructions?");
if (answer_yesno(curwin, attr_keycode)) {
mkchstr(chbuf, BUFSIZE, attr_normal, attr_keycode, 0, 1,
WIN_COLS - YESNO_COLS - 6, &width, 1,
_("Do you need any instructions? [^{Y^}/^{N^}] "));
newtxwin(5, width + YESNO_COLS + 4, 6, WCENTER, true,
attr_normal_window);
leftch(curwin, 2, 2, chbuf, 1, &width);
if (answer_yesno(curwin)) {
show_help();
}
@ -384,16 +440,18 @@ void ask_player_names (void)
newtxwin(number_players + 5, WIN_COLS - 4, 9, WCENTER,
true, attr_normal_window);
center(curwin, 1, attr_title, " Enter Player Names ");
center(curwin, 1, 0, attr_title, 0, 0, 1, _(" Enter Player Names "));
for (i = 0; i < number_players; i++) {
player[i].name = NULL;
player[i].name_utf8 = NULL;
entered[i] = false;
mvwprintw(curwin, i + 3, 2, "Player %d:", i + 1);
left(curwin, i + 3, 2, attr_normal, 0, 0, 1,
/* xgettext:c-format, range: 1..8 */
_("Player %d: "), i + 1);
}
int x = getcurx(curwin) + 1;
int x = getcurx(curwin);
int w = getmaxx(curwin) - x - 2;
cur = 0;
@ -405,7 +463,7 @@ void ask_player_names (void)
switch (ret) {
case OK:
// Make sure name is not an empty string
len = strlen(player[cur].name);
len = wcslen(player[cur].name);
entered[cur] = (len != 0);
if (len == 0) {
beep();
@ -414,7 +472,7 @@ void ask_player_names (void)
// Make sure name has not been entered already
for (i = 0; i < number_players; i++) {
if (i != cur && player[i].name != NULL
&& strcmp(player[i].name, player[cur].name) == 0) {
&& wcscmp(player[i].name, player[cur].name) == 0) {
entered[cur] = false;
beep();
break;
@ -466,15 +524,21 @@ void ask_player_names (void)
}
}
newtxwin(5, 50, 6, WCENTER, true, attr_normal_window);
mvwaddstr(curwin, 2, 2, "Does any player need instructions?");
if (answer_yesno(curwin, attr_keycode)) {
mkchstr(chbuf, BUFSIZE, attr_normal, attr_keycode, 0, 1,
WIN_COLS - YESNO_COLS - 6, &width, 1,
_("Does any player need instructions? [^{Y^}/^{N^}] "));
newtxwin(5, width + YESNO_COLS + 4, 6, WCENTER, true,
attr_normal_window);
leftch(curwin, 2, 2, chbuf, 1, &width);
if (answer_yesno(curwin)) {
show_help();
}
}
deltxwin(); // "Need instructions?" window
deltxwin(); // "Enter player names" window
free(chbuf);
}
@ -483,8 +547,8 @@ void ask_player_names (void)
void end_game (void)
{
int i;
char *buf;
chtype *chbuf;
int lines, widthbuf[5];
if (abort_game) {
@ -492,74 +556,74 @@ void end_game (void)
return;
}
buf = malloc(BUFSIZE);
if (buf == NULL) {
err_exit_nomem();
}
chbuf = xmalloc(BUFSIZE * sizeof(chtype));
newtxwin(7, 40, 9, WCENTER, true, attr_error_window);
txdlgbox(MAX_DLG_LINES, 50, 9, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight, 0, 0,
attr_error_waitforkey, _(" Game Over "),
ngettext("The game is over after one turn.",
"The game is over after %d turns.",
turn_number - 1), turn_number - 1);
center(curwin, 1, attr_error_title, " Game Over ");
center(curwin, 3, attr_error_highlight, "The game is over after %d turns",
turn_number - 1);
wait_for_key(curwin, 5, attr_error_waitforkey);
deltxwin();
for (i = 0; i < number_players; i++) {
for (int i = 0; i < number_players; i++) {
show_status(i);
}
if (number_players == 1) {
l_strfmon(buf, BUFSIZE, "%1n", total_value(0));
newtxwin(9, 60, 8, WCENTER, true, attr_normal_window);
center(curwin, 1, attr_title, " Total Value ");
center2(curwin, 4, attr_normal, attr_highlight,
"Your total value was ", "%s", buf);
wait_for_key(curwin, 7, attr_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 60, 8, WCENTER, attr_normal_window,
attr_title, attr_normal, attr_highlight, 0, attr_waitforkey,
_(" Total Value "),
/* xgettext:c-format */
_("Your total value was ^{%N^}."), total_value(0));
} else {
// Sort players on the basis of total value
for (i = 0; i < number_players; i++) {
for (int i = 0; i < number_players; i++) {
player[i].sort_value = total_value(i);
}
qsort(player, number_players, sizeof(player_info_t), cmp_player);
newtxwin(number_players + 10, WIN_COLS - 4, 3, WCENTER,
lines = mkchstr(chbuf, BUFSIZE, attr_normal, attr_highlight,
attr_blink, 5, WIN_COLS - 8, widthbuf, 5,
(player[0].sort_value == 0) ?
_("The winner is ^{%ls^}\n"
"who is ^[*** BANKRUPT ***^]") :
/* xgettext:c-format */
_("The winner is ^{%ls^}\n"
"with a value of ^{%N^}."),
player[0].name, player[0].sort_value);
newtxwin(number_players + lines + 8, WIN_COLS - 4, 3, WCENTER,
true, attr_normal_window);
center(curwin, 1, 0, attr_title, 0, 0, 1, _(" Game Winner "));
centerch(curwin, 3, 0, chbuf, lines, widthbuf);
center(curwin, 1, attr_title, " Game Winner ");
center2(curwin, 3, attr_normal, attr_highlight, "The winner is ",
"%s", player[0].name);
if (player[0].sort_value == 0.0) {
center2(curwin, 4, attr_normal, attr_blink, "who is ",
"*** BANKRUPT ***");
} else {
l_strfmon(buf, BUFSIZE, "%1n", player[0].sort_value);
center2(curwin, 4, attr_normal, attr_highlight,
"with a value of ", "%s", buf);
}
int w = getmaxx(curwin);
int w = getmaxx(curwin) - 33;
wattrset(curwin, attr_subtitle);
snprintf(buf, BUFSIZE, "Total Value (%s)", lconvinfo.currency_symbol);
mvwprintw(curwin, 6, 2, "%5s %-*.*s %18s ", "", w, w, "Player", buf);
wattrset(curwin, attr_normal);
mvwhline(curwin, lines + 4, 2, ' ' | attr_subtitle, w - 4);
left(curwin, lines + 4, ORDINAL_COLS + 4, attr_subtitle, 0, 0, 1,
/* TRANSLATORS: "Player" is used as a column title in a
table containing all player names. */
pgettext("subtitle", "Player"));
right(curwin, lines + 4, w - 4, attr_subtitle, 0, 0, 1,
/* TRANSLATORS: "Total Value" refers to the total worth
(shares, cash and debt) of any given player. %ls is the
currency symbol of the current locale. */
pgettext("subtitle", "Total Value (%ls)"), currency_symbol);
for (i = 0; i < number_players; i++) {
l_strfmon(buf, BUFSIZE, "%!18n", player[i].sort_value);
mvwprintw(curwin, i + 7, 2, "%5s %-*.*s %18s ",
ordinal[i + 1], w, w, player[i].name, buf);
for (int i = 0; i < number_players; i++) {
right(curwin, i + lines + 5, ORDINAL_COLS + 2, attr_normal, 0, 0,
1, gettext(ordinal[i + 1]));
left(curwin, i + lines + 5, ORDINAL_COLS + 4, attr_normal, 0, 0,
1, "%ls", player[i].name);
right(curwin, i + lines + 5, w - 2, attr_normal, 0, 0,
1, " %!N ", player[i].sort_value);
}
wait_for_key(curwin, getmaxy(curwin) - 2, attr_waitforkey);
deltxwin();
}
free(buf);
free(chbuf);
}
@ -568,79 +632,31 @@ void end_game (void)
void show_map (bool closewin)
{
int n, x, y;
newtxwin(MAX_Y + 4, WIN_COLS, 1, WCENTER, true, attr_map_window);
// Draw various borders
// Draw various borders and highlights
mvwaddch(curwin, 2, 0, ACS_LTEE);
whline(curwin, ACS_HLINE, getmaxx(curwin) - 2);
mvwaddch(curwin, 2, getmaxx(curwin) - 1, ACS_RTEE);
mvwhline(curwin, 1, 2, ' ' | attr_mapwin_title, getmaxx(curwin) - 4);
// Display current player and turn number
wattrset(curwin, attr_mapwin_title);
mvwaddstr(curwin, 1, 2, " ");
waddstr(curwin, "Player: ");
n = getmaxx(curwin) - getcurx(curwin) - 4;
wattrset(curwin, attr_mapwin_highlight);
wprintw(curwin, "%-*.*s", n, n, player[current_player].name);
wattrset(curwin, attr_mapwin_title);
waddstr(curwin, " ");
if (turn_number != max_turn) {
const char *initial = "Turn: ";
char *buf = malloc(BUFSIZE);
if (buf == NULL) {
err_exit_nomem();
}
int len1 = strlen(initial);
int len2 = snprintf(buf, BUFSIZE, "%d", turn_number);
mvwaddstr(curwin, 1, getmaxx(curwin) - (len1 + len2) - 6, " ");
waddstr(curwin, initial);
attrpr(curwin, attr_mapwin_highlight, "%s", buf);
waddstr(curwin, " ");
free(buf);
} else {
const char *buf = "*** Last Turn ***";
int len = strlen(buf);
mvwaddstr(curwin, 1, getmaxx(curwin) - len - 6, " ");
attrpr(curwin, attr_mapwin_blink, "%s", buf);
waddstr(curwin, " ");
}
wattrset(curwin, attr_map_window);
left(curwin, 1, 4, attr_mapwin_title, attr_mapwin_highlight, 0, 1,
_("Player: ^{%ls^}"), player[current_player].name);
right(curwin, 1, getmaxx(curwin) - 2, attr_mapwin_title,
attr_mapwin_highlight, attr_mapwin_blink, 1,
(turn_number != max_turn) ? _(" Turn: ^{%d^} ") :
_(" ^[*** Last Turn ***^] "), turn_number);
// Display the actual map
for (y = 0; y < MAX_Y; y++) {
for (int y = 0; y < MAX_Y; y++) {
wmove(curwin, y + 3, 2);
for (x = 0; x < MAX_X; x++) {
map_val_t m = galaxy_map[x][y];
for (int x = 0; x < MAX_X; x++) {
chtype *mapstr = CHTYPE_MAP_VAL(galaxy_map[x][y]);
switch (m) {
case MAP_EMPTY:
waddch(curwin, PRINTABLE_MAP_VAL(m) | attr_map_empty);
break;
case MAP_OUTPOST:
waddch(curwin, PRINTABLE_MAP_VAL(m) | attr_map_outpost);
break;
case MAP_STAR:
waddch(curwin, PRINTABLE_MAP_VAL(m) | attr_map_star);
break;
default:
waddch(curwin, PRINTABLE_MAP_VAL(m) | attr_map_company);
break;
while (*mapstr != 0) {
waddch(curwin, *mapstr++);
}
waddch(curwin, ' ' | attr_map_empty);
}
}
@ -667,27 +683,25 @@ void show_map (bool closewin)
void show_status (int num)
{
double val;
int i, line;
int w, i, line;
assert(num >= 0 && num < number_players);
newtxwin(MAX_COMPANIES + 15, WIN_COLS, 1, WCENTER, true,
attr_normal_window);
center(curwin, 1, attr_title, " Stock Portfolio ");
center2(curwin, 2, attr_normal, attr_highlight, "Player: ", "%s",
player[num].name);
center(curwin, 1, 0, attr_title, 0, 0, 1, _(" Stock Portfolio "));
center(curwin, 2, 0, attr_normal, attr_highlight, 0, 1,
_("Player: ^{%ls^}"), player[num].name);
val = total_value(num);
if (val == 0.0) {
center(curwin, 11, attr_blink, "* * * B A N K R U P T * * *");
center(curwin, 11, 0, attr_normal, attr_highlight, attr_blink, 1,
/* TRANSLATORS: The current player is bankrupt (has no
shares or cash, ie, whose total value is zero) */
_("^[* * * B A N K R U P T * * *^]"));
} else {
char *buf = malloc(BUFSIZE);
if (buf == NULL) {
err_exit_nomem();
}
w = getmaxx(curwin);
// Check to see if any companies are on the map
bool none = true;
@ -699,51 +713,115 @@ void show_status (int num)
}
if (none) {
center(curwin, 8, attr_normal, "No companies on the map");
center(curwin, 8, 0, attr_normal, attr_highlight, 0, 1,
_("No companies on the map"));
} else {
// Handle the locale's currency symbol
snprintf(buf, BUFSIZE, "share (%s)", lconvinfo.currency_symbol);
mvwhline(curwin, 4, 2, ' ' | attr_subtitle, w - 4);
mvwhline(curwin, 5, 2, ' ' | attr_subtitle, w - 4);
wattrset(curwin, attr_subtitle);
mvwprintw(curwin, 4, 2, " %-22s %12s %10s %10s %10s ",
"", "Price per", "", "Holdings", "Company");
mvwprintw(curwin, 5, 2, " %-22s %12s %10s %10s %10s ",
"Company", buf, "Return (%)", "(shares)", "owner (%)");
wattrset(curwin, attr_normal);
left(curwin, 4, 4, attr_subtitle, 0, 0, 2,
/* TRANSLATORS: "Company" is a two-line column label in
a table containing a list of companies. */
pgettext("subtitle", "\nCompany"));
right(curwin, 4, w - 4, attr_subtitle, 0, 0, 2,
/* TRANSLATORS: "Ownership" is a two-line column label
in a table containing the current player's
percentage ownership in any given company. The
maximum column width is 10 characters (see
OWNERSHIP_COLS in src/intf.h). */
pgettext("subtitle", "Ownership\n(%%)"));
right(curwin, 4, w - 6 - OWNERSHIP_COLS, attr_subtitle, 0, 0, 2,
/* TRANSLATORS: "Holdings" is a two-line column label
in a table containing the number of shares the
current player owns in any given company. The
maximum column width is 10 characters (see
STOCK_OWNED_COLS in src/intf.h). */
pgettext("subtitle", "Holdings\n(shares)"));
right(curwin, 4, w - 8 - OWNERSHIP_COLS - STOCK_OWNED_COLS,
attr_subtitle, 0, 0, 2,
/* TRANSLATORS: "Return" is a two-line column label in
a table containing the share return as a percentage
in any given company. The maximum column width is
10 characters (see SHARE_RETURN_COLS in src/intf.h). */
pgettext("subtitle", "Return\n(%%)"));
right(curwin, 4, w - 10 - OWNERSHIP_COLS - STOCK_OWNED_COLS
- SHARE_RETURN_COLS, attr_subtitle, 0, 0, 2,
/* TRANSLATORS: "Price per share" is a two-line column
label in a table containing the price per share in
any given company. %ls is the currency symbol in
the current locale. The maximum column width is 12
characters INCLUDING the currency symbol (see
SHARE_PRICE_COLS in src/intf.h). */
pgettext("subtitle", "Price per\nshare (%ls)"),
currency_symbol);
for (line = 6, i = 0; i < MAX_COMPANIES; i++) {
if (company[i].on_map) {
l_strfmon(buf, BUFSIZE, "%!12n", company[i].share_price);
mvwprintw(curwin, line, 2,
" %-22s %10s %10.2f %'10ld %10.2f ",
company[i].name, buf,
company[i].share_return * 100.0,
player[num].stock_owned[i],
(company[i].stock_issued == 0) ? 0.0 :
((double) player[num].stock_owned[i] * 100.0)
/ company[i].stock_issued);
left(curwin, line, 4, attr_normal, 0, 0, 1, "%ls",
company[i].name);
right(curwin, line, w - 2, attr_normal, 0, 0, 1, "%.2f ",
(company[i].stock_issued == 0) ? 0.0 :
((double) player[num].stock_owned[i] * 100.0)
/ company[i].stock_issued);
right(curwin, line, w - 4 - OWNERSHIP_COLS, attr_normal,
0, 0, 1, "%'ld ", player[num].stock_owned[i]);
right(curwin, line, w - 6 - OWNERSHIP_COLS
- STOCK_OWNED_COLS, attr_normal, 0, 0, 1, "%.2f ",
company[i].share_return * 100.0);
right(curwin, line, w - 8 - OWNERSHIP_COLS
- STOCK_OWNED_COLS - SHARE_RETURN_COLS, attr_normal,
0, 0, 1, " %!N ", company[i].share_price);
line++;
}
}
}
line = 15;
l_strfmon(buf, BUFSIZE, "%18n", player[num].cash);
center2(curwin, line++, attr_normal, attr_highlight, "Current cash: ",
" %s ", buf);
line = MAX_COMPANIES + 7;
chtype *chbuf = xmalloc(BUFSIZE * sizeof(chtype));
int width, x;
mkchstr(chbuf, BUFSIZE, attr_highlight, 0, 0, 1, w / 2, &width, 1,
/* TRANSLATORS: The "Total value", "Current cash",
"Current debt" and "Interest rate" labels MUST all be
the same length (ie, right-padded with spaces as
needed) and must have at least one trailing space so
that the display routines work correctly. The maximum
length of each label is 36 characters.
Note that some of these labels are used for both the
Player Status window and the Trading Bank window. */
pgettext("label", "Total value: "));
x = (w + width - (TOTAL_VALUE_COLS + 2)) / 2;
right(curwin, line, x, attr_normal, attr_highlight, 0, 1,
pgettext("label", "Current cash: "));
right(curwin, line, x + TOTAL_VALUE_COLS + 2, attr_normal,
attr_highlight, 0, 1, " ^{%N^} ", player[num].cash);
line++;
if (player[num].debt != 0.0) {
l_strfmon(buf, BUFSIZE, "%18n", player[num].debt);
center2(curwin, line++, attr_normal, attr_highlight,
"Current debt: ", " %s ", buf);
center2(curwin, line++, attr_normal, attr_highlight,
"Interest rate: ", " %17.2f%% ", interest_rate * 100.0);
right(curwin, line, x, attr_normal, attr_highlight, 0, 1,
pgettext("label", "Current debt: "));
right(curwin, line, x + TOTAL_VALUE_COLS + 2, attr_normal,
attr_highlight, 0, 1, " ^{%N^} ", player[num].debt);
line++;
right(curwin, line, x, attr_normal, attr_highlight, 0, 1,
pgettext("label", "Interest rate: "));
right(curwin, line, x + TOTAL_VALUE_COLS + 2, attr_normal,
attr_highlight, 0, 1, " ^{%.2f%%^} ", interest_rate * 100.0);
line++;
}
l_strfmon(buf, BUFSIZE, "%18n", val);
center2(curwin, line + 1, attr_highlight, attr_title,
"Total value: ", " %s ", buf);
rightch(curwin, line + 1, x, chbuf, 1, &width);
whline(curwin, ' ' | attr_title, TOTAL_VALUE_COLS + 2);
right(curwin, line + 1, x + TOTAL_VALUE_COLS + 2, attr_title, 0, 0, 1,
" %N ", val);
free(buf);
free(chbuf);
}
wait_for_key(curwin, getmaxy(curwin) - 2, attr_waitforkey);
@ -758,13 +836,12 @@ void show_status (int num)
double total_value (int num)
{
double val;
int i;
assert(num >= 0 && num < number_players);
val = player[num].cash - player[num].debt;
for (i = 0; i < MAX_COMPANIES; i++) {
for (int i = 0; i < MAX_COMPANIES; i++) {
if (company[i].on_map) {
val += player[num].stock_owned[i] * company[i].share_price;
}

View File

@ -28,6 +28,7 @@
*/
#include "system.h"
#include "globals.h"
@ -37,28 +38,84 @@
// Company names
const char *company_name[MAX_COMPANIES] = {
"Altair Starways",
"Betelgeuse, Ltd",
"Capella Freight Co",
"Denebola Shippers",
"Eridani Expediters",
"Fornax Express",
"Gemeni Inc",
"Hercules and Co"
/* TRANSLATORS: The eight company names do NOT have to be literal
translations of the English names. In fact, if possible, the
names should start with successive letters of your alphabet (in
English, for example, "A" to "H"). No company name should be more
than 24 characters (column positions, to be precise) long. */
N_("Altair Starways"),
N_("Betelgeuse, Ltd"),
N_("Capella Freight Co"),
N_("Denebola Shippers"),
N_("Eridani Expediters"),
N_("Fornax Express"),
N_("Gemeni Inc"),
N_("Hercules and Co")
};
// Default keycodes (keyboard input characters) for each company
const char *default_keycode_company =
/* TRANSLATORS: This string specifies the keycodes (keyboard input
codes) used to enter the Stock Transaction window for each
company. There must be exactly eight characters, one for each
company in order, before the ASCII vertical line "|"; these must
be EITHER all in upper-case or all in lower-case. If at all
possible, these should be successive letters in your alphabet (in
English, "A" to "H"). Do NOT use digits or control characters.
Do not change or translate anything after the vertical line. */
N_("ABCDEFGH|input|Companies");
// Default keycodes (keyboard input characters) for each move
const char *default_keycode_game_move =
/* TRANSLATORS: This string specifies the keycodes used to select a
game move. There must be exactly 20 characters, one for each
move, before the ASCII vertical line "|"; these must be EITHER all
in upper-case or all in lower-case. If at all possible, these
should be successive letters in your alphabet. Do NOT use digits
or control characters. Do not change or translate anything after
the vertical line. */
N_("ABCDEFGHIJKLMNOPQRST|input|GameMoves");
// Default printable output representations for each map element
const char *default_printable_map_val =
/* TRANSLATORS: This string is used to display the galaxy map to
screen. There must be exactly 11 characters before the ASCII
vertical line. The first ("." in English) is used for empty
space, the second ("+") for outposts, the third ("*") for stars,
the remaining for the eight companies. Do not change or translate
anything after the vertical line. */
N_(".+*ABCDEFGH|output|MapVals");
// Default printable output representations for each move
const char *default_printable_game_move =
/* TRANSLATORS: This string is used to display the game moves
(choices). There must be exactly 20 characters (NUMBER_MOVES)
before the ASCII vertical line. The first character corresponds
to the first character in the "input|GameMoves" string, and so on.
Do not change or translate anything after the vertical line. */
N_("abcdefghijklmnopqrst|output|GameMoves");
// Ordinal strings
const char *ordinal[MAX_PLAYERS + 1] = {
"0th",
"1st",
"2nd",
"3rd",
"4th",
"5th",
"6th",
"7th",
"8th"
"",
/* TRANSLATORS: The ordinal strings "1st" to "8th" are used in the
Game Winner dialog box at the end of the game. If ordinals depend
on the gender of the player, it may be simpler to list cardinal
numbers instead (eg, "No. 1"). Up to five characters are allowed
(see ORDINAL_COLS in src/intf.h). */
N_("1st"),
N_("2nd"),
N_("3rd"),
N_("4th"),
N_("5th"),
N_("6th"),
N_("7th"),
N_("8th")
};

View File

@ -32,9 +32,6 @@
#define included_GLOBALS_H 1
#include <stdbool.h>
/************************************************************************
* Game constants *
************************************************************************/
@ -97,7 +94,7 @@
// Information about each company
typedef struct company_info {
const char *name; // Company name
wchar_t *name; // Company name
double share_price; // Share price
double share_return; // Return per share
long int stock_issued; // Total stock sold to players
@ -108,7 +105,8 @@ typedef struct company_info {
// Information about each player
typedef struct player_info {
char *name; // Player name
wchar_t *name; // Player name
char *name_utf8; // Player name (in UTF-8, for load/save)
double cash; // Cash available
double debt; // Amount of debt
long int stock_owned[MAX_COMPANIES]; // How much stock is owned
@ -130,12 +128,6 @@ typedef enum map_val {
#define MAP_TO_COMPANY(m) ((m) - MAP_A)
#define IS_MAP_COMPANY(m) ((m) >= MAP_A && (m) <= MAP_LAST)
#define PRINTABLE_MAP_VAL(m) ((char) (m))
#define COMPANY_TO_KEY(i) ((i) + 'A')
#define KEY_TO_COMPANY(k) ((k) - 'A')
#define IS_COMPANY_KEY(k) ((k) >= 'A' && (k) < COMPANY_TO_KEY(MAX_COMPANIES))
// Information about a move
typedef struct move_rec {
@ -143,10 +135,6 @@ typedef struct move_rec {
int y;
} move_rec_t;
#define MOVE_TO_KEY(m) ((m) + 'a')
#define KEY_TO_MOVE(k) ((k) - 'a')
#define IS_MOVE_KEY(k) ((k) >= 'a' && (k) < MOVE_TO_KEY(NUMBER_MOVES))
// Player moves / selection values
typedef enum selection {
@ -170,6 +158,18 @@ typedef enum selection {
// Company names
extern const char *company_name[MAX_COMPANIES];
// Default keycodes (keyboard input characters) for each company
extern const char *default_keycode_company;
// Default keycodes (keyboard input characters) for each move
extern const char *default_keycode_game_move;
// Default printable output representations for each map element
extern const char *default_printable_map_val;
// Default printable output representations for each move
extern const char *default_printable_game_move;
// Ordinal strings
extern const char *ordinal[MAX_PLAYERS + 1];

View File

@ -35,113 +35,188 @@
* Help text definition *
************************************************************************/
static const char *help_text[] = {
"^BStar Traders^N is a simple game of interstellar trading. The object of the\n"
"game is to amass the greatest amount of wealth possible. This is done by\n"
"creating interstellar shipping lanes, expanding them and buying shares in\n"
"the companies controlling them. Shares appreciate in value as company\n"
"operations expand. In addition, the return on each share (as a percentage)\n"
"also changes. Players may also borrow from the Interstellar Trading Bank to\n"
"finance additional purchases on the Stock Exchange.\n"
"\n"
"The map of the galaxy is represented by a ^B~x^N x ^B~y^N grid. A typical section\n"
"of it may be:\n"
"\n"
" ^e . . ^s*^e . . . ^s*^e ^s*^e . ^N\n"
" ^e . . . . . . . . . ^N ^e . ^N represents ^Bempty space^N,\n"
" ^e . ^s*^e . . . . . . . ^N ^s * ^N represents a ^Bstar^N.\n"
" ^e . . . . . . . ^s*^e . ^N\n"
" ^e . . . . ^s*^e . . . . ^N\n"
,
#define HELP_TEXT_PAGES 10
"The computer selects ^B~m^N moves (labeled ^k~1^N to ^k~M^N) at random, and places these\n"
"on the map. To select any of the highlighted positions, press that letter.\n"
"As an example, some of the moves on the map may be:\n"
"\n"
"\n"
" ^e ^k~1^e . ^s*^e . . . ^s*^e ^s*^e . ^N\n"
" ^e . . . ^k~3^e . . . . . ^N\n"
" ^e . ^s*^e . . . . ^k~5^e . . ^N Moves ^k~1^N to ^k~5^N shown.\n"
" ^e . ^k~2^e . . ^k~4^e . . ^s*^e . ^N\n"
" ^e . . . . ^s*^e . . . . ^N\n"
"\n"
"\n"
"Selecting a position that is ^Bnot^N next to a star (such as moves ^k~1^N, ^k~3^N or ^k~5^N)\n"
"will set up an ^Boutpost^N, not belonging to any company. Thus, if move ^k~3^N was\n"
"selected on the above map, a ^o + ^N would be placed at that position.\n"
,
static const char *help_text[HELP_TEXT_PAGES] = {
"If, on the other hand, a position next to a star (or another outpost) is\n"
"selected, a ^Bcompany^N would be formed and its first letter would appear on the\n"
"map. As a reward for creating the company, you are granted the first five\n"
"shares. Up to ^B~c^N companies can be created in this way.\n"
"\n"
"If a position next to an existing company is selected, the company would\n"
"expand its operations by one square. This increases the cost of its shares\n"
"and hence your return. Thus, if the map was as shown below, selecting ^k~6^N\n"
"or ^k~8^N increases Company ^B~B^N's shipping lane:\n"
"\n"
" ^e ^k~1^e . ^s*^e . . . ^s*^e ^s*^e . ^N\n"
" ^e . . . ^o+^e . . ^k~6^e . . ^N\n"
" ^e . ^s*^e . . . . ^c~B^e ^c~B^e ^c~B^e ^N Move ^k~6^N or ^k~8^N increases Company ^B~B^N.\n"
" ^e . ^k~2^e . . ^k~4^e . . ^s*^e ^c~B^e ^N\n"
" ^e . . . . ^s*^e . . . ^k~8^e ^N\n"
,
/*
TRANSLATORS: The help text for Star Traders is marked up using a
custom mark-up format NOT used anywhere else in the source code.
"Selecting positions next to stars increases the value of your stock by about\n"
"five times as much as an extension not next to a star. Thus move ^k~6^N should\n"
"be preferred to move ^k~8^N.\n"
"\n"
" ^e ^c~C^e . ^s*^e . . . ^s*^e ^s*^e . ^N\n"
" ^e ^k~1^e ^o+^e . ^o+^e . . ^k~6^e . . ^N\n"
" ^e . ^s*^e . . . . ^c~B^e ^c~B^e ^c~B^e ^N Move ^k~6^N is preferred to ^k~8^N.\n"
" ^e . ^k~2^e . . ^k~4^e . . ^s*^e ^c~B^e ^N\n"
" ^e . . . . ^s*^e . . . ^k~8^e ^N\n"
"\n"
"You can also expand any company by selecting positions next to outposts.\n"
"Such outposts will be swallowed up by that company. Thus, move ^k~1^N will\n"
"extend Company ^BC^N by ^Btwo^N squares. As a bonus, outposts next to stars are\n"
"more valuable: the company's share price will increase by a greater amount\n"
"than it would for outposts not next to stars.\n"
,
Each string is a single page of text that is displayed in an area 76
columns wide by 16 lines high. Each line is delimited by "\n". NO
word-wrapping is performed: you must place the "\n" characters in the
appropriate place. Ideally, each line within the string should be
also (manually) space-justified or centred. TAB characters and other
control codes must NOT be used. If a string starts with "@" as the
very first character, that string is ignored (as are all strings
following): this allows a variable number of help text pages (from
one to ten). Multibyte strings are handled correctly (even those
requiring shift sequences!).
"If two companies are separated on the map by only one square, then they can\n"
"be ^Bmerged^N into one company by selecting that position (if available). For\n"
"example, on the map below, companies ^B~A^N and ^B~B^N can be merged by selecting ^k~5^N.\n"
"When this occurs, the company with the greater assets value takes over the\n"
"other one. Here, Company ^B~B^N might take over Company ^B~A^N. Company ^B~A^N ceases to\n"
"exist, although it may reappear as an entirely new company at a later stage.\n"
"\n"
" ^e ^k~1^e . ^s*^e . . . ^s*^e ^s*^e . ^N\n"
" ^e . . . ^c~A^e ^c~A^e ^k~5^e ^c~B^e . . ^N\n"
" ^e . ^s*^e . . ^c~A^e . ^c~B^e ^c~B^e ^c~B^e ^N Move ^k~5^N merges companies ^B~A^N and ^B~B^N.\n"
" ^e . ^k~2^e . . . . . ^s*^e ^c~B^e ^N\n"
" ^e . . . . ^s*^e . ^o+^e . . ^N\n"
"\n"
"When companies merge, players are granted shares in the dominant company\n"
"proportional to the amount owned in the old company. As well, a cash bonus\n"
"is also paid, proportional to the percentage of the old company owned.\n"
,
The ASCII circumflex accent character "^" switches to a different
character rendition (also called attributes), depending on the
character following the "^":
"Once you select your move, you enter the ^BInterstellar Stock Exchange^N. Here\n"
"you can purchase shares, sell them, borrow from the Trading Bank or repay\n"
"some of your debt (if applicable). Note that each company issues a limited\n"
"number of shares --- you cannot go on buying for ever! You can, however,\n"
"bid for more shares to be issued. You have a better chance of succeeding if\n"
"you own a larger proportion of the company.\n"
"\n"
"The game usually ends after ^B~t^N turns. However, you can end the game sooner\n"
"by pressing ^K<CTRL><C>^N when asked to select a move. As well, individual\n"
"players can declare themselves bankrupt at any time. If your debt is large\n"
"enough, the Bank may do this for you! If you do not complete your game in\n"
"the time you have available, you may save the game and continue it later.\n"
"\n"
"\n"
"The ^Bwinner of the game^N is the person with the greatest net worth (total\n"
"value of cash, stock and debt). ^HGood luck^N and may the best person win!\n"
,
^^ - Print the circumflex accent (ASCII code U+005E)
^N - Switch to using the normal character rendition
^B - Switch to using the bold character rendition
^H - Switch to using the highlight character rendition
^K - Switch to using the keycode character rendition (such as used for "<CTRL><C>")
^e - Switch to using the character rendition used for empty space
^o - Switch to using the character rendition used for outposts
^s - Switch to using the character rendition used for stars
^c - Switch to using the character rendition used for companies
^k - Switch to using the character rendition used for keyboard choices on the galaxy map
NULL
The help text parsing routines also understand the following "value
escapes" introduced by the ASCII tilde character "~"; these act like
"%" conversion specifiers in printf():
~~ - Print the tilde character (ASCII code U+007E) [*]
~x - Print the width of the galaxy map (MAX_X) [**]
~y - Print the height of the galaxy map (MAX_Y) [**]
~m - Print the number of moves available (NUMBER_MOVES) [**]
~c - Print the maximum number of companies that can be formed (MAX_COMPANIES) [*]
~t - Prints the default number of turns in the game (DEFAULT_MAX_TURN) [**]
~1 to ~9 - Print the keycode for the N-th choice of move [***]
~M - Print the keycode for the last choice of move [***]
~A to ~H - Print the character used to represent the company on the galaxy map [***]
~. - Print the character used to represent empty space on the map [***]
~+ - Print the character used to represent outposts on the map [***]
~* - Print the character used to represent stars on the map [***]
[*] Takes one character space (column space) in the output
[**] Takes two column spaces in the output
[***] Takes one or two column spaces in the output, depending on the
appropriate strings in the current PO file.
Note that all keycodes and map representation characters use locale-
specific characters; double-width characters ARE supported. Note
also that the tilde value escapes do NOT change the current character
rendition: a circumflex accent escape is needed for that. For
example, to display the first choice of move as it would be shown on
the galaxy map, use something like "^k~1^N" (a six-character sequence
that would translate to just one character (or maybe two) in the
output text).
*/
N_( ""
"^BStar Traders^N is a simple game of interstellar trading. The object of the\n"
"game is to amass the greatest amount of wealth possible. This is done by\n"
"creating interstellar shipping lanes, expanding them and buying shares in\n"
"the companies controlling them. Shares appreciate in value as company\n"
"operations expand. In addition, the return on each share (as a percentage)\n"
"also changes. Players may also borrow from the Interstellar Trading Bank to\n"
"finance additional purchases on the Stock Exchange.\n"
"\n"
"The map of the galaxy is represented by a ^B~x^N x ^B~y^N grid. A typical section\n"
"of it may be:\n"
"\n"
" ^e ~. ~. ^s~*^e ~. ~. ~. ^s~*^e ^s~*^e ~. ^N\n"
" ^e ~. ~. ~. ~. ~. ~. ~. ~. ~. ^N ^e ~. ^N represents ^Bempty space^N,\n"
" ^e ~. ^s~*^e ~. ~. ~. ~. ~. ~. ~. ^N ^s ~* ^N represents a ^Bstar^N.\n"
" ^e ~. ~. ~. ~. ~. ~. ~. ^s~*^e ~. ^N\n"
" ^e ~. ~. ~. ~. ^s~*^e ~. ~. ~. ~. ^N\n"
""),
N_( ""
"The computer selects ^B~m^N moves (labeled ^k~1^N to ^k~M^N) at random, and places these\n"
"on the map. To select any of the highlighted positions, press that letter.\n"
"For example, some of the moves on the map may be:\n"
"\n"
"\n"
" ^e ^k~1^e ~. ^s~*^e ~. ~. ~. ^s~*^e ^s~*^e ~. ^N\n"
" ^e ~. ~. ~. ^k~3^e ~. ~. ~. ~. ~. ^N\n"
" ^e ~. ^s~*^e ~. ~. ~. ~. ^k~5^e ~. ~. ^N Moves ^k~1^N to ^k~5^N shown.\n"
" ^e ~. ^k~2^e ~. ~. ^k~4^e ~. ~. ^s~*^e ~. ^N\n"
" ^e ~. ~. ~. ~. ^s~*^e ~. ~. ~. ~. ^N\n"
"\n"
"\n"
"Selecting a position that is ^Bnot^N next to a star (such as moves ^k~1^N, ^k~3^N or ^k~5^N)\n"
"will set up an ^Boutpost^N, not belonging to any company. Thus, if move ^k~3^N is\n"
"selected on the above map, a ^o ~+ ^N would be placed at that position.\n"
""),
N_( ""
"If, on the other hand, a position next to a star (or another outpost) is\n"
"selected, a ^Bcompany^N would be formed and its letter would appear on the map.\n"
"As a reward for creating the company, you are granted the first five shares.\n"
"Up to ^B~c^N companies can be created in this way.\n"
"\n"
"If a position next to an existing company is selected, the company would\n"
"expand its operations by one square. This increases the cost of its shares\n"
"and hence your return. Thus, if the map was as shown below, selecting ^k~6^N\n"
"or ^k~8^N increases Company ^B~B^N's shipping lane:\n"
"\n"
" ^e ^k~1^e ~. ^s~*^e ~. ~. ~. ^s~*^e ^s~*^e ~. ^N\n"
" ^e ~. ~. ~. ^o~+^e ~. ~. ^k~6^e ~. ~. ^N\n"
" ^e ~. ^s~*^e ~. ~. ~. ~. ^c~B^e ^c~B^e ^c~B^e ^N Move ^k~6^N or ^k~8^N increases Company ^B~B^N.\n"
" ^e ~. ^k~2^e ~. ~. ^k~4^e ~. ~. ^s~*^e ^c~B^e ^N\n"
" ^e ~. ~. ~. ~. ^s~*^e ~. ~. ~. ^k~8^e ^N\n"
""),
N_( ""
"Selecting positions next to stars increases the value of your stock by about\n"
"five times as much as an extension not next to a star. Thus move ^k~6^N should\n"
"be preferred to move ^k~8^N.\n"
"\n"
" ^e ^c~C^e ~. ^s~*^e ~. ~. ~. ^s~*^e ^s~*^e ~. ^N\n"
" ^e ^k~1^e ^o~+^e ~. ^o~+^e ~. ~. ^k~6^e ~. ~. ^N\n"
" ^e ~. ^s~*^e ~. ~. ~. ~. ^c~B^e ^c~B^e ^c~B^e ^N Move ^k~6^N is preferred to ^k~8^N.\n"
" ^e ~. ^k~2^e ~. ~. ^k~4^e ~. ~. ^s~*^e ^c~B^e ^N\n"
" ^e ~. ~. ~. ~. ^s~*^e ~. ~. ~. ^k~8^e ^N\n"
"\n"
"You may also expand any company by selecting positions next to outposts.\n"
"Such outposts will be swallowed up by that company. Thus, move ^k~1^N will\n"
"extend Company ^B~C^N by ^Btwo^N squares. As a bonus, outposts next to stars are\n"
"more valuable: the company's share price will increase by a greater amount\n"
"than it would for outposts not next to stars.\n"
""),
N_( ""
"If two companies are separated on the map by only one square, then they can\n"
"be ^Bmerged^N into one company by selecting that position (if available). For\n"
"example, on the map below, companies ^B~A^N and ^B~B^N can be merged by selecting ^k~5^N.\n"
"When this occurs, the company with the greater assets value takes over the\n"
"other one. Here, Company ^B~B^N might take over Company ^B~A^N. Company ^B~A^N ceases to\n"
"exist, although it may reappear as an entirely new company at a later stage.\n"
"\n"
" ^e ^k~1^e ~. ^s~*^e ~. ~. ~. ^s~*^e ^s~*^e ~. ^N\n"
" ^e ~. ~. ~. ^c~A^e ^c~A^e ^k~5^e ^c~B^e ~. ~. ^N\n"
" ^e ~. ^s~*^e ~. ~. ^c~A^e ~. ^c~B^e ^c~B^e ^c~B^e ^N Move ^k~5^N merges companies ^B~A^N and ^B~B^N.\n"
" ^e ~. ^k~2^e ~. ~. ~. ~. ~. ^s~*^e ^c~B^e ^N\n"
" ^e ~. ~. ~. ~. ^s~*^e ~. ^o~+^e ~. ~. ^N\n"
"\n"
"When companies merge, players are granted shares in the dominant company\n"
"proportional to the amount owned in the old company. As well, a cash bonus\n"
"is also paid, proportional to the percentage of the old company owned.\n"
""),
N_( ""
"Once you select your move, you enter the ^BInterstellar Stock Exchange^N. Here\n"
"you may purchase shares, sell them, borrow from the Trading Bank or repay\n"
"some of your debt (if applicable). Note that each company issues a limited\n"
"number of shares -- you cannot go on buying for ever! You may, however, bid\n"
"for more shares to be issued. You have a better chance of succeeding if you\n"
"own a larger proportion of the company.\n"
"\n"
"The game usually ends after ^B~t^N turns. However, you may end the game sooner\n"
"by pressing ^K<CTRL><C>^N when asked to select a move. As well, individual\n"
"players can declare themselves bankrupt at any time. If your debt is large\n"
"enough, the Bank may do this for you! If you do not complete your game in\n"
"the time you have available, you may save the game and continue it later.\n"
"\n"
"\n"
"The ^Bwinner of the game^N is the person with the greatest net worth (total\n"
"value of cash, stock and debt). ^HGood luck^N and may the best person win!\n"
"")
#ifdef ENABLE_NLS
, N_("@ Help text, page 7")
, N_("@ Help text, page 8")
, N_("@ Help text, page 9")
, N_("@ Help text, page 10")
#endif
};
@ -157,210 +232,299 @@ static const char *help_text[] = {
void show_help (void)
{
wchar_t *wchelp_text[HELP_TEXT_PAGES];
wchar_t *wcbuf = xmalloc(BIGBUFSIZE * sizeof(wchar_t));
chtype *outbuf = xmalloc(BIGBUFSIZE * sizeof(chtype));
int curpage = 0;
int numpages;
int numpages = 0;
bool done = false;
// Count how many pages appear in the help text
for (numpages = 0; help_text[numpages] != NULL; numpages++)
;
// Count how many pages appear in the (translated) help text
while (numpages < HELP_TEXT_PAGES) {
const char *s = gettext(help_text[numpages]);
if (s == NULL || *s == '\0' || *s == '@')
break;
if (numpages == 0)
xmbstowcs(wcbuf, s, BIGBUFSIZE);
wchelp_text[numpages] = xwcsdup(wcbuf);
numpages++;
}
if (numpages == 0) {
free(outbuf);
free(wcbuf);
return;
}
newtxwin(WIN_LINES - 1, WIN_COLS, 1, WCENTER, false, 0);
while (! done) {
// Display a page of instructions
wbkgdset(curwin, attr_normal_window);
werase(curwin);
wbkgd(curwin, attr_normal_window);
box(curwin, 0, 0);
center(curwin, 1, attr_title, " How to Play ");
center(curwin, 2, attr_normal, "Page %d of %d", curpage + 1, numpages);
center(curwin, 1, 0, attr_title, 0, 0, 1, _(" How to Play "));
center(curwin, 2, 0, attr_normal, attr_highlight, 0, 1,
_("Page %d of %d"), curpage + 1, numpages);
wmove(curwin, 4, 2);
// Process the help text string
const char *s = help_text[curpage];
const wchar_t *htxt = wchelp_text[curpage];
char convbuf[MB_LEN_MAX + 1];
char *cp;
mbstate_t mbstate;
chtype *outp;
size_t i, n;
int count = BIGBUFSIZE;
int maxchar = MB_CUR_MAX;
int curattr = attr_normal;
while (*s != '\0') {
switch (*s) {
case '\n':
// Start a new line, suitably indented
wmove(curwin, getcury(curwin) + 1, 2);
memset(&mbstate, 0, sizeof(mbstate));
outp = outbuf;
while (*htxt != L'\0' && count > maxchar * 2) {
switch (*htxt) {
case L'\n':
// Start a new line
*outp++ = '\n';
count--;
break;
case '^':
// Set the current attribute
switch (*++s) {
case '^':
waddch(curwin, *s | curattr);
break;
case L'^':
// Switch to a different character rendition
switch (*++htxt) {
case L'^':
wcbuf[0] = *htxt;
wcbuf[1] = L'\0';
goto addwcbuf;
case 'N':
case L'N':
curattr = attr_normal;
wattrset(curwin, curattr);
break;
case 'B':
case L'B':
curattr = attr_normal | A_BOLD;
wattrset(curwin, curattr);
break;
case 'H':
case L'H':
curattr = attr_highlight;
wattrset(curwin, curattr);
break;
case 'K':
case L'K':
curattr = attr_keycode;
wattrset(curwin, curattr);
break;
case 'e':
case L'e':
curattr = attr_map_empty;
wattrset(curwin, curattr);
break;
case 'o':
case L'o':
curattr = attr_map_outpost;
wattrset(curwin, curattr);
break;
case 's':
case L's':
curattr = attr_map_star;
wattrset(curwin, curattr);
break;
case 'c':
case L'c':
curattr = attr_map_company;
wattrset(curwin, curattr);
break;
case 'k':
case L'k':
curattr = attr_map_choice;
wattrset(curwin, curattr);
break;
default:
waddch(curwin, '^' | curattr);
waddch(curwin, *s | curattr);
wcbuf[0] = L'^';
wcbuf[1] = *htxt;
wcbuf[2] = L'\0';
goto addwcbuf;
}
break;
case '~':
case L'~':
// Print a global constant
switch (*++s) {
case '~':
waddch(curwin, *s | curattr);
break;
switch (*++htxt) {
case L'~':
wcbuf[0] = *htxt;
wcbuf[1] = L'\0';
goto addwcbuf;
case 'x':
wprintw(curwin, "%2d", MAX_X);
break;
case L'x':
swprintf(wcbuf, BIGBUFSIZE, L"%2d", MAX_X);
goto addwcbuf;
case 'y':
wprintw(curwin, "%2d", MAX_Y);
break;
case L'y':
swprintf(wcbuf, BIGBUFSIZE, L"%2d", MAX_Y);
goto addwcbuf;
case 'm':
wprintw(curwin, "%2d", NUMBER_MOVES);
break;
case L'm':
swprintf(wcbuf, BIGBUFSIZE, L"%2d", NUMBER_MOVES);
goto addwcbuf;
case 'c':
wprintw(curwin, "%d", MAX_COMPANIES);
break;
case L'c':
swprintf(wcbuf, BIGBUFSIZE, L"%d", MAX_COMPANIES);
goto addwcbuf;
case 't':
wprintw(curwin, "%2d", DEFAULT_MAX_TURN);
break;
case L't':
swprintf(wcbuf, BIGBUFSIZE, L"%2d", DEFAULT_MAX_TURN);
goto addwcbuf;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
case L'1':
case L'2':
case L'3':
case L'4':
case L'5':
case L'6':
case L'7':
case L'8':
case L'9':
// N-th choice of move, as a key press
wprintw(curwin, "%c", MOVE_TO_KEY(*s - '1'));
break;
wcbuf[0] = PRINTABLE_GAME_MOVE(*htxt - L'1');
wcbuf[1] = L'\0';
goto addwcbuf;
case 'M':
case L'M':
// Last choice of move, as a key press
wprintw(curwin, "%c", MOVE_TO_KEY(NUMBER_MOVES - 1));
break;
wcbuf[0] = PRINTABLE_GAME_MOVE(NUMBER_MOVES - 1);
wcbuf[1] = L'\0';
goto addwcbuf;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
case 'G':
case 'H':
case L'.':
// Map representation of empty space
wcbuf[0] = PRINTABLE_MAP_VAL(MAP_EMPTY);
wcbuf[1] = L'\0';
goto addwcbuf;
case L'+':
// Map representation of an outpost
wcbuf[0] = PRINTABLE_MAP_VAL(MAP_OUTPOST);
wcbuf[1] = L'\0';
goto addwcbuf;
case L'*':
// Map representation of a star
wcbuf[0] = PRINTABLE_MAP_VAL(MAP_STAR);
wcbuf[1] = L'\0';
goto addwcbuf;
case L'A':
case L'B':
case L'C':
case L'D':
case L'E':
case L'F':
case L'G':
case L'H':
// Map representation of company
wprintw(curwin, "%c",
PRINTABLE_MAP_VAL(COMPANY_TO_MAP(*s - 'A')));
break;
assert((*htxt - L'A') < MAX_COMPANIES);
wcbuf[0] = PRINTABLE_MAP_VAL(COMPANY_TO_MAP(*htxt - L'A'));
wcbuf[1] = L'\0';
goto addwcbuf;
default:
waddch(curwin, '~' | curattr);
waddch(curwin, *s | curattr);
wcbuf[0] = L'~';
wcbuf[1] = *htxt;
wcbuf[2] = L'\0';
goto addwcbuf;
}
break;
default:
// Print the character
waddch(curwin, *s | curattr);
wcbuf[0] = *htxt;
wcbuf[1] = L'\0';
addwcbuf:
for (wchar_t *p = wcbuf; *p != L'\0' && count > maxchar * 2; p++) {
n = xwcrtomb(convbuf, *p, &mbstate);
for (i = 0, cp = convbuf; i < n; i++, cp++, outp++, count--) {
*outp = (unsigned char) *cp | curattr;
}
}
}
s++;
htxt++;
}
center(curwin, 21, attr_waitforkey, (curpage == 0) ?
"[ Press <SPACE> to continue ] " :
"[ Press <SPACE> to continue or <BACKSPACE> for the previous page ] ");
// Add the terminating NUL (possibly with a preceding shift sequence)
n = xwcrtomb(convbuf, L'\0', &mbstate);
for (i = 0, cp = convbuf; i < n; i++, cp++, outp++, count--) {
*outp = (unsigned char) *cp;
}
assert(count >= 0);
// Display the formatted text in outbuf
for (outp = outbuf; *outp != 0; outp++) {
if (*outp == '\n') {
wmove(curwin, getcury(curwin) + 1, 2);
} else {
waddch(curwin, *outp);
}
}
center(curwin, getmaxy(curwin) - 2, 0, attr_waitforkey, 0, 0, 1,
(curpage == 0) ? _("[ Press <SPACE> to continue ] ") :
/* TRANSLATORS: The specific use of <SPACE> and
<BACKSPACE> is not essential: you can use <DEL>,
<PAGE-UP>, <UP>, <LEFT> or <BACK-TAB> instead of
<BACKSPACE>, and almost any other key instead of
<SPACE> (other than <ESC>, <CANCEL>, <EXIT>, <CTRL><C>,
<CTRL><G> or <CTRL><\>). */
_("[ Press <SPACE> to continue or <BACKSPACE> "
"for the previous page ] "));
wrefresh(curwin);
int key = gettxchar(curwin);
switch (key) {
case KEY_BS:
case KEY_BACKSPACE:
case KEY_DEL:
case KEY_PPAGE:
case KEY_UP:
case KEY_LEFT:
case KEY_BTAB:
if (curpage == 0) {
beep();
} else {
curpage--;
}
break;
case KEY_ESC:
case KEY_CANCEL:
case KEY_EXIT:
case KEY_CTRL('C'):
case KEY_CTRL('G'):
case KEY_CTRL('\\'):
done = true;
break;
default:
wint_t key;
if (gettxchar(curwin, &key) == OK) {
// Ordinary wide character
curpage++;
done = (curpage == numpages);
} else {
// Function or control character
switch (key) {
case KEY_BS:
case KEY_BACKSPACE:
case KEY_DEL:
case KEY_PPAGE:
case KEY_UP:
case KEY_LEFT:
case KEY_BTAB:
if (curpage == 0) {
beep();
} else {
curpage--;
}
break;
case KEY_ESC:
case KEY_CANCEL:
case KEY_EXIT:
case KEY_CTRL('C'):
case KEY_CTRL('G'):
case KEY_CTRL('\\'):
done = true;
break;
default:
curpage++;
done = (curpage == numpages);
}
}
}
deltxwin();
txrefresh();
for (curpage = 0; curpage < numpages; curpage++) {
free(wchelp_text[curpage]);
}
free(outbuf);
free(wcbuf);
}

View File

@ -42,8 +42,9 @@
Returns: (nothing)
This function displays instructions on how to play Star Traders in a
Curses window. It does not depend on any global game variable. On
exit, the previous screen is restored and refreshed.
Curses window. It does not depend on any global game variables other
than printable_map_val[] and printable_game_move[]. On exit, the
previous screen is restored and refreshed.
*/
extern void show_help (void);

2739
src/intf.c

File diff suppressed because it is too large Load Diff

View File

@ -33,9 +33,6 @@
#define included_INTF_H 1
#include "system.h"
/************************************************************************
* Constants and type declarations *
************************************************************************/
@ -43,18 +40,45 @@
/*
This version of Star Traders only utilises WIN_COLS x WIN_LINES of a
terminal screen; this terminal must be at least MIN_COLS x MIN_LINES in
size; the newtxwin() function automatically places a new window in the
centre-top of the terminal screen. The program does not yet handle
terminal resizing events.
size. Windows are placed in the centre-top of the terminal screen.
*/
#define MIN_LINES 24 // Minimum number of lines in terminal
#define MIN_COLS 80 // Minimum number of columns in terminal
#define WIN_LINES MIN_LINES // Number of lines in main window
#define WIN_COLS MIN_COLS // Number of columns in main window
#define WIN_LINES MIN_LINES // Number of lines used in main window
#define WIN_COLS MIN_COLS // Number of columns used in main window
#define WCENTER -1 // Centre the new window
#define WCENTER -1 // Centre the new window
#define MAX_DLG_LINES 10 // Default maximum lines of text in dialog box
// Space (number of terminal columns) to allow for various fields
#define YESNO_COLS 4 // Space to allow for "Yes" or "No" response
#define ORDINAL_COLS 5 // Space for ordinals (1st, 2nd, etc)
#define TOTAL_VALUE_COLS 18 // Space for total value (monetary)
#define SHARE_PRICE_COLS 12 // Space for "Price per share"
#define SHARE_RETURN_COLS 10 // Space for "Return per share"
#define STOCK_OWNED_COLS 10 // Space for "Holdings (shares)"
#define OWNERSHIP_COLS 10 // Space for "Company ownership (%)"
#define STOCK_ISSUED_COLS 10 // Space for "Shares issued"
#define STOCK_LEFT_COLS 10 // Space for "Shares left"
#define BANK_VALUE_COLS 18 // Space for amounts in bank window
#define BANK_INPUT_COLS 16 // Space for input text box in bank
#define TRADE_VALUE_COLS 16 // Space for amounts in trade window
#define TRADE_INPUT_COLS 10 // Space for input text box in trade window
#define MERGE_BONUS_COLS 12 // Space for "Bonus" (company merger)
#define MERGE_OLD_STOCK_COLS 8 // Space for "Old stocks" (company merger)
#define MERGE_NEW_STOCK_COLS 8 // Space for "New stocks" (company merger)
#define MERGE_TOTAL_STOCK_COLS 8 // Space for "Total stocks" (company merger)
// Check if resizing events are supported
#ifdef KEY_RESIZE
# define HANDLE_RESIZE_EVENTS 1
#else
# undef HANDLE_RESIZE_EVENTS
#endif
// Visibility of the cursor in Curses (for curs_set())
@ -77,13 +101,11 @@ typedef enum curs_type {
#define KEY_CTRL(x) ((x) - 0100) // ASCII control character
#define KEY_ILLEGAL 077777 // No key should ever return this!
// Keycodes for inserting the default value in input routines
#define KEY_DEFAULTVAL1 '='
#define KEY_DEFAULTVAL2 ';'
#define CHAR_DEFVAL1 L'='
#define CHAR_DEFVAL2 L';'
// Control-arrow key combinations, as returned by NCurses
// Control-arrow key combinations, as returned by Ncurses
#ifndef KEY_CDOWN
# define KEY_CDOWN 01007 // CTRL + Down Arrow
# define KEY_CUP 01060 // CTRL + Up Arrow
@ -91,15 +113,9 @@ typedef enum curs_type {
# define KEY_CRIGHT 01052 // CTRL + Right Arrow
#endif
// Keycodes only defined by NCurses
#ifndef KEY_RESIZE
# define KEY_RESIZE KEY_ILLEGAL
#endif
#ifndef KEY_EVENT
# define KEY_EVENT KEY_ILLEGAL
#endif
#ifndef KEY_MOUSE
# define KEY_MOUSE KEY_ILLEGAL
// Function-key result, for Curses that do not define it
#ifndef KEY_CODE_YES
# define KEY_CODE_YES 0400
#endif
// Timeout value (in ms) for Meta-X-style keyboard input
@ -115,6 +131,7 @@ typedef enum curs_type {
************************************************************************/
extern WINDOW *curwin; // Top-most (current) window
extern bool use_color; // True to use colour
// Character renditions (attributes) used by Star Traders
@ -152,6 +169,36 @@ extern chtype attr_error_highlight; // Error window highlighted string
extern chtype attr_error_waitforkey; // "Press any key", error window
/************************************************************************
* Game printing macros and global variable declarations *
************************************************************************/
// Macros and variables for printing the galaxy map
#define MAP_TO_INDEX(m) \
(((m) == MAP_EMPTY) ? 0 : \
(((m) == MAP_OUTPOST) ? 1 : \
(((m) == MAP_STAR) ? 2 : \
((m) - MAP_A + 3))))
#define PRINTABLE_MAP_VAL(m) printable_map_val[MAP_TO_INDEX(m)]
#define CHTYPE_MAP_VAL(m) chtype_map_val[MAP_TO_INDEX(m)]
extern wchar_t *keycode_company; // Keycodes for each company
extern wchar_t *printable_map_val; // Printable output for each map value
extern chtype *chtype_map_val[MAX_COMPANIES + 3]; // as chtype strings
// Macros and variables for printing the current game moves
#define PRINTABLE_GAME_MOVE(m) (printable_game_move[m])
#define CHTYPE_GAME_MOVE(m) (chtype_game_move[m])
extern wchar_t *keycode_game_move; // Keycodes for each game move
extern wchar_t *printable_game_move; // Printable output for each game move
extern chtype *chtype_game_move[NUMBER_MOVES]; // as chtype strings
/************************************************************************
* Basic text input/output function prototypes *
************************************************************************/
@ -191,10 +238,10 @@ extern void end_screen (void);
Function: newtxwin - Create a new window, inserted into window stack
Parameters: nlines - Number of lines in new window
ncols - Number of columns in new window
begin_y - Starting line number (0 to LINES-1)
begin_x - Starting column number (0 to COLS-1)
begin_y - Starting line number (0 to LINES-1) or WCENTER
begin_x - Starting column number (0 to COLS-1) or WCENTER
dofill - True to draw background and box frame
bkgd_attr - Background attribute
bkgd_attr - Background character rendition
Returns: WINDOW * - Pointer to new window
This function creates a window using the Curses newwin() function and
@ -261,124 +308,284 @@ extern int txrefresh (void);
/*
Function: attrpr - Print a string with a particular character rendition
Parameters: win - Window to use (should be curwin)
attr - Character rendition to use for the string
format - printf()-like format string
... - printf()-like arguments
Returns: int - Return code from wprintw(): OK or ERR
Function: txdlgbox - Display a dialog box and wait for any key
Parameters: maxlines - Maximum number of lines of text in window
ncols - Number of columns in dialog box window
begin_y - Starting line number (0 to LINES-1) or WCENTER
begin_x - Starting column number (0 to COLS-1) or WCENTER
bkgd_attr - Background character rendition
title_attr - Character rendition to use for dialog box title
norm_attr - Normal character rendition in box
alt1_attr - Alternate character rendition 1 (highlight)
alt2_attr - Alternate character rendition 2 (more highlighted)
keywait_attr - "Press any key" character rendition
boxtitle - Dialog box title (may be NULL)
format - Dialog box text, as passed to mkchstr()
... - Dialog box text format parameters
Returns: int - OK is always returned
This function sets the character rendition (attributes) to attr, prints
the string using wprintw(), then restores the previous character
rendition. The return code is as returned from wprintw(). Note that
wrefresh() is NOT called.
This function creates a dialog box window using newtxwin(), displays
boxtitle centred on the first line (if boxtitle is not NULL), displays
format (and associated parameters) centred using mkchstr(), then waits
for the user to press any key before closing the dialog box window.
Note that txrefresh() is NOT called once the window is closed.
*/
extern int attrpr (WINDOW *win, chtype attr, const char *restrict format, ...)
__attribute__((format (printf, 3, 4)));
extern int txdlgbox (int maxlines, int ncols, int begin_y, int begin_x,
chtype bkgd_attr, chtype title_attr, chtype norm_attr,
chtype alt1_attr, chtype alt2_attr, chtype keywait_attr,
const char *restrict boxtitle,
const char *restrict format, ...);
/*
Function: center - Centre a string in a given window
Parameters: win - Window to use (should be curwin)
y - Line on which to centre the string
attr - Character rendition to use for the string
format - printf()-like format string
... - printf()-like arguments
Returns: int - Return code from wprintw(): OK or ERR
Function: mkchstr - Prepare a string for printing to screen
Parameters: chbuf - Pointer to chtype buffer in which to store string
chbufsize - Number of chtype elements in chbuf
attr_norm - Normal character rendition to use
attr_alt1 - First alternate character rendition to use
attr_alt2 - Second alternate character rendition to use
maxlines - Maximum number of screen lines to use
maxwidth - Maximum width of each line, in chars
widthbuf - Pointer to buffer to store widths of each line
widthbufsize - Number of int elements in widthbuf
format - Format string as described below
... - Arguments for the format string
Returns: int - Number of lines actually used
This function prints a string formatted with wprintw() in the centre of
line y in the window win, using the character rendition (attributes) in
attr. If the string is too long to fit the window, it is truncated.
Please note that wrefresh() is NOT called.
This function converts the format string and following arguments into
chbuf, a chtype string that can be used for calls to leftch(), centerch()
and rightch(). At most maxlines lines are used, each with a maximum
width of maxwidth. The actual widths of each resulting line are stored
in widthbuf (which must not be NULL). If maxlines is greater than 1,
lines are wrapped as needed.
At the current time, the implementation of this function does NOT
handle multibyte strings correctly: strlen() is used to determine the
printing width of the string.
The format string is similar to but more limited than printf(). In
particular, only the following conversion specifiers are understood:
%% - Insert the ASCII percent sign (ASCII code U+0025)
%c - Insert the next parameter as a character (type char)
%lc - Insert the next parameter as a wide char (type wchar_t)
%s - Insert the next parameter as a string (type char *)
%ls - Insert the next parameter as a wide string (type wchar_t *)
%d - Insert the next parameter as an integer (type int)
%'d - As above, using the locale's thousands group separator
%ld - Insert the next parameter as a long int
%'ld - As above, using the locale's thousands group separator
%f - Insert the next parameter as a floating point number (double)
%.mf - As above, with precision "m" (a positive integer > 0)
%'.mf - As above, using the locale's thousands group separator
%N - Insert the next parameter as a double, using the locale's
national currency format (extension to printf())
%!N - As above, using the locale's national currency format without
the actual currency symbol (extension to printf())
Instead of using "%" to convert the next parameter, "%m$" can be used
to indicate fixed parameter m (where m is an integer from 1 to 8). For
example, "%4$s" inserts the fourth parameter after "format" as a string
into chbuf. As with printf(), using "%m$" together with ordinary "%"
forms is undefined. If "%m$" is used, no parameter m can be skipped.
Note that no other flag, field width, precision or length modifier
characters are recognised: if needed, these should be formatted FIRST
with snprintf(), then inserted using %s as appropriate.
In addition to the conversion specifiers, the following character
rendition flags are understood, where the "^" character is a literal
ASCII circumflex accent:
^^ - Insert the circumflex accent (ASCII code U+005E)
^{ - Switch to using attr_alt1 character rendition (alternate mode 1)
^} - Switch to using attr_norm character rendition
^[ - Switch to using attr_alt2 character rendition (alternate mode 2)
^] - Switch to using attr_norm character rendition
Printable characters other than these are inserted as literals. The
character '\n' will force the start of a new line; no other control (or
non-printable) characters are allowed. By default, attr_norm is used
as the character rendition (attributes).
This function returns the actual number of lines used (from 0 to
maxlines). If an error is detected, the application terminates.
*/
extern int center (WINDOW *win, int y, chtype attr,
const char *restrict format, ...)
__attribute__((format (printf, 4, 5)));
extern int mkchstr (chtype *restrict chbuf, int chbufsize, chtype attr_norm,
chtype attr_alt1, chtype attr_alt2, int maxlines,
int maxwidth, int *restrict widthbuf, int widthbufsize,
const char *restrict format, ...);
/*
Function: center2 - Centre two strings in a given window
Parameters: win - Window to use (should be curwin)
y - Line on which to centre the strings
attr1 - Character rendition to use for initial string
attr2 - Character rendition to use for main string
initial - Initial string (no printf() formatting)
format - printf()-like format string for main string
... - printf()-like arguments
Returns: int - Return code from wprintw(): OK or ERR
Function: vmkchstr - Prepare a string for printing to screen
Parameters: chbuf - Pointer to chtype buffer in which to store string
chbufsize - Number of chtype elements in chbuf
attr_norm - Normal character rendition to use
attr_alt1 - First alternate character rendition to use
attr_alt2 - Second alternate character rendition to use
maxlines - Maximum number of screen lines to use
maxwidth - Maximum width of each line, in chars
widthbuf - Pointer to buffer to store widths of each line
widthbufsize - Number of int elements in widthbuf
format - Format string as described for mkchstr()
args - Variable argument list
Returns: int - Number of lines actually used
This function prints two strings side-by-side on line y in the centre
of window win. The first (initial) string is printed with character
rendition (attributes) attr1 using waddstr(). The second (main) string
uses wprintw(format, ...) with rendition attr2. If the main string is
too long to fit in the window alongside the initial string, the main
string is truncated to fit. Please note that wrefresh() is NOT called.
As with center(), the current implementation does NOT handle multibyte
strings correctly.
This function is exactly the same as mkchstr(), except that it is
called with a va_list parameter args instead of a variable number of
arguments. Note that va_end() is NOT called on args, and that args is
undefined after this function.
*/
extern int center2 (WINDOW *win, int y, chtype attr1, chtype attr2,
const char *initial, const char *restrict format, ...)
__attribute__((format (printf, 6, 7)));
extern int vmkchstr (chtype *restrict chbuf, int chbufsize, chtype attr_norm,
chtype attr_alt1, chtype attr_alt2, int maxlines,
int maxwidth, int *restrict widthbuf, int widthbufsize,
const char *restrict format, va_list args);
/*
Function: center3 - Centre three strings in a given window
Parameters: win - Window to use (should be curwin)
y - Line on which to centre the strings
attr1 - Character rendition to use for initial string
attr3 - Character rendition to use for final string
attr2 - Character rendition to use for main string
initial - Initial string (no printf() formatting)
final - Final string (no printf() formatting)
format - printf()-like format string for main string
... - printf()-like arguments
Returns: int - Return code from wprintw(): OK or ERR
Function: leftch - Print strings in chstr left-aligned
Parameters: win - Window to use (should be curwin)
y - Line on which to print first string
x - Starting column number for each line
chstr - chtype string as returned from mkchstr()
lines - Number of lines in chstr as returned from mkchstr()
widthbuf - Widths of each line as returned from mkchstr()
Returns: int - Always returns OK
This function prints three strings side-by-side on line y in the centre
of window win. The first (initial) string is printed with character
rendition (attributes) attr1 using waddstr(). The second (main) string
uses wprintw(format, ...) with rendition attr2. The third (final)
string is then printed with rendition attr3 using waddstr(). If the
strings are too long to fit the window width, the main (centre) string
is truncated. Please note that wrefresh() is NOT called. Also note
the order of rendition values: 1, 3, 2, NOT 1, 2, 3!
As with center(), the current implementation does NOT handle multibyte
strings correctly.
This function takes the strings in the chtype array chstr and prints
them left-aligned in the window win. Note that wrefresh() is NOT
called.
*/
extern int center3 (WINDOW *win, int y, chtype attr1, chtype attr3,
chtype attr2, const char *initial, const char *final,
const char *restrict format, ...)
__attribute__((format (printf, 8, 9)));
extern int leftch (WINDOW *win, int y, int x, const chtype *restrict chstr,
int lines, const int *restrict widthbuf);
/*
Function: gettxchar - Read a character from the keyboard
Function: centerch - Print strings in chstr centred in window
Parameters: win - Window to use (should be curwin)
y - Line on which to print first string
offset - Column offset to add to position for each line
chstr - chtype string as returned from mkchstr()
lines - Number of lines in chstr as returned from mkchstr()
widthbuf - Widths of each line as returned from mkchstr()
Returns: int - ERR if more lines in chstr[] than lines, else OK
This function takes the strings in the chtype array chstr and prints
them centred in the window win, offset by the parameter offset. Note
that wrefresh() is NOT called. ERR is returned if there are more lines
in chstr[] than are passed in the parameter lines.
*/
extern int centerch (WINDOW *win, int y, int offset,
const chtype *restrict chstr, int lines,
const int *restrict widthbuf);
/*
Function: rightch - Print strings in chstr right-aligned
Parameters: win - Window to use (should be curwin)
y - Line on which to print first string
x - Ending column number for each line
chstr - chtype string as returned from mkchstr()
lines - Number of lines in chstr as returned from mkchstr()
widthbuf - Widths of each line as returned from mkchstr()
Returns: int - ERR if more lines in chstr[] than lines, else OK
This function takes the strings in the chtype array chstr and prints
them right-aligned in the window win, with each line ending just before
column x. Note that wrefresh() is NOT called. ERR is returned if
there are more lines in chstr[] than are passed in the parameter lines.
*/
extern int rightch (WINDOW *win, int y, int x, const chtype *restrict chstr,
int lines, const int *restrict widthbuf);
/*
Function: left - Print strings left-aligned
Parameters: win - Window to use (should be curwin)
Returns: int - The keyboard character
y - Line on which to print first string
x - Starting column number for each line
attr_norm - Normal character rendition to use
attr_alt1 - First alternate character rendition to use
attr_alt2 - Second alternate character rendition to use
maxlines - Maximum number of screen lines to use
format - Format string as described for mkchstr()
... - Arguments for the format string
Returns: int - Number of lines actually used
This function reads a single character from the keyboard. The key is
NOT echoed to the terminal display, nor is the cursor visibility
affected.
This implementation does not handle multibyte characters correctly:
each part of the multibyte character most likely appears as a separate
keyboard press.
This shortcut function prepares a chtype string using mkchstr(), then
prints the string using leftch(). At most MAX_DLG_LINES are printed,
with the maximum width being that of the window win - x - 2 (the "2" is
for the right-hand border).
*/
extern int gettxchar (WINDOW *win);
extern int left (WINDOW *win, int y, int x, chtype attr_norm, chtype attr_alt1,
chtype attr_alt2, int maxlines, const char *restrict format,
...);
/*
Function: gettxline - Read a line from the keyboard (low-level)
Function: center - Print strings centred in window
Parameters: win - Window to use (should be curwin)
y - Line on which to print first string
offset - Column offset to add to position for each line
attr_norm - Normal character rendition to use
attr_alt1 - First alternate character rendition to use
attr_alt2 - Second alternate character rendition to use
maxlines - Maximum number of screen lines to use
format - Format string as described for mkchstr()
... - Arguments for the format string
Returns: int - Number of lines actually used
This shortcut function prepares a chtype string using mkchstr(), then
prints the string using centerch(). At most MAX_DLG_LINES are printed,
with the maximum width being that of the window win - 4 (for borders).
*/
extern int center (WINDOW *win, int y, int offset, chtype attr_norm,
chtype attr_alt1, chtype attr_alt2, int maxlines,
const char *restrict format, ...);
/*
Function: right - Print strings right-aligned
Parameters: win - Window to use (should be curwin)
y - Line on which to print first string
x - Ending column number for each line
attr_norm - Normal character rendition to use
attr_alt1 - First alternate character rendition to use
attr_alt2 - Second alternate character rendition to use
maxlines - Maximum number of screen lines to use
format - Format string as described for mkchstr()
... - Arguments for the format string
Returns: int - Number of lines actually used
This shortcut function prepares a chtype string using mkchstr(), then
prints the string using rightch(). At most MAX_DLG_LINES are printed,
with the maximum width being that of x - 2 (the "2" is for the
left-hand border).
*/
extern int right (WINDOW *win, int y, int x, chtype attr_norm, chtype attr_alt1,
chtype attr_alt2, int maxlines, const char *restrict format,
...);
/*
Function: gettxchar - Read a wide character from the keyboard
Parameters: win - Window to use (should be curwin)
wch - Pointer to keyboard wide character
Returns: int - OK or KEY_CODE_YES
This function waits until the user presses a key on the keyboard, then
reads that key as a single wide character. If it is a function key or
a control key, it is stored in wch and KEY_CODE_YES is returned.
Otherwise, it is an ordinary key: it is also stored in wch and OK is
returned. ERR is never returned. The key is NOT echoed to the
terminal display, nor is the cursor visibility affected.
*/
extern int gettxchar (WINDOW *win, wint_t *restrict wch);
/*
Function: gettxline - Read a line of input from the keyboard (low-level)
Parameters: win - Window to use (should be curwin)
buf - Pointer to preallocated buffer
bufsize - Size of buffer in bytes
bufsize - Size of buffer (number of wchar_t elements)
modified - Pointer to modified status (result)
multifield - Allow <TAB>, etc, to exit this function
emptyval - String used if input line is empty
@ -386,14 +593,14 @@ extern int gettxchar (WINDOW *win);
allowed - Characters allowed in the input line
stripspc - True to strip leading/trailing spaces
y, x - Start of the input field (line, column)
width - Width of the input field
width - Width of the input field (column spaces)
attr - Character rendition to use for input field
Returns: int - Status code: OK, ERR or KEY_ keycode
This low-level function draws an input field width characters long
This low-level function shows an input field width column-spaces long
using attr as the character rendition, then reads a line of input from
the keyboard and places it into the preallocated buffer buf[] of size
bufsize. On entry, buf[] must contain a valid C string; this string is
bufsize. On entry, buf[] must contain a valid string; this string is
used as the initial contents of the input field. On exit, buf[]
contains the final string as edited or input by the user. This string
is printed in place of the input field using the original character
@ -405,13 +612,13 @@ extern int gettxchar (WINDOW *win);
empty string is entered, the string pointed to by emptyval (if not
NULL) is stored in buf[].
If CANCEL, EXIT, ESC, ^C, ^\ or ^G is pressed, ERR is returned. In
If ESC, CANCEL, EXIT, ^C, ^G or ^\ is pressed, ERR is returned. In
this case, buf[] contains the string as left by the user: emptyval is
NOT used, nor are leading and trailing spaces stripped.
If multifield is true, the UP and DOWN arrow keys, as well as TAB,
Shift-TAB, ^P (Previous) and ^N (Next) return KEY_UP or KEY_DOWN as
appropriate. As with CANCEL etc., emptyval is NOT used, nor are
Shift-TAB, ^P (Previous) and ^N (Next) return either KEY_UP or KEY_DOWN
as appropriate. As with ESC etc., emptyval is NOT used, nor are
leading and trailing spaces stripped.
In all of these cases, the boolean variable *modified (if modified is
@ -419,30 +626,25 @@ extern int gettxchar (WINDOW *win);
way (including if the user made any changed, spaces were stripped or if
emptyval was copied into buf[]).
If KEY_DEFAULTVAL1 or KEY_DEFAULTVAL2 is pressed when the input line is
If either KEY_DEFVAL1 or KEY_DEFVAL2 is pressed when the input line is
empty, the string pointed to by defaultval (if not NULL) is placed in
the buffer as if typed by the user. Editing is NOT terminated in this
case.
If allowed is not NULL, only characters in that string are allowed to
be entered into the input line. For example, if allowed points to
"0123456789abcdefABCDEF", only those characters would be allowed (in
L"0123456789abcdefABCDEF", only those characters would be allowed (in
this instance, allowing the user to type in a hexadecimal number).
Note that the character rendition (attributes) in attr may contain a
printing character. For example, A_BOLD | '_' is a valid rendition
that causes the input field to be a series of "_" characters in bold.
This implementation does not handle multibyte characters correctly:
each part of the multibyte character most likely appears as a separate
keyboard press and is handled as a separate character, causing the
cursor position to be incorrect. In addition, allowed is compared on a
byte-by-byte basis, not character-by-character.
Note also that the cursor becomes invisible after calling this function.
*/
extern int gettxline (WINDOW *win, char *buf, int bufsize,
extern int gettxline (WINDOW *win, wchar_t *restrict buf, int bufsize,
bool *restrict modified, bool multifield,
const char *emptyval, const char *defaultval,
const char *allowed, bool stripspc, int y, int x,
const wchar_t *emptyval, const wchar_t *defaultval,
const wchar_t *allowed, bool stripspc, int y, int x,
int width, chtype attr);
@ -458,18 +660,19 @@ extern int gettxline (WINDOW *win, char *buf, int bufsize,
Returns: int - Status code: OK, ERR or KEY_ keycode
This function calls gettxline() to allow the user to enter a string via
the keyboard. On entry, bufptr must be the address of a char * pointer
variable; that pointer (*bufptr) must either be NULL or contain the
address of a buffer previously allocated with gettxstr(). If *bufptr
is NULL, a buffer of BUFSIZE is automatically allocated using malloc();
this buffer is used to store and return the input line.
the keyboard. On entry, bufptr must be the address of a wchar_t *
pointer variable; that pointer (*bufptr) must either be NULL or contain
the address of a buffer previously allocated with gettxstr(). If
*bufptr is NULL, a buffer of BUFSIZE is automatically allocated using
malloc(); this buffer is used to store and return the input line.
Apart from bufptr, all parameters are as used for gettxline(). The
gettxline() parameters emptyval and defaultval are passed as "",
gettxline() parameters emptyval and defaultval are passed as L"",
allowed is NULL and stripspc is true.
*/
extern int gettxstr (WINDOW *win, char **bufptr, bool *restrict modified,
bool multifield, int y, int x, int width, chtype attr);
extern int gettxstr (WINDOW *win, wchar_t *restrict *restrict bufptr,
bool *restrict modified, bool multifield,
int y, int x, int width, chtype attr);
/*
@ -481,7 +684,7 @@ extern int gettxstr (WINDOW *win, char **bufptr, bool *restrict modified,
emptyval - Value to use for empty input
defaultval - Value to use for default input
y, x - Start of the input field (line, column)
width - Width of the input field
width - Width of the input field (column spaces)
attr - Character rendition to use for input field
Returns: int - Status code: OK, ERR or KEY_ keycode
@ -493,12 +696,11 @@ extern int gettxstr (WINDOW *win, char **bufptr, bool *restrict modified,
result from gettxline() is passed back to the caller. Note that the
low-level function gettxline() is called with multifield set to false.
This function is locale-aware, although multibyte strings are not
handled correctly. In particular, the default value is formatted using
strfmon() and uses the locale monetary default decimal places
(frac_digits). In addition, the user is allowed to use the locale's
radix character (decimal point) and the thousands separator, as well as
the monetary versions of these.
This function is locale-aware. In particular, the default value is
formatted using strfmon() and uses the locale monetary default decimal
places (frac_digits). In addition, the user is allowed to use the
locale's radix character (decimal point) and the thousands separator,
as well as the monetary versions of these.
*/
extern int gettxdouble (WINDOW *win, double *restrict result, double min,
double max, double emptyval, double defaultval,
@ -521,9 +723,9 @@ extern int gettxdouble (WINDOW *win, double *restrict result, double min,
This function behaves almost exactly like gettxdouble(), except that
only integer numbers are allowed to be entered.
This function is locale-aware, although multibyte strings are not
handled correctly. In particular, the user is allowed to use the
locale's thousands separator and the monetary thousands separator.
This function is locale-aware. In particular, the user is allowed to
use the locale's thousands separator and the monetary thousands
separator.
*/
extern int gettxlong (WINDOW *win, long int *restrict result, long int min,
long int max, long int emptyval, long int defaultval,
@ -533,17 +735,15 @@ extern int gettxlong (WINDOW *win, long int *restrict result, long int min,
/*
Function: answer_yesno - Wait for a Yes/No answer
Parameters: win - Window to use (should be curwin)
attr_keys - Window rendition to use for key choices
Returns: bool - True if Yes was selected, false if No
This function prompts the user by printing " [Y/N] " using appropriate
character renditions ("Y" and "N" in attr_keys, the rest in the current
rendition), then waits for the user to press either "Y" (for Yes) or
"N" (for No) on the keyboard, then prints the answer using A_BOLD.
True is returned if "Y" was selected, false if "N". Note that the
cursor becomes invisible after calling this function.
This function waits for the user to press either the locale-specific
equivalent of "Y" (for Yes) or "N" (for No) on the keyboard, then
prints the answer using A_BOLD. True is returned if "Y" was selected,
false if "N". Note that the cursor becomes invisible after calling
this function.
*/
extern bool answer_yesno (WINDOW *win, chtype attr_keys);
extern bool answer_yesno (WINDOW *win);
/*
@ -558,10 +758,6 @@ extern bool answer_yesno (WINDOW *win, chtype attr_keys);
The reason the user is not asked "Press any key to continue" is
historical: many, many people used to ask "where is the <ANY> key?" :-)
The current implementation does not handle multibyte characters
correctly: only the first byte of the character is consumed, with
further bytes left in the keyboard queue.
*/
extern void wait_for_key (WINDOW *win, int y, chtype attr);

View File

@ -207,7 +207,6 @@ void select_moves (void)
selection_t get_move (void)
{
int i, x, y;
selection_t selection = SEL_NONE;
@ -219,89 +218,101 @@ selection_t get_move (void)
show_map(false);
// Display current move choices on the galaxy map
for (i = 0; i < NUMBER_MOVES; i++) {
mvwaddch(curwin, game_move[i].y + 3, game_move[i].x * 2 + 2,
MOVE_TO_KEY(i) | attr_map_choice);
for (int i = 0; i < NUMBER_MOVES; i++) {
mvwaddchstr(curwin, game_move[i].y + 3, game_move[i].x * 2 + 2,
CHTYPE_GAME_MOVE(i));
}
wrefresh(curwin);
// Show menu of choices for the player
newtxwin(5, WIN_COLS, 19, WCENTER, false, 0);
while (selection == SEL_NONE) {
wbkgd(curwin, attr_normal_window);
wbkgdset(curwin, attr_normal_window);
werase(curwin);
box(curwin, 0, 0);
wmove(curwin, 2, 2);
attrpr(curwin, attr_keycode, "<1>");
waddstr(curwin, " Display stock portfolio");
left(curwin, 2, 2, attr_normal, attr_keycode, 0, 1,
_("^{<1>^} Display stock portfolio"));
left(curwin, 3, 2, attr_normal, attr_keycode, 0, 1,
_("^{<2>^} Declare bankruptcy"));
left(curwin, 2, getmaxx(curwin) / 2, attr_normal, attr_keycode, 0, 1,
_("^{<3>^} Save and end the game"));
left(curwin, 3, getmaxx(curwin) / 2, attr_normal, attr_keycode, 0, 1,
_("^{<CTRL><C>^} Quit the game"));
wmove(curwin, 3, 2);
attrpr(curwin, attr_keycode, "<2>");
waddstr(curwin, " Declare bankruptcy");
wmove(curwin, 2, 42);
attrpr(curwin, attr_keycode, "<3>");
waddstr(curwin, " Save and end the game");
wmove(curwin, 3, 42);
attrpr(curwin, attr_keycode, "<CTRL><C>");
waddstr(curwin, " Quit the game");
mvwaddstr(curwin, 1, 9, "Select move ");
waddstr(curwin, "[");
attrpr(curwin, attr_choice, "%c", MOVE_TO_KEY(0));
waddstr(curwin, "-");
attrpr(curwin, attr_choice, "%c", MOVE_TO_KEY(NUMBER_MOVES - 1));
waddstr(curwin, "/");
attrpr(curwin, attr_keycode, "1");
waddstr(curwin, "-");
attrpr(curwin, attr_keycode, "3");
waddstr(curwin, "/");
attrpr(curwin, attr_keycode, "<CTRL><C>");
waddstr(curwin, "]: ");
right(curwin, 1, getmaxx(curwin) / 2, attr_normal, attr_keycode,
attr_choice, 1,
_("Select move [^[%lc^]-^[%lc^]/^{1^}-^{3^}/^{<CTRL><C>^}]: "),
PRINTABLE_GAME_MOVE(0), PRINTABLE_GAME_MOVE(NUMBER_MOVES - 1));
curs_set(CURS_ON);
wrefresh(curwin);
// Get the actual selection made by the player
while (selection == SEL_NONE) {
int key = tolower(gettxchar(curwin));
wint_t key;
if (IS_MOVE_KEY(key)) {
selection = KEY_TO_MOVE(key);
if (gettxchar(curwin, &key) == OK) {
// Ordinary wide character
int i;
bool found;
curs_set(CURS_OFF);
waddstr(curwin, "Move ");
attrpr(curwin, attr_choice, "%c", key);
if (iswupper(*keycode_game_move)) {
key = towupper(key);
} else if (iswlower(*keycode_game_move)) {
key = towlower(key);
}
for (i = 0, found = false; keycode_game_move[i] != L'\0'; i++) {
if (keycode_game_move[i] == key) {
found = true;
selection = i;
curs_set(CURS_OFF);
left(curwin, 1, getmaxx(curwin) / 2, attr_normal,
attr_choice, 0, 1,
/* TRANSLATORS: "Move" refers to the choice of
moves made by the current player (out of a
selection of 20 moves). */
_("Move ^{%lc^}"), PRINTABLE_GAME_MOVE(i));
break;
}
}
if (! found) {
switch (key) {
case L'1':
curs_set(CURS_OFF);
show_status(current_player);
curs_set(CURS_ON);
break;
case L'2':
selection = SEL_BANKRUPT;
curs_set(CURS_OFF);
left(curwin, 1, getmaxx(curwin) / 2, attr_normal,
attr_normal | A_BOLD, 0, 1,
_("^{<2>^} (Declare bankruptcy)"));
break;
case L'3':
selection = SEL_SAVE;
curs_set(CURS_OFF);
left(curwin, 1, getmaxx(curwin) / 2, attr_normal,
attr_normal | A_BOLD, 0, 1,
_("^{<3>^} (Save and end the game)"));
break;
default:
beep();
}
}
} else {
// Function or control key
switch (key) {
case '1':
curs_set(CURS_OFF);
show_status(current_player);
curs_set(CURS_ON);
break;
case '2':
selection = SEL_BANKRUPT;
curs_set(CURS_OFF);
wattron(curwin, A_BOLD);
waddstr(curwin, "<2>");
wattroff(curwin, A_BOLD);
waddstr(curwin, " (Declare bankruptcy)");
break;
case '3':
selection = SEL_SAVE;
curs_set(CURS_OFF);
wattron(curwin, A_BOLD);
waddstr(curwin, "<3>");
wattroff(curwin, A_BOLD);
waddstr(curwin, " (Save and end the game)");
break;
case KEY_ESC:
case KEY_CANCEL:
case KEY_EXIT:
@ -311,10 +322,9 @@ selection_t get_move (void)
selection = SEL_QUIT;
curs_set(CURS_OFF);
wattron(curwin, A_BOLD);
waddstr(curwin, "<CTRL><C>");
wattroff(curwin, A_BOLD);
waddstr(curwin, " (Quit the game)");
left(curwin, 1, getmaxx(curwin) / 2, attr_normal,
attr_normal | A_BOLD, 0, 1,
_("^{<CTRL><C>^} (Quit the game)"));
break;
default:
@ -324,30 +334,31 @@ selection_t get_move (void)
}
// Clear the menu choices (but not the prompt!)
wattrset(curwin, attr_normal);
for (y = 2; y < 4; y++) {
wmove(curwin, y, 2);
for (x = 2; x < getmaxx(curwin) - 2; x++) {
waddch(curwin, ' ' | attr_normal);
}
}
wrefresh(curwin);
mvwhline(curwin, 2, 2, ' ' | attr_normal, getmaxx(curwin) - 4);
mvwhline(curwin, 3, 2, ' ' | attr_normal, getmaxx(curwin) - 4);
// Ask the player to confirm their choice
mvwaddstr(curwin, 2, 22, "Are you sure?");
if (! answer_yesno(curwin, attr_keycode)) {
right(curwin, 2, getmaxx(curwin) / 2, attr_normal, attr_keycode, 0, 1,
_("Are you sure? [^{Y^}/^{N^}] "));
wrefresh(curwin);
if (! answer_yesno(curwin)) {
selection = SEL_NONE;
}
// Save the game if required
if (selection == SEL_SAVE) {
chtype *chbuf = xmalloc(BUFSIZE * sizeof(chtype));
int width;
bool saved = false;
if (game_loaded) {
// Save the game to the same game number
newtxwin(5, 30, 7, WCENTER, true, attr_status_window);
center(curwin, 2, attr_status_window,
"Saving game %d... ", game_num);
mkchstr(chbuf, BUFSIZE, attr_status_window, 0, 0, 1, WIN_COLS
- 7, &width, 1, _("Saving game %d... "), game_num);
newtxwin(5, width + 5, 7, WCENTER, true, attr_status_window);
centerch(curwin, 2, 0, chbuf, 1, &width);
wrefresh(curwin);
saved = save_game(game_num);
@ -357,34 +368,46 @@ selection_t get_move (void)
}
if (! saved) {
int key;
bool done;
// Ask which game to save
newtxwin(6, 54, 8, WCENTER, true, attr_normal_window);
center(curwin, 1, attr_title, " Save Game ");
mvwaddstr(curwin, 3, 2, "Enter game number ");
waddstr(curwin, "[");
attrpr(curwin, attr_keycode, "1");
waddstr(curwin, "-");
attrpr(curwin, attr_keycode, "9");
waddstr(curwin, "]");
waddstr(curwin, " or ");
attrpr(curwin, attr_keycode, "<CTRL><C>");
waddstr(curwin, " to cancel: ");
bool done;
int widthbuf[2];
int lines, maxwidth;
int choice;
lines = mkchstr(chbuf, BUFSIZE, attr_normal, attr_keycode, 0,
2, WIN_COLS - 7, widthbuf, 2,
_("Enter game number [^{1^}-^{9^}] "
"or ^{<CTRL><C>^} to cancel: "));
assert(lines == 1 || lines == 2);
maxwidth = ((lines == 1) ? widthbuf[0] :
MAX(widthbuf[0], widthbuf[1])) + 5;
newtxwin(lines + 4, maxwidth, 8, WCENTER, true,
attr_normal_window);
leftch(curwin, 2, 2, chbuf, lines, widthbuf);
curs_set(CURS_ON);
wrefresh(curwin);
done = false;
while (! done) {
key = gettxchar(curwin);
wint_t key;
if (key >= '1' && key <= '9') {
wechochar(curwin, key | A_BOLD);
done = true;
if (gettxchar(curwin, &key) == OK) {
// Ordinary wide character
if (key >= L'1' && key <= L'9') {
left(curwin, getcury(curwin), getcurx(curwin),
A_BOLD, 0, 0, 1, "%lc", key);
wrefresh(curwin);
choice = key - L'0';
done = true;
} else {
beep();
}
} else {
// Function or control key
switch (key) {
case KEY_ESC:
case KEY_CANCEL:
@ -392,7 +415,7 @@ selection_t get_move (void)
case KEY_CTRL('C'):
case KEY_CTRL('G'):
case KEY_CTRL('\\'):
key = KEY_CANCEL;
choice = ERR;
done = true;
break;
@ -404,13 +427,17 @@ selection_t get_move (void)
curs_set(CURS_OFF);
if (key != KEY_CANCEL) {
game_num = key - '0';
if (choice != ERR) {
// Try to save the game, if possible
newtxwin(5, 30, 7, WCENTER, true, attr_status_window);
center(curwin, 2, attr_status_window,
"Saving game %d... ", game_num);
game_num = choice;
mkchstr(chbuf, BUFSIZE, attr_status_window, 0, 0, 1,
WIN_COLS - 7, &width, 1,
_("Saving game %d... "), game_num);
newtxwin(5, width + 5, 7, WCENTER, true,
attr_status_window);
centerch(curwin, 2, 0, chbuf, 1, &width);
wrefresh(curwin);
saved = save_game(game_num);
@ -432,6 +459,8 @@ selection_t get_move (void)
selection = SEL_NONE;
}
free(chbuf);
}
}
@ -475,13 +504,13 @@ void process_move (selection_t selection)
assign_vals(x, y, left, right, up, down);
if (left == MAP_EMPTY && right == MAP_EMPTY &&
up == MAP_EMPTY && down == MAP_EMPTY) {
if ( left == MAP_EMPTY && right == MAP_EMPTY
&& up == MAP_EMPTY && down == MAP_EMPTY) {
// The position is out in the middle of nowhere...
galaxy_map[x][y] = MAP_OUTPOST;
} else if (! IS_MAP_COMPANY(left) && ! IS_MAP_COMPANY(right)
&& ! IS_MAP_COMPANY(up) && ! IS_MAP_COMPANY(down)) {
} else if ( ! IS_MAP_COMPANY(left) && ! IS_MAP_COMPANY(right)
&& ! IS_MAP_COMPANY(up) && ! IS_MAP_COMPANY(down)) {
// See if a company can be established
try_start_new_company(x, y);
@ -632,58 +661,25 @@ void next_player (void)
void bankrupt_player (bool forced)
{
bool longname;
int i;
/* It would be nice if we had functions that would do word-wrapping
for us automatically! */
longname = (strlen(player[current_player].name) > 20);
if (forced) {
newtxwin(longname ? 9 : 8, 54, 7, WCENTER, true, attr_error_window);
txdlgbox(MAX_DLG_LINES, 50, 7, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight, 0, 0,
attr_error_waitforkey, _(" Bankruptcy Court "),
_("%ls has been declared bankrupt "
"by the Interstellar Trading Bank."),
player[current_player].name);
} else {
newtxwin(longname ? 8 : 7, 50, 7, WCENTER, true, attr_error_window);
txdlgbox(MAX_DLG_LINES, 50, 7, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight, 0, 0,
attr_error_waitforkey, _(" Bankruptcy Court "),
_("%ls has declared bankruptcy."),
player[current_player].name);
}
center(curwin, 1, attr_error_title, " Bankruptcy Court ");
if (forced) {
if (longname) {
center(curwin, 3, attr_error_highlight, "%s",
player[current_player].name);
center(curwin, 4, attr_error_highlight,
"has been declared bankrupt by the");
center(curwin, 5, attr_error_highlight,
"Interstellar Trading Bank");
} else {
center(curwin, 3, attr_error_highlight,
"%s has been declared bankrupt",
player[current_player].name);
center(curwin, 4, attr_error_highlight,
"by the Interstellar Trading Bank");
}
} else {
if (longname) {
center(curwin, 3, attr_error_highlight, "%s",
player[current_player].name);
center(curwin, 4, attr_error_highlight,
"has declared bankruptcy");
} else {
center(curwin, 3, attr_error_highlight,
"%s has declared bankruptcy",
player[current_player].name);
}
}
wait_for_key(curwin, getmaxy(curwin) - 2, attr_error_waitforkey);
deltxwin();
txrefresh();
// Confiscate all assets belonging to player
player[current_player].in_game = false;
for (i = 0; i < MAX_COMPANIES; i++) {
for (int i = 0; i < MAX_COMPANIES; i++) {
company[i].stock_issued -= player[current_player].stock_owned[i];
player[current_player].stock_owned[i] = 0;
}
@ -692,7 +688,7 @@ void bankrupt_player (bool forced)
// Is anyone still left in the game?
bool all_out = true;
for (i = 0; i < number_players; i++) {
for (int i = 0; i < number_players; i++) {
if (player[i].in_game) {
all_out = false;
break;
@ -742,16 +738,11 @@ void try_start_new_company (int x, int y)
} else {
// Create the new company
newtxwin(8, 50, 7, WCENTER, true, attr_normal_window);
center(curwin, 1, attr_title, " New Company ");
center(curwin, 3, attr_normal, "A new company has been formed!");
center2(curwin, 4, attr_normal, attr_highlight, "Its name is ",
"%s", company[i].name);
wait_for_key(curwin, 6, attr_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 50, 7, WCENTER, attr_normal_window,
attr_title, attr_normal, attr_highlight, 0, attr_waitforkey,
_(" New Company "),
_("A new company has been formed!\nIts name is ^{%ls^}."),
company[i].name);
txrefresh();
galaxy_map[x][y] = COMPANY_TO_MAP(i);
@ -779,22 +770,23 @@ void merge_companies (map_val_t a, map_val_t b)
int aa = MAP_TO_COMPANY(a);
int bb = MAP_TO_COMPANY(b);
assert(aa >= 0 && aa < MAX_COMPANIES);
assert(bb >= 0 && bb < MAX_COMPANIES);
double val_aa = company[aa].share_price * company[aa].stock_issued *
company[aa].share_return;
double val_bb = company[bb].share_price * company[bb].stock_issued *
company[bb].share_return;
long int old_stock, new_stock, total_new;
int x, y, i, line;
double bonus;
char *buf;
long int old_stock, new_stock, total_new;
chtype *chbuf = xmalloc(BUFSIZE * sizeof(chtype));
int lines, width, widthbuf[4];
chtype *chbuf_aa, *chbuf_bb;
int width_aa, width_bb;
int x, y, w, i, ln;
buf = malloc(BUFSIZE);
if (buf == NULL) {
err_exit_nomem();
}
if (val_aa < val_bb) {
// Make sure aa is the dominant company
map_val_t t;
@ -806,31 +798,81 @@ void merge_companies (map_val_t a, map_val_t b)
// Display information about the merger
newtxwin(number_players + 14, WIN_COLS - 4, 9 - number_players,
WCENTER, true, attr_normal_window);
lines = mkchstr(chbuf, BUFSIZE, attr_normal, attr_highlight, 0, 4,
WIN_COLS - 8, widthbuf, 4,
_("^{%ls^} has just merged into ^{%ls^}.\n"
"Please note the following transactions:\n"),
company[bb].name, company[aa].name);
center(curwin, 1, attr_title, " Company Merger ");
center3(curwin, 3, attr_highlight, attr_highlight, attr_normal,
company[bb].name, company[aa].name, " has just merged into ");
newtxwin(number_players + lines + 10, WIN_COLS - 4, lines + 6
- number_players, WCENTER, true, attr_normal_window);
center(curwin, 1, 0, attr_title, 0, 0, 1, _(" Company Merger "));
centerch(curwin, 3, 0, chbuf, lines, widthbuf);
center(curwin, 5, attr_normal, "Please note the following transactions:");
mkchstr(chbuf, BUFSIZE, attr_highlight, 0, 0, 1, getmaxx(curwin) / 2,
&width_aa, 1, "%ls", company[aa].name);
chbuf_aa = xchstrdup(chbuf);
center2(curwin, 7, attr_normal, attr_highlight, " Old stock: ",
"%-20s", company[bb].name);
center2(curwin, 8, attr_normal, attr_highlight, " New stock: ",
"%-20s", company[aa].name);
mkchstr(chbuf, BUFSIZE, attr_highlight, 0, 0, 1, getmaxx(curwin) / 2,
&width_bb, 1, "%ls", company[bb].name);
chbuf_bb = xchstrdup(chbuf);
// Handle the locale's currency symbol
snprintf(buf, BUFSIZE, "Bonus (%s)", lconvinfo.currency_symbol);
mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, getmaxx(curwin) / 2,
&width, 1,
/* TRANSLATORS: "Old stock" refers to the company that has
just ceased existence due to a merger.
int w = getmaxx(curwin) - 52;
wattrset(curwin, attr_subtitle);
mvwprintw(curwin, 10, 2, " %-*.*s %8s %8s %8s %12s ", w, w,
"Player", "Old", "New", "Total", buf);
wattrset(curwin, attr_normal);
Note that the "Old stock" and "New stock" labels MUST be
the same length and must contain a trailing space for the
display routines to work correctly. The maximum length of
each label is 36 characters. */
pgettext("label", "Old stock: "));
w = getmaxx(curwin);
x = (w + width - MAX(width_aa, width_bb)) / 2;
rightch(curwin, lines + 3, x, chbuf, 1, &width);
leftch(curwin, lines + 3, x, chbuf_bb, 1, &width_bb);
right(curwin, lines + 4, x, attr_normal, 0, 0, 1,
/* TRANSLATORS: "New stock" refers to the company that has
absorbed another due to a merger. */
pgettext("label", "New Stock: "));
leftch(curwin, lines + 4, x, chbuf_aa, 1, &width_aa);
mvwhline(curwin, lines + 6, 2, ' ' | attr_subtitle, w - 4);
left(curwin, lines + 6, 4, attr_subtitle, 0, 0, 1,
/* TRANSLATORS: "Player" is used as a column title in a
table containing all player names. */
pgettext("subtitle", "Player"));
right(curwin, lines + 6, w - 4, attr_subtitle, 0, 0, 1,
/* TRANSLATORS: "Bonus" refers to the bonus cash amount paid to
each player after two companies merge. %ls is the currency
symbol in the current locale. The maximum column width is
12 characters INCLUDING the currency symbol (see
MERGE_BONUS_COLS in src/intf.h). */
pgettext("subtitle", "Bonus (%ls)"), currency_symbol);
right(curwin, lines + 6, w - 6 - MERGE_BONUS_COLS, attr_subtitle, 0, 0, 1,
/* TRANSLATORS: "Total" refers to the total number of shares in
the new company after a merger. The maximum column width is
8 characters (see MERGE_TOTAL_STOCK_COLS in src/intf.h). */
pgettext("subtitle", "Total"));
right(curwin, lines + 6, w - 8 - MERGE_BONUS_COLS - MERGE_TOTAL_STOCK_COLS,
attr_subtitle, 0, 0, 1,
/* TRANSLATORS: "New" refers to how many (new) shares each
player receives in the surviving company after a merger.
The maximum column width is 8 characters (see
MERGE_NEW_STOCK_COLS in src/intf.h). */
pgettext("subtitle", "New"));
right(curwin, lines + 6, w - 10 - MERGE_BONUS_COLS - MERGE_TOTAL_STOCK_COLS
- MERGE_NEW_STOCK_COLS, attr_subtitle, 0, 0, 1,
/* TRANSLATORS: "Old" refers to how many shares each player had
in the company ceasing existence. The maximum column width
is 8 characters (see MERGE_OLD_STOCK_COLS in src/intf.h). */
pgettext("subtitle", "Old"));
total_new = 0;
for (line = 11, i = 0; i < number_players; i++) {
for (ln = lines + 7, i = 0; i < number_players; i++) {
if (player[i].in_game) {
// Calculate new stock and any bonus
old_stock = player[i].stock_owned[bb];
@ -845,11 +887,22 @@ void merge_companies (map_val_t a, map_val_t b)
player[i].stock_owned[bb] = 0;
player[i].cash += bonus;
l_strfmon(buf, BUFSIZE, "%!12n", bonus);
mvwprintw(curwin, line, 2, " %-*.*s %'8ld %'8ld %'8ld %12s ",
w, w, player[i].name, old_stock, new_stock,
player[i].stock_owned[aa], buf);
line++;
mkchstr(chbuf, BUFSIZE, attr_normal, 0, 0, 1, w - 12
- MERGE_BONUS_COLS - MERGE_TOTAL_STOCK_COLS
- MERGE_NEW_STOCK_COLS - MERGE_OLD_STOCK_COLS,
&width, 1, "%ls", player[i].name);
leftch(curwin, ln, 4, chbuf, 1, &width);
right(curwin, ln, w - 4, attr_normal, 0, 0, 1, "%!N", bonus);
right(curwin, ln, w - 6 - MERGE_BONUS_COLS, attr_normal, 0, 0, 1,
"%'ld", player[i].stock_owned[aa]);
right(curwin, ln, w - 8 - MERGE_BONUS_COLS - MERGE_TOTAL_STOCK_COLS,
attr_normal, 0, 0, 1, "%'ld", new_stock);
right(curwin, ln, w - 10 - MERGE_BONUS_COLS - MERGE_TOTAL_STOCK_COLS
- MERGE_NEW_STOCK_COLS, attr_normal, 0, 0, 1, "%'ld",
old_stock);
ln++;
}
}
@ -876,7 +929,9 @@ void merge_companies (map_val_t a, map_val_t b)
deltxwin(); // "Company merger" window
txrefresh();
free(buf);
free(chbuf_bb);
free(chbuf_aa);
free(chbuf);
}
@ -948,7 +1003,6 @@ void inc_share_price (int num, double inc)
void adjust_values (void)
{
int i, x, y;
int which;
@ -958,67 +1012,84 @@ void adjust_values (void)
if (company[which].on_map) {
if (randf() < ALL_ASSETS_TAKEN) {
newtxwin(10, 60, 6, WCENTER, true, attr_error_window);
center(curwin, 1, attr_error_title, " Bankruptcy Court ");
center(curwin, 3, attr_error_highlight, "%s has been declared",
company[which].name);
center(curwin, 4, attr_error_highlight,
"bankrupt by the Interstellar Trading Bank.");
center(curwin, 6, attr_error_window,
"All assets have been taken to repay outstanding loans.");
wait_for_key(curwin, 8, attr_error_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 60, 6, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight,
attr_error_normal, 0, attr_error_waitforkey,
_(" Bankruptcy Court "),
_("%ls has been declared bankrupt "
"by the Interstellar Trading Bank.\n\n"
"^{All assets have been taken "
"to repay outstanding loans.^}"),
company[which].name);
txrefresh();
} else {
double rate = randf();
char *buf;
buf = malloc(BUFSIZE);
if (buf == NULL) {
err_exit_nomem();
}
chtype *chbuf = xmalloc(BUFSIZE * sizeof(chtype));
chtype *chbuf_amt;
int w, x, lines, width, width_amt, widthbuf[6];
for (i = 0; i < number_players; i++) {
for (int i = 0; i < number_players; i++) {
if (player[i].in_game) {
player[i].cash += player[i].stock_owned[which] * rate;
}
}
newtxwin(14, 60, 4, WCENTER, true, attr_error_window);
lines = mkchstr(chbuf, BUFSIZE, attr_error_highlight,
attr_error_normal, 0, 6, 60 - 4, widthbuf, 6,
_("%ls has been declared bankrupt by the "
"Interstellar Trading Bank.\n\n"
"^{The Bank has agreed to pay stock holders ^}"
"%.2f%%^{ of the share value on each share "
"owned.^}"),
company[which].name, rate * 100.0);
center(curwin, 1, attr_error_title, " Bankruptcy Court ");
center(curwin, 3, attr_error_highlight, "%s has been declared",
company[which].name);
center(curwin, 4, attr_error_highlight,
"bankrupt by the Interstellar Trading Bank.");
newtxwin(9 + lines, 60, 4, WCENTER, true, attr_error_window);
w = getmaxx(curwin);
center2(curwin, 6, attr_error_normal, attr_error_highlight,
"The Bank has agreed to pay stock holders ",
"%4.2f%%", rate * 100.0);
center(curwin, 7, attr_error_normal,
"of the share value on each share owned.");
center(curwin, 1, 0, attr_error_title, 0, 0, 1,
_(" Bankruptcy Court "));
centerch(curwin, 3, 0, chbuf, lines, widthbuf);
l_strfmon(buf, BUFSIZE, "%12n", company[which].share_price);
center2(curwin, 9, attr_error_normal, attr_error_highlight,
"Old share value: ", "%s", buf);
mkchstr(chbuf, BUFSIZE, attr_error_highlight, 0, 0, 1, w / 2,
&width_amt, 1, "%N", company[which].share_price);
chbuf_amt = xchstrdup(chbuf);
l_strfmon(buf, BUFSIZE, "%12n", company[which].share_price
* rate);
center2(curwin, 10, attr_error_normal, attr_error_highlight,
"Amount paid per share: ", "%s", buf);
mkchstr(chbuf, BUFSIZE, attr_error_normal, 0, 0, 1, w / 2,
&width, 1,
/* TRANSLATORS: The label "Amount paid per share"
refers to payment made by the Interstellar
Trading Bank to each player upon company
bankruptcy. This label MUST be the same
length as "Old share value" and MUST have at
least one trailing space for the display
routines to work correctly. The maximum
length is 28 characters. */
pgettext("label", "Amount paid per share: "));
x = (w + width - width_amt) / 2;
wait_for_key(curwin, 12, attr_error_waitforkey);
right(curwin, lines + 4, x, attr_error_normal, 0, 0, 1,
/* TRANSLATORS: "Old share value" refers to the
share price of a company before it was forced
into bankruptcy by the Bank. This label must be
the same width as "Amount paid per share". */
pgettext("label", "Old share value: "));
leftch(curwin, lines + 4, x, chbuf_amt, 1, &width_amt);
rightch(curwin, lines + 5, x, chbuf, 1, &width);
left(curwin, lines + 5, x, attr_error_highlight, 0, 0, 1,
"%N", company[which].share_price * rate);
wait_for_key(curwin, getmaxy(curwin) - 2, attr_error_waitforkey);
deltxwin();
txrefresh();
free(buf);
free(chbuf_amt);
free(chbuf);
}
for (i = 0; i < number_players; i++) {
for (int i = 0; i < number_players; i++) {
player[i].stock_owned[which] = 0;
}
@ -1028,8 +1099,8 @@ void adjust_values (void)
company[which].max_stock = 0;
company[which].on_map = false;
for (x = 0; x < MAX_X; x++) {
for (y = 0; y < MAX_Y; y++) {
for (int x = 0; x < MAX_X; x++) {
for (int y = 0; y < MAX_Y; y++) {
if (galaxy_map[x][y] == COMPANY_TO_MAP((unsigned int) which)) {
galaxy_map[x][y] = MAP_EMPTY;
}
@ -1047,7 +1118,7 @@ void adjust_values (void)
}
// Make sure that a company's return is not too large
for (i = 0; i < MAX_COMPANIES; i++) {
for (int i = 0; i < MAX_COMPANIES; i++) {
if (company[i].on_map && company[i].share_return > MAX_COMPANY_RETURN) {
company[i].share_return /= randf() + RETURN_DIVIDER;
}
@ -1067,9 +1138,10 @@ void adjust_values (void)
}
// Give the current player the companies' dividends
for (i = 0; i < MAX_COMPANIES; i++) {
for (int i = 0; i < MAX_COMPANIES; i++) {
if (company[i].on_map && company[i].stock_issued != 0) {
player[current_player].cash += player[current_player].stock_owned[i]
player[current_player].cash +=
player[current_player].stock_owned[i]
* company[i].share_price * company[i].share_return
+ ((double) player[current_player].stock_owned[i]
/ company[i].stock_issued) * company[i].share_price
@ -1090,31 +1162,16 @@ void adjust_values (void)
// Check if a player's debt is too large
if (total_value(current_player) <= -MAX_OVERDRAFT) {
double impounded;
char *buf;
double impounded = MIN(player[current_player].cash,
player[current_player].debt);
buf = malloc(BUFSIZE);
if (buf == NULL) {
err_exit_nomem();
}
impounded = MIN(player[current_player].cash,
player[current_player].debt);
newtxwin(8, 60, 7, WCENTER, true, attr_error_window);
center(curwin, 1, attr_error_title, " Interstellar Trading Bank ");
l_strfmon(buf, BUFSIZE, "%1n", player[current_player].debt);
center(curwin, 3, attr_error_highlight,
"Your debt has amounted to %s", buf);
l_strfmon(buf, BUFSIZE, "%1n", impounded);
center3(curwin, 4, attr_error_normal, attr_error_normal,
attr_error_highlight, "The Bank has impounded ",
" from your cash", "%s", buf);
wait_for_key(curwin, 6, attr_error_waitforkey);
deltxwin();
txdlgbox(MAX_DLG_LINES, 60, 7, WCENTER, attr_error_window,
attr_error_title, attr_error_highlight, attr_error_normal,
0, attr_error_waitforkey, _(" Interstellar Trading Bank "),
/* xgettext:c-format */
_("Your debt has amounted to %N!\n"
"^{The Bank has impounded ^}%N^{ from your cash.^}"),
player[current_player].debt, impounded);
txrefresh();
player[current_player].cash -= impounded;

View File

@ -55,18 +55,22 @@
#include <stdlib.h>
#include <stdbool.h>
#include <stdarg.h>
#include <limits.h>
#include <locale.h>
#include <ctype.h>
#include <string.h>
#include <errno.h>
#include <wchar.h>
#include <wctype.h>
// Headers defined by X/Open Single Unix Specification v4
#include <unistd.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <monetary.h>
#include <langinfo.h>
// Headers defined by the GNU C Library
@ -74,8 +78,28 @@
#include <getopt.h>
// Internationalisation using GNU gettext
#include "gettext.h" // This handles ENABLE_NLS correctly
#define _(string) gettext(string)
#define N_(string) gettext_noop(string)
// Character set conversion for game files
#ifdef HAVE_ICONV
# define USE_UTF8_GAME_FILE 1
# include "striconv.h"
#else
# undef USE_UTF8_GAME_FILE
#endif
// X/Open-compatible Curses library
#define _XOPEN_SOURCE_EXTENDED 1 // Required by old versions of NcursesW
#if defined(HAVE_NCURSESW_CURSES_H)
# include <ncursesw/curses.h>
#elif defined(HAVE_NCURSESW_H)

View File

@ -147,7 +147,7 @@ static void end_program (void);
int main (int argc, char *argv[])
{
// Strip off leading pathname components from program name
init_program_name(argv);
init_program_name(argv[0]);
// Initialise the locale
if (setlocale(LC_ALL, "") == NULL) {
@ -155,6 +155,10 @@ int main (int argc, char *argv[])
"(check LANG, LC_ALL and LANGUAGE in environment)");
}
// Use correct message catalogs for the locale
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
// Process command line arguments
process_cmdline(argc, argv);
@ -228,8 +232,8 @@ void process_cmdline (int argc, char *argv[])
option_max_turn = strtol(optarg, &p, 10);
if (option_max_turn < MIN_MAX_TURN || p == NULL || *p != '\0') {
fprintf(stderr, "%s: invalid value for --max-turn: `%s'\n",
program_name(), optarg);
fprintf(stderr, _("%s: invalid value for --max-turn: `%s'\n"),
program_name, optarg);
show_usage(EXIT_FAILURE);
}
}
@ -244,8 +248,8 @@ void process_cmdline (int argc, char *argv[])
if (optind < argc && argv[optind] != NULL) {
if (*argv[optind] == '-') {
fprintf(stderr, "%s: invalid operand `%s'\n", program_name(),
argv[optind]);
fprintf(stderr, _("%s: invalid operand `%s'\n"),
program_name, argv[optind]);
show_usage(EXIT_FAILURE);
}
@ -253,8 +257,8 @@ void process_cmdline (int argc, char *argv[])
&& *argv[optind] >= '1' && *argv[optind] <= '9') {
game_num = *argv[optind] - '0';
} else {
fprintf(stderr, "%s: invalid game number `%s'\n",
program_name(), argv[optind]);
fprintf(stderr, _("%s: invalid game number `%s'\n"),
program_name, argv[optind]);
show_usage(EXIT_FAILURE);
}
@ -262,8 +266,8 @@ void process_cmdline (int argc, char *argv[])
}
if (optind < argc && argv[optind] != NULL) {
fprintf(stderr, "%s: extra operand `%s'\n", program_name(),
argv[optind]);
fprintf(stderr, _("%s: extra operand `%s'\n"),
program_name, argv[optind]);
show_usage(EXIT_FAILURE);
}
}
@ -274,8 +278,11 @@ void process_cmdline (int argc, char *argv[])
void show_version (void)
{
printf("\
" PACKAGE_NAME " (%s) %s\n\
/* TRANSLATORS: "John Zaitseff" [IPA d͡ʒɒn ˈzaɪ̯t͡səf] is the proper
name of the author. The IPA pronunciation in this comment is in
UTF-8 encoding. */
printf(_("\
Star Traders (%s) %s\n\
Copyright (C) %s, John Zaitseff.\n\
\n\
Star Traders is a simple game of interstellar trading, where the object\n\
@ -286,7 +293,7 @@ This program is free software that is distributed under the terms of the\n\
GNU General Public License, version 3 or later. You are welcome to\n\
modify and/or distribute it under certain conditions. This program has\n\
NO WARRANTY, to the extent permitted by law; see the License for details.\n\
", program_name(), PACKAGE_VERSION, "1990-2011");
"), program_name, PACKAGE_VERSION, "1990-2011");
exit(EXIT_SUCCESS);
}
@ -297,39 +304,49 @@ NO WARRANTY, to the extent permitted by law; see the License for details.\n\
void show_usage (int status)
{
const char *pn = program_name();
if (status != EXIT_SUCCESS) {
fprintf(stderr, "%s: Try `%s --help' for more information.\n",
pn, pn);
fprintf(stderr, _("%s: Try `%s --help' for more information.\n"),
program_name, program_name);
} else {
printf("Usage: %s [OPTION ...] [GAME]\n", pn);
printf("\
printf(_("Usage: %s [OPTION ...] [GAME]\n"), program_name);
printf(_("\
Play Star Traders, a simple game of interstellar trading.\n\n\
");
printf("\
"));
printf(_("\
Options:\n\
-V, --version output version information and exit\n\
-h, --help display this help and exit\n\
--no-color don't use colour for displaying text\n\
--no-color don't use color for displaying text\n\
--max-turn=NUM set the number of turns to NUM\n\n\
");
printf("\
"));
printf(_("\
If GAME is specified as a number between 1 and 9, load and continue\n\
playing that game. If GAME is not specified, start a new game.\n\n\
");
"));
#ifdef PACKAGE_AUTHOR
printf("Report bugs to %s <%s>.\n", PACKAGE_AUTHOR, PACKAGE_BUGREPORT);
/* TRANSLATORS: The first %s is the proper name of the package
author, John Zaitseff [IPA d͡ʒɒn ˈzaɪ̯t͡səf]; the second %s is
the e-mail address for reporting bugs. Please add ANOTHER
line with the (translated) text "Report translation bugs to
<ADDRESS>\n", with ADDRESS replaced with either an e-mail
address or web URL for reporting bugs in your translation. */
printf(_("Report bugs to %s <%s>.\n"), PACKAGE_AUTHOR, PACKAGE_BUGREPORT);
#else
printf("Report bugs to <%s>.\n", PACKAGE_BUGREPORT);
/* TRANSLATORS: %s is the e-mail address for reporting bugs. As
with the previous string, please add ANOTHER line with the
(translated) text "Report translation bugs to <ADDRESS>\n",
with ADDRESS replaced with either an e-mail address or web URL
for reporting bugs in your translation. */
printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
#endif
#ifdef PACKAGE_PACKAGER_BUG_REPORTS
printf("Report %s bugs to <%s>.\n", PACKAGE_PACKAGER, PACKAGE_PACKAGER_BUG_REPORTS);
/* TRANSLATORS: The first %s is for packagers and may be
something like "Debian". */
printf(_("Report %s bugs to <%s>.\n"), PACKAGE_PACKAGER, PACKAGE_PACKAGER_BUG_REPORTS);
#endif
#ifdef PACKAGE_URL
printf(PACKAGE_NAME " home page: <%s>.\n", PACKAGE_URL);
printf(_("Star Traders home page: <%s>.\n"), PACKAGE_URL);
#endif
}

View File

@ -52,11 +52,17 @@
* Global definitions *
************************************************************************/
#define GAME_FILE_HEADER PACKAGE_NAME " Saved Game"
#define GAME_FILE_API_VERSION "7.0" // For game loads and saves
#define GAME_FILE_SENTINEL 42 // End of game file sentinel
#define GAME_FILE_HEADER "Star Traders Saved Game"
#define GAME_FILE_API_VERSION "File API 7.2" // For game loads and saves
#define GAME_FILE_SENTINEL 42 // End of game file sentinel
#ifdef USE_UTF8_GAME_FILE
# define GAME_FILE_CHARSET "UTF-8" // For strings in game file
# define GAME_FILE_TRANSLIT "//TRANSLIT" // Transliterate (GNU libiconv)
#endif
#define BUFSIZE 1024 // For various string buffers
#define BIGBUFSIZE 2048 // For buffers known to be larger
#endif /* included_TRADER_H */

View File

@ -35,9 +35,19 @@
* Global variable definitions *
************************************************************************/
const char *program_name = NULL; // Canonical program name
// Global copy, suitably modified, of localeconv() information
struct lconv lconvinfo;
// localeconv() information, converted to wide strings
wchar_t *decimal_point; // Locale's radix character
wchar_t *thousands_sep; // Locale's thousands separator
wchar_t *currency_symbol; // Local currency symbol
wchar_t *mon_decimal_point; // Local monetary radix character
wchar_t *mon_thousands_sep; // Local monetary thousands separator
/************************************************************************
* Module-specific constants and variable definitions *
@ -57,11 +67,9 @@ struct lconv lconvinfo;
* Module-specific variables *
************************************************************************/
static char *program_name_str = NULL; // Canonical program name
static char *home_directory_str = NULL; // Full pathname to home
static char *data_directory_str = NULL; // Writable data dir pathname
static char *current_mon_locale; // As returned by setlocale()
static bool add_currency_symbol = false; // Do we need to add "$"?
@ -73,37 +81,27 @@ static bool add_currency_symbol = false; // Do we need to add "$"?
/***********************************************************************/
// init_program_name: Make the program name "canonical"
// init_program_name: Make the program name canonical
void init_program_name (char *argv[])
void init_program_name (const char *argv0)
{
if (argv == NULL || argv[0] == NULL || *argv[0] == '\0') {
program_name_str = PACKAGE;
/* This implementation assumes a POSIX environment with an ASCII-safe
character encoding (such as ASCII or UTF-8). */
if (argv0 == NULL || *argv0 == '\0') {
program_name = PACKAGE;
} else {
char *p = strrchr(argv[0], '/');
char *p = strrchr(argv0, '/');
if (p != NULL && *++p != '\0') {
argv[0] = p;
program_name = xstrdup(p);
} else {
program_name = xstrdup(argv0);
}
program_name_str = argv[0];
}
}
/***********************************************************************/
// program_name: Return the canonical program name
const char *program_name (void)
{
if (program_name_str == NULL) {
init_program_name(NULL);
}
return program_name_str;
}
/***********************************************************************/
// home_directory: Return home directory pathname
@ -111,10 +109,10 @@ const char *home_directory (void)
{
if (home_directory_str == NULL) {
// Use the HOME environment variable where possible
char *home = getenv("HOME");
const char *home = getenv("HOME");
if (home != NULL && *home != '\0') {
home_directory_str = strdup(home);
home_directory_str = xstrdup(home);
}
}
@ -129,20 +127,19 @@ const char *data_directory (void)
{
/* This implementation assumes a POSIX environment by using "/" as
the directory separator. It also assumes a dot-starting directory
name is permissible (again, true on POSIX systems) */
name is permissible (again, true on POSIX systems) and that the
character encoding is ASCII-safe. */
if (data_directory_str == NULL) {
const char *name = program_name();
const char *home = home_directory();
if (name != NULL && home != NULL) {
char *p = malloc(strlen(home) + strlen(name) + 3);
if (p != NULL) {
strcpy(p, home);
strcat(p, "/.");
strcat(p, name);
data_directory_str = p;
}
if (program_name != NULL && home != NULL) {
char *p = xmalloc(strlen(home) + strlen(program_name) + 3);
strcpy(p, home);
strcat(p, "/.");
strcat(p, program_name);
data_directory_str = p;
}
}
@ -155,8 +152,8 @@ const char *data_directory (void)
char *game_filename (int gamenum)
{
/* This implementation assumes a POSIX environment by using "/" as
the directory separator */
/* This implementation assumes a POSIX environment and an ASCII-safe
character encoding. */
char buf[GAME_FILENAME_BUFSIZE]; // Buffer for part of filename
const char *dd; // Data directory
@ -170,20 +167,13 @@ char *game_filename (int gamenum)
snprintf(buf, GAME_FILENAME_BUFSIZE, GAME_FILENAME_PROTO, gamenum);
if (dd == NULL) {
char *p = malloc(strlen(buf) + 1);
if (p != NULL) {
strcpy(p, buf);
}
return p;
return xstrdup(buf);
} else {
char *p = malloc(strlen(dd) + strlen(buf) + 2);
char *p = xmalloc(strlen(dd) + strlen(buf) + 2);
if (p != NULL) {
strcpy(p, dd);
strcat(p, "/");
strcat(p, buf);
}
strcpy(p, dd);
strcat(p, "/");
strcat(p, buf);
return p;
}
}
@ -206,7 +196,7 @@ void err_exit (const char *restrict format, ...)
end_screen();
fprintf(stderr, "%s: ", program_name());
fprintf(stderr, _("%s: "), program_name);
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
@ -227,12 +217,12 @@ void errno_exit (const char *restrict format, ...)
end_screen();
fprintf(stderr, "%s: ", program_name());
fprintf(stderr, _("%s: "), program_name);
if (format != NULL) {
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
fputs(": ", stderr);
fputs(_(": "), stderr);
}
fprintf(stderr, "%s\n", strerror(saved_errno));
@ -245,7 +235,7 @@ void errno_exit (const char *restrict format, ...)
void err_exit_nomem (void)
{
err_exit("out of memory");
err_exit(_("out of memory"));
}
@ -306,22 +296,27 @@ extern int randi (int limit)
void init_locale (void)
{
char *cur, *cloc;
struct lconv *lc;
wchar_t *buf;
current_mon_locale = setlocale(LC_MONETARY, NULL);
cur = xstrdup(setlocale(LC_MONETARY, NULL));
lc = localeconv();
assert(current_mon_locale != NULL);
assert(lc != NULL);
lconvinfo = *lc;
/* Are we in the POSIX locale? This test may not be portable as the
string returned by setlocale() is supposed to be opaque. */
add_currency_symbol = false;
if (strcmp(current_mon_locale, "POSIX") == 0
|| strcmp(current_mon_locale, "C") == 0) {
/* Are we in the POSIX locale? The string returned by setlocale() is
supposed to be opaque, but in practise is not. To be on the safe
side, we explicitly set the locale to "C", then test the returned
value of that, too. */
cloc = setlocale(LC_MONETARY, "C");
if ( strcmp(cur, cloc) == 0
|| strcmp(cur, "POSIX") == 0 || strcmp(cur, "C") == 0
|| strcmp(cur, "C.UTF-8") == 0 || strcmp(cur, "C.utf8") == 0) {
add_currency_symbol = true;
lconvinfo.currency_symbol = MOD_POSIX_CURRENCY_SYMBOL;
@ -329,31 +324,56 @@ void init_locale (void)
lconvinfo.p_cs_precedes = MOD_POSIX_P_CS_PRECEDES;
lconvinfo.p_sep_by_space = MOD_POSIX_P_SEP_BY_SPACE;
}
// Convert localeconv() information to wide strings
buf = xmalloc(BUFSIZE * sizeof(wchar_t));
xmbstowcs(buf, lconvinfo.decimal_point, BUFSIZE);
decimal_point = xwcsdup(buf);
xmbstowcs(buf, lconvinfo.thousands_sep, BUFSIZE);
thousands_sep = xwcsdup(buf);
xmbstowcs(buf, lconvinfo.currency_symbol, BUFSIZE);
currency_symbol = xwcsdup(buf);
xmbstowcs(buf, lconvinfo.mon_decimal_point, BUFSIZE);
mon_decimal_point = xwcsdup(buf);
xmbstowcs(buf, lconvinfo.mon_thousands_sep, BUFSIZE);
mon_thousands_sep = xwcsdup(buf);
free(buf);
setlocale(LC_MONETARY, cur);
free(cur);
}
/***********************************************************************/
// l_strfmon: Convert monetary value to a string
ssize_t l_strfmon (char *restrict s, size_t maxsize,
ssize_t l_strfmon (char *restrict buf, size_t maxsize,
const char *restrict format, double val)
{
/* The current implementation assumes MOD_POSIX_P_CS_PRECEDES is 1
(currency symbol precedes value) and that MOD_POSIX_P_SEP_BY_SPACE
is 0 (no space separates currency symbol and value). It does,
however, handle currency symbols of length > 1 */
however, handle currency symbols of length > 1. */
assert(MOD_POSIX_P_CS_PRECEDES == 1);
assert(MOD_POSIX_P_SEP_BY_SPACE == 0);
ssize_t ret = strfmon(s, maxsize, format, val);
ssize_t ret = strfmon(buf, maxsize, format, val);
if (ret > 0 && add_currency_symbol) {
if (strstr(format, "!") == NULL) {
/* Insert lconvinfo.currency_symbol to s.
NB: add_currecy_symbol == true assumes POSIX locale:
single-byte strings are in effect, so strlen(), etc, work
correctly. */
NB: add_currecy_symbol == true assumes a POSIX locale and
that the character encoding is ASCII-safe (such as by
being ASCII itself, or UTF-8). */
const char *sym = lconvinfo.currency_symbol;
int symlen = strlen(sym);
char *p;
@ -362,7 +382,7 @@ ssize_t l_strfmon (char *restrict s, size_t maxsize,
assert(maxsize > (unsigned int) symlen);
// Count number of leading spaces
for (p = s, spc = 0; *p == ' '; p++, spc++)
for (p = buf, spc = 0; *p == ' '; p++, spc++)
;
if (symlen <= spc) {
@ -374,12 +394,12 @@ ssize_t l_strfmon (char *restrict s, size_t maxsize,
} else {
// Make space for currency symbol, then copy it
memmove(s + symlen - spc, s, maxsize - (symlen - spc));
s[maxsize - 1] = '\0';
memmove(buf + symlen - spc, buf, maxsize - (symlen - spc));
buf[maxsize - 1] = '\0';
for ( ; *sym != '\0'; sym++, s++) {
for ( ; *sym != '\0'; sym++, buf++) {
// Make sure terminating NUL character is NOT copied!
*s = *sym;
*buf = *sym;
}
ret = MIN((unsigned int) ret + symlen - spc, maxsize - 1);
@ -437,5 +457,170 @@ char *unscramble (int key, char *restrict buf, int bufsize)
}
/************************************************************************
* Miscellaneous function definitions *
************************************************************************/
// These functions are documented in the file "utils.h"
/***********************************************************************/
// xmalloc: Allocate a new block of memory, with checking
void *xmalloc (size_t size)
{
void *p;
if (size < 1)
size = 1;
p = malloc(size);
if (p == NULL) {
err_exit_nomem();
}
return p;
}
/***********************************************************************/
// xstrdup: Duplicate a string, with checking
char *xstrdup (const char *str)
{
char *s;
if (str == NULL)
str = "";
s = strdup(str);
if (s == NULL) {
err_exit_nomem();
}
return s;
}
/***********************************************************************/
// chstrdup: Duplicate a chtype buffer
chtype *xchstrdup (const chtype *restrict chstr)
{
const chtype *p;
int len;
chtype *ret;
// Determine chstr length, including ending NUL
for (len = 1, p = chstr; *p != '\0'; p++, len++)
;
ret = xmalloc(len * sizeof(chtype));
memcpy(ret, chstr, len * sizeof(chtype));
ret[len - 1] = '\0'; // Terminating NUL, just in case not present
return ret;
}
/***********************************************************************/
// xwcsdup: Duplicate a wide-character string, with checking
wchar_t *xwcsdup (const wchar_t *str)
{
wchar_t *s;
if (str == NULL)
str = L"";
s = wcsdup(str);
if (s == NULL) {
err_exit_nomem();
}
return s;
}
/***********************************************************************/
// xmbstowcs: Convert a multibyte string to a wide-character string
size_t xmbstowcs (wchar_t *restrict dest, const char *restrict src, size_t len)
{
assert(dest != NULL);
assert(len > 0);
char *s = xstrdup(src);
size_t n;
while (true) {
mbstate_t mbstate;
char *p = s;
memset(&mbstate, 0, sizeof(mbstate));
if ((n = mbsrtowcs(dest, (const char **) &p, len, &mbstate))
== (size_t) -1) {
if (errno == EILSEQ) {
// Illegal sequence detected: replace it and try again
*p = EILSEQ_REPL;
} else {
errno_exit(_("xmbstowcs: `%s'"), src);
}
} else if (p != NULL) {
// Multibyte string was too long: truncate dest
dest[len - 1] = L'\0';
n--;
break;
} else {
break;
}
}
free(s);
return n;
}
/***********************************************************************/
// xwcrtomb: Convert a wide character to a multibyte sequence
size_t xwcrtomb (char *restrict dest, wchar_t wc, mbstate_t *restrict mbstate)
{
mbstate_t mbcopy;
size_t n;
assert(dest != NULL);
assert(mbstate != NULL);
memcpy(&mbcopy, mbstate, sizeof(mbcopy));
if ((n = wcrtomb(dest, wc, &mbcopy)) == (size_t) -1) {
if (errno == EILSEQ) {
/* wc cannot be represented in current locale.
Note that the shift state in mbcopy is now undefined.
Hence, restore the original, try to store an ending shift
sequence, then EILSEQ_REPL. */
memcpy(&mbcopy, mbstate, sizeof(mbcopy));
if ((n = wcrtomb(dest, L'\0', &mbcopy)) == (size_t) -1) {
errno_exit(_("xwcrtomb: NUL"));
}
dest[n] = EILSEQ_REPL;
dest[n++] = '\0';
} else {
errno_exit(_("xwcrtomb: `%lc'"), wc);
}
}
memcpy(mbstate, &mbcopy, sizeof(mbcopy));
return n;
}
/***********************************************************************/
// End of file

View File

@ -42,42 +42,43 @@
#define MAX(a, b) ((a) > (b) ? (a) : (b))
#define EILSEQ_REPL '?' // Illegal character sequence replacement
#define EILSEQ_REPL_WC L'?' // ... wide character version
/************************************************************************
* Global variable declarations *
************************************************************************/
extern const char *program_name; // Canonical program name
// Global copy, suitably modified, of localeconv() information
extern struct lconv lconvinfo;
// localeconv() information, converted to wide strings
extern wchar_t *decimal_point; // Locale's radix character
extern wchar_t *thousands_sep; // Locale's thousands separator
extern wchar_t *currency_symbol; // Local currency symbol
extern wchar_t *mon_decimal_point; // Local monetary radix character
extern wchar_t *mon_thousands_sep; // Local monetary thousands separator
/************************************************************************
* Initialisation and environment function prototypes *
************************************************************************/
/*
Function: init_program_name - Make the program name "canonical"
Parameters: argv - Same as passed to main()
Function: init_program_name - Make the program name canonical
Parameters: argv0 - Same as passed to main() as argv[0]
Returns: (nothing)
This function modifies the argv[0] pointer to eliminate any leading
This function modifies the argv0 pointer to eliminate any leading
pathname (directory) components from the program name, leaving just the
basename of the program. It also saves a copy that can be accessed via
the program_name() function.
the program_name global variable.
*/
extern void init_program_name (char *argv[]);
/*
Function: program_name - Return the canonical program name
Parameters: (none)
Returns: const char * - Pointer to program name
This function returns the canonical program name (the program name as
invoked on the command line, without any leading pathname components).
NULL should never be returned; however, init_program_name() SHOULD be
called before using this function.
*/
extern const char *program_name (void);
extern void init_program_name (const char *argv0);
/*
@ -100,7 +101,7 @@ extern const char *home_directory (void);
This function returns the full pathname to a potentially-writable
subdirectory within the user's home directory. Essentially, this
function returns home_directory() + "/." + program_name(). Note that
function returns home_directory() + "/." + program_name. Note that
this path is NOT created by this function, nor is the writability of
this path checked. NULL is returned if this path cannot be determined.
*/
@ -226,18 +227,19 @@ extern int randi (int limit);
Parameters: (none)
Returns: (nothing)
This function initialises the global variable lconvinfo with values
suitable for this program. In particular, if the POSIX or C locale is
in effect, the currency_symbol and frac_digits members are updated to
be something reasonable. This function must be called before using
localeconf_info.
This function initialises the global variable lconvinfo, as well as
decimal_point, thousands_sep, currency_symbol, mon_decimal_point and
mon_thousands_sep, with values suitable for this program. In
particular, if the POSIX or C locale is in effect, the currency_symbol
and frac_digits members of lconvinfo are updated to be something
reasonable. This function must be called before using localeconf_info.
*/
extern void init_locale (void);
/*
Function: l_strfmon - Convert monetary value to a string
Parameters: s - Buffer to receive result
Parameters: buf - Buffer to receive result
maxsize - Maximum size of buffer
format - strfmon() format to use
val - Monetary value to convert
@ -249,7 +251,7 @@ extern void init_locale (void);
function overcomes the limitation that the POSIX locale does not define
anything for localeconv()->currency_symbol.
*/
extern ssize_t l_strfmon (char *restrict s, size_t maxsize,
extern ssize_t l_strfmon (char *restrict buf, size_t maxsize,
const char *restrict format, double val);
@ -300,4 +302,90 @@ extern char *scramble (int key, char *restrict buf, int bufsize);
extern char *unscramble (int key, char *restrict buf, int bufsize);
/************************************************************************
* Miscellaneous function prototypes *
************************************************************************/
/*
Function: xmalloc - Allocate a new block of memory, with checking
Parameters: size - Size of new block of memory in bytes
Returns: void * - Pointer to new block of memory
This wrapper function allocates a new block of memory by calling
malloc(), then checks if a NULL pointer has been returned. If so, the
program terminates with an "Out of memory" error.
*/
extern void *xmalloc (size_t size);
/*
Function: xstrdup - Duplicate a string, with checking
Parameters: str - String to duplicate
Returns: char * - Pointer to new string, allocated with malloc()
This wrapper function duplicates a string by calling strdup(), then
checks if a NULL pointer has been returned. If so, the program
terminates with an "Out of memory" error.
*/
extern char *xstrdup (const char *str);
/*
Function: xchstrdup - Duplicate a chtype string
Parameters: chstr - String to duplicate
Returns: chtype * - Pointer to new (duplicated) string
This function returns a new string of type chtype * that contains a
copy of the string in chstr. No errors are returned: if sufficient
memory is not available, the program terminates with an "Out of memory"
message.
*/
extern chtype *xchstrdup (const chtype *restrict chstr);
/*
Function: xwcsdup - Duplicate a wide-character string, with checking
Parameters: str - String to duplicate
Returns: wchar_t * - Pointer to new string, allocated with malloc()
This wrapper function duplicates a string by calling wcsdup(), then
checks if a NULL pointer has been returned. If so, the program
terminates with an "Out of memory" error.
*/
extern wchar_t *xwcsdup (const wchar_t *str);
/*
Function: xmbstowcs - Convert a multibyte string to a wide-character string
Parameters: dest - Location of wide-string buffer
src - String to convert
len - Size of dest, in multiples of wchar_t
Returns: size_t - Number of characters placed in dest (excluding NUL)
This wrapper function converts a multibyte string to a wide-character
one by calling mbsrtowcs() continually until the whole string is
converted. If any illegal sequences are present, they are converted to
the EILSEQ_REPL character. If the destination buffer is too small, the
string is truncated.
*/
extern size_t xmbstowcs (wchar_t *restrict dest, const char *restrict src,
size_t len);
/*
Function: xwcrtomb - Convert a wide character to a multibyte sequence
Parameters: dest - Location of multibyte buffer (size >= MB_CUR_MAX + 1)
wc - Character to convert
mbstate - Pointer to current multibyte shift state
Returns: size_t - Number of characters placed in dest
This wrapper function converts the wide character in wc (which may be
NUL) by calling wcrtomb(). If wc cannot be represented in the current
locale, EILSEQ_REPL is used instead (with any characters needed to move
to an initial shift state prior to EILSEQ_REPL).
*/
extern size_t xwcrtomb (char *restrict dest, wchar_t wc,
mbstate_t *restrict mbstate);
#endif /* included_UTILS_H */