ADDED Makefile Index: Makefile ================================================================== --- /dev/null +++ Makefile @@ -0,0 +1,5 @@ +SUBDIRS = enet src + +src: enet + +include buildsys.mk ADDED autogen.sh Index: autogen.sh ================================================================== --- /dev/null +++ autogen.sh @@ -0,0 +1,12 @@ +#!/bin/sh +set -e + +# Set a version for OpenBSD +if test x"$(uname -s)" = x"OpenBSD"; then + : ${AUTOCONF_VERSION:=2.71} + : ${AUTOMAKE_VERSION:=1.16} + export AUTOCONF_VERSION AUTOMAKE_VERSION +fi + +aclocal -I build-aux/m4 +autoconf ADDED build-aux/config.guess Index: build-aux/config.guess ================================================================== --- /dev/null +++ build-aux/config.guess @@ -0,0 +1,1807 @@ +#! /bin/sh +# Attempt to guess a canonical system name. +# Copyright 1992-2023 Free Software Foundation, Inc. + +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2023-08-22' + +# This file 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 . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). +# +# Originally written by Per Bothner; maintained since 2000 by Ben Elliston. +# +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess +# +# Please send patches to . + + +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] + +Output the configuration name of the system '$me' is run on. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.guess ($timestamp) + +Originally written by Per Bothner. +Copyright 1992-2023 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try '$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + * ) + break ;; + esac +done + +if test $# != 0; then + echo "$me: too many arguments$help" >&2 + exit 1 +fi + +# Just in case it came from the environment. +GUESS= + +# CC_FOR_BUILD -- compiler used by this script. Note that the use of a +# compiler to aid in system detection is discouraged as it requires +# temporary files to be created and, as you can see below, it is a +# headache to deal with in a portable fashion. + +# Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still +# use 'HOST_CC' if defined, but it is deprecated. + +# Portable tmp directory creation inspired by the Autoconf team. + +tmp= +# shellcheck disable=SC2172 +trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15 + +set_cc_for_build() { + # prevent multiple calls if $tmp is already set + test "$tmp" && return 0 + : "${TMPDIR=/tmp}" + # shellcheck disable=SC2039,SC3028 + { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } || + { test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } || + { tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } || + { echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; } + dummy=$tmp/dummy + case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in + ,,) echo "int x;" > "$dummy.c" + for driver in cc gcc c89 c99 ; do + if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then + CC_FOR_BUILD=$driver + break + fi + done + if test x"$CC_FOR_BUILD" = x ; then + CC_FOR_BUILD=no_compiler_found + fi + ;; + ,,*) CC_FOR_BUILD=$CC ;; + ,*,*) CC_FOR_BUILD=$HOST_CC ;; + esac +} + +# This is needed to find uname on a Pyramid OSx when run in the BSD universe. +# (ghazi@noc.rutgers.edu 1994-08-24) +if test -f /.attbin/uname ; then + PATH=$PATH:/.attbin ; export PATH +fi + +UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown +UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown +UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown +UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown + +case $UNAME_SYSTEM in +Linux|GNU|GNU/*) + LIBC=unknown + + set_cc_for_build + cat <<-EOF > "$dummy.c" + #if defined(__ANDROID__) + LIBC=android + #else + #include + #if defined(__UCLIBC__) + LIBC=uclibc + #elif defined(__dietlibc__) + LIBC=dietlibc + #elif defined(__GLIBC__) + LIBC=gnu + #else + #include + /* First heuristic to detect musl libc. */ + #ifdef __DEFINED_va_list + LIBC=musl + #endif + #endif + #endif + EOF + cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'` + eval "$cc_set_libc" + + # Second heuristic to detect musl libc. + if [ "$LIBC" = unknown ] && + command -v ldd >/dev/null && + ldd --version 2>&1 | grep -q ^musl; then + LIBC=musl + fi + + # If the system lacks a compiler, then just pick glibc. + # We could probably try harder. + if [ "$LIBC" = unknown ]; then + LIBC=gnu + fi + ;; +esac + +# Note: order is significant - the case branches are not exclusive. + +case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in + *:NetBSD:*:*) + # NetBSD (nbsd) targets should (where applicable) match one or + # more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*, + # *-*-netbsdecoff* and *-*-netbsd*. For targets that recently + # switched to ELF, *-*-netbsd* would select the old + # object file format. This provides both forward + # compatibility and a consistent mechanism for selecting the + # object file format. + # + # Note: NetBSD doesn't particularly care about the vendor + # portion of the name. We always set it to "unknown". + UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \ + /sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \ + echo unknown)` + case $UNAME_MACHINE_ARCH in + aarch64eb) machine=aarch64_be-unknown ;; + armeb) machine=armeb-unknown ;; + arm*) machine=arm-unknown ;; + sh3el) machine=shl-unknown ;; + sh3eb) machine=sh-unknown ;; + sh5el) machine=sh5le-unknown ;; + earmv*) + arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'` + endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'` + machine=${arch}${endian}-unknown + ;; + *) machine=$UNAME_MACHINE_ARCH-unknown ;; + esac + # The Operating System including object format, if it has switched + # to ELF recently (or will in the future) and ABI. + case $UNAME_MACHINE_ARCH in + earm*) + os=netbsdelf + ;; + arm*|i386|m68k|ns32k|sh3*|sparc|vax) + set_cc_for_build + if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ELF__ + then + # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout). + # Return netbsd for either. FIX? + os=netbsd + else + os=netbsdelf + fi + ;; + *) + os=netbsd + ;; + esac + # Determine ABI tags. + case $UNAME_MACHINE_ARCH in + earm*) + expr='s/^earmv[0-9]/-eabi/;s/eb$//' + abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"` + ;; + esac + # The OS release + # Debian GNU/NetBSD machines have a different userland, and + # thus, need a distinct triplet. However, they do not need + # kernel version information, so it can be replaced with a + # suitable tag, in the style of linux-gnu. + case $UNAME_VERSION in + Debian*) + release='-gnu' + ;; + *) + release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2` + ;; + esac + # Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM: + # contains redundant information, the shorter form: + # CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used. + GUESS=$machine-${os}${release}${abi-} + ;; + *:Bitrig:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE + ;; + *:OpenBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE + ;; + *:SecBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE + ;; + *:LibertyBSD:*:*) + UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'` + GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE + ;; + *:MidnightBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE + ;; + *:ekkoBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE + ;; + *:SolidBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE + ;; + *:OS108:*:*) + GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE + ;; + macppc:MirBSD:*:*) + GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE + ;; + *:MirBSD:*:*) + GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE + ;; + *:Sortix:*:*) + GUESS=$UNAME_MACHINE-unknown-sortix + ;; + *:Twizzler:*:*) + GUESS=$UNAME_MACHINE-unknown-twizzler + ;; + *:Redox:*:*) + GUESS=$UNAME_MACHINE-unknown-redox + ;; + mips:OSF1:*.*) + GUESS=mips-dec-osf1 + ;; + alpha:OSF1:*:*) + # Reset EXIT trap before exiting to avoid spurious non-zero exit code. + trap '' 0 + case $UNAME_RELEASE in + *4.0) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'` + ;; + *5.*) + UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'` + ;; + esac + # According to Compaq, /usr/sbin/psrinfo has been available on + # OSF/1 and Tru64 systems produced since 1995. I hope that + # covers most systems running today. This code pipes the CPU + # types through head -n 1, so we only detect the type of CPU 0. + ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^ The alpha \(.*\) processor.*$/\1/p' | head -n 1` + case $ALPHA_CPU_TYPE in + "EV4 (21064)") + UNAME_MACHINE=alpha ;; + "EV4.5 (21064)") + UNAME_MACHINE=alpha ;; + "LCA4 (21066/21068)") + UNAME_MACHINE=alpha ;; + "EV5 (21164)") + UNAME_MACHINE=alphaev5 ;; + "EV5.6 (21164A)") + UNAME_MACHINE=alphaev56 ;; + "EV5.6 (21164PC)") + UNAME_MACHINE=alphapca56 ;; + "EV5.7 (21164PC)") + UNAME_MACHINE=alphapca57 ;; + "EV6 (21264)") + UNAME_MACHINE=alphaev6 ;; + "EV6.7 (21264A)") + UNAME_MACHINE=alphaev67 ;; + "EV6.8CB (21264C)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8AL (21264B)") + UNAME_MACHINE=alphaev68 ;; + "EV6.8CX (21264D)") + UNAME_MACHINE=alphaev68 ;; + "EV6.9A (21264/EV69A)") + UNAME_MACHINE=alphaev69 ;; + "EV7 (21364)") + UNAME_MACHINE=alphaev7 ;; + "EV7.9 (21364A)") + UNAME_MACHINE=alphaev79 ;; + esac + # A Pn.n version is a patched version. + # A Vn.n version is a released version. + # A Tn.n version is a released field test version. + # A Xn.n version is an unreleased experimental baselevel. + # 1.2 uses "1.2" for uname -r. + OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + GUESS=$UNAME_MACHINE-dec-osf$OSF_REL + ;; + Amiga*:UNIX_System_V:4.0:*) + GUESS=m68k-unknown-sysv4 + ;; + *:[Aa]miga[Oo][Ss]:*:*) + GUESS=$UNAME_MACHINE-unknown-amigaos + ;; + *:[Mm]orph[Oo][Ss]:*:*) + GUESS=$UNAME_MACHINE-unknown-morphos + ;; + *:OS/390:*:*) + GUESS=i370-ibm-openedition + ;; + *:z/VM:*:*) + GUESS=s390-ibm-zvmoe + ;; + *:OS400:*:*) + GUESS=powerpc-ibm-os400 + ;; + arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*) + GUESS=arm-acorn-riscix$UNAME_RELEASE + ;; + arm*:riscos:*:*|arm*:RISCOS:*:*) + GUESS=arm-unknown-riscos + ;; + SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*) + GUESS=hppa1.1-hitachi-hiuxmpp + ;; + Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*) + # akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE. + case `(/bin/universe) 2>/dev/null` in + att) GUESS=pyramid-pyramid-sysv3 ;; + *) GUESS=pyramid-pyramid-bsd ;; + esac + ;; + NILE*:*:*:dcosx) + GUESS=pyramid-pyramid-svr4 + ;; + DRS?6000:unix:4.0:6*) + GUESS=sparc-icl-nx6 + ;; + DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*) + case `/usr/bin/uname -p` in + sparc) GUESS=sparc-icl-nx7 ;; + esac + ;; + s390x:SunOS:*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL + ;; + sun4H:SunOS:5.*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-hal-solaris2$SUN_REL + ;; + sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris2$SUN_REL + ;; + i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*) + GUESS=i386-pc-auroraux$UNAME_RELEASE + ;; + i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*) + set_cc_for_build + SUN_ARCH=i386 + # If there is a compiler, see if it is configured for 64-bit objects. + # Note that the Sun cc does not turn __LP64__ into 1 like gcc does. + # This test works for both compilers. + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + SUN_ARCH=x86_64 + fi + fi + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=$SUN_ARCH-pc-solaris2$SUN_REL + ;; + sun4*:SunOS:6*:*) + # According to config.sub, this is the proper way to canonicalize + # SunOS6. Hard to guess exactly what SunOS6 will be like, but + # it's likely to be more like Solaris than SunOS4. + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=sparc-sun-solaris3$SUN_REL + ;; + sun4*:SunOS:*:*) + case `/usr/bin/arch -k` in + Series*|S4*) + UNAME_RELEASE=`uname -v` + ;; + esac + # Japanese Language versions have a version number like '4.1.3-JL'. + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'` + GUESS=sparc-sun-sunos$SUN_REL + ;; + sun3*:SunOS:*:*) + GUESS=m68k-sun-sunos$UNAME_RELEASE + ;; + sun*:*:4.2BSD:*) + UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null` + test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3 + case `/bin/arch` in + sun3) + GUESS=m68k-sun-sunos$UNAME_RELEASE + ;; + sun4) + GUESS=sparc-sun-sunos$UNAME_RELEASE + ;; + esac + ;; + aushp:SunOS:*:*) + GUESS=sparc-auspex-sunos$UNAME_RELEASE + ;; + # The situation for MiNT is a little confusing. The machine name + # can be virtually everything (everything which is not + # "atarist" or "atariste" at least should have a processor + # > m68000). The system name ranges from "MiNT" over "FreeMiNT" + # to the lowercase version "mint" (or "freemint"). Finally + # the system name "TOS" denotes a system which is actually not + # MiNT. But MiNT is downward compatible to TOS, so this should + # be no problem. + atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*) + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; + atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*) + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; + *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*) + GUESS=m68k-atari-mint$UNAME_RELEASE + ;; + milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*) + GUESS=m68k-milan-mint$UNAME_RELEASE + ;; + hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*) + GUESS=m68k-hades-mint$UNAME_RELEASE + ;; + *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*) + GUESS=m68k-unknown-mint$UNAME_RELEASE + ;; + m68k:machten:*:*) + GUESS=m68k-apple-machten$UNAME_RELEASE + ;; + powerpc:machten:*:*) + GUESS=powerpc-apple-machten$UNAME_RELEASE + ;; + RISC*:Mach:*:*) + GUESS=mips-dec-mach_bsd4.3 + ;; + RISC*:ULTRIX:*:*) + GUESS=mips-dec-ultrix$UNAME_RELEASE + ;; + VAX*:ULTRIX*:*:*) + GUESS=vax-dec-ultrix$UNAME_RELEASE + ;; + 2020:CLIX:*:* | 2430:CLIX:*:*) + GUESS=clipper-intergraph-clix$UNAME_RELEASE + ;; + mips:*:*:UMIPS | mips:*:*:RISCos) + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" +#ifdef __cplusplus +#include /* for printf() prototype */ + int main (int argc, char *argv[]) { +#else + int main (argc, argv) int argc; char *argv[]; { +#endif + #if defined (host_mips) && defined (MIPSEB) + #if defined (SYSTYPE_SYSV) + printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_SVR4) + printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0); + #endif + #if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD) + printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0); + #endif + #endif + exit (-1); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && + dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` && + SYSTEM_NAME=`"$dummy" "$dummyarg"` && + { echo "$SYSTEM_NAME"; exit; } + GUESS=mips-mips-riscos$UNAME_RELEASE + ;; + Motorola:PowerMAX_OS:*:*) + GUESS=powerpc-motorola-powermax + ;; + Motorola:*:4.3:PL8-*) + GUESS=powerpc-harris-powermax + ;; + Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*) + GUESS=powerpc-harris-powermax + ;; + Night_Hawk:Power_UNIX:*:*) + GUESS=powerpc-harris-powerunix + ;; + m88k:CX/UX:7*:*) + GUESS=m88k-harris-cxux7 + ;; + m88k:*:4*:R4*) + GUESS=m88k-motorola-sysv4 + ;; + m88k:*:3*:R3*) + GUESS=m88k-motorola-sysv3 + ;; + AViiON:dgux:*:*) + # DG/UX returns AViiON for all architectures + UNAME_PROCESSOR=`/usr/bin/uname -p` + if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110 + then + if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \ + test "$TARGET_BINARY_INTERFACE"x = x + then + GUESS=m88k-dg-dgux$UNAME_RELEASE + else + GUESS=m88k-dg-dguxbcs$UNAME_RELEASE + fi + else + GUESS=i586-dg-dgux$UNAME_RELEASE + fi + ;; + M88*:DolphinOS:*:*) # DolphinOS (SVR3) + GUESS=m88k-dolphin-sysv3 + ;; + M88*:*:R3*:*) + # Delta 88k system running SVR3 + GUESS=m88k-motorola-sysv3 + ;; + XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3) + GUESS=m88k-tektronix-sysv3 + ;; + Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD) + GUESS=m68k-tektronix-bsd + ;; + *:IRIX*:*:*) + IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'` + GUESS=mips-sgi-irix$IRIX_REL + ;; + ????????:AIX?:[12].1:2) # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX. + GUESS=romp-ibm-aix # uname -m gives an 8 hex-code CPU id + ;; # Note that: echo "'`uname -s`'" gives 'AIX ' + i*86:AIX:*:*) + GUESS=i386-ibm-aix + ;; + ia64:AIX:*:*) + if test -x /usr/bin/oslevel ; then + IBM_REV=`/usr/bin/oslevel` + else + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE + fi + GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV + ;; + *:AIX:2:3) + if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + #include + + main() + { + if (!__power_pc()) + exit(1); + puts("powerpc-ibm-aix3.2.5"); + exit(0); + } +EOF + if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` + then + GUESS=$SYSTEM_NAME + else + GUESS=rs6000-ibm-aix3.2.5 + fi + elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then + GUESS=rs6000-ibm-aix3.2.4 + else + GUESS=rs6000-ibm-aix3.2 + fi + ;; + *:AIX:*:[4567]) + IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'` + if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then + IBM_ARCH=rs6000 + else + IBM_ARCH=powerpc + fi + if test -x /usr/bin/lslpp ; then + IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \ + awk -F: '{ print $3 }' | sed s/[0-9]*$/0/` + else + IBM_REV=$UNAME_VERSION.$UNAME_RELEASE + fi + GUESS=$IBM_ARCH-ibm-aix$IBM_REV + ;; + *:AIX:*:*) + GUESS=rs6000-ibm-aix + ;; + ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*) + GUESS=romp-ibm-bsd4.4 + ;; + ibmrt:*BSD:*|romp-ibm:BSD:*) # covers RT/PC BSD and + GUESS=romp-ibm-bsd$UNAME_RELEASE # 4.3 with uname added to + ;; # report: romp-ibm BSD 4.3 + *:BOSX:*:*) + GUESS=rs6000-bull-bosx + ;; + DPX/2?00:B.O.S.:*:*) + GUESS=m68k-bull-sysv3 + ;; + 9000/[34]??:4.3bsd:1.*:*) + GUESS=m68k-hp-bsd + ;; + hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*) + GUESS=m68k-hp-bsd4.4 + ;; + 9000/[34678]??:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + case $UNAME_MACHINE in + 9000/31?) HP_ARCH=m68000 ;; + 9000/[34]??) HP_ARCH=m68k ;; + 9000/[678][0-9][0-9]) + if test -x /usr/bin/getconf; then + sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null` + sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null` + case $sc_cpu_version in + 523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0 + 528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1 + 532) # CPU_PA_RISC2_0 + case $sc_kernel_bits in + 32) HP_ARCH=hppa2.0n ;; + 64) HP_ARCH=hppa2.0w ;; + '') HP_ARCH=hppa2.0 ;; # HP-UX 10.20 + esac ;; + esac + fi + if test "$HP_ARCH" = ""; then + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + + #define _HPUX_SOURCE + #include + #include + + int main () + { + #if defined(_SC_KERNEL_BITS) + long bits = sysconf(_SC_KERNEL_BITS); + #endif + long cpu = sysconf (_SC_CPU_VERSION); + + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1"); break; + case CPU_PA_RISC2_0: + #if defined(_SC_KERNEL_BITS) + switch (bits) + { + case 64: puts ("hppa2.0w"); break; + case 32: puts ("hppa2.0n"); break; + default: puts ("hppa2.0"); break; + } break; + #else /* !defined(_SC_KERNEL_BITS) */ + puts ("hppa2.0"); break; + #endif + default: puts ("hppa1.0"); break; + } + exit (0); + } +EOF + (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"` + test -z "$HP_ARCH" && HP_ARCH=hppa + fi ;; + esac + if test "$HP_ARCH" = hppa2.0w + then + set_cc_for_build + + # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating + # 32-bit code. hppa64-hp-hpux* has the same kernel and a compiler + # generating 64-bit code. GNU and HP use different nomenclature: + # + # $ CC_FOR_BUILD=cc ./config.guess + # => hppa2.0w-hp-hpux11.23 + # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess + # => hppa64-hp-hpux11.23 + + if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | + grep -q __LP64__ + then + HP_ARCH=hppa2.0w + else + HP_ARCH=hppa64 + fi + fi + GUESS=$HP_ARCH-hp-hpux$HPUX_REV + ;; + ia64:HP-UX:*:*) + HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'` + GUESS=ia64-hp-hpux$HPUX_REV + ;; + 3050*:HI-UX:*:*) + set_cc_for_build + sed 's/^ //' << EOF > "$dummy.c" + #include + int + main () + { + long cpu = sysconf (_SC_CPU_VERSION); + /* The order matters, because CPU_IS_HP_MC68K erroneously returns + true for CPU_PA_RISC1_0. CPU_IS_PA_RISC returns correct + results, however. */ + if (CPU_IS_PA_RISC (cpu)) + { + switch (cpu) + { + case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break; + case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break; + case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break; + default: puts ("hppa-hitachi-hiuxwe2"); break; + } + } + else if (CPU_IS_HP_MC68K (cpu)) + puts ("m68k-hitachi-hiuxwe2"); + else puts ("unknown-hitachi-hiuxwe2"); + exit (0); + } +EOF + $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` && + { echo "$SYSTEM_NAME"; exit; } + GUESS=unknown-hitachi-hiuxwe2 + ;; + 9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*) + GUESS=hppa1.1-hp-bsd + ;; + 9000/8??:4.3bsd:*:*) + GUESS=hppa1.0-hp-bsd + ;; + *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*) + GUESS=hppa1.0-hp-mpeix + ;; + hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*) + GUESS=hppa1.1-hp-osf + ;; + hp8??:OSF1:*:*) + GUESS=hppa1.0-hp-osf + ;; + i*86:OSF1:*:*) + if test -x /usr/sbin/sysversion ; then + GUESS=$UNAME_MACHINE-unknown-osf1mk + else + GUESS=$UNAME_MACHINE-unknown-osf1 + fi + ;; + parisc*:Lites*:*:*) + GUESS=hppa1.1-hp-lites + ;; + C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*) + GUESS=c1-convex-bsd + ;; + C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*) + if getsysinfo -f scalar_acc + then echo c32-convex-bsd + else echo c2-convex-bsd + fi + exit ;; + C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*) + GUESS=c34-convex-bsd + ;; + C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*) + GUESS=c38-convex-bsd + ;; + C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*) + GUESS=c4-convex-bsd + ;; + CRAY*Y-MP:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=ymp-cray-unicos$CRAY_REL + ;; + CRAY*[A-Z]90:*:*:*) + echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \ + | sed -e 's/CRAY.*\([A-Z]90\)/\1/' \ + -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \ + -e 's/\.[^.]*$/.X/' + exit ;; + CRAY*TS:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=t90-cray-unicos$CRAY_REL + ;; + CRAY*T3E:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=alphaev5-cray-unicosmk$CRAY_REL + ;; + CRAY*SV1:*:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=sv1-cray-unicos$CRAY_REL + ;; + *:UNICOS/mp:*:*) + CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'` + GUESS=craynv-cray-unicosmp$CRAY_REL + ;; + F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*) + FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz` + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'` + GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; + 5000:UNIX_System_V:4.*:*) + FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'` + FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'` + GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL} + ;; + i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*) + GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE + ;; + sparc*:BSD/OS:*:*) + GUESS=sparc-unknown-bsdi$UNAME_RELEASE + ;; + *:BSD/OS:*:*) + GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE + ;; + arm:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + set_cc_for_build + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi + else + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf + fi + ;; + *:FreeBSD:*:*) + UNAME_PROCESSOR=`uname -p` + case $UNAME_PROCESSOR in + amd64) + UNAME_PROCESSOR=x86_64 ;; + i386) + UNAME_PROCESSOR=i586 ;; + esac + FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL + ;; + i*:CYGWIN*:*) + GUESS=$UNAME_MACHINE-pc-cygwin + ;; + *:MINGW64*:*) + GUESS=$UNAME_MACHINE-pc-mingw64 + ;; + *:MINGW*:*) + GUESS=$UNAME_MACHINE-pc-mingw32 + ;; + *:MSYS*:*) + GUESS=$UNAME_MACHINE-pc-msys + ;; + i*:PW*:*) + GUESS=$UNAME_MACHINE-pc-pw32 + ;; + *:SerenityOS:*:*) + GUESS=$UNAME_MACHINE-pc-serenity + ;; + *:Interix*:*) + case $UNAME_MACHINE in + x86) + GUESS=i586-pc-interix$UNAME_RELEASE + ;; + authenticamd | genuineintel | EM64T) + GUESS=x86_64-unknown-interix$UNAME_RELEASE + ;; + IA64) + GUESS=ia64-unknown-interix$UNAME_RELEASE + ;; + esac ;; + i*:UWIN*:*) + GUESS=$UNAME_MACHINE-pc-uwin + ;; + amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*) + GUESS=x86_64-pc-cygwin + ;; + prep*:SunOS:5.*:*) + SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'` + GUESS=powerpcle-unknown-solaris2$SUN_REL + ;; + *:GNU:*:*) + # the GNU system + GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'` + GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL + ;; + *:GNU/*:*:*) + # other systems with GNU libc and userland + GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"` + GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC + ;; + x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*) + GUESS="$UNAME_MACHINE-pc-managarm-mlibc" + ;; + *:[Mm]anagarm:*:*) + GUESS="$UNAME_MACHINE-unknown-managarm-mlibc" + ;; + *:Minix:*:*) + GUESS=$UNAME_MACHINE-unknown-minix + ;; + aarch64:Linux:*:*) + set_cc_for_build + CPU=$UNAME_MACHINE + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __ARM_EABI__ + #ifdef __ARM_PCS_VFP + ABI=eabihf + #else + ABI=eabi + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;; + esac + fi + GUESS=$CPU-unknown-linux-$LIBCABI + ;; + aarch64_be:Linux:*:*) + UNAME_MACHINE=aarch64_be + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + alpha:Linux:*:*) + case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in + EV5) UNAME_MACHINE=alphaev5 ;; + EV56) UNAME_MACHINE=alphaev56 ;; + PCA56) UNAME_MACHINE=alphapca56 ;; + PCA57) UNAME_MACHINE=alphapca56 ;; + EV6) UNAME_MACHINE=alphaev6 ;; + EV67) UNAME_MACHINE=alphaev67 ;; + EV68*) UNAME_MACHINE=alphaev68 ;; + esac + objdump --private-headers /bin/sh | grep -q ld.so.1 + if test "$?" = 0 ; then LIBC=gnulibc1 ; fi + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + arm*:Linux:*:*) + set_cc_for_build + if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_EABI__ + then + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + else + if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \ + | grep -q __ARM_PCS_VFP + then + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi + else + GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf + fi + fi + ;; + avr32*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + cris:Linux:*:*) + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; + crisv32:Linux:*:*) + GUESS=$UNAME_MACHINE-axis-linux-$LIBC + ;; + e2k:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + frv:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + hexagon:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + i*86:Linux:*:*) + GUESS=$UNAME_MACHINE-pc-linux-$LIBC + ;; + ia64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + k1om:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + kvx:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + kvx:cos:*:*) + GUESS=$UNAME_MACHINE-unknown-cos + ;; + kvx:mbr:*:*) + GUESS=$UNAME_MACHINE-unknown-mbr + ;; + loongarch32:Linux:*:* | loongarch64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + m32r*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + m68*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + mips:Linux:*:* | mips64:Linux:*:*) + set_cc_for_build + IS_GLIBC=0 + test x"${LIBC}" = xgnu && IS_GLIBC=1 + sed 's/^ //' << EOF > "$dummy.c" + #undef CPU + #undef mips + #undef mipsel + #undef mips64 + #undef mips64el + #if ${IS_GLIBC} && defined(_ABI64) + LIBCABI=gnuabi64 + #else + #if ${IS_GLIBC} && defined(_ABIN32) + LIBCABI=gnuabin32 + #else + LIBCABI=${LIBC} + #endif + #endif + + #if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa64r6 + #else + #if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6 + CPU=mipsisa32r6 + #else + #if defined(__mips64) + CPU=mips64 + #else + CPU=mips + #endif + #endif + #endif + + #if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL) + MIPS_ENDIAN=el + #else + #if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB) + MIPS_ENDIAN= + #else + MIPS_ENDIAN= + #endif + #endif +EOF + cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'` + eval "$cc_set_vars" + test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; } + ;; + mips64el:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + openrisc*:Linux:*:*) + GUESS=or1k-unknown-linux-$LIBC + ;; + or32:Linux:*:* | or1k*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + padre:Linux:*:*) + GUESS=sparc-unknown-linux-$LIBC + ;; + parisc64:Linux:*:* | hppa64:Linux:*:*) + GUESS=hppa64-unknown-linux-$LIBC + ;; + parisc:Linux:*:* | hppa:Linux:*:*) + # Look for CPU level + case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in + PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;; + PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;; + *) GUESS=hppa-unknown-linux-$LIBC ;; + esac + ;; + ppc64:Linux:*:*) + GUESS=powerpc64-unknown-linux-$LIBC + ;; + ppc:Linux:*:*) + GUESS=powerpc-unknown-linux-$LIBC + ;; + ppc64le:Linux:*:*) + GUESS=powerpc64le-unknown-linux-$LIBC + ;; + ppcle:Linux:*:*) + GUESS=powerpcle-unknown-linux-$LIBC + ;; + riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + s390:Linux:*:* | s390x:Linux:*:*) + GUESS=$UNAME_MACHINE-ibm-linux-$LIBC + ;; + sh64*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + sh*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + sparc:Linux:*:* | sparc64:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + tile*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + vax:Linux:*:*) + GUESS=$UNAME_MACHINE-dec-linux-$LIBC + ;; + x86_64:Linux:*:*) + set_cc_for_build + CPU=$UNAME_MACHINE + LIBCABI=$LIBC + if test "$CC_FOR_BUILD" != no_compiler_found; then + ABI=64 + sed 's/^ //' << EOF > "$dummy.c" + #ifdef __i386__ + ABI=x86 + #else + #ifdef __ILP32__ + ABI=x32 + #endif + #endif +EOF + cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'` + eval "$cc_set_abi" + case $ABI in + x86) CPU=i686 ;; + x32) LIBCABI=${LIBC}x32 ;; + esac + fi + GUESS=$CPU-pc-linux-$LIBCABI + ;; + xtensa*:Linux:*:*) + GUESS=$UNAME_MACHINE-unknown-linux-$LIBC + ;; + i*86:DYNIX/ptx:4*:*) + # ptx 4.0 does uname -s correctly, with DYNIX/ptx in there. + # earlier versions are messed up and put the nodename in both + # sysname and nodename. + GUESS=i386-sequent-sysv4 + ;; + i*86:UNIX_SV:4.2MP:2.*) + # Unixware is an offshoot of SVR4, but it has its own version + # number series starting with 2... + # I am not positive that other SVR4 systems won't match this, + # I just have to hope. -- rms. + # Use sysv4.2uw... so that sysv4* matches it. + GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION + ;; + i*86:OS/2:*:*) + # If we were able to find 'uname', then EMX Unix compatibility + # is probably installed. + GUESS=$UNAME_MACHINE-pc-os2-emx + ;; + i*86:XTS-300:*:STOP) + GUESS=$UNAME_MACHINE-unknown-stop + ;; + i*86:atheos:*:*) + GUESS=$UNAME_MACHINE-unknown-atheos + ;; + i*86:syllable:*:*) + GUESS=$UNAME_MACHINE-pc-syllable + ;; + i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*) + GUESS=i386-unknown-lynxos$UNAME_RELEASE + ;; + i*86:*DOS:*:*) + GUESS=$UNAME_MACHINE-pc-msdosdjgpp + ;; + i*86:*:4.*:*) + UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'` + if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then + GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL + else + GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL + fi + ;; + i*86:*:5:[678]*) + # UnixWare 7.x, OpenUNIX and OpenServer 6. + case `/bin/uname -X | grep "^Machine"` in + *486*) UNAME_MACHINE=i486 ;; + *Pentium) UNAME_MACHINE=i586 ;; + *Pent*|*Celeron) UNAME_MACHINE=i686 ;; + esac + GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION} + ;; + i*86:*:3.2:*) + if test -f /usr/options/cb.name; then + UNAME_REL=`sed -n 's/.*Version //p' /dev/null >/dev/null ; then + UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')` + (/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486 + (/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \ + && UNAME_MACHINE=i586 + (/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \ + && UNAME_MACHINE=i686 + (/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \ + && UNAME_MACHINE=i686 + GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL + else + GUESS=$UNAME_MACHINE-pc-sysv32 + fi + ;; + pc:*:*:*) + # Left here for compatibility: + # uname -m prints for DJGPP always 'pc', but it prints nothing about + # the processor, so we play safe by assuming i586. + # Note: whatever this is, it MUST be the same as what config.sub + # prints for the "djgpp" host, or else GDB configure will decide that + # this is a cross-build. + GUESS=i586-pc-msdosdjgpp + ;; + Intel:Mach:3*:*) + GUESS=i386-pc-mach3 + ;; + paragon:*:*:*) + GUESS=i860-intel-osf1 + ;; + i860:*:4.*:*) # i860-SVR4 + if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then + GUESS=i860-stardent-sysv$UNAME_RELEASE # Stardent Vistra i860-SVR4 + else # Add other i860-SVR4 vendors below as they are discovered. + GUESS=i860-unknown-sysv$UNAME_RELEASE # Unknown i860-SVR4 + fi + ;; + mini*:CTIX:SYS*5:*) + # "miniframe" + GUESS=m68010-convergent-sysv + ;; + mc68k:UNIX:SYSTEM5:3.51m) + GUESS=m68k-convergent-sysv + ;; + M680?0:D-NIX:5.3:*) + GUESS=m68k-diab-dnix + ;; + M68*:*:R3V[5678]*:*) + test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;; + 3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0) + OS_REL='' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + 3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*) + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4; exit; } ;; + NCR*:*:4.2:* | MPRAS*:*:4.2:*) + OS_REL='.3' + test -r /etc/.relid \ + && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid` + /bin/uname -p 2>/dev/null | grep 86 >/dev/null \ + && { echo i486-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } + /bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \ + && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;; + m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*) + GUESS=m68k-unknown-lynxos$UNAME_RELEASE + ;; + mc68030:UNIX_System_V:4.*:*) + GUESS=m68k-atari-sysv4 + ;; + TSUNAMI:LynxOS:2.*:*) + GUESS=sparc-unknown-lynxos$UNAME_RELEASE + ;; + rs6000:LynxOS:2.*:*) + GUESS=rs6000-unknown-lynxos$UNAME_RELEASE + ;; + PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*) + GUESS=powerpc-unknown-lynxos$UNAME_RELEASE + ;; + SM[BE]S:UNIX_SV:*:*) + GUESS=mips-dde-sysv$UNAME_RELEASE + ;; + RM*:ReliantUNIX-*:*:*) + GUESS=mips-sni-sysv4 + ;; + RM*:SINIX-*:*:*) + GUESS=mips-sni-sysv4 + ;; + *:SINIX-*:*:*) + if uname -p 2>/dev/null >/dev/null ; then + UNAME_MACHINE=`(uname -p) 2>/dev/null` + GUESS=$UNAME_MACHINE-sni-sysv4 + else + GUESS=ns32k-sni-sysv + fi + ;; + PENTIUM:*:4.0*:*) # Unisys 'ClearPath HMP IX 4000' SVR4/MP effort + # says + GUESS=i586-unisys-sysv4 + ;; + *:UNIX_System_V:4*:FTX*) + # From Gerald Hewes . + # How about differentiating between stratus architectures? -djm + GUESS=hppa1.1-stratus-sysv4 + ;; + *:*:*:FTX*) + # From seanf@swdc.stratus.com. + GUESS=i860-stratus-sysv4 + ;; + i*86:VOS:*:*) + # From Paul.Green@stratus.com. + GUESS=$UNAME_MACHINE-stratus-vos + ;; + *:VOS:*:*) + # From Paul.Green@stratus.com. + GUESS=hppa1.1-stratus-vos + ;; + mc68*:A/UX:*:*) + GUESS=m68k-apple-aux$UNAME_RELEASE + ;; + news*:NEWS-OS:6*:*) + GUESS=mips-sony-newsos6 + ;; + R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*) + if test -d /usr/nec; then + GUESS=mips-nec-sysv$UNAME_RELEASE + else + GUESS=mips-unknown-sysv$UNAME_RELEASE + fi + ;; + BeBox:BeOS:*:*) # BeOS running on hardware made by Be, PPC only. + GUESS=powerpc-be-beos + ;; + BeMac:BeOS:*:*) # BeOS running on Mac or Mac clone, PPC only. + GUESS=powerpc-apple-beos + ;; + BePC:BeOS:*:*) # BeOS running on Intel PC compatible. + GUESS=i586-pc-beos + ;; + BePC:Haiku:*:*) # Haiku running on Intel PC compatible. + GUESS=i586-pc-haiku + ;; + ppc:Haiku:*:*) # Haiku running on Apple PowerPC + GUESS=powerpc-apple-haiku + ;; + *:Haiku:*:*) # Haiku modern gcc (not bound by BeOS compat) + GUESS=$UNAME_MACHINE-unknown-haiku + ;; + SX-4:SUPER-UX:*:*) + GUESS=sx4-nec-superux$UNAME_RELEASE + ;; + SX-5:SUPER-UX:*:*) + GUESS=sx5-nec-superux$UNAME_RELEASE + ;; + SX-6:SUPER-UX:*:*) + GUESS=sx6-nec-superux$UNAME_RELEASE + ;; + SX-7:SUPER-UX:*:*) + GUESS=sx7-nec-superux$UNAME_RELEASE + ;; + SX-8:SUPER-UX:*:*) + GUESS=sx8-nec-superux$UNAME_RELEASE + ;; + SX-8R:SUPER-UX:*:*) + GUESS=sx8r-nec-superux$UNAME_RELEASE + ;; + SX-ACE:SUPER-UX:*:*) + GUESS=sxace-nec-superux$UNAME_RELEASE + ;; + Power*:Rhapsody:*:*) + GUESS=powerpc-apple-rhapsody$UNAME_RELEASE + ;; + *:Rhapsody:*:*) + GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE + ;; + arm64:Darwin:*:*) + GUESS=aarch64-apple-darwin$UNAME_RELEASE + ;; + *:Darwin:*:*) + UNAME_PROCESSOR=`uname -p` + case $UNAME_PROCESSOR in + unknown) UNAME_PROCESSOR=powerpc ;; + esac + if command -v xcode-select > /dev/null 2> /dev/null && \ + ! xcode-select --print-path > /dev/null 2> /dev/null ; then + # Avoid executing cc if there is no toolchain installed as + # cc will be a stub that puts up a graphical alert + # prompting the user to install developer tools. + CC_FOR_BUILD=no_compiler_found + else + set_cc_for_build + fi + if test "$CC_FOR_BUILD" != no_compiler_found; then + if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_64BIT_ARCH >/dev/null + then + case $UNAME_PROCESSOR in + i386) UNAME_PROCESSOR=x86_64 ;; + powerpc) UNAME_PROCESSOR=powerpc64 ;; + esac + fi + # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc + if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \ + (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \ + grep IS_PPC >/dev/null + then + UNAME_PROCESSOR=powerpc + fi + elif test "$UNAME_PROCESSOR" = i386 ; then + # uname -m returns i386 or x86_64 + UNAME_PROCESSOR=$UNAME_MACHINE + fi + GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE + ;; + *:procnto*:*:* | *:QNX:[0123456789]*:*) + UNAME_PROCESSOR=`uname -p` + if test "$UNAME_PROCESSOR" = x86; then + UNAME_PROCESSOR=i386 + UNAME_MACHINE=pc + fi + GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE + ;; + *:QNX:*:4*) + GUESS=i386-pc-qnx + ;; + NEO-*:NONSTOP_KERNEL:*:*) + GUESS=neo-tandem-nsk$UNAME_RELEASE + ;; + NSE-*:NONSTOP_KERNEL:*:*) + GUESS=nse-tandem-nsk$UNAME_RELEASE + ;; + NSR-*:NONSTOP_KERNEL:*:*) + GUESS=nsr-tandem-nsk$UNAME_RELEASE + ;; + NSV-*:NONSTOP_KERNEL:*:*) + GUESS=nsv-tandem-nsk$UNAME_RELEASE + ;; + NSX-*:NONSTOP_KERNEL:*:*) + GUESS=nsx-tandem-nsk$UNAME_RELEASE + ;; + *:NonStop-UX:*:*) + GUESS=mips-compaq-nonstopux + ;; + BS2000:POSIX*:*:*) + GUESS=bs2000-siemens-sysv + ;; + DS/*:UNIX_System_V:*:*) + GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE + ;; + *:Plan9:*:*) + # "uname -m" is not consistent, so use $cputype instead. 386 + # is converted to i386 for consistency with other x86 + # operating systems. + if test "${cputype-}" = 386; then + UNAME_MACHINE=i386 + elif test "x${cputype-}" != x; then + UNAME_MACHINE=$cputype + fi + GUESS=$UNAME_MACHINE-unknown-plan9 + ;; + *:TOPS-10:*:*) + GUESS=pdp10-unknown-tops10 + ;; + *:TENEX:*:*) + GUESS=pdp10-unknown-tenex + ;; + KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*) + GUESS=pdp10-dec-tops20 + ;; + XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*) + GUESS=pdp10-xkl-tops20 + ;; + *:TOPS-20:*:*) + GUESS=pdp10-unknown-tops20 + ;; + *:ITS:*:*) + GUESS=pdp10-unknown-its + ;; + SEI:*:*:SEIUX) + GUESS=mips-sei-seiux$UNAME_RELEASE + ;; + *:DragonFly:*:*) + DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'` + GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL + ;; + *:*VMS:*:*) + UNAME_MACHINE=`(uname -p) 2>/dev/null` + case $UNAME_MACHINE in + A*) GUESS=alpha-dec-vms ;; + I*) GUESS=ia64-dec-vms ;; + V*) GUESS=vax-dec-vms ;; + esac ;; + *:XENIX:*:SysV) + GUESS=i386-pc-xenix + ;; + i*86:skyos:*:*) + SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'` + GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL + ;; + i*86:rdos:*:*) + GUESS=$UNAME_MACHINE-pc-rdos + ;; + i*86:Fiwix:*:*) + GUESS=$UNAME_MACHINE-pc-fiwix + ;; + *:AROS:*:*) + GUESS=$UNAME_MACHINE-unknown-aros + ;; + x86_64:VMkernel:*:*) + GUESS=$UNAME_MACHINE-unknown-esx + ;; + amd64:Isilon\ OneFS:*:*) + GUESS=x86_64-unknown-onefs + ;; + *:Unleashed:*:*) + GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE + ;; +esac + +# Do we have a guess based on uname results? +if test "x$GUESS" != x; then + echo "$GUESS" + exit +fi + +# No uname command or uname output not recognized. +set_cc_for_build +cat > "$dummy.c" < +#include +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#include +#if defined(_SIZE_T_) || defined(SIGLOST) +#include +#endif +#endif +#endif +main () +{ +#if defined (sony) +#if defined (MIPSEB) + /* BFD wants "bsd" instead of "newsos". Perhaps BFD should be changed, + I don't know.... */ + printf ("mips-sony-bsd\n"); exit (0); +#else +#include + printf ("m68k-sony-newsos%s\n", +#ifdef NEWSOS4 + "4" +#else + "" +#endif + ); exit (0); +#endif +#endif + +#if defined (NeXT) +#if !defined (__ARCHITECTURE__) +#define __ARCHITECTURE__ "m68k" +#endif + int version; + version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`; + if (version < 4) + printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version); + else + printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version); + exit (0); +#endif + +#if defined (MULTIMAX) || defined (n16) +#if defined (UMAXV) + printf ("ns32k-encore-sysv\n"); exit (0); +#else +#if defined (CMU) + printf ("ns32k-encore-mach\n"); exit (0); +#else + printf ("ns32k-encore-bsd\n"); exit (0); +#endif +#endif +#endif + +#if defined (__386BSD__) + printf ("i386-pc-bsd\n"); exit (0); +#endif + +#if defined (sequent) +#if defined (i386) + printf ("i386-sequent-dynix\n"); exit (0); +#endif +#if defined (ns32000) + printf ("ns32k-sequent-dynix\n"); exit (0); +#endif +#endif + +#if defined (_SEQUENT_) + struct utsname un; + + uname(&un); + if (strncmp(un.version, "V2", 2) == 0) { + printf ("i386-sequent-ptx2\n"); exit (0); + } + if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */ + printf ("i386-sequent-ptx1\n"); exit (0); + } + printf ("i386-sequent-ptx\n"); exit (0); +#endif + +#if defined (vax) +#if !defined (ultrix) +#include +#if defined (BSD) +#if BSD == 43 + printf ("vax-dec-bsd4.3\n"); exit (0); +#else +#if BSD == 199006 + printf ("vax-dec-bsd4.3reno\n"); exit (0); +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#endif +#else + printf ("vax-dec-bsd\n"); exit (0); +#endif +#else +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname un; + uname (&un); + printf ("vax-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("vax-dec-ultrix\n"); exit (0); +#endif +#endif +#endif +#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__) +#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__) +#if defined(_SIZE_T_) || defined(SIGLOST) + struct utsname *un; + uname (&un); + printf ("mips-dec-ultrix%s\n", un.release); exit (0); +#else + printf ("mips-dec-ultrix\n"); exit (0); +#endif +#endif +#endif + +#if defined (alliant) && defined (i860) + printf ("i860-alliant-bsd\n"); exit (0); +#endif + + exit (1); +} +EOF + +$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` && + { echo "$SYSTEM_NAME"; exit; } + +# Apollos put the system type in the environment. +test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; } + +echo "$0: unable to guess system type" >&2 + +case $UNAME_MACHINE:$UNAME_SYSTEM in + mips:Linux | mips64:Linux) + # If we got here on MIPS GNU/Linux, output extra information. + cat >&2 <&2 <&2 </dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null` + +hostinfo = `(hostinfo) 2>/dev/null` +/bin/universe = `(/bin/universe) 2>/dev/null` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null` +/bin/arch = `(/bin/arch) 2>/dev/null` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null` + +UNAME_MACHINE = "$UNAME_MACHINE" +UNAME_RELEASE = "$UNAME_RELEASE" +UNAME_SYSTEM = "$UNAME_SYSTEM" +UNAME_VERSION = "$UNAME_VERSION" +EOF +fi + +exit 1 + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: ADDED build-aux/config.sub Index: build-aux/config.sub ================================================================== --- /dev/null +++ build-aux/config.sub @@ -0,0 +1,1960 @@ +#! /bin/sh +# Configuration validation subroutine script. +# Copyright 1992-2023 Free Software Foundation, Inc. + +# shellcheck disable=SC2006,SC2268 # see below for rationale + +timestamp='2023-09-19' + +# This file 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 . +# +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that +# program. This Exception is an additional permission under section 7 +# of the GNU General Public License, version 3 ("GPLv3"). + + +# Please send patches to . +# +# Configuration subroutine to validate and canonicalize a configuration type. +# Supply the specified configuration type as an argument. +# If it is invalid, we print an error message on stderr and exit with code 1. +# Otherwise, we print the canonical config type on stdout and succeed. + +# You can get the latest version of this script from: +# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub + +# This file is supposed to be the same for all GNU packages +# and recognize all the CPU types, system types and aliases +# that are meaningful with *any* GNU software. +# Each package is responsible for reporting which valid configurations +# it does not support. The user should be able to distinguish +# a failure to support a valid configuration from a meaningless +# configuration. + +# The goal of this file is to map all the various variations of a given +# machine specification into a single specification in the form: +# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM +# or in some cases, the newer four-part form: +# CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM +# It is wrong to echo any other type of specification. + +# The "shellcheck disable" line above the timestamp inhibits complaints +# about features and limitations of the classic Bourne shell that were +# superseded or lifted in POSIX. However, this script identifies a wide +# variety of pre-POSIX systems that do not have POSIX shells at all, and +# even some reasonably current systems (Solaris 10 as case-in-point) still +# have a pre-POSIX /bin/sh. + +me=`echo "$0" | sed -e 's,.*/,,'` + +usage="\ +Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS + +Canonicalize a configuration name. + +Options: + -h, --help print this help, then exit + -t, --time-stamp print date of last modification, then exit + -v, --version print version number, then exit + +Report bugs and patches to ." + +version="\ +GNU config.sub ($timestamp) + +Copyright 1992-2023 Free Software Foundation, Inc. + +This is free software; see the source for copying conditions. There is NO +warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE." + +help=" +Try '$me --help' for more information." + +# Parse command line +while test $# -gt 0 ; do + case $1 in + --time-stamp | --time* | -t ) + echo "$timestamp" ; exit ;; + --version | -v ) + echo "$version" ; exit ;; + --help | --h* | -h ) + echo "$usage"; exit ;; + -- ) # Stop option processing + shift; break ;; + - ) # Use stdin as input. + break ;; + -* ) + echo "$me: invalid option $1$help" >&2 + exit 1 ;; + + *local*) + # First pass through any local machine types. + echo "$1" + exit ;; + + * ) + break ;; + esac +done + +case $# in + 0) echo "$me: missing argument$help" >&2 + exit 1;; + 1) ;; + *) echo "$me: too many arguments$help" >&2 + exit 1;; +esac + +# Split fields of configuration type +# shellcheck disable=SC2162 +saved_IFS=$IFS +IFS="-" read field1 field2 field3 field4 <&2 + exit 1 + ;; + *-*-*-*) + basic_machine=$field1-$field2 + basic_os=$field3-$field4 + ;; + *-*-*) + # Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two + # parts + maybe_os=$field2-$field3 + case $maybe_os in + nto-qnx* | linux-* | uclinux-uclibc* \ + | uclinux-gnu* | kfreebsd*-gnu* | knetbsd*-gnu* | netbsd*-gnu* \ + | netbsd*-eabi* | kopensolaris*-gnu* | cloudabi*-eabi* \ + | storm-chaos* | os2-emx* | rtmk-nova* | managarm-* \ + | windows-* ) + basic_machine=$field1 + basic_os=$maybe_os + ;; + android-linux) + basic_machine=$field1-unknown + basic_os=linux-android + ;; + *) + basic_machine=$field1-$field2 + basic_os=$field3 + ;; + esac + ;; + *-*) + # A lone config we happen to match not fitting any pattern + case $field1-$field2 in + decstation-3100) + basic_machine=mips-dec + basic_os= + ;; + *-*) + # Second component is usually, but not always the OS + case $field2 in + # Prevent following clause from handling this valid os + sun*os*) + basic_machine=$field1 + basic_os=$field2 + ;; + zephyr*) + basic_machine=$field1-unknown + basic_os=$field2 + ;; + # Manufacturers + dec* | mips* | sequent* | encore* | pc533* | sgi* | sony* \ + | att* | 7300* | 3300* | delta* | motorola* | sun[234]* \ + | unicom* | ibm* | next | hp | isi* | apollo | altos* \ + | convergent* | ncr* | news | 32* | 3600* | 3100* \ + | hitachi* | c[123]* | convex* | sun | crds | omron* | dg \ + | ultra | tti* | harris | dolphin | highlevel | gould \ + | cbm | ns | masscomp | apple | axis | knuth | cray \ + | microblaze* | sim | cisco \ + | oki | wec | wrs | winbond) + basic_machine=$field1-$field2 + basic_os= + ;; + *) + basic_machine=$field1 + basic_os=$field2 + ;; + esac + ;; + esac + ;; + *) + # Convert single-component short-hands not valid as part of + # multi-component configurations. + case $field1 in + 386bsd) + basic_machine=i386-pc + basic_os=bsd + ;; + a29khif) + basic_machine=a29k-amd + basic_os=udi + ;; + adobe68k) + basic_machine=m68010-adobe + basic_os=scout + ;; + alliant) + basic_machine=fx80-alliant + basic_os= + ;; + altos | altos3068) + basic_machine=m68k-altos + basic_os= + ;; + am29k) + basic_machine=a29k-none + basic_os=bsd + ;; + amdahl) + basic_machine=580-amdahl + basic_os=sysv + ;; + amiga) + basic_machine=m68k-unknown + basic_os= + ;; + amigaos | amigados) + basic_machine=m68k-unknown + basic_os=amigaos + ;; + amigaunix | amix) + basic_machine=m68k-unknown + basic_os=sysv4 + ;; + apollo68) + basic_machine=m68k-apollo + basic_os=sysv + ;; + apollo68bsd) + basic_machine=m68k-apollo + basic_os=bsd + ;; + aros) + basic_machine=i386-pc + basic_os=aros + ;; + aux) + basic_machine=m68k-apple + basic_os=aux + ;; + balance) + basic_machine=ns32k-sequent + basic_os=dynix + ;; + blackfin) + basic_machine=bfin-unknown + basic_os=linux + ;; + cegcc) + basic_machine=arm-unknown + basic_os=cegcc + ;; + convex-c1) + basic_machine=c1-convex + basic_os=bsd + ;; + convex-c2) + basic_machine=c2-convex + basic_os=bsd + ;; + convex-c32) + basic_machine=c32-convex + basic_os=bsd + ;; + convex-c34) + basic_machine=c34-convex + basic_os=bsd + ;; + convex-c38) + basic_machine=c38-convex + basic_os=bsd + ;; + cray) + basic_machine=j90-cray + basic_os=unicos + ;; + crds | unos) + basic_machine=m68k-crds + basic_os= + ;; + da30) + basic_machine=m68k-da30 + basic_os= + ;; + decstation | pmax | pmin | dec3100 | decstatn) + basic_machine=mips-dec + basic_os= + ;; + delta88) + basic_machine=m88k-motorola + basic_os=sysv3 + ;; + dicos) + basic_machine=i686-pc + basic_os=dicos + ;; + djgpp) + basic_machine=i586-pc + basic_os=msdosdjgpp + ;; + ebmon29k) + basic_machine=a29k-amd + basic_os=ebmon + ;; + es1800 | OSE68k | ose68k | ose | OSE) + basic_machine=m68k-ericsson + basic_os=ose + ;; + gmicro) + basic_machine=tron-gmicro + basic_os=sysv + ;; + go32) + basic_machine=i386-pc + basic_os=go32 + ;; + h8300hms) + basic_machine=h8300-hitachi + basic_os=hms + ;; + h8300xray) + basic_machine=h8300-hitachi + basic_os=xray + ;; + h8500hms) + basic_machine=h8500-hitachi + basic_os=hms + ;; + harris) + basic_machine=m88k-harris + basic_os=sysv3 + ;; + hp300 | hp300hpux) + basic_machine=m68k-hp + basic_os=hpux + ;; + hp300bsd) + basic_machine=m68k-hp + basic_os=bsd + ;; + hppaosf) + basic_machine=hppa1.1-hp + basic_os=osf + ;; + hppro) + basic_machine=hppa1.1-hp + basic_os=proelf + ;; + i386mach) + basic_machine=i386-mach + basic_os=mach + ;; + isi68 | isi) + basic_machine=m68k-isi + basic_os=sysv + ;; + m68knommu) + basic_machine=m68k-unknown + basic_os=linux + ;; + magnum | m3230) + basic_machine=mips-mips + basic_os=sysv + ;; + merlin) + basic_machine=ns32k-utek + basic_os=sysv + ;; + mingw64) + basic_machine=x86_64-pc + basic_os=mingw64 + ;; + mingw32) + basic_machine=i686-pc + basic_os=mingw32 + ;; + mingw32ce) + basic_machine=arm-unknown + basic_os=mingw32ce + ;; + monitor) + basic_machine=m68k-rom68k + basic_os=coff + ;; + morphos) + basic_machine=powerpc-unknown + basic_os=morphos + ;; + moxiebox) + basic_machine=moxie-unknown + basic_os=moxiebox + ;; + msdos) + basic_machine=i386-pc + basic_os=msdos + ;; + msys) + basic_machine=i686-pc + basic_os=msys + ;; + mvs) + basic_machine=i370-ibm + basic_os=mvs + ;; + nacl) + basic_machine=le32-unknown + basic_os=nacl + ;; + ncr3000) + basic_machine=i486-ncr + basic_os=sysv4 + ;; + netbsd386) + basic_machine=i386-pc + basic_os=netbsd + ;; + netwinder) + basic_machine=armv4l-rebel + basic_os=linux + ;; + news | news700 | news800 | news900) + basic_machine=m68k-sony + basic_os=newsos + ;; + news1000) + basic_machine=m68030-sony + basic_os=newsos + ;; + necv70) + basic_machine=v70-nec + basic_os=sysv + ;; + nh3000) + basic_machine=m68k-harris + basic_os=cxux + ;; + nh[45]000) + basic_machine=m88k-harris + basic_os=cxux + ;; + nindy960) + basic_machine=i960-intel + basic_os=nindy + ;; + mon960) + basic_machine=i960-intel + basic_os=mon960 + ;; + nonstopux) + basic_machine=mips-compaq + basic_os=nonstopux + ;; + os400) + basic_machine=powerpc-ibm + basic_os=os400 + ;; + OSE68000 | ose68000) + basic_machine=m68000-ericsson + basic_os=ose + ;; + os68k) + basic_machine=m68k-none + basic_os=os68k + ;; + paragon) + basic_machine=i860-intel + basic_os=osf + ;; + parisc) + basic_machine=hppa-unknown + basic_os=linux + ;; + psp) + basic_machine=mipsallegrexel-sony + basic_os=psp + ;; + pw32) + basic_machine=i586-unknown + basic_os=pw32 + ;; + rdos | rdos64) + basic_machine=x86_64-pc + basic_os=rdos + ;; + rdos32) + basic_machine=i386-pc + basic_os=rdos + ;; + rom68k) + basic_machine=m68k-rom68k + basic_os=coff + ;; + sa29200) + basic_machine=a29k-amd + basic_os=udi + ;; + sei) + basic_machine=mips-sei + basic_os=seiux + ;; + sequent) + basic_machine=i386-sequent + basic_os= + ;; + sps7) + basic_machine=m68k-bull + basic_os=sysv2 + ;; + st2000) + basic_machine=m68k-tandem + basic_os= + ;; + stratus) + basic_machine=i860-stratus + basic_os=sysv4 + ;; + sun2) + basic_machine=m68000-sun + basic_os= + ;; + sun2os3) + basic_machine=m68000-sun + basic_os=sunos3 + ;; + sun2os4) + basic_machine=m68000-sun + basic_os=sunos4 + ;; + sun3) + basic_machine=m68k-sun + basic_os= + ;; + sun3os3) + basic_machine=m68k-sun + basic_os=sunos3 + ;; + sun3os4) + basic_machine=m68k-sun + basic_os=sunos4 + ;; + sun4) + basic_machine=sparc-sun + basic_os= + ;; + sun4os3) + basic_machine=sparc-sun + basic_os=sunos3 + ;; + sun4os4) + basic_machine=sparc-sun + basic_os=sunos4 + ;; + sun4sol2) + basic_machine=sparc-sun + basic_os=solaris2 + ;; + sun386 | sun386i | roadrunner) + basic_machine=i386-sun + basic_os= + ;; + sv1) + basic_machine=sv1-cray + basic_os=unicos + ;; + symmetry) + basic_machine=i386-sequent + basic_os=dynix + ;; + t3e) + basic_machine=alphaev5-cray + basic_os=unicos + ;; + t90) + basic_machine=t90-cray + basic_os=unicos + ;; + toad1) + basic_machine=pdp10-xkl + basic_os=tops20 + ;; + tpf) + basic_machine=s390x-ibm + basic_os=tpf + ;; + udi29k) + basic_machine=a29k-amd + basic_os=udi + ;; + ultra3) + basic_machine=a29k-nyu + basic_os=sym1 + ;; + v810 | necv810) + basic_machine=v810-nec + basic_os=none + ;; + vaxv) + basic_machine=vax-dec + basic_os=sysv + ;; + vms) + basic_machine=vax-dec + basic_os=vms + ;; + vsta) + basic_machine=i386-pc + basic_os=vsta + ;; + vxworks960) + basic_machine=i960-wrs + basic_os=vxworks + ;; + vxworks68) + basic_machine=m68k-wrs + basic_os=vxworks + ;; + vxworks29k) + basic_machine=a29k-wrs + basic_os=vxworks + ;; + xbox) + basic_machine=i686-pc + basic_os=mingw32 + ;; + ymp) + basic_machine=ymp-cray + basic_os=unicos + ;; + *) + basic_machine=$1 + basic_os= + ;; + esac + ;; +esac + +# Decode 1-component or ad-hoc basic machines +case $basic_machine in + # Here we handle the default manufacturer of certain CPU types. It is in + # some cases the only manufacturer, in others, it is the most popular. + w89k) + cpu=hppa1.1 + vendor=winbond + ;; + op50n) + cpu=hppa1.1 + vendor=oki + ;; + op60c) + cpu=hppa1.1 + vendor=oki + ;; + ibm*) + cpu=i370 + vendor=ibm + ;; + orion105) + cpu=clipper + vendor=highlevel + ;; + mac | mpw | mac-mpw) + cpu=m68k + vendor=apple + ;; + pmac | pmac-mpw) + cpu=powerpc + vendor=apple + ;; + + # Recognize the various machine names and aliases which stand + # for a CPU type and a company and sometimes even an OS. + 3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc) + cpu=m68000 + vendor=att + ;; + 3b*) + cpu=we32k + vendor=att + ;; + bluegene*) + cpu=powerpc + vendor=ibm + basic_os=cnk + ;; + decsystem10* | dec10*) + cpu=pdp10 + vendor=dec + basic_os=tops10 + ;; + decsystem20* | dec20*) + cpu=pdp10 + vendor=dec + basic_os=tops20 + ;; + delta | 3300 | motorola-3300 | motorola-delta \ + | 3300-motorola | delta-motorola) + cpu=m68k + vendor=motorola + ;; + dpx2*) + cpu=m68k + vendor=bull + basic_os=sysv3 + ;; + encore | umax | mmax) + cpu=ns32k + vendor=encore + ;; + elxsi) + cpu=elxsi + vendor=elxsi + basic_os=${basic_os:-bsd} + ;; + fx2800) + cpu=i860 + vendor=alliant + ;; + genix) + cpu=ns32k + vendor=ns + ;; + h3050r* | hiux*) + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 + ;; + hp3k9[0-9][0-9] | hp9[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k2[0-9][0-9] | hp9k31[0-9]) + cpu=m68000 + vendor=hp + ;; + hp9k3[2-9][0-9]) + cpu=m68k + vendor=hp + ;; + hp9k6[0-9][0-9] | hp6[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + hp9k7[0-79][0-9] | hp7[0-79][0-9]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k78[0-9] | hp78[0-9]) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893) + # FIXME: really hppa2.0-hp + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][13679] | hp8[0-9][13679]) + cpu=hppa1.1 + vendor=hp + ;; + hp9k8[0-9][0-9] | hp8[0-9][0-9]) + cpu=hppa1.0 + vendor=hp + ;; + i*86v32) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv32 + ;; + i*86v4*) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv4 + ;; + i*86v) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=sysv + ;; + i*86sol2) + cpu=`echo "$1" | sed -e 's/86.*/86/'` + vendor=pc + basic_os=solaris2 + ;; + j90 | j90-cray) + cpu=j90 + vendor=cray + basic_os=${basic_os:-unicos} + ;; + iris | iris4d) + cpu=mips + vendor=sgi + case $basic_os in + irix*) + ;; + *) + basic_os=irix4 + ;; + esac + ;; + miniframe) + cpu=m68000 + vendor=convergent + ;; + *mint | mint[0-9]* | *MiNT | *MiNT[0-9]*) + cpu=m68k + vendor=atari + basic_os=mint + ;; + news-3600 | risc-news) + cpu=mips + vendor=sony + basic_os=newsos + ;; + next | m*-next) + cpu=m68k + vendor=next + case $basic_os in + openstep*) + ;; + nextstep*) + ;; + ns2*) + basic_os=nextstep2 + ;; + *) + basic_os=nextstep3 + ;; + esac + ;; + np1) + cpu=np1 + vendor=gould + ;; + op50n-* | op60c-*) + cpu=hppa1.1 + vendor=oki + basic_os=proelf + ;; + pa-hitachi) + cpu=hppa1.1 + vendor=hitachi + basic_os=hiuxwe2 + ;; + pbd) + cpu=sparc + vendor=tti + ;; + pbb) + cpu=m68k + vendor=tti + ;; + pc532) + cpu=ns32k + vendor=pc532 + ;; + pn) + cpu=pn + vendor=gould + ;; + power) + cpu=power + vendor=ibm + ;; + ps2) + cpu=i386 + vendor=ibm + ;; + rm[46]00) + cpu=mips + vendor=siemens + ;; + rtpc | rtpc-*) + cpu=romp + vendor=ibm + ;; + sde) + cpu=mipsisa32 + vendor=sde + basic_os=${basic_os:-elf} + ;; + simso-wrs) + cpu=sparclite + vendor=wrs + basic_os=vxworks + ;; + tower | tower-32) + cpu=m68k + vendor=ncr + ;; + vpp*|vx|vx-*) + cpu=f301 + vendor=fujitsu + ;; + w65) + cpu=w65 + vendor=wdc + ;; + w89k-*) + cpu=hppa1.1 + vendor=winbond + basic_os=proelf + ;; + none) + cpu=none + vendor=none + ;; + leon|leon[3-9]) + cpu=sparc + vendor=$basic_machine + ;; + leon-*|leon[3-9]-*) + cpu=sparc + vendor=`echo "$basic_machine" | sed 's/-.*//'` + ;; + + *-*) + # shellcheck disable=SC2162 + saved_IFS=$IFS + IFS="-" read cpu vendor <&2 + exit 1 + ;; + esac + ;; +esac + +# Here we canonicalize certain aliases for manufacturers. +case $vendor in + digital*) + vendor=dec + ;; + commodore*) + vendor=cbm + ;; + *) + ;; +esac + +# Decode manufacturer-specific aliases for certain operating systems. + +if test x"$basic_os" != x +then + +# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just +# set os. +obj= +case $basic_os in + gnu/linux*) + kernel=linux + os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'` + ;; + os2-emx) + kernel=os2 + os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'` + ;; + nto-qnx*) + kernel=nto + os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'` + ;; + *-*) + # shellcheck disable=SC2162 + saved_IFS=$IFS + IFS="-" read kernel os <&2 + fi + ;; + *) + echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2 + exit 1 + ;; +esac + +case $obj in + aout* | coff* | elf* | pe*) + ;; + '') + # empty is fine + ;; + *) + echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2 + exit 1 + ;; +esac + +# Here we handle the constraint that a (synthetic) cpu and os are +# valid only in combination with each other and nowhere else. +case $cpu-$os in + # The "javascript-unknown-ghcjs" triple is used by GHC; we + # accept it here in order to tolerate that, but reject any + # variations. + javascript-ghcjs) + ;; + javascript-* | *-ghcjs) + echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2 + exit 1 + ;; +esac + +# As a final step for OS-related things, validate the OS-kernel combination +# (given a valid OS), if there is a kernel. +case $kernel-$os-$obj in + linux-gnu*- | linux-dietlibc*- | linux-android*- | linux-newlib*- \ + | linux-musl*- | linux-relibc*- | linux-uclibc*- | linux-mlibc*- ) + ;; + uclinux-uclibc*- ) + ;; + managarm-mlibc*- | managarm-kernel*- ) + ;; + windows*-msvc*-) + ;; + -dietlibc*- | -newlib*- | -musl*- | -relibc*- | -uclibc*- | -mlibc*- ) + # These are just libc implementations, not actual OSes, and thus + # require a kernel. + echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2 + exit 1 + ;; + -kernel*- ) + echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2 + exit 1 + ;; + *-kernel*- ) + echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2 + exit 1 + ;; + *-msvc*- ) + echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2 + exit 1 + ;; + kfreebsd*-gnu*- | kopensolaris*-gnu*-) + ;; + vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-) + ;; + nto-qnx*-) + ;; + os2-emx-) + ;; + *-eabi*- | *-gnueabi*-) + ;; + none--*) + # None (no kernel, i.e. freestanding / bare metal), + # can be paired with an machine code file format + ;; + -*-) + # Blank kernel with real OS is always fine. + ;; + --*) + # Blank kernel and OS with real machine code file format is always fine. + ;; + *-*-*) + echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2 + exit 1 + ;; +esac + +# Here we handle the case where we know the os, and the CPU type, but not the +# manufacturer. We pick the logical manufacturer. +case $vendor in + unknown) + case $cpu-$os in + *-riscix*) + vendor=acorn + ;; + *-sunos*) + vendor=sun + ;; + *-cnk* | *-aix*) + vendor=ibm + ;; + *-beos*) + vendor=be + ;; + *-hpux*) + vendor=hp + ;; + *-mpeix*) + vendor=hp + ;; + *-hiux*) + vendor=hitachi + ;; + *-unos*) + vendor=crds + ;; + *-dgux*) + vendor=dg + ;; + *-luna*) + vendor=omron + ;; + *-genix*) + vendor=ns + ;; + *-clix*) + vendor=intergraph + ;; + *-mvs* | *-opened*) + vendor=ibm + ;; + *-os400*) + vendor=ibm + ;; + s390-* | s390x-*) + vendor=ibm + ;; + *-ptx*) + vendor=sequent + ;; + *-tpf*) + vendor=ibm + ;; + *-vxsim* | *-vxworks* | *-windiss*) + vendor=wrs + ;; + *-aux*) + vendor=apple + ;; + *-hms*) + vendor=hitachi + ;; + *-mpw* | *-macos*) + vendor=apple + ;; + *-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*) + vendor=atari + ;; + *-vos*) + vendor=stratus + ;; + esac + ;; +esac + +echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}" +exit + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "timestamp='" +# time-stamp-format: "%:y-%02m-%02d" +# time-stamp-end: "'" +# End: ADDED build-aux/install-sh Index: build-aux/install-sh ================================================================== --- /dev/null +++ build-aux/install-sh @@ -0,0 +1,541 @@ +#!/bin/sh +# install - install a program, script, or datafile + +scriptversion=2023-11-23.18; # UTC + +# This originates from X11R5 (mit/util/scripts/install.sh), which was +# later released in X11R6 (xc/config/util/install.sh) with the +# following copyright and license. +# +# Copyright (C) 1994 X Consortium +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +# AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNEC- +# TION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# Except as contained in this notice, the name of the X Consortium shall not +# be used in advertising or otherwise to promote the sale, use or other deal- +# ings in this Software without prior written authorization from the X Consor- +# tium. +# +# +# FSF changes to this file are in the public domain. +# +# Calling this script install-sh is preferred over install.sh, to prevent +# 'make' implicit rules from creating a file called install from it +# when there is no Makefile. +# +# This script is compatible with the BSD install script, but was written +# from scratch. + +tab=' ' +nl=' +' +IFS=" $tab$nl" + +# Set DOITPROG to "echo" to test this script. + +doit=${DOITPROG-} +doit_exec=${doit:-exec} + +# Put in absolute file names if you don't have them in your path; +# or use environment vars. + +chgrpprog=${CHGRPPROG-chgrp} +chmodprog=${CHMODPROG-chmod} +chownprog=${CHOWNPROG-chown} +cmpprog=${CMPPROG-cmp} +cpprog=${CPPROG-cp} +mkdirprog=${MKDIRPROG-mkdir} +mvprog=${MVPROG-mv} +rmprog=${RMPROG-rm} +stripprog=${STRIPPROG-strip} + +posix_mkdir= + +# Desired mode of installed file. +mode=0755 + +# Create dirs (including intermediate dirs) using mode 755. +# This is like GNU 'install' as of coreutils 8.32 (2020). +mkdir_umask=22 + +backupsuffix= +chgrpcmd= +chmodcmd=$chmodprog +chowncmd= +mvcmd=$mvprog +rmcmd="$rmprog -f" +stripcmd= + +src= +dst= +dir_arg= +dst_arg= + +copy_on_change=false +is_target_a_directory=possibly + +usage="\ +Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE + or: $0 [OPTION]... SRCFILES... DIRECTORY + or: $0 [OPTION]... -t DIRECTORY SRCFILES... + or: $0 [OPTION]... -d DIRECTORIES... + +In the 1st form, copy SRCFILE to DSTFILE. +In the 2nd and 3rd, copy all SRCFILES to DIRECTORY. +In the 4th, create DIRECTORIES. + +Options: + --help display this help and exit. + --version display version info and exit. + + -c (ignored) + -C install only if different (preserve data modification time) + -d create directories instead of installing files. + -g GROUP $chgrpprog installed files to GROUP. + -m MODE $chmodprog installed files to MODE. + -o USER $chownprog installed files to USER. + -p pass -p to $cpprog. + -s $stripprog installed files. + -S SUFFIX attempt to back up existing files, with suffix SUFFIX. + -t DIRECTORY install into DIRECTORY. + -T report an error if DSTFILE is a directory. + +Environment variables override the default commands: + CHGRPPROG CHMODPROG CHOWNPROG CMPPROG CPPROG MKDIRPROG MVPROG + RMPROG STRIPPROG + +By default, rm is invoked with -f; when overridden with RMPROG, +it's up to you to specify -f if you want it. + +If -S is not specified, no backups are attempted. + +Report bugs to . +GNU Automake home page: . +General help using GNU software: ." + +while test $# -ne 0; do + case $1 in + -c) ;; + + -C) copy_on_change=true;; + + -d) dir_arg=true;; + + -g) chgrpcmd="$chgrpprog $2" + shift;; + + --help) echo "$usage"; exit $?;; + + -m) mode=$2 + case $mode in + *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; + + -o) chowncmd="$chownprog $2" + shift;; + + -p) cpprog="$cpprog -p";; + + -s) stripcmd=$stripprog;; + + -S) backupsuffix="$2" + shift;; + + -t) + is_target_a_directory=always + dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; + + -T) is_target_a_directory=never;; + + --version) echo "$0 $scriptversion"; exit $?;; + + --) shift + break;; + + -*) echo "$0: invalid option: $1" >&2 + exit 1;; + + *) break;; + esac + shift +done + +# We allow the use of options -d and -T together, by making -d +# take the precedence; this is for compatibility with GNU install. + +if test -n "$dir_arg"; then + if test -n "$dst_arg"; then + echo "$0: target directory not allowed when installing a directory." >&2 + exit 1 + fi +fi + +if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then + # When -d is used, all remaining arguments are directories to create. + # When -t is used, the destination is already specified. + # Otherwise, the last argument is the destination. Remove it from $@. + for arg + do + if test -n "$dst_arg"; then + # $@ is not empty: it contains at least $arg. + set fnord "$@" "$dst_arg" + shift # fnord + fi + shift # arg + dst_arg=$arg + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + done +fi + +if test $# -eq 0; then + if test -z "$dir_arg"; then + echo "$0: no input file specified." >&2 + exit 1 + fi + # It's OK to call 'install-sh -d' without argument. + # This can happen when creating conditional directories. + exit 0 +fi + +if test -z "$dir_arg"; then + if test $# -gt 1 || test "$is_target_a_directory" = always; then + if test ! -d "$dst_arg"; then + echo "$0: $dst_arg: Is not a directory." >&2 + exit 1 + fi + fi +fi + +if test -z "$dir_arg"; then + do_exit='(exit $ret); exit $ret' + trap "ret=129; $do_exit" 1 + trap "ret=130; $do_exit" 2 + trap "ret=141; $do_exit" 13 + trap "ret=143; $do_exit" 15 + + # Set umask so as not to create temps with too-generous modes. + # However, 'strip' requires both read and write access to temps. + case $mode in + # Optimize common cases. + *644) cp_umask=133;; + *755) cp_umask=22;; + + *[0-7]) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw='% 200' + fi + cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; + *) + if test -z "$stripcmd"; then + u_plus_rw= + else + u_plus_rw=,u+rw + fi + cp_umask=$mode$u_plus_rw;; + esac +fi + +for src +do + # Protect names problematic for 'test' and other utilities. + case $src in + -* | [=\(\)!]) src=./$src;; + esac + + if test -n "$dir_arg"; then + dst=$src + dstdir=$dst + test -d "$dstdir" + dstdir_status=$? + # Don't chown directories that already exist. + if test $dstdir_status = 0; then + chowncmd="" + fi + else + + # Waiting for this to be detected by the "$cpprog $src $dsttmp" command + # might cause directories to be created, which would be especially bad + # if $src (and thus $dsttmp) contains '*'. + if test ! -f "$src" && test ! -d "$src"; then + echo "$0: $src does not exist." >&2 + exit 1 + fi + + if test -z "$dst_arg"; then + echo "$0: no destination specified." >&2 + exit 1 + fi + dst=$dst_arg + + # If destination is a directory, append the input filename. + if test -d "$dst"; then + if test "$is_target_a_directory" = never; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 + fi + dstdir=$dst + dstbase=`basename "$src"` + case $dst in + */) dst=$dst$dstbase;; + *) dst=$dst/$dstbase;; + esac + dstdir_status=0 + else + dstdir=`dirname "$dst"` + test -d "$dstdir" + dstdir_status=$? + fi + fi + + case $dstdir in + */) dstdirslash=$dstdir;; + *) dstdirslash=$dstdir/;; + esac + + obsolete_mkdir_used=false + + if test $dstdir_status != 0; then + case $posix_mkdir in + '') + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi + + posix_mkdir=false + # The $RANDOM variable is not portable (e.g., dash). Use it + # here however when possible just to lower collision chance. + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + + trap ' + ret=$? + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" 2>/dev/null + exit $ret + ' 0 + + # Because "mkdir -p" follows existing symlinks and we likely work + # directly in world-writeable /tmp, make sure that the '$tmpdir' + # directory is successfully created first before we actually test + # 'mkdir -p'. + if (umask $mkdir_umask && + $mkdirprog $mkdir_mode "$tmpdir" && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/a/b") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + test_tmpdir="$tmpdir/a" + ls_ld_tmpdir=`ls -ld "$test_tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$test_tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$test_tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/a/b" "$tmpdir/a" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- "$tmpdir" 2>/dev/null + fi + trap '' 0;; + esac + + if + $posix_mkdir && ( + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + ) + then : + else + + # mkdir does not conform to POSIX, + # or it failed possibly due to a race condition. Create the + # directory the slow way, step by step, checking for races as we go. + + case $dstdir in + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; + esac + + oIFS=$IFS + IFS=/ + set -f + set fnord $dstdir + shift + set +f + IFS=$oIFS + + prefixes= + + for d + do + test X"$d" = X && continue + + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ + done + + if test -n "$prefixes"; then + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true + fi + fi + fi + + if test -n "$dir_arg"; then + { test -z "$chowncmd" || $doit $chowncmd "$dst"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dst"; } && + { test "$obsolete_mkdir_used$chowncmd$chgrpcmd" = false || + test -z "$chmodcmd" || $doit $chmodcmd $mode "$dst"; } || exit 1 + else + + # Make a couple of temp file names in the proper directory. + dsttmp=${dstdirslash}_inst.$$_ + rmtmp=${dstdirslash}_rm.$$_ + + # Trap to clean up those temp files at exit. + trap 'ret=$?; rm -f "$dsttmp" "$rmtmp" && exit $ret' 0 + + # Copy the file name to the temp name. + (umask $cp_umask && + { test -z "$stripcmd" || { + # Create $dsttmp read-write so that cp doesn't create it read-only, + # which would cause strip to fail. + if test -z "$doit"; then + : >"$dsttmp" # No need to fork-exec 'touch'. + else + $doit touch "$dsttmp" + fi + } + } && + $doit_exec $cpprog "$src" "$dsttmp") && + + # and set any options; do chmod last to preserve setuid bits. + # + # If any of these fail, we abort the whole thing. If we want to + # ignore errors from any of these, just make sure not to ignore + # errors from the above "$doit $cpprog $src $dsttmp" command. + # + { test -z "$chowncmd" || $doit $chowncmd "$dsttmp"; } && + { test -z "$chgrpcmd" || $doit $chgrpcmd "$dsttmp"; } && + { test -z "$stripcmd" || $doit $stripcmd "$dsttmp"; } && + { test -z "$chmodcmd" || $doit $chmodcmd $mode "$dsttmp"; } && + + # If -C, don't bother to copy if it wouldn't change the file. + if $copy_on_change && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + set -f && + set X $old && old=:$2:$4:$5:$6 && + set X $new && new=:$2:$4:$5:$6 && + set +f && + test "$old" = "$new" && + $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 + then + rm -f "$dsttmp" + else + # If $backupsuffix is set, and the file being installed + # already exists, attempt a backup. Don't worry if it fails, + # e.g., if mv doesn't support -f. + if test -n "$backupsuffix" && test -f "$dst"; then + $doit $mvcmd -f "$dst" "$dst$backupsuffix" 2>/dev/null + fi + + # Rename the file to the real destination. + $doit $mvcmd -f "$dsttmp" "$dst" 2>/dev/null || + + # The rename failed, perhaps because mv can't rename something else + # to itself, or perhaps because mv is so ancient that it does not + # support -f. + { + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && + + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" + } + fi || exit 1 + + trap '' 0 + fi +done + +# Local variables: +# eval: (add-hook 'before-save-hook 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC0" +# time-stamp-end: "; # UTC" +# End: ADDED build-aux/m4/buildsys.m4 Index: build-aux/m4/buildsys.m4 ================================================================== --- /dev/null +++ build-aux/m4/buildsys.m4 @@ -0,0 +1,393 @@ +dnl +dnl Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2016, 2017, +dnl 2018, 2020, 2021, 2022, 2023, 2024 +dnl Jonathan Schleifer +dnl +dnl https://fossil.nil.im/buildsys +dnl +dnl Permission to use, copy, modify, and/or distribute this software for any +dnl purpose with or without fee is hereby granted, provided that the above +dnl copyright notice and this permission notice is present in all copies. +dnl +dnl THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +dnl AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +dnl IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +dnl ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +dnl LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +dnl CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +dnl SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +dnl INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +dnl CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +dnl ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +dnl POSSIBILITY OF SUCH DAMAGE. +dnl + +AC_DEFUN([BUILDSYS_INIT], [ + AC_REQUIRE([AC_CANONICAL_BUILD]) + AC_REQUIRE([AC_CANONICAL_HOST]) + + AC_ARG_ENABLE(rpath, + AS_HELP_STRING([--disable-rpath], [do not use rpath])) + + AC_ARG_ENABLE(silent-rules, + AS_HELP_STRING([--disable-silent-rules], + [print executed commands during build])) + + case "$build_os" in + darwin*) + case "$host_os" in + darwin*) + AC_SUBST(BUILD_AND_HOST_ARE_DARWIN, yes) + ;; + esac + ;; + esac + + AC_PROG_INSTALL + case "$INSTALL" in + ./build-aux/install-sh*) + INSTALL="$PWD/$INSTALL" + ;; + esac + + AC_CONFIG_COMMANDS_PRE([ + AS_IF([test x"$GCC" = x"yes"], + [AC_SUBST(DEP_CFLAGS, '-MD -MF $${out%.o}.dep')]) + AS_IF([test x"$GXX" = x"yes"], + [AC_SUBST(DEP_CXXFLAGS, '-MD -MF $${out%.o}.dep')]) + AS_IF([test x"$GOBJC" = x"yes"], + [AC_SUBST(DEP_OBJCFLAGS, '-MD -MF $${out%.o}.dep')]) + AS_IF([test x"$GOBJCXX" = x"yes"], + [AC_SUBST(DEP_OBJCXXFLAGS, '-MD -MF $${out%.o}.dep')]) + + AC_SUBST(AMIGA_LIB_CFLAGS) + AC_SUBST(AMIGA_LIB_LDFLAGS) + + case "$build_os" in + morphos*) + dnl Don't use tput on MorphOS: The colored output is + dnl quite unreadable and in some MorphOS versions the + dnl output from tput is not 8-bit safe, with awk (for + dnl AC_SUBST) failing as a result. + ;; + *) + AC_PATH_PROG(TPUT, tput) + ;; + esac + + AS_IF([test x"$TPUT" != x""], [ + if x=$($TPUT el 2>/dev/null); then + AC_SUBST(TERM_EL, "$x") + else + AC_SUBST(TERM_EL, "$($TPUT ce 2>/dev/null)") + fi + + if x=$($TPUT sgr0 2>/dev/null); then + AC_SUBST(TERM_SGR0, "$x") + else + AC_SUBST(TERM_SGR0, "$($TPUT me 2>/dev/null)") + fi + + if x=$($TPUT bold 2>/dev/null); then + AC_SUBST(TERM_BOLD, "$x") + else + AC_SUBST(TERM_BOLD, "$($TPUT md 2>/dev/null)") + fi + + if x=$($TPUT setaf 1 2>/dev/null); then + AC_SUBST(TERM_SETAF1, "$x") + AC_SUBST(TERM_SETAF2, + "$($TPUT setaf 2 2>/dev/null)") + AC_SUBST(TERM_SETAF3, + "$($TPUT setaf 3 2>/dev/null)") + AC_SUBST(TERM_SETAF4, + "$($TPUT setaf 4 2>/dev/null)") + AC_SUBST(TERM_SETAF6, + "$($TPUT setaf 6 2>/dev/null)") + dnl OpenBSD seems to want 3 parameters for terminals + dnl ending in -256color, but the additional two + dnl parameters don't seem to do anything, so we set + dnl them to 0. + elif x=$($TPUT setaf 1 0 0 2>/dev/null); then + AC_SUBST(TERM_SETAF1, "$x") + AC_SUBST(TERM_SETAF2, + "$($TPUT setaf 2 0 0 2>/dev/null)") + AC_SUBST(TERM_SETAF3, + "$($TPUT setaf 3 0 0 2>/dev/null)") + AC_SUBST(TERM_SETAF4, + "$($TPUT setaf 4 0 0 2>/dev/null)") + AC_SUBST(TERM_SETAF6, + "$($TPUT setaf 6 0 0 2>/dev/null)") + else + AC_SUBST(TERM_SETAF1, + "$($TPUT AF 1 2>/dev/null)") + AC_SUBST(TERM_SETAF2, + "$($TPUT AF 2 2>/dev/null)") + AC_SUBST(TERM_SETAF3, + "$($TPUT AF 3 2>/dev/null)") + AC_SUBST(TERM_SETAF4, + "$($TPUT AF 4 2>/dev/null)") + AC_SUBST(TERM_SETAF6, + "$($TPUT AF 6 2>/dev/null)") + fi + ]) + + AS_IF([test x"$enable_silent_rules" != x"no"], [ + AC_SUBST(SILENT, '.SILENT:') + AC_SUBST(MAKEFLAGS_SILENT, '-s') + ]) + ]) +]) + +AC_DEFUN([BUILDSYS_CHECK_IOS], [ + case "$host_os" in + darwin*) + AC_MSG_CHECKING(whether host is iOS) + AC_EGREP_CPP(yes, [ + #include + + #if (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE) || \ + (defined(TARGET_OS_SIMULATOR) && \ + TARGET_OS_SIMULATOR) + yes + #endif + ], [ + host_is_ios="yes" + AC_SUBST(HOST_IS_IOS, yes) + ], [ + host_is_ios="no" + ]) + AC_MSG_RESULT($host_is_ios) + AC_CHECK_TOOL(CODESIGN, codesign) + ;; + esac +]) + +AC_DEFUN([BUILDSYS_PROG_IMPLIB], [ + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_MSG_CHECKING(whether we need an implib) + case "$host_os" in + cygwin* | mingw*) + AC_MSG_RESULT(yes) + PROG_IMPLIB_NEEDED='yes' + PROG_IMPLIB_LDFLAGS='-Wl,--export-all-symbols,--out-implib,lib${PROG}.a' + ;; + *) + AC_MSG_RESULT(no) + PROG_IMPLIB_NEEDED='no' + PROG_IMPLIB_LDFLAGS='' + ;; + esac + + AC_SUBST(PROG_IMPLIB_NEEDED) + AC_SUBST(PROG_IMPLIB_LDFLAGS) +]) + +AC_DEFUN([BUILDSYS_SHARED_LIB], [ + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_REQUIRE([BUILDSYS_CHECK_IOS]) + AC_MSG_CHECKING(for shared library type) + + case "$host" in + *-*-darwin*) + AC_MSG_RESULT(Darwin) + LIB_CFLAGS='-fPIC -DPIC' + LIB_LDFLAGS='-dynamiclib -current_version ${LIB_MAJOR}.${LIB_MINOR} -compatibility_version ${LIB_MAJOR}' + LIB_LDFLAGS_INSTALL_NAME='-Wl,-install_name,${libdir}/$${out%.dylib}.${LIB_MAJOR}.dylib' + LIB_PREFIX='lib' + LIB_SUFFIX='.dylib' + AS_IF([test x"$enable_rpath" != x"no"], [ + LDFLAGS_RPATH='-Wl,-rpath,${libdir}' + ]) + INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$${i%.dylib}.${LIB_MAJOR}.${LIB_MINOR}.dylib && ${LN_S} -f $${i%.dylib}.${LIB_MAJOR}.${LIB_MINOR}.dylib ${DESTDIR}${libdir}/$${i%.dylib}.${LIB_MAJOR}.dylib && ${LN_S} -f $${i%.dylib}.${LIB_MAJOR}.${LIB_MINOR}.dylib ${DESTDIR}${libdir}/$$i' + UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$${i%.dylib}.${LIB_MAJOR}.dylib ${DESTDIR}${libdir}/$${i%.dylib}.${LIB_MAJOR}.${LIB_MINOR}.dylib' + CLEAN_LIB='' + ;; + *-*-mingw* | *-*-cygwin*) + AC_MSG_RESULT(MinGW / Cygwin) + LIB_CFLAGS='' + LIB_LDFLAGS='-shared -Wl,--export-all-symbols' + LIB_LDFLAGS_INSTALL_NAME='' + LIB_PREFIX='' + LIB_SUFFIX='${LIB_MAJOR}.dll' + LINK_LIB='&& rm -f lib$${out%${LIB_SUFFIX}}.dll.a && ${LN_S} $$out lib$${out%${LIB_SUFFIX}}.dll.a' + INSTALL_LIB='&& ${MKDIR_P} ${DESTDIR}${bindir} && ${INSTALL} -m 755 $$i ${DESTDIR}${bindir}/$$i && ${INSTALL} -m 755 lib$${i%${LIB_SUFFIX}}.dll.a ${DESTDIR}${libdir}/lib$${i%${LIB_SUFFIX}}.dll.a' + UNINSTALL_LIB='&& rm -f ${DESTDIR}${bindir}/$$i ${DESTDIR}${libdir}/lib$${i%${LIB_SUFFIX}}.dll.a' + CLEAN_LIB='${SHARED_LIB}.a ${SHARED_LIB_NOINST}.a' + ;; + *-*-openbsd* | *-*-mirbsd*) + AC_MSG_RESULT(OpenBSD) + LIB_CFLAGS='-fPIC -DPIC' + LIB_LDFLAGS='-shared' + LIB_LDFLAGS_INSTALL_NAME='' + LIB_PREFIX='lib' + LIB_SUFFIX='.so.${LIB_MAJOR}.${LIB_MINOR}' + AS_IF([test x"$enable_rpath" != x"no"], [ + LDFLAGS_RPATH='-Wl,-rpath,${libdir}' + ]) + INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i' + UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i' + CLEAN_LIB='' + ;; + *-*-solaris*) + AC_MSG_RESULT(Solaris) + LIB_CFLAGS='-fPIC -DPIC' + LIB_LDFLAGS='-shared -Wl,-soname=$$out.${LIB_MAJOR}.${LIB_MINOR}' + LIB_LDFLAGS_INSTALL_NAME='' + LIB_PREFIX='lib' + LIB_SUFFIX='.so' + AS_IF([test x"$enable_rpath" != x"no"], [ + LDFLAGS_RPATH='-Wl,-rpath,${libdir}' + ]) + INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR} && rm -f ${DESTDIR}${libdir}/$$i && ${LN_S} $$i.${LIB_MAJOR}.${LIB_MINOR} ${DESTDIR}${libdir}/$$i' + UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR}' + CLEAN_LIB='' + ;; + *-*-android*) + AC_MSG_RESULT(Android) + LIB_CFLAGS='-fPIC -DPIC' + LIB_LDFLAGS='-shared -Wl,-soname=$$out.${LIB_MAJOR}' + LIB_LDFLAGS_INSTALL_NAME='' + LIB_PREFIX='lib' + LIB_SUFFIX='.so' + INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR}.${LIB_PATCH} && ${LN_S} -f $$i.${LIB_MAJOR}.${LIB_MINOR}.${LIB_PATCH} ${DESTDIR}${libdir}/$$i.${LIB_MAJOR} && ${LN_S} -f $$i.${LIB_MAJOR}.${LIB_MINOR}.${LIB_PATCH} ${DESTDIR}${libdir}/$$i' + UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR} ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR}.${LIB_PATCH}' + CLEAN_LIB='' + ;; + hppa*-*-hpux*) + AC_MSG_RESULT([HP-UX (PA-RISC)]) + LIB_CFLAGS='-fPIC -DPIC' + LIB_LDFLAGS='-shared -Wl,+h,$$out' + LIB_LDFLAGS_INSTALL_NAME='' + LIB_PREFIX='lib' + LIB_SUFFIX='.${LIB_MAJOR}' + LINK_LIB='&& rm -f $${out%%.*}.sl && ${LN_S} $$out $${out%%.*}.sl' + AS_IF([test x"$enable_rpath" != x"no"], [ + LDFLAGS_RPATH='-Wl,+b,${libdir}' + ]) + INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i && ${LN_S} -f $$i ${DESTDIR}${libdir}/$${i%%.*}.sl' + UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$${i%%.*}.sl' + CLEAN_LIB='' + ;; + ia64*-*-hpux*) + AC_MSG_RESULT([HP-UX (Itanium)]) + LIB_CFLAGS='-fPIC -DPIC' + LIB_LDFLAGS='-shared -Wl,+h,$$out' + LIB_LDFLAGS_INSTALL_NAME='' + LIB_PREFIX='lib' + LIB_SUFFIX='.${LIB_MAJOR}' + LINK_LIB='&& rm -f $${out%%.*}.so && ${LN_S} $$out $${out%%.*}.so' + AS_IF([test x"$enable_rpath" != x"no"], [ + LDFLAGS_RPATH='-Wl,+b,${libdir}' + ]) + INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i && ${LN_S} -f $$i ${DESTDIR}${libdir}/$${i%%.*}.so' + UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$${i%%.*}.so' + CLEAN_LIB='' + ;; + *) + AC_MSG_RESULT(ELF) + LIB_CFLAGS='-fPIC -DPIC' + LIB_LDFLAGS='-shared -Wl,-soname=$$out.${LIB_MAJOR}' + LIB_LDFLAGS_INSTALL_NAME='' + LIB_PREFIX='lib' + LIB_SUFFIX='.so' + AS_IF([test x"$enable_rpath" != x"no"], [ + LDFLAGS_RPATH='-Wl,-rpath,${libdir}' + ]) + INSTALL_LIB='&& ${INSTALL} -m 755 $$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR}.${LIB_PATCH} && ${LN_S} -f $$i.${LIB_MAJOR}.${LIB_MINOR}.${LIB_PATCH} ${DESTDIR}${libdir}/$$i.${LIB_MAJOR} && ${LN_S} -f $$i.${LIB_MAJOR}.${LIB_MINOR}.${LIB_PATCH} ${DESTDIR}${libdir}/$$i' + UNINSTALL_LIB='&& rm -f ${DESTDIR}${libdir}/$$i ${DESTDIR}${libdir}/$$i.${LIB_MAJOR} ${DESTDIR}${libdir}/$$i.${LIB_MAJOR}.${LIB_MINOR}.${LIB_PATCH}' + CLEAN_LIB='' + ;; + esac + + AC_SUBST(LIB_CFLAGS) + AC_SUBST(LIB_LDFLAGS) + AC_SUBST(LIB_LDFLAGS_INSTALL_NAME) + AC_SUBST(LIB_PREFIX) + AC_SUBST(LIB_SUFFIX) + AC_SUBST(LINK_LIB) + AC_SUBST(LDFLAGS_RPATH) + AC_SUBST(INSTALL_LIB) + AC_SUBST(UNINSTALL_LIB) + AC_SUBST(CLEAN_LIB) +]) + +AC_DEFUN([BUILDSYS_FRAMEWORK], [ + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_REQUIRE([BUILDSYS_CHECK_IOS]) + AC_REQUIRE([BUILDSYS_SHARED_LIB]) + + case "$host_os" in + darwin*) + FRAMEWORK_LDFLAGS='-dynamiclib -current_version ${LIB_MAJOR}.${LIB_MINOR} -compatibility_version ${LIB_MAJOR}' + AS_IF([test x"$host_is_ios" = x"yes"], [ + FRAMEWORK_LDFLAGS_INSTALL_NAME='-Wl,-install_name,@executable_path/Frameworks/$$out/$${out%.framework}' + ], [ + FRAMEWORK_LDFLAGS_INSTALL_NAME='-Wl,-install_name,@executable_path/../Frameworks/$$out/$${out%.framework}' + ]) + + AC_SUBST(FRAMEWORK_LDFLAGS) + AC_SUBST(FRAMEWORK_LDFLAGS_INSTALL_NAME) + AC_SUBST(FRAMEWORK_LIBS) + + $1 + ;; + esac +]) + +AC_DEFUN([BUILDSYS_PLUGIN], [ + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_REQUIRE([BUILDSYS_CHECK_IOS]) + AC_MSG_CHECKING(for plugin type) + + case "$host" in + *-*-darwin*) + AC_MSG_RESULT(Darwin) + PLUGIN_CFLAGS='-fPIC -DPIC' + PLUGIN_LDFLAGS='-bundle' + PLUGIN_SUFFIX='.dylib' + ;; + *-*-mingw* | *-*-cygwin*) + AC_MSG_RESULT(MinGW / Cygwin) + PLUGIN_CFLAGS='' + PLUGIN_LDFLAGS='-shared -Wl,--export-all-symbols' + PLUGIN_SUFFIX='.dll' + ;; + hppa*-*-hpux*) + AC_MSG_RESULT([HP-UX (PA-RISC)]) + PLUGIN_CFLAGS='-fPIC -DPIC' + PLUGIN_LDFLAGS='-shared' + PLUGIN_SUFFIX='.sl' + ;; + *) + AC_MSG_RESULT(ELF) + PLUGIN_CFLAGS='-fPIC -DPIC' + PLUGIN_LDFLAGS='-shared' + PLUGIN_SUFFIX='.so' + ;; + esac + + AC_SUBST(PLUGIN_CFLAGS) + AC_SUBST(PLUGIN_LDFLAGS) + AC_SUBST(PLUGIN_SUFFIX) +]) + +AC_DEFUN([BUILDSYS_BUNDLE], [ + AC_REQUIRE([AC_CANONICAL_HOST]) + AC_REQUIRE([BUILDSYS_CHECK_IOS]) + AC_REQUIRE([BUILDSYS_PLUGIN]) + + case "$host_os" in + darwin*) + AS_IF([test x"$host_is_ios" = x"yes"], [ + LINK_BUNDLE='${MKDIR_P} $$out && ${INSTALL} -m 644 Info.plist $$out/Info.plist && ${LD} -o $$out/$${out%.bundle} ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS}' + ], [ + LINK_BUNDLE='${MKDIR_P} $$out/Contents/MacOS && ${INSTALL} -m 644 Info.plist $$out/Contents/Info.plist && ${LD} -o $$out/Contents/MacOS/$${out%.bundle} ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS}' + ]) + + AC_SUBST(LINK_BUNDLE) + + $1 + ;; + esac +]) ADDED build-aux/m4/pkg.m4 Index: build-aux/m4/pkg.m4 ================================================================== --- /dev/null +++ build-aux/m4/pkg.m4 @@ -0,0 +1,343 @@ +# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*- +# serial 11 (pkg-config-0.29.1) + +dnl Copyright © 2004 Scott James Remnant . +dnl Copyright © 2012-2015 Dan Nicholson +dnl +dnl This program is free software; you can redistribute it and/or modify +dnl it under the terms of the GNU General Public License as published by +dnl the Free Software Foundation; either version 2 of the License, or +dnl (at your option) any later version. +dnl +dnl This program is distributed in the hope that it will be useful, but +dnl WITHOUT ANY WARRANTY; without even the implied warranty of +dnl MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +dnl General Public License for more details. +dnl +dnl You should have received a copy of the GNU General Public License +dnl along with this program; if not, write to the Free Software +dnl Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA +dnl 02111-1307, USA. +dnl +dnl As a special exception to the GNU General Public License, if you +dnl distribute this file as part of a program that contains a +dnl configuration script generated by Autoconf, you may include it under +dnl the same distribution terms that you use for the rest of that +dnl program. + +dnl PKG_PREREQ(MIN-VERSION) +dnl ----------------------- +dnl Since: 0.29 +dnl +dnl Verify that the version of the pkg-config macros are at least +dnl MIN-VERSION. Unlike PKG_PROG_PKG_CONFIG, which checks the user's +dnl installed version of pkg-config, this checks the developer's version +dnl of pkg.m4 when generating configure. +dnl +dnl To ensure that this macro is defined, also add: +dnl m4_ifndef([PKG_PREREQ], +dnl [m4_fatal([must install pkg-config 0.29 or later before running autoconf/autogen])]) +dnl +dnl See the "Since" comment for each macro you use to see what version +dnl of the macros you require. +m4_defun([PKG_PREREQ], +[m4_define([PKG_MACROS_VERSION], [0.29.1]) +m4_if(m4_version_compare(PKG_MACROS_VERSION, [$1]), -1, + [m4_fatal([pkg.m4 version $1 or higher is required but ]PKG_MACROS_VERSION[ found])]) +])dnl PKG_PREREQ + +dnl PKG_PROG_PKG_CONFIG([MIN-VERSION]) +dnl ---------------------------------- +dnl Since: 0.16 +dnl +dnl Search for the pkg-config tool and set the PKG_CONFIG variable to +dnl first found in the path. Checks that the version of pkg-config found +dnl is at least MIN-VERSION. If MIN-VERSION is not specified, 0.9.0 is +dnl used since that's the first version where most current features of +dnl pkg-config existed. +AC_DEFUN([PKG_PROG_PKG_CONFIG], +[m4_pattern_forbid([^_?PKG_[A-Z_]+$]) +m4_pattern_allow([^PKG_CONFIG(_(PATH|LIBDIR|SYSROOT_DIR|ALLOW_SYSTEM_(CFLAGS|LIBS)))?$]) +m4_pattern_allow([^PKG_CONFIG_(DISABLE_UNINSTALLED|TOP_BUILD_DIR|DEBUG_SPEW)$]) +AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility]) +AC_ARG_VAR([PKG_CONFIG_PATH], [directories to add to pkg-config's search path]) +AC_ARG_VAR([PKG_CONFIG_LIBDIR], [path overriding pkg-config's built-in search path]) + +if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then + AC_PATH_TOOL([PKG_CONFIG], [pkg-config]) +fi +if test -n "$PKG_CONFIG"; then + _pkg_min_version=m4_default([$1], [0.9.0]) + AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version]) + if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then + AC_MSG_RESULT([yes]) + else + AC_MSG_RESULT([no]) + PKG_CONFIG="" + fi +fi[]dnl +])dnl PKG_PROG_PKG_CONFIG + +dnl PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ------------------------------------------------------------------- +dnl Since: 0.18 +dnl +dnl Check to see whether a particular set of modules exists. Similar to +dnl PKG_CHECK_MODULES(), but does not set variables or print errors. +dnl +dnl Please remember that m4 expands AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +dnl only at the first occurence in configure.ac, so if the first place +dnl it's called might be skipped (such as if it is within an "if", you +dnl have to call PKG_CHECK_EXISTS manually +AC_DEFUN([PKG_CHECK_EXISTS], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +if test -n "$PKG_CONFIG" && \ + AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then + m4_default([$2], [:]) +m4_ifvaln([$3], [else + $3])dnl +fi]) + +dnl _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES]) +dnl --------------------------------------------- +dnl Internal wrapper calling pkg-config via PKG_CONFIG and setting +dnl pkg_failed based on the result. +m4_define([_PKG_CONFIG], +[if test -n "$$1"; then + pkg_cv_[]$1="$$1" + elif test -n "$PKG_CONFIG"; then + PKG_CHECK_EXISTS([$3], + [pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null` + test "x$?" != "x0" && pkg_failed=yes ], + [pkg_failed=yes]) + else + pkg_failed=untried +fi[]dnl +])dnl _PKG_CONFIG + +dnl _PKG_SHORT_ERRORS_SUPPORTED +dnl --------------------------- +dnl Internal check to see if pkg-config supports short errors. +AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG]) +if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then + _pkg_short_errors_supported=yes +else + _pkg_short_errors_supported=no +fi[]dnl +])dnl _PKG_SHORT_ERRORS_SUPPORTED + + +dnl PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +dnl [ACTION-IF-NOT-FOUND]) +dnl -------------------------------------------------------------- +dnl Since: 0.4.0 +dnl +dnl Note that if there is a possibility the first call to +dnl PKG_CHECK_MODULES might not happen, you should be sure to include an +dnl explicit call to PKG_PROG_PKG_CONFIG in your configure.ac +AC_DEFUN([PKG_CHECK_MODULES], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl +AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl + +pkg_failed=no +AC_MSG_CHECKING([for $1]) + +_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2]) +_PKG_CONFIG([$1][_LIBS], [libs], [$2]) + +m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS +and $1[]_LIBS to avoid the need to call pkg-config. +See the pkg-config man page for more details.]) + +if test $pkg_failed = yes; then + AC_MSG_RESULT([no]) + _PKG_SHORT_ERRORS_SUPPORTED + if test $_pkg_short_errors_supported = yes; then + $1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --print-errors --cflags --libs "$2" 2>&1` + else + $1[]_PKG_ERRORS=`$PKG_CONFIG --print-errors --cflags --libs "$2" 2>&1` + fi + # Put the nasty error message in config.log where it belongs + echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD + + m4_default([$4], [AC_MSG_ERROR( +[Package requirements ($2) were not met: + +$$1_PKG_ERRORS + +Consider adjusting the PKG_CONFIG_PATH environment variable if you +installed software in a non-standard prefix. + +_PKG_TEXT])[]dnl + ]) +elif test $pkg_failed = untried; then + AC_MSG_RESULT([no]) + m4_default([$4], [AC_MSG_FAILURE( +[The pkg-config script could not be found or is too old. Make sure it +is in your PATH or set the PKG_CONFIG environment variable to the full +path to pkg-config. + +_PKG_TEXT + +To get pkg-config, see .])[]dnl + ]) +else + $1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS + $1[]_LIBS=$pkg_cv_[]$1[]_LIBS + AC_MSG_RESULT([yes]) + $3 +fi[]dnl +])dnl PKG_CHECK_MODULES + + +dnl PKG_CHECK_MODULES_STATIC(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND], +dnl [ACTION-IF-NOT-FOUND]) +dnl --------------------------------------------------------------------- +dnl Since: 0.29 +dnl +dnl Checks for existence of MODULES and gathers its build flags with +dnl static libraries enabled. Sets VARIABLE-PREFIX_CFLAGS from --cflags +dnl and VARIABLE-PREFIX_LIBS from --libs. +dnl +dnl Note that if there is a possibility the first call to +dnl PKG_CHECK_MODULES_STATIC might not happen, you should be sure to +dnl include an explicit call to PKG_PROG_PKG_CONFIG in your +dnl configure.ac. +AC_DEFUN([PKG_CHECK_MODULES_STATIC], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +_save_PKG_CONFIG=$PKG_CONFIG +PKG_CONFIG="$PKG_CONFIG --static" +PKG_CHECK_MODULES($@) +PKG_CONFIG=$_save_PKG_CONFIG[]dnl +])dnl PKG_CHECK_MODULES_STATIC + + +dnl PKG_INSTALLDIR([DIRECTORY]) +dnl ------------------------- +dnl Since: 0.27 +dnl +dnl Substitutes the variable pkgconfigdir as the location where a module +dnl should install pkg-config .pc files. By default the directory is +dnl $libdir/pkgconfig, but the default can be changed by passing +dnl DIRECTORY. The user can override through the --with-pkgconfigdir +dnl parameter. +AC_DEFUN([PKG_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${libdir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([pkgconfigdir], + [AS_HELP_STRING([--with-pkgconfigdir], pkg_description)],, + [with_pkgconfigdir=]pkg_default) +AC_SUBST([pkgconfigdir], [$with_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +])dnl PKG_INSTALLDIR + + +dnl PKG_NOARCH_INSTALLDIR([DIRECTORY]) +dnl -------------------------------- +dnl Since: 0.27 +dnl +dnl Substitutes the variable noarch_pkgconfigdir as the location where a +dnl module should install arch-independent pkg-config .pc files. By +dnl default the directory is $datadir/pkgconfig, but the default can be +dnl changed by passing DIRECTORY. The user can override through the +dnl --with-noarch-pkgconfigdir parameter. +AC_DEFUN([PKG_NOARCH_INSTALLDIR], +[m4_pushdef([pkg_default], [m4_default([$1], ['${datadir}/pkgconfig'])]) +m4_pushdef([pkg_description], + [pkg-config arch-independent installation directory @<:@]pkg_default[@:>@]) +AC_ARG_WITH([noarch-pkgconfigdir], + [AS_HELP_STRING([--with-noarch-pkgconfigdir], pkg_description)],, + [with_noarch_pkgconfigdir=]pkg_default) +AC_SUBST([noarch_pkgconfigdir], [$with_noarch_pkgconfigdir]) +m4_popdef([pkg_default]) +m4_popdef([pkg_description]) +])dnl PKG_NOARCH_INSTALLDIR + + +dnl PKG_CHECK_VAR(VARIABLE, MODULE, CONFIG-VARIABLE, +dnl [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND]) +dnl ------------------------------------------- +dnl Since: 0.28 +dnl +dnl Retrieves the value of the pkg-config variable for the given module. +AC_DEFUN([PKG_CHECK_VAR], +[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl +AC_ARG_VAR([$1], [value of $3 for $2, overriding pkg-config])dnl + +_PKG_CONFIG([$1], [variable="][$3]["], [$2]) +AS_VAR_COPY([$1], [pkg_cv_][$1]) + +AS_VAR_IF([$1], [""], [$5], [$4])dnl +])dnl PKG_CHECK_VAR + +dnl PKG_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [ACTION-IF-FOUND],[ACTION-IF-NOT-FOUND], +dnl [DESCRIPTION], [DEFAULT]) +dnl ------------------------------------------ +dnl +dnl Prepare a "--with-" configure option using the lowercase +dnl [VARIABLE-PREFIX] name, merging the behaviour of AC_ARG_WITH and +dnl PKG_CHECK_MODULES in a single macro. +AC_DEFUN([PKG_WITH_MODULES], +[ +m4_pushdef([with_arg], m4_tolower([$1])) + +m4_pushdef([description], + [m4_default([$5], [build with ]with_arg[ support])]) + +m4_pushdef([def_arg], [m4_default([$6], [auto])]) +m4_pushdef([def_action_if_found], [AS_TR_SH([with_]with_arg)=yes]) +m4_pushdef([def_action_if_not_found], [AS_TR_SH([with_]with_arg)=no]) + +m4_case(def_arg, + [yes],[m4_pushdef([with_without], [--without-]with_arg)], + [m4_pushdef([with_without],[--with-]with_arg)]) + +AC_ARG_WITH(with_arg, + AS_HELP_STRING(with_without, description[ @<:@default=]def_arg[@:>@]),, + [AS_TR_SH([with_]with_arg)=def_arg]) + +AS_CASE([$AS_TR_SH([with_]with_arg)], + [yes],[PKG_CHECK_MODULES([$1],[$2],$3,$4)], + [auto],[PKG_CHECK_MODULES([$1],[$2], + [m4_n([def_action_if_found]) $3], + [m4_n([def_action_if_not_found]) $4])]) + +m4_popdef([with_arg]) +m4_popdef([description]) +m4_popdef([def_arg]) + +])dnl PKG_WITH_MODULES + +dnl PKG_HAVE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [DESCRIPTION], [DEFAULT]) +dnl ----------------------------------------------- +dnl +dnl Convenience macro to trigger AM_CONDITIONAL after PKG_WITH_MODULES +dnl check._[VARIABLE-PREFIX] is exported as make variable. +AC_DEFUN([PKG_HAVE_WITH_MODULES], +[ +PKG_WITH_MODULES([$1],[$2],,,[$3],[$4]) + +AM_CONDITIONAL([HAVE_][$1], + [test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"]) +])dnl PKG_HAVE_WITH_MODULES + +dnl PKG_HAVE_DEFINE_WITH_MODULES(VARIABLE-PREFIX, MODULES, +dnl [DESCRIPTION], [DEFAULT]) +dnl ------------------------------------------------------ +dnl +dnl Convenience macro to run AM_CONDITIONAL and AC_DEFINE after +dnl PKG_WITH_MODULES check. HAVE_[VARIABLE-PREFIX] is exported as make +dnl and preprocessor variable. +AC_DEFUN([PKG_HAVE_DEFINE_WITH_MODULES], +[ +PKG_HAVE_WITH_MODULES([$1],[$2],[$3],[$4]) + +AS_IF([test "$AS_TR_SH([with_]m4_tolower([$1]))" = "yes"], + [AC_DEFINE([HAVE_][$1], 1, [Enable ]m4_tolower([$1])[ support])]) +])dnl PKG_HAVE_DEFINE_WITH_MODULES ADDED buildsys.mk.in Index: buildsys.mk.in ================================================================== --- /dev/null +++ buildsys.mk.in @@ -0,0 +1,1130 @@ +# +# Copyright (c) 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, +# 2017, 2018, 2020, 2021, 2022, 2023, 2024 +# Jonathan Schleifer +# +# https://fossil.nil.im/buildsys +# +# Permission to use, copy, modify, and/or distribute this software for any +# purpose with or without fee is hereby granted, provided that the above +# copyright notice and this permission notice is present in all copies. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +# POSSIBILITY OF SUCH DAMAGE. +# + +PACKAGE_NAME = @PACKAGE_NAME@ +PACKAGE_VERSION = @PACKAGE_VERSION@ +AS = @AS@ +CC = @CC@ +CXX = @CXX@ +CPP = @CPP@ +DC = @DC@ +ERLC = @ERLC@ +OBJC = @OBJC@ +OBJCXX = @OBJCXX@ +AR = @AR@ +LD = ${CC} +RANLIB = @RANLIB@ +PYTHON = @PYTHON@ +ASFLAGS = @ASFLAGS@ +CFLAGS = @CFLAGS@ +CXXFLAGS = @CXXFLAGS@ +CPPFLAGS = @CPPFLAGS@ +DFLAGS = @DFLAGS@ +ERLCFLAGS = @ERLCFLAGS@ +OBJCFLAGS = @OBJCFLAGS@ +OBJCXXFLAGS = @OBJCXXFLAGS@ +LDFLAGS = @LDFLAGS@ +LDFLAGS_RPATH = @LDFLAGS_RPATH@ +LIBS = @LIBS@ +PYTHON_FLAGS = @PYTHON_FLAGS@ +PROG_IMPLIB_NEEDED = @PROG_IMPLIB_NEEDED@ +PROG_IMPLIB_LDFLAGS = @PROG_IMPLIB_LDFLAGS@ +PROG_SUFFIX = @EXEEXT@ +LIB_CFLAGS = @LIB_CFLAGS@ +LIB_LDFLAGS = @LIB_LDFLAGS@ +LIB_LDFLAGS_INSTALL_NAME = @LIB_LDFLAGS_INSTALL_NAME@ +LIB_PREFIX = @LIB_PREFIX@ +LIB_SUFFIX = @LIB_SUFFIX@ +LINK_LIB = @LINK_LIB@ +AMIGA_LIB_CFLAGS = @AMIGA_LIB_CFLAGS@ +AMIGA_LIB_LDFLAGS = @AMIGA_LIB_LDFLAGS@ +PLUGIN_CFLAGS = @PLUGIN_CFLAGS@ +PLUGIN_LDFLAGS = @PLUGIN_LDFLAGS@ +PLUGIN_SUFFIX = @PLUGIN_SUFFIX@ +FRAMEWORK_LDFLAGS = @FRAMEWORK_LDFLAGS@ +FRAMEWORK_LDFLAGS_INSTALL_NAME = @FRAMEWORK_LDFLAGS_INSTALL_NAME@ +FRAMEWORK_LIBS = @FRAMEWORK_LIBS@ +CODESIGN = @CODESIGN@ +CODESIGN_IDENTITY ?= - +CLEAN_LIB = @CLEAN_LIB@ +DEP_ASFLAGS = @DEP_ASFLAGS@ +DEP_CFLAGS = @DEP_CFLAGS@ +DEP_CXXFLAGS = @DEP_CXXFLAGS@ +DEP_OBJCFLAGS = @DEP_OBJCFLAGS@ +DEP_OBJCXXFLAGS = @DEP_OBJCXXFLAGS@ +LN_S = @LN_S@ +MKDIR_P = mkdir -p +INSTALL = @INSTALL@ +SHELL = @SHELL@ +MSGFMT = @MSGFMT@ +JAVAC = @JAVAC@ +JAVACFLAGS = @JAVACFLAGS@ +JAR = @JAR@ +RC = @RC@ +BUILD_AND_HOST_ARE_DARWIN = @BUILD_AND_HOST_ARE_DARWIN@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@ +amigalibdir ?= ${prefix}/libs +plugindir ?= ${libdir}/${PACKAGE_NAME} +bundledir ?= ${prefix}/Library/PlugIns/${PACKAGE_NAME} +datarootdir = @datarootdir@ +datadir = @datadir@ +includedir = @includedir@ +includesubdir ?= ${PACKAGE_NAME} +INSTALL_INCLUDES ?= yes +localedir = @localedir@ +localename ?= ${PACKAGE_NAME} +mandir = @mandir@ +mansubdir ?= man1 + +OBJS1 = ${SRCS:.c=.o} +OBJS2 = ${OBJS1:.cc=.o} +OBJS3 = ${OBJS2:.cxx=.o} +OBJS4 = ${OBJS3:.d=.o} +OBJS5 = ${OBJS4:.erl=.beam} +OBJS6 = ${OBJS5:.java=.class} +OBJS7 = ${OBJS6:.m=.o} +OBJS8 = ${OBJS7:.mm=.o} +OBJS9 = ${OBJS8:.py=.pyc} +OBJS10 = ${OBJS9:.rc=.o} +OBJS11 = ${OBJS10:.S=.o} +OBJS += ${OBJS11:.xpm=.o} + +LIB_OBJS = ${OBJS:.o=.lib.o} +AMIGA_LIB_OBJS = ${OBJS:.o=.amigalib.o} +PLUGIN_OBJS = ${OBJS:.o=.plugin.o} + +DEPS = ${OBJS:.o=.dep} \ + ${LIB_OBJS:.o=.dep} \ + ${AMIGA_LIB_OBJS:.o=.dep} \ + ${PLUGIN_OBJS:.o=.dep} + +MO_FILES = ${LOCALES:.po=.mo} + +@SILENT@ +.SUFFIXES: +.SUFFIXES: .amigalib.o .beam .c .cc .class .cxx .d .erl .lib.o .java \ + .mo .m .mm .o .plugin.o .po .py .pyc .rc .S .xpm +.PHONY: all subdirs subdirs-after pre-depend depend install \ + install-extra uninstall uninstall-extra clean distclean locales \ + copy-headers-into-framework ${SUBDIRS} ${SUBDIRS_AFTER} + +all: + ${MAKE} @MAKEFLAGS_SILENT@ pre-all + ${MAKE} @MAKEFLAGS_SILENT@ subdirs + ${MAKE} @MAKEFLAGS_SILENT@ depend + ${MAKE} @MAKEFLAGS_SILENT@ \ + ${STATIC_LIB} ${STATIC_LIB_NOINST} \ + ${STATIC_PIC_LIB} ${STATIC_PIC_LIB_NOINST} \ + ${STATIC_AMIGA_LIB}${STATIC_AMIGA_LIB_NOINST} \ + ${SHARED_LIB} ${SHARED_LIB_NOINST} \ + ${FRAMEWORK} ${FRAMEWORK_NOINST} \ + ${AMIGA_LIB} ${AMIGA_LIB_NOINST} \ + ${PLUGIN} ${PLUGIN_NOINST} \ + ${BUNDLE} ${BUNDLE_NOINST} \ + ${PROG} ${PROG_NOINST} \ + ${JARFILE} locales + ${MAKE} @MAKEFLAGS_SILENT@ subdirs-after + ${MAKE} @MAKEFLAGS_SILENT@ post-all + +pre-all post-all: + +subdirs: ${SUBDIRS} +subdirs-after: ${SUBDIRS_AFTER} + +${SUBDIRS} ${SUBDIRS_AFTER}: + for i in $@; do \ + ${DIR_ENTER}; \ + ${MAKE} @MAKEFLAGS_SILENT@ || exit $$?; \ + ${DIR_LEAVE}; \ + done + +depend: pre-depend + : >.deps + for i in "" ${DEPS}; do \ + test x"$$i" = x"" && continue; \ + echo "-include \$${.CURDIR}/$$i" >>.deps; \ + done + +pre-depend: + +${PROG} ${PROG_NOINST}: ${EXT_DEPS} ${OBJS} ${OBJS_EXTRA} + ${LINK_STATUS} + out="$@"; \ + if ${LD} -o $@ ${OBJS} ${OBJS_EXTRA} ${LDFLAGS} ${LIBS}; then \ + ${LINK_OK}; \ + else \ + ${LINK_FAILED}; \ + fi + +${JARFILE}: ${EXT_DEPS} ${JAR_MANIFEST} ${OBJS} ${OBJS_EXTRA} + ${LINK_STATUS} + if test x"${JAR_MANIFEST}" != x""; then \ + if ${JAR} cfm ${JARFILE} ${JAR_MANIFEST} ${OBJS} \ + ${OBJS_EXTRA}; then \ + ${LINK_OK}; \ + else \ + ${LINK_FAILED}; \ + fi \ + else \ + if ${JAR} cf ${JARFILE} ${OBJS} ${OBJS_EXTRA}; then \ + ${LINK_OK}; \ + else \ + ${LINK_FAILED}; \ + fi \ + fi + +${SHARED_LIB} ${SHARED_LIB_NOINST}: ${EXT_DEPS} ${LIB_OBJS} ${LIB_OBJS_EXTRA} + ${LINK_STATUS} + out="$@"; \ + if ${LD} -o $@ ${LIB_OBJS} ${LIB_OBJS_EXTRA} ${LIB_LDFLAGS} \ + ${LIB_LDFLAGS_INSTALL_NAME} ${LDFLAGS} ${LIBS} ${LINK_LIB}; then \ + ${LINK_OK}; \ + else \ + ${LINK_FAILED}; \ + fi + +${FRAMEWORK} ${FRAMEWORK_NOINST}: ${EXT_DEPS} ${LIB_OBJS} ${LIB_OBJS_EXTRA} + ${LINK_STATUS} + out="$@"; \ + if test x"@HOST_IS_IOS@" = x"yes"; then \ + if rm -fr $@ && \ + ${MAKE} @MAKEFLAGS_SILENT@ \ + COPY_HEADERS_IF_SUBDIR=${includesubdir} \ + COPY_HEADERS_DESTINATION=$$PWD/$@/Headers \ + copy-headers-into-framework && \ + ${INSTALL} -m 644 Info.plist $@/Info.plist && \ + if test -f module.modulemap; then \ + ${MKDIR_P} $@/Modules && \ + ${INSTALL} -m 644 module.modulemap \ + $@/Modules/module.modulemap; \ + fi && \ + ${LD} -o $@/$${out%.framework} \ + ${LIB_OBJS} ${LIB_OBJS_EXTRA} ${FRAMEWORK_LDFLAGS} \ + ${FRAMEWORK_LDFLAGS_INSTALL_NAME} ${LDFLAGS} \ + ${FRAMEWORK_LIBS} && \ + ${CODESIGN} -fs ${CODESIGN_IDENTITY} $@; then \ + ${LINK_OK}; \ + else \ + rm -fr $$out; false; \ + ${LINK_FAILED}; \ + fi; \ + else \ + versiondir="$@/Versions/${LIB_MAJOR}"; \ + if rm -fr $@ && \ + ${MKDIR_P} $$versiondir && \ + ${LN_S} ${LIB_MAJOR} $@/Versions/Current && \ + ${MAKE} @MAKEFLAGS_SILENT@ \ + COPY_HEADERS_IF_SUBDIR=${includesubdir} \ + COPY_HEADERS_DESTINATION=$$PWD/$$versiondir/Headers \ + copy-headers-into-framework && \ + ${LN_S} Versions/Current/Headers $@/Headers && \ + if test -f Info.plist; then \ + ${MKDIR_P} $$versiondir/Resources && \ + ${INSTALL} -m 644 Info.plist \ + $$versiondir/Resources/Info.plist && \ + ${LN_S} Versions/Current/Resources $@/Resources; \ + fi && \ + if test -f module.modulemap; then \ + ${MKDIR_P} $$versiondir/Modules && \ + ${INSTALL} -m 644 module.modulemap \ + $$versiondir/Modules/module.modulemap && \ + ${LN_S} Versions/Current/Modules $@/Modules; \ + fi && \ + ${LD} -o $$versiondir/$${out%.framework} \ + ${LIB_OBJS} ${LIB_OBJS_EXTRA} ${FRAMEWORK_LDFLAGS} \ + ${FRAMEWORK_LDFLAGS_INSTALL_NAME} ${LDFLAGS} \ + ${FRAMEWORK_LIBS} && \ + ${LN_S} Versions/Current/$${out%.framework} \ + $@/$${out%.framework} && \ + ${CODESIGN} -fs ${CODESIGN_IDENTITY} $@; then \ + ${LINK_OK}; \ + else \ + rm -fr $$out; false; \ + ${LINK_FAILED}; \ + fi; \ + fi + +copy-headers-into-framework: + for i in "" ${SUBDIRS} ${SUBDIRS_AFTER}; do \ + test x"$$i" = x"" && continue; \ + cd $$i || exit 1; \ + ${MAKE} @MAKEFLAGS_SILENT@ copy-headers-into-framework || \ + exit $$?; \ + cd .. || exit 1; \ + done + + if test x"${includesubdir}" = x"${COPY_HEADERS_IF_SUBDIR}"; then \ + for i in "" ${INCLUDES}; do \ + test x"$$i" = x"" && continue; \ + ${MKDIR_P} \ + $$(dirname ${COPY_HEADERS_DESTINATION}/$$i) || \ + exit $$?; \ + ${INSTALL} -m 644 $$i \ + ${COPY_HEADERS_DESTINATION}/$$i || exit $$?; \ + done \ + fi + +${AMIGA_LIB} ${AMIGA_LIB_NOINST}: ${EXT_DEPS} ${AMIGA_LIB_OBJS_START} \ + ${AMIGA_LIB_OBJS} ${AMIGA_LIB_OBJS_EXTRA} + ${LINK_STATUS} + if ${LD} -o $@ ${AMIGA_LIB_OBJS_START} ${AMIGA_LIB_OBJS} \ + ${AMIGA_LIB_OBJS_EXTRA} ${AMIGA_LIB_LDFLAGS} \ + ${AMIGA_LIB_LIBS}; then \ + ${LINK_OK}; \ + else \ + ${LINK_FAILED}; \ + fi + +${PLUGIN} ${PLUGIN_NOINST}: ${EXT_DEPS} ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} + ${LINK_STATUS} + out="$@"; \ + if ${LD} -o $$out ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} ${PLUGIN_LDFLAGS} ${LDFLAGS} ${LIBS}; then \ + ${LINK_OK}; \ + else \ + rm -fr $$out; false; \ + ${LINK_FAILED}; \ + fi + +${BUNDLE} ${BUNDLE_NOINST}: ${EXT_DEPS} ${PLUGIN_OBJS} ${PLUGIN_OBJS_EXTRA} + ${LINK_STATUS} + out="$@"; \ + if rm -fr $$out && @LINK_BUNDLE@ && ${CODESIGN} -fs ${CODESIGN_IDENTITY} $$out; then \ + ${LINK_OK}; \ + else \ + rm -fr $$out; false; \ + ${LINK_FAILED}; \ + fi + +${STATIC_LIB} ${STATIC_LIB_NOINST}: ${EXT_DEPS} ${OBJS} ${OBJS_EXTRA} + ${LINK_STATUS} + rm -f $@ + if test x"${BUILD_AND_HOST_ARE_DARWIN}" = x"yes"; then \ + if /usr/bin/libtool -static -o $@ ${OBJS} ${OBJS_EXTRA}; then \ + ${LINK_OK}; \ + else \ + rm -f $@; false; \ + ${LINK_FAILED}; \ + fi; \ + else \ + out="$@"; \ + objs=""; \ + ars=""; \ + for i in ${OBJS} ${OBJS_EXTRA}; do \ + case $$i in \ + *.a) \ + ars="$$ars $$i" \ + ;; \ + *.o) \ + objs="$$objs $$i" \ + ;; \ + esac \ + done; \ + for i in $$ars; do \ + dir=".$$(echo $$i | sed 's/\//_/g').objs"; \ + rm -fr $$dir; \ + mkdir -p $$dir; \ + cd $$dir; \ + ${AR} x ../$$i; \ + for j in *.o; do \ + objs="$$objs $$dir/$$j"; \ + done; \ + cd ..; \ + done; \ + if ${AR} cr $@ $$objs && ${RANLIB} $@; then \ + ${LINK_OK}; \ + else \ + rm -f $@; false; \ + ${LINK_FAILED}; \ + fi; \ + for i in $$ars; do \ + dir=".$$(echo $$i | sed 's/\//_/g').objs"; \ + rm -fr $$dir; \ + done; \ + fi + +${STATIC_PIC_LIB} ${STATIC_PIC_LIB_NOINST}: ${EXT_DEPS} ${LIB_OBJS} \ + ${LIB_OBJS_EXTRA} + ${LINK_STATUS} + rm -f $@ + if test x"${BUILD_AND_HOST_ARE_DARWIN}" = x"yes"; then \ + if /usr/bin/libtool -static -o $@ ${LIB_OBJS} \ + ${LIB_OBJS_EXTRA}; then \ + ${LINK_OK}; \ + else \ + rm -f $@; false; \ + ${LINK_FAILED}; \ + fi; \ + else \ + out="$@"; \ + objs=""; \ + ars=""; \ + for i in ${LIB_OBJS} ${LIB_OBJS_EXTRA}; do \ + case $$i in \ + *.a) \ + ars="$$ars $$i" \ + ;; \ + *.o) \ + objs="$$objs $$i" \ + ;; \ + esac \ + done; \ + for i in $$ars; do \ + dir=".$$(echo $$i | sed 's/\//_/g').objs"; \ + rm -fr $$dir; \ + mkdir -p $$dir; \ + cd $$dir; \ + ${AR} x ../$$i; \ + for j in *.o; do \ + objs="$$objs $$dir/$$j"; \ + done; \ + cd ..; \ + done; \ + if ${AR} cr $@ $$objs && ${RANLIB} $@; then \ + ${LINK_OK}; \ + else \ + rm -f $@; false; \ + ${LINK_FAILED}; \ + fi; \ + for i in $$ars; do \ + dir=".$$(echo $$i | sed 's/\//_/g').objs"; \ + rm -fr $$dir; \ + done; \ + fi + +${STATIC_AMIGA_LIB} ${STATIC_AMIGA_LIB_NOINST}: ${EXT_DEPS} ${AMIGA_LIB_OBJS} \ + ${AMIGA_LIB_OBJS_EXTRA} + ${LINK_STATUS} + rm -f $@ + out="$@"; \ + objs=""; \ + ars=""; \ + for i in ${AMIGA_LIB_OBJS} ${AMIGA_LIB_OBJS_EXTRA}; do \ + case $$i in \ + *.a) \ + ars="$$ars $$i" \ + ;; \ + *.o) \ + objs="$$objs $$i" \ + ;; \ + esac \ + done; \ + for i in $$ars; do \ + dir=".$$(echo $$i | sed 's/\//_/g').objs"; \ + rm -fr $$dir; \ + mkdir -p $$dir; \ + cd $$dir; \ + ${AR} x ../$$i; \ + for j in *.o; do \ + objs="$$objs $$dir/$$j"; \ + done; \ + cd ..; \ + done; \ + if ${AR} cr $@ $$objs && ${RANLIB} $@; then \ + ${LINK_OK}; \ + else \ + rm -f $@; false; \ + ${LINK_FAILED}; \ + fi; \ + for i in $$ars; do \ + dir=".$$(echo $$i | sed 's/\//_/g').objs"; \ + rm -fr $$dir; \ + done + +locales: ${MO_FILES} + +.c.o: + ${COMPILE_STATUS} + in="$<"; \ + out="$@"; \ + if ${CC} ${CFLAGS} ${CPPFLAGS} ${CFLAGS_$<} ${CFLAGS_$@} ${DEP_CFLAGS} \ + -c -o $@ $<; then \ + ${COMPILE_OK}; \ + else \ + ${COMPILE_FAILED}; \ + fi +.c.lib.o: + ${COMPILE_LIB_STATUS} + in="$<"; \ + out="$@"; \ + if ${CC} ${LIB_CFLAGS} ${CFLAGS} ${CPPFLAGS} ${CFLAGS_$<} ${CFLAGS_$@} \ + ${DEP_CFLAGS} -c -o $@ $<; then \ + ${COMPILE_LIB_OK}; \ + else \ + ${COMPILE_LIB_FAILED}; \ + fi +.c.amigalib.o: + ${COMPILE_AMIGA_LIB_STATUS} + in="$<"; \ + out="$@"; \ + if ${CC} ${AMIGA_LIB_CFLAGS} ${CFLAGS} ${CPPFLAGS} ${CFLAGS_$<} \ + ${CFLAGS_$@} ${DEP_CFLAGS} -c -o $@ $<; then \ + ${COMPILE_AMIGA_LIB_OK}; \ + else \ + ${COMPILE_AMIGA_LIB_FAILED}; \ + fi +.c.plugin.o: + ${COMPILE_PLUGIN_STATUS} + in="$<"; \ + out="$@"; \ + if ${CC} ${PLUGIN_CFLAGS} ${CFLAGS} ${CPPFLAGS} ${CFLAGS_$<} \ + ${CFLAGS_$@} ${DEP_CFLAGS} -c -o $@ $<; then \ + ${COMPILE_PLUGIN_OK}; \ + else \ + ${COMPILE_PLUGIN_FAILED}; \ + fi + +.cc.o .cxx.o: + ${COMPILE_STATUS} + in="$<"; \ + out="$@"; \ + if ${CXX} ${CXXFLAGS} ${CPPFLAGS} ${CXXFLAGS_$<} ${CXXFLAGS_$@} \ + ${DEP_CXXFLAGS} -c -o $@ $<; then \ + ${COMPILE_OK}; \ + else \ + ${COMPILE_FAILED}; \ + fi +.cc.lib.o .cxx.lib.o: + ${COMPILE_LIB_STATUS} + in="$<"; \ + out="$@"; \ + if ${CXX} ${LIB_CFLAGS} ${CXXFLAGS} ${CPPFLAGS} ${CXXFLAGS_$<} \ + ${CXXFLAGS_$@} ${DEP_CXXFLAGS} -c -o $@ $<; then \ + ${COMPILE_LIB_OK}; \ + else \ + ${COMPILE_LIB_FAILED}; \ + fi +.cc.amigalib.o .cxx.amigalib.o: + ${COMPILE_AMIGA_LIB_STATUS} + in="$<"; \ + out="$@"; \ + if ${CXX} ${AMIGA_LIB_CFLAGS} ${CXXFLAGS} ${CPPFLAGS} ${CXXFLAGS_$<} \ + ${CXXFLAGS_$@} ${DEP_CXXFLAGS} -c -o $@ $<; then \ + ${COMPILE_AMIGA_LIB_OK}; \ + else \ + ${COMPILE_AMIGA_LIB_FAILED}; \ + fi +.cc.plugin.o .cxx.plugin.o: + ${COMPILE_PLUGIN_STATUS} + in="$<"; \ + out="$@"; \ + if ${CXX} ${PLUGIN_CFLAGS} ${CXXFLAGS} ${CPPFLAGS} ${CXXFLAGS_$<} \ + ${CXXFLAGS_$@} ${DEP_CXXFLAGS} -c -o $@ $<; then \ + ${COMPILE_PLUGIN_OK}; \ + else \ + ${COMPILE_PLUGIN_FAILED}; \ + fi + +.d.o: + ${COMPILE_STATUS} + in="$<"; \ + out="$@"; \ + if test x"$(basename ${DC})" = x"dmd"; then \ + if ${DC} ${DFLAGS} -c -of$@ $<; then \ + ${COMPILE_OK}; \ + else \ + ${COMPILE_FAILED}; \ + fi \ + else \ + if ${DC} ${DFLAGS} -c -o $@ $<; then \ + ${COMPILE_OK}; \ + else \ + ${COMPILE_FAILED}; \ + fi \ + fi + +.erl.beam: + ${COMPILE_STATUS} + in="$<"; \ + out="$@"; \ + if ${ERLC} ${ERLCFLAGS} -o $@ $<; then \ + ${COMPILE_OK}; \ + else \ + ${COMPILE_FAILED}; \ + fi + +.java.class: + ${COMPILE_STATUS} + in="$<"; \ + out="$@"; \ + if ${JAVAC} ${JAVACFLAGS} $<; then \ + ${COMPILE_OK}; \ + else \ + ${COMPILE_FAILED}; \ + fi + +.m.o: + ${COMPILE_STATUS} + in="$<"; \ + out="$@"; \ + if ${OBJC} ${OBJCFLAGS} ${CPPFLAGS} ${OBJCFLAGS_$<} ${OBJCFLAGS_$@} \ + ${DEP_OBJCFLAGS} -c -o $@ $<; then \ + ${COMPILE_OK}; \ + else \ + ${COMPILE_FAILED}; \ + fi +.m.lib.o: + ${COMPILE_LIB_STATUS} + in="$<"; \ + out="$@"; \ + if ${OBJC} ${LIB_CFLAGS} ${OBJCFLAGS} ${CPPFLAGS} ${OBJCFLAGS_$<} \ + ${OBJCFLAGS_$@} ${DEP_OBJCFLAGS} -c -o $@ $<; then \ + ${COMPILE_LIB_OK}; \ + else \ + ${COMPILE_LIB_FAILED}; \ + fi +.m.amigalib.o: + ${COMPILE_AMIGA_LIB_STATUS} + in="$<"; \ + out="$@"; \ + if ${OBJC} ${AMIGA_LIB_CFLAGS} ${OBJCFLAGS} ${CPPFLAGS} \ + ${OBJCFLAGS_$<} ${OBJCFLAGS_$@} ${DEP_OBJCFLAGS} -c -o $@ $<; then \ + ${COMPILE_AMIGA_LIB_OK}; \ + else \ + ${COMPILE_AMIGA_LIB_FAILED}; \ + fi +.m.plugin.o: + ${COMPILE_PLUGIN_STATUS} + in="$<"; \ + out="$@"; \ + if ${OBJC} ${PLUGIN_CFLAGS} ${OBJCFLAGS} ${CPPFLAGS} ${OBJCFLAGS_$<} \ + ${OBJCFLAGS_$@} ${DEP_OBJCFLAGS} -c -o $@ $<; then \ + ${COMPILE_PLUGIN_OK}; \ + else \ + ${COMPILE_PLUGIN_FAILED}; \ + fi + +.mm.o: + ${COMPILE_STATUS} + in="$<"; \ + out="$@"; \ + if ${OBJCXX} ${OBJCXXFLAGS} ${CPPFLAGS} ${OBJCXXFLAGS_$<} \ + ${OBJCXXFLAGS_$@} ${DEP_OBJCXXFLAGS} -c -o $@ $<; then \ + ${COMPILE_OK}; \ + else \ + ${COMPILE_FAILED}; \ + fi +.mm.lib.o: + ${COMPILE_LIB_STATUS} + in="$<"; \ + out="$@"; \ + if ${OBJCXX} ${LIB_CFLAGS} ${OBJCXXFLAGS} ${CPPFLAGS} \ + ${OBJCXXFLAGS_$<} ${OBJCXXFLAGS_$@} ${DEP_OBJCXXFLAGS} -c -o $@ \ + $<; then \ + ${COMPILE_LIB_OK}; \ + else \ + ${COMPILE_LIB_FAILED}; \ + fi +.mm.amigalib.o: + ${COMPILE_AMIGA_LIB_STATUS} + in="$<"; \ + out="$@"; \ + if ${OBJCXX} ${AMIGA_LIB_CFLAGS} ${OBJCXXFLAGS} ${CPPFLAGS} \ + ${OBJCXXFLAGS_$<} ${OBJCXXFLAGS_$@} ${DEP_OBJCXXFLAGS} -c -o $@ \ + $<; then \ + ${COMPILE_AMIGA_LIB_OK}; \ + else \ + ${COMPILE_AMIGA_LIB_FAILED}; \ + fi +.mm.plugin.o: + ${COMPILE_PLUGIN_STATUS} + in="$<"; \ + out="$@"; \ + if ${OBJCXX} ${PLUGIN_CFLAGS} ${OBJCXXFLAGS} ${CPPFLAGS} \ + ${OBJCXXFLAGS_$<} ${OBJCXXFLAGS_$@} ${DEP_OBJCXXFLAGS} -c -o $@ \ + $<; then \ + ${COMPILE_PLUGIN_OK}; \ + else \ + ${COMPILE_PLUGIN_FAILED}; \ + fi + +.po.mo: + ${COMPILE_STATUS} + in="$<"; \ + out="$@"; \ + if ${MSGFMT} -c -o $@ $<; then \ + ${COMPILE_OK}; \ + else \ + ${COMPILE_FAILED}; \ + fi + +.py.pyc: + ${COMPILE_STATUS} + in="$<"; \ + out="$@"; \ + if ${PYTHON} ${PYTHON_FLAGS} -c \ + "import py_compile; py_compile.compile('$<')"; then \ + ${COMPILE_OK}; \ + else \ + ${COMPILE_FAILED}; \ + fi + +.rc.o .rc.lib.o .rc.plugin.o: + ${COMPILE_STATUS} + in="$<"; \ + out="$@"; \ + if ${RC} ${RCFLAGS} ${CPPFLAGS} -J rc -O coff -o $@ $<; then \ + ${COMPILE_OK}; \ + else \ + ${COMPILE_FAILED}; \ + fi + +.S.o .S.amigalib.o: + ${COMPILE_STATUS} + in="$<"; \ + out="$@"; \ + if ${AS} ${ASFLAGS} ${CPPFLAGS} ${ASFLAGS_$<} ${ASFLAGS_$@} \ + ${DEP_ASFLAGS} -c -o $@ $<; then \ + ${COMPILE_OK}; \ + else \ + ${COMPILE_FAILED}; \ + fi +.S.lib.o: + ${COMPILE_LIB_STATUS} + in="$<"; \ + out="$@"; \ + if ${AS} ${LIB_CFLAGS} ${ASFLAGS} ${CPPFLAGS} ${ASFLAGS_$<} \ + ${ASFLAGS_$@} ${DEP_ASFLAGS} -c -o $@ $<; then \ + ${COMPILE_LIB_OK}; \ + else \ + ${COMPILE_LIB_FAILED}; \ + fi +.S.plugin.o: + ${COMPILE_PLUGIN_STATUS} + in="$<"; \ + out="$@"; \ + if ${AS} ${PLUGIN_CFLAGS} ${ASFLAGS} ${CPPFLAGS} ${ASFLAGS_$<} \ + ${ASFLAGS_$@} ${DEP_ASFLAGS} -c -o $@ $<; then \ + ${COMPILE_PLUGIN_OK}; \ + else \ + ${COMPILE_PLUGIN_FAILED}; \ + fi + +.xpm.o: + ${COMPILE_STATUS} + in="$<"; \ + out="$@"; \ + if ${CC} ${CFLAGS} ${CPPFLAGS} ${CFLAGS_$<} ${CFLAGS_$@} -x c -c -o $@ \ + $<; then \ + ${COMPILE_OK}; \ + else \ + ${COMPILE_FAILED}; \ + fi +.xpm.lib.o: + ${COMPILE_LIB_STATUS} + in="$<"; \ + out="$@"; \ + if ${CC} ${LIB_CFLAGS} ${CFLAGS} ${CPPFLAGS} ${CFLAGS_$<} ${CFLAGS_$@} \ + -x c -c -o $@ $<; then \ + ${COMPILE_LIB_OK}; \ + else \ + ${COMPILE_LIB_FAILED}; \ + fi +.xpm.amigalib.o: + ${COMPILE_AMIGA_LIB_STATUS} + in="$<"; \ + out="$@"; \ + if ${CC} ${AMIGA_LIB_CFLAGS} ${CFLAGS} ${CPPFLAGS} ${CFLAGS_$<} \ + ${CFLAGS_$@} -x c -c -o $@ $<; then \ + ${COMPILE_AMIGA_LIB_OK}; \ + else \ + ${COMPILE_AMIGA_LIB_FAILED}; \ + fi +.xpm.plugin.o: + ${COMPILE_PLUGIN_STATUS} + in="$<"; \ + out="$@"; \ + if ${CC} ${PLUGIN_CFLAGS} ${CFLAGS} ${CPPFLAGS} ${CFLAGS_$<} \ + ${CFLAGS_$@} -x c -c -o $@ $<; then \ + ${COMPILE_PLUGIN_OK}; \ + else \ + ${COMPILE_PLUGIN_FAILED}; \ + fi + +install: all install-extra + for i in "" ${SUBDIRS} ${SUBDIRS_AFTER}; do \ + test x"$$i" = x"" && continue; \ + ${DIR_ENTER}; \ + ${MAKE} @MAKEFLAGS_SILENT@ install || exit $$?; \ + ${DIR_LEAVE}; \ + done + + for i in "" ${SHARED_LIB}; do \ + test x"$$i" = x"" && continue; \ + ${INSTALL_STATUS}; \ + if ${MKDIR_P} ${DESTDIR}${libdir} @INSTALL_LIB@; then \ + ${INSTALL_OK}; \ + else \ + ${INSTALL_FAILED}; \ + fi \ + done + + for i in "" ${FRAMEWORK}; do \ + test x"$$i" = x"" && continue; \ + ${INSTALL_STATUS}; \ + rm -fr ${DESTDIR}${prefix}/Library/Frameworks/$$i; \ + if ${MKDIR_P} ${DESTDIR}${prefix}/Library/Frameworks && \ + cp -R $$i ${DESTDIR}${prefix}/Library/Frameworks/; then \ + ${INSTALL_OK}; \ + else \ + ${INSTALL_FAILED}; \ + fi \ + done + + for i in "" ${AMIGA_LIB}; do \ + test x"$$i" = x"" && continue; \ + ${INSTALL_STATUS}; \ + if ${MKDIR_P} ${DESTDIR}${amigalibdir} && \ + ${INSTALL} -m 755 $$i ${DESTDIR}${amigalibdir}/$$i; then \ + ${INSTALL_OK}; \ + else \ + ${INSTALL_FAILED}; \ + fi \ + done + + for i in "" ${STATIC_LIB} ${STATIC_PIC_LIB} ${STATIC_AMIGA_LIB}; do \ + test x"$$i" = x"" && continue; \ + ${INSTALL_STATUS}; \ + if ${MKDIR_P} ${DESTDIR}${libdir} && \ + ${INSTALL} -m 644 $$i ${DESTDIR}${libdir}/$$i; then \ + ${INSTALL_OK}; \ + else \ + ${INSTALL_FAILED}; \ + fi \ + done + + for i in "" ${PLUGIN}; do \ + test x"$$i" = x"" && continue; \ + ${INSTALL_STATUS}; \ + if ${MKDIR_P} ${DESTDIR}${plugindir} && ${INSTALL} -m 755 $$i ${DESTDIR}${plugindir}/$$i; then \ + ${INSTALL_OK}; \ + else \ + ${INSTALL_FAILED}; \ + fi \ + done + + for i in "" ${BUNDLE}; do \ + test x"$$i" = x"" && continue; \ + ${INSTALL_STATUS}; \ + rm -fr ${DESTDIR}${bundledir}/$$i; \ + if ${MKDIR_P} ${DESTDIR}${bundledir} && \ + cp -R $$i ${DESTDIR}${bundledir}/; then \ + ${INSTALL_OK}; \ + else \ + ${INSTALL_FAILED}; \ + fi \ + done + + for i in "" ${DATA}; do \ + test x"$$i" = x"" && continue; \ + ${INSTALL_STATUS}; \ + if ${MKDIR_P} $$(dirname \ + ${DESTDIR}${datadir}/${PACKAGE_NAME}/$$i) && \ + ${INSTALL} -m 644 $$i \ + ${DESTDIR}${datadir}/${PACKAGE_NAME}/$$i; then \ + ${INSTALL_OK}; \ + else \ + ${INSTALL_FAILED}; \ + fi \ + done + + for i in "" ${PROG}; do \ + test x"$$i" = x"" && continue; \ + ${INSTALL_STATUS}; \ + if ${MKDIR_P} ${DESTDIR}${bindir} && \ + ${INSTALL} -m 755 $$i ${DESTDIR}${bindir}/$$i; then \ + ${INSTALL_OK}; \ + else \ + ${INSTALL_FAILED}; \ + fi \ + done + + if test x"${INSTALL_INCLUDES}" = x"yes"; then \ + for i in "" ${INCLUDES}; do \ + test x"$$i" = x"" && continue; \ + ${INSTALL_STATUS}; \ + if ${MKDIR_P} $$(dirname \ + ${DESTDIR}${includedir}/${includesubdir}/$$i) && \ + ${INSTALL} -m 644 $$i \ + ${DESTDIR}${includedir}/${includesubdir}/$$i; then \ + ${INSTALL_OK}; \ + else \ + ${INSTALL_FAILED}; \ + fi \ + done \ + fi + + for i in "" ${MO_FILES}; do \ + test x"$$i" = x"" && continue; \ + ${INSTALL_STATUS}; \ + dest="${localedir}/$${i%.mo}/LC_MESSAGES/${localename}.mo"; \ + dest="${DESTDIR}$$dest"; \ + if ${MKDIR_P} ${DESTDIR}${localedir}/$${i%.mo}/LC_MESSAGES && \ + ${INSTALL} -m 644 $$i $$dest; then \ + ${INSTALL_OK}; \ + else \ + ${INSTALL_FAILED}; \ + fi \ + done + + for i in "" ${MAN}; do \ + test x"$$i" = x"" && continue; \ + ${INSTALL_STATUS}; \ + dest="${DESTDIR}${mandir}/${mansubdir}/$$i"; \ + if ${MKDIR_P} ${DESTDIR}${mandir}/${mansubdir} && \ + ${INSTALL} -m 644 $$i $$dest; then \ + ${INSTALL_OK}; \ + else \ + ${INSTALL_FAILED}; \ + fi \ + done + +install-extra: + +uninstall: + for i in "" ${SUBDIRS} ${SUBDIRS_AFTER}; do \ + test x"$$i" = x"" && continue; \ + ${DIR_ENTER}; \ + ${MAKE} @MAKEFLAGS_SILENT@ uninstall || exit $$?; \ + ${DIR_LEAVE}; \ + done + + for i in "" ${SHARED_LIB}; do \ + test x"$$i" = x"" && continue; \ + if test -f ${DESTDIR}${libdir}/$$i \ + -o -f ${DESTDIR}${bindir}/$$i; then \ + if : @UNINSTALL_LIB@; then \ + ${DELETE_OK}; \ + else \ + ${DELETE_FAILED}; \ + fi \ + fi; \ + done + + for i in "" ${FRAMEWORK}; do \ + test x"$$i" = x"" && continue; \ + if test -d ${DESTDIR}${prefix}/Library/Frameworks/$$i; then \ + if rm -fr ${DESTDIR}${prefix}/Library/Frameworks/$$i; \ + then \ + ${DELETE_OK}; \ + else \ + ${DELETE_FAILED}; \ + fi \ + fi \ + done + rmdir ${DESTDIR}${prefix}/Library/Frameworks >/dev/null 2>&1 || true + rmdir ${DESTDIR}${prefix}/Library >/dev/null 2>&1 || true + + for i in "" ${STATIC_LIB} ${STATIC_PIC_LIB} ${STATIC_AMIGA_LIB}; do \ + test x"$$i" = x"" && continue; \ + if test -f ${DESTDIR}${libdir}/$$i; then \ + if rm -f ${DESTDIR}${libdir}/$$i; then \ + ${DELETE_OK}; \ + else \ + ${DELETE_FAILED}; \ + fi \ + fi \ + done + + for i in "" ${PLUGIN}; do \ + test x"$$i" = x"" && continue; \ + if test -e ${DESTDIR}${plugindir}/$$i; then \ + if rm -f ${DESTDIR}${plugindir}/$$i; then \ + ${DELETE_OK}; \ + else \ + ${DELETE_FAILED}; \ + fi \ + fi \ + done + rmdir ${DESTDIR}${plugindir} >/dev/null 2>&1 || true + + for i in "" ${BUNDLE}; do \ + test x"$$i" = x"" && continue; \ + if test -d ${DESTDIR}${bundledir}/$$i; then \ + if rm -fr ${DESTDIR}${bundledir}/$$i; \ + then \ + ${DELETE_OK}; \ + else \ + ${DELETE_FAILED}; \ + fi \ + fi \ + done + rmdir ${DESTDIR}${bundledir} >/dev/null 2>&1 || true + rmdir ${DESTDIR}${prefix}/Library >/dev/null 2>&1 || true + + for i in "" ${DATA}; do \ + test x"$$i" = x"" && continue; \ + if test -f ${DESTDIR}${datadir}/${PACKAGE_NAME}/$$i; then \ + if rm -f ${DESTDIR}${datadir}/${PACKAGE_NAME}/$$i; \ + then \ + ${DELETE_OK}; \ + else \ + ${DELETE_FAILED}; \ + fi \ + fi; \ + rmdir "$$(dirname ${DESTDIR}${datadir}/${PACKAGE_NAME}/$$i)" \ + >/dev/null 2>&1 || true; \ + done + rmdir ${DESTDIR}${datadir}/${PACKAGE_NAME} >/dev/null 2>&1 || true + + for i in "" ${PROG}; do \ + test x"$$i" = x"" && continue; \ + if test -f ${DESTDIR}${bindir}/$$i; then \ + if rm -f ${DESTDIR}${bindir}/$$i; then \ + ${DELETE_OK}; \ + else \ + ${DELETE_FAILED}; \ + fi \ + fi \ + done + + for i in "" ${INCLUDES}; do \ + test x"$$i" = x"" && continue; \ + if test -f ${DESTDIR}${includedir}/${includesubdir}/$$i; then \ + if rm -f ${DESTDIR}${includedir}/${includesubdir}/$$i; \ + then \ + ${DELETE_OK}; \ + else \ + ${DELETE_FAILED}; \ + fi \ + fi \ + done + rmdir ${DESTDIR}${includedir}/${includesubdir} >/dev/null 2>&1 || true + + for i in "" ${MO_FILES}; do \ + test x"$$i" = x"" && continue; \ + mo="${localedir}/$${i%.mo}/LC_MESSAGES/${localename}.mo"; \ + mo="${DESTDIR}$$mo"; \ + if test -f $$mo; then \ + if rm -f $$mo; then \ + ${DELETE_OK}; \ + else \ + ${DELETE_FAILED}; \ + fi \ + fi \ + done + + for i in "" ${MAN}; do \ + test x"$$i" = x"" && continue; \ + if test -f ${DESTDIR}${mandir}/${mansubdir}/$$i; then \ + if rm -f ${DESTDIR}${mandir}/${mansubdir}/$$i; then \ + ${DELETE_OK}; \ + else \ + ${DELETE_FAILED}; \ + fi \ + fi \ + done + + ${MAKE} @MAKEFLAGS_SILENT@ uninstall-extra + +uninstall-extra: + +clean: + for i in "" ${SUBDIRS} ${SUBDIRS_AFTER}; do \ + test x"$$i" = x"" && continue; \ + ${DIR_ENTER}; \ + ${MAKE} @MAKEFLAGS_SILENT@ clean || exit $$?; \ + ${DIR_LEAVE}; \ + done + + : >.deps + + for i in "" ${DEPS} ${OBJS} ${OBJS_EXTRA} ${LIB_OBJS} \ + ${LIB_OBJS_EXTRA} ${AMIGA_LIB_OBJS} ${AMIGA_LIB_OBJS_START} \ + ${AMIGA_LIB_OBJS_EXTRA} ${PLUGIN_OBJS} ${PROG} ${PROG_NOINST} \ + ${SHARED_LIB} ${SHARED_LIB_NOINST} ${AMIGA_LIB} \ + ${AMIGA_LIB_NOINST} ${STATIC_LIB} ${STATIC_LIB_NOINST} \ + ${STATIC_PIC_LIB} ${STATIC_PIC_LIB_NOINST} ${STATIC_AMIGA_LIB} \ + ${STATIC_AMIGA_LIB_NOINST} ${FRAMEWORK} ${FRAMEWORK_NOINST} \ + ${PLUGIN} ${PLUGIN_NOINST} ${BUNDLE} ${BUNDLE_NOINST} \ + ${CLEAN_LIB} ${MO_FILES} ${CLEAN}; do \ + test x"$$i" = x"" && continue; \ + if test -f $$i -o -d $$i; then \ + if rm -fr $$i; then \ + ${DELETE_OK}; \ + else \ + ${DELETE_FAILED}; \ + fi \ + fi \ + done + +distclean: clean + for i in "" ${SUBDIRS} ${SUBDIRS_AFTER}; do \ + test x"$$i" = x"" && continue; \ + ${DIR_ENTER}; \ + ${MAKE} @MAKEFLAGS_SILENT@ distclean || exit $$?; \ + ${DIR_LEAVE}; \ + done + + for i in "" ${DISTCLEAN} .deps *~; do \ + test x"$$i" = x"" && continue; \ + if test -f $$i -o -d $$i; then \ + if rm -fr $$i; then \ + ${DELETE_OK}; \ + else \ + ${DELETE_FAILED}; \ + fi \ + fi \ + done + +print-hierarchy: + for i in "" ${SUBDIRS} ${SUBDIRS_AFTER}; do \ + test x"$$i" = x"" && continue; \ + echo ${PRINT_HIERARCHY_PREFIX}$$i; \ + cd $$i || exit $$?; \ + ${MAKE} @MAKEFLAGS_SILENT@ PRINT_HIERARCHY_PREFIX=$$i/ \ + print-hierarchy || exit $$?; \ + cd .. || exit $$?; \ + done + +print-var: + printf '%s\n' '${${VAR}}' + +DIR_ENTER = printf "@TERM_EL@@TERM_SETAF6@Entering directory @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF6@.@TERM_SGR0@\n" "$$i"; cd $$i || exit $$? +DIR_LEAVE = printf "@TERM_EL@@TERM_SETAF6@Leaving directory @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF6@.@TERM_SGR0@\n" "$$i"; cd .. || exit $$? +COMPILE_STATUS = printf "@TERM_EL@@TERM_SETAF3@Compiling @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF3@...@TERM_SGR0@\r" "$<" +COMPILE_OK = printf "@TERM_EL@@TERM_SETAF2@Successfully compiled @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF2@.@TERM_SGR0@\n" "$<" +COMPILE_FAILED = err=$$?; printf "@TERM_EL@@TERM_SETAF1@Failed to compile @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF1@!@TERM_SGR0@\n" "$<"; exit $$err +COMPILE_LIB_STATUS = printf "@TERM_EL@@TERM_SETAF3@Compiling @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF3@ (lib)...@TERM_SGR0@\r" "$<" +COMPILE_LIB_OK = printf "@TERM_EL@@TERM_SETAF2@Successfully compiled @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF2@ (lib).@TERM_SGR0@\n" "$<" +COMPILE_LIB_FAILED = err=$$?; printf "@TERM_EL@@TERM_SETAF1@Failed to compile @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF1@ (lib)!@TERM_SGR0@\n" "$<"; exit $$err +COMPILE_AMIGA_LIB_STATUS = printf "@TERM_EL@@TERM_SETAF3@Compiling @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF3@ (Amiga lib)...@TERM_SGR0@\r" "$<" +COMPILE_AMIGA_LIB_OK = printf "@TERM_EL@@TERM_SETAF2@Successfully compiled @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF2@ (Amiga lib).@TERM_SGR0@\n" "$<" +COMPILE_AMIGA_LIB_FAILED = err=$$?; printf "@TERM_EL@@TERM_SETAF1@Failed to compile @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF1@ (Amiga lib)!@TERM_SGR0@\n" "$<"; exit $$err +COMPILE_PLUGIN_STATUS = printf "@TERM_EL@@TERM_SETAF3@Compiling @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF3@ (plugin)...@TERM_SGR0@\r" "$<" +COMPILE_PLUGIN_OK = printf "@TERM_EL@@TERM_SETAF2@Successfully compiled @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF2@ (plugin).@TERM_SGR0@\n" "$<" +COMPILE_PLUGIN_FAILED = err=$$?; printf "@TERM_EL@@TERM_SETAF1@Failed to compile @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF1@ (plugin)!@TERM_SGR0@\n" "$<"; exit $$err +LINK_STATUS = printf "@TERM_EL@@TERM_SETAF3@Linking @TERM_BOLD@$@@TERM_SGR0@@TERM_SETAF3@...@TERM_SGR0@\r" +LINK_OK = printf "@TERM_EL@@TERM_SETAF2@Successfully linked @TERM_BOLD@$@@TERM_SGR0@@TERM_SETAF2@.@TERM_SGR0@\n" +LINK_FAILED = err=$$?; printf "@TERM_EL@@TERM_SETAF1@Failed to link @TERM_BOLD@$@@TERM_SGR0@@TERM_SETAF1@!@TERM_SGR0@\n"; exit $$err +INSTALL_STATUS = printf "@TERM_EL@@TERM_SETAF3@Installing @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF3@...@TERM_SGR0@\r" "$$i" +INSTALL_OK = printf "@TERM_EL@@TERM_SETAF2@Successfully installed @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF2@.@TERM_SGR0@\n" "$$i" +INSTALL_FAILED = err=$$?; printf "@TERM_EL@@TERM_SETAF1@Failed to install @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF1@!@TERM_SGR0@\n" "$$i"; exit $$err +DELETE_OK = printf "@TERM_EL@@TERM_SETAF4@Deleted @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF4@.@TERM_SGR0@\n" "$$i" +DELETE_FAILED = err=$$?; printf "@TERM_EL@@TERM_SETAF1@Failed to delete @TERM_BOLD@%s@TERM_SGR0@@TERM_SETAF1@!@TERM_SGR0@\n" "$$i"; exit $$err + +.CURDIR ?= . +-include ${.CURDIR}/.deps ADDED configure.ac Index: configure.ac ================================================================== --- /dev/null +++ configure.ac @@ -0,0 +1,44 @@ +AC_INIT(cube, 0.1, js@nil.im) +AC_CONFIG_SRCDIR(src) +AC_CONFIG_AUX_DIR(build-aux) +AC_CONFIG_MACRO_DIR(build-aux/m4) + +for i in configure.ac build-aux/m4/*; do + AS_IF([test $i -nt configure], [ + AC_MSG_ERROR([$i is newer than configure! Run ./autogen.sh!]) + ]) +done + +BUILDSYS_INIT + +AC_PROG_CC +AC_PROG_CPP + +AC_PROG_CXX +AC_PROG_CXXCPP + +AC_PATH_TOOL(AR, ar) +AC_PATH_TOOL(RANLIB, ranlib) + +PKG_CHECK_MODULES(SDL, [ + sdl12_compat >= 1.2 + SDL_image >= 1.2 + SDL_mixer >= 1.2 +]) +AC_SUBST(SDL_CFLAGS) +AC_SUBST(SDL_LIBS) + +PKG_CHECK_MODULES(GL, gl) +PKG_CHECK_MODULES(GLU, glu) +PKG_CHECK_MODULES(ZLIB, zlib) + +case "$host_os" in +mingw32*) + ;; +*) + PKG_CHECK_MODULES(X11, x11) + ;; +esac + +AC_CONFIG_FILES([buildsys.mk extra.mk]) +AC_OUTPUT ADDED enet/Makefile Index: enet/Makefile ================================================================== --- /dev/null +++ enet/Makefile @@ -0,0 +1,15 @@ +STATIC_LIB_NOINST = libenet.a + +SRCS = callbacks.c \ + host.c \ + list.c \ + memory.c \ + packet.c \ + peer.c \ + protocol.c \ + unix.c \ + win32.c + +include ../buildsys.mk + +CPPFLAGS += -Iinclude Index: enet/include/enet/enet.h ================================================================== --- enet/include/enet/enet.h +++ enet/include/enet/enet.h @@ -9,13 +9,13 @@ extern "C" { #endif #include - +#include -#ifdef WIN32 +#ifdef _WIN32 #include "enet/win32.h" #else #include "enet/unix.h" #endif Index: enet/protocol.c ================================================================== --- enet/protocol.c +++ enet/protocol.c @@ -1127,11 +1127,11 @@ currentPeer -> packetsSent > 0) { enet_uint32 packetLoss = currentPeer -> packetsLost * ENET_PEER_PACKET_LOSS_SCALE / currentPeer -> packetsSent; #ifdef ENET_DEBUG -#ifdef WIN32 +#ifdef _WIN32 printf ( #else fprintf (stderr, #endif "peer %u: %f%%+-%f%% packet loss, %u+-%u ms round trip time, %f%% throttle, %u/%u outgoing, %u/%u incoming\n", currentPeer -> incomingPeerID, currentPeer -> packetLoss / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> packetLossVariance / (float) ENET_PEER_PACKET_LOSS_SCALE, currentPeer -> roundTripTime, currentPeer -> roundTripTimeVariance, currentPeer -> packetThrottle / (float) ENET_PEER_PACKET_THROTTLE_SCALE, enet_list_size (& currentPeer -> outgoingReliableCommands), enet_list_size (& currentPeer -> outgoingUnreliableCommands), currentPeer -> channels != NULL ? enet_list_size (& currentPeer -> channels -> incomingReliableCommands) : 0, enet_list_size (& currentPeer -> channels -> incomingUnreliableCommands)); Index: enet/unix.c ================================================================== --- enet/unix.c +++ enet/unix.c @@ -1,10 +1,10 @@ /** @file unix.c @brief ENet Unix system specific functions */ -#ifndef WIN32 +#ifndef _WIN32 #include #include #include #include @@ -23,11 +23,11 @@ #ifdef HAS_POLL #include #endif -#ifndef HAS_SOCKLEN_T +#if 0 typedef int socklen_t; #endif #ifndef MSG_NOSIGNAL #define MSG_NOSIGNAL 0 Index: enet/win32.c ================================================================== --- enet/win32.c +++ enet/win32.c @@ -1,10 +1,10 @@ /** @file win32.c @brief ENet Win32 system specific functions */ -#ifdef WIN32 +#ifdef _WIN32 #include #define ENET_BUILDING_LIB 1 #include "enet/enet.h" ADDED extra.mk.in Index: extra.mk.in ================================================================== --- /dev/null +++ extra.mk.in @@ -0,0 +1,10 @@ +GL_CFLAGS = @GL_CFLAGS@ +GL_LIBS = @GL_LIBS@ +GLU_CFLAGS = @GLU_CFLAGS@ +GLU_LIBS = @GLU_LIBS@ +SDL_CFLAGS = @SDL_CFLAGS@ +SDL_LIBS = @SDL_LIBS@ +X11_CFLAGS = @X11_CFLAGS@ +X11_LIBS = @X11_LIBS@ +ZLIB_CFLAGS = @ZLIB_CFLAGS@ +ZLIB_LIBS = @ZLIB_LIBS@ ADDED src/Makefile Index: src/Makefile ================================================================== --- /dev/null +++ src/Makefile @@ -0,0 +1,52 @@ +PROG = client + +SRCS = client.o \ + clientextras.o \ + clientgame.o \ + clients2c.o \ + command.o \ + console.o \ + editing.o \ + entities.o \ + main.o \ + menus.o \ + monster.o \ + physics.o \ + rendercubes.o \ + renderextras.o \ + rendergl.o \ + rendermd2.o \ + renderparticles.o \ + rendertext.o \ + rndmap.o \ + savegamedemo.o \ + server.o \ + serverbrowser.o \ + serverms.o \ + serverutil.o \ + sound.o \ + tools.o \ + weapon.o \ + world.o \ + worldio.o \ + worldlight.o \ + worldocull.o \ + worldrender.o + +include ../buildsys.mk +include ../extra.mk + +CPPFLAGS += -I../enet/include \ + ${SDL_CFLAGS} \ + ${GL_CFLAGS} \ + ${GLU_CFLAGS} \ + ${X11_CFLAGS} \ + ${ZLIB_CFLAGS} +CXXFLAGS += -fpermissive +LIBS += -L../enet -lenet \ + ${SDL_LIBS} \ + ${GL_LIBS} \ + ${GLU_LIBS} \ + ${X11_LIBS} \ + ${ZLIB_LIBS} +LD = ${CXX} DELETED src/client.cpp Index: src/client.cpp ================================================================== --- src/client.cpp +++ /dev/null @@ -1,309 +0,0 @@ -// client.cpp, mostly network related client game code - -#include "cube.h" - -ENetHost *clienthost = NULL; -int connecting = 0; -int connattempts = 0; -int disconnecting = 0; -int clientnum = -1; // our client id in the game -bool c2sinit = false; // whether we need to tell the other clients our stats - -int getclientnum() { return clientnum; }; - -bool multiplayer() -{ - // check not correct on listen server? - if(clienthost) conoutf("operation not available in multiplayer"); - return clienthost!=NULL; -}; - -bool allowedittoggle() -{ - bool allow = !clienthost || gamemode==1; - if(!allow) conoutf("editing in multiplayer requires coopedit mode (1)"); - return allow; -}; - -VARF(rate, 0, 0, 25000, if(clienthost && (!rate || rate>1000)) enet_host_bandwidth_limit (clienthost, rate, rate)); - -void throttle(); - -VARF(throttle_interval, 0, 5, 30, throttle()); -VARF(throttle_accel, 0, 2, 32, throttle()); -VARF(throttle_decel, 0, 2, 32, throttle()); - -void throttle() -{ - if(!clienthost || connecting) return; - assert(ENET_PEER_PACKET_THROTTLE_SCALE==32); - enet_peer_throttle_configure(clienthost->peers, throttle_interval*1000, throttle_accel, throttle_decel); -}; - -void newname(char *name) { c2sinit = false; strn0cpy(player1->name, name, 16); }; -void newteam(char *name) { c2sinit = false; strn0cpy(player1->team, name, 5); }; - -COMMANDN(team, newteam, ARG_1STR); -COMMANDN(name, newname, ARG_1STR); - -void writeclientinfo(FILE *f) -{ - fprintf(f, "name \"%s\"\nteam \"%s\"\n", player1->name, player1->team); -}; - -void connects(char *servername) -{ - disconnect(1); // reset state - addserver(servername); - - conoutf("attempting to connect to %s", servername); - ENetAddress address = { ENET_HOST_ANY, CUBE_SERVER_PORT }; - if(enet_address_set_host(&address, servername) < 0) - { - conoutf("could not resolve server %s", servername); - return; - }; - - clienthost = enet_host_create(NULL, 1, rate, rate); - - if(clienthost) - { - enet_host_connect(clienthost, &address, 1); - enet_host_flush(clienthost); - connecting = lastmillis; - connattempts = 0; - } - else - { - conoutf("could not connect to server"); - disconnect(); - }; -}; - -void disconnect(int onlyclean, int async) -{ - if(clienthost) - { - if(!connecting && !disconnecting) - { - enet_peer_disconnect(clienthost->peers); - enet_host_flush(clienthost); - disconnecting = lastmillis; - }; - if(clienthost->peers->state != ENET_PEER_STATE_DISCONNECTED) - { - if(async) return; - enet_peer_reset(clienthost->peers); - }; - enet_host_destroy(clienthost); - }; - - if(clienthost && !connecting) conoutf("disconnected"); - clienthost = NULL; - connecting = 0; - connattempts = 0; - disconnecting = 0; - clientnum = -1; - c2sinit = false; - player1->lifesequence = 0; - loopv(players) zapdynent(players[i]); - - localdisconnect(); - - if(!onlyclean) { stop(); localconnect(); }; -}; - -void trydisconnect() -{ - if(!clienthost) - { - conoutf("not connected"); - return; - }; - if(connecting) - { - conoutf("aborting connection attempt"); - disconnect(); - return; - }; - conoutf("attempting to disconnect..."); - disconnect(0, !disconnecting); -}; - -string ctext; -void toserver(char *text) { conoutf("%s:\f %s", player1->name, text); strn0cpy(ctext, text, 80); }; -void echo(char *text) { conoutf("%s", text); }; - -COMMAND(echo, ARG_VARI); -COMMANDN(say, toserver, ARG_VARI); -COMMANDN(connect, connects, ARG_1STR); -COMMANDN(disconnect, trydisconnect, ARG_NONE); - -// collect c2s messages conveniently - -vector messages; - -void addmsg(int rel, int num, int type, ...) -{ - if(demoplayback) return; - if(num!=msgsizelookup(type)) { sprintf_sd(s)("inconsistant msg size for %d (%d != %d)", type, num, msgsizelookup(type)); fatal(s); }; - if(messages.length()==100) { conoutf("command flood protection (type %d)", type); return; }; - ivector &msg = messages.add(); - msg.add(num); - msg.add(rel); - msg.add(type); - va_list marker; - va_start(marker, type); - loopi(num-1) msg.add(va_arg(marker, int)); - va_end(marker); -}; - -void server_err() -{ - conoutf("server network error, disconnecting..."); - disconnect(); -}; - -int lastupdate = 0, lastping = 0; -string toservermap; -bool senditemstoserver = false; // after a map change, since server doesn't have map data - -string clientpassword; -void password(char *p) { strcpy_s(clientpassword, p); }; -COMMAND(password, ARG_1STR); - -bool netmapstart() { senditemstoserver = true; return clienthost!=NULL; }; - -void initclientnet() -{ - ctext[0] = 0; - toservermap[0] = 0; - clientpassword[0] = 0; - newname("unnamed"); - newteam("red"); -}; - -void sendpackettoserv(void *packet) -{ - if(clienthost) { enet_host_broadcast(clienthost, 0, (ENetPacket *)packet); enet_host_flush(clienthost); } - else localclienttoserver((ENetPacket *)packet); -} - -void c2sinfo(dynent *d) // send update to the server -{ - if(clientnum<0) return; // we haven't had a welcome message from the server yet - if(lastmillis-lastupdate<40) return; // don't update faster than 25fps - ENetPacket *packet = enet_packet_create (NULL, MAXTRANS, 0); - uchar *start = packet->data; - uchar *p = start+2; - bool serveriteminitdone = false; - if(toservermap[0]) // suggest server to change map - { // do this exclusively as map change may invalidate rest of update - packet->flags = ENET_PACKET_FLAG_RELIABLE; - putint(p, SV_MAPCHANGE); - sendstring(toservermap, p); - toservermap[0] = 0; - putint(p, nextmode); - } - else - { - putint(p, SV_POS); - putint(p, clientnum); - putint(p, (int)(d->o.x*DMF)); // quantize coordinates to 1/16th of a cube, between 1 and 3 bytes - putint(p, (int)(d->o.y*DMF)); - putint(p, (int)(d->o.z*DMF)); - putint(p, (int)(d->yaw*DAF)); - putint(p, (int)(d->pitch*DAF)); - putint(p, (int)(d->roll*DAF)); - putint(p, (int)(d->vel.x*DVF)); // quantize to 1/100, almost always 1 byte - putint(p, (int)(d->vel.y*DVF)); - putint(p, (int)(d->vel.z*DVF)); - // pack rest in 1 byte: strafe:2, move:2, onfloor:1, state:3 - putint(p, (d->strafe&3) | ((d->move&3)<<2) | (((int)d->onfloor)<<4) | ((editmode ? CS_EDITING : d->state)<<5) ); - - if(senditemstoserver) - { - packet->flags = ENET_PACKET_FLAG_RELIABLE; - putint(p, SV_ITEMLIST); - if(!m_noitems) putitems(p); - putint(p, -1); - senditemstoserver = false; - serveriteminitdone = true; - }; - if(ctext[0]) // player chat, not flood protected for now - { - packet->flags = ENET_PACKET_FLAG_RELIABLE; - putint(p, SV_TEXT); - sendstring(ctext, p); - ctext[0] = 0; - }; - if(!c2sinit) // tell other clients who I am - { - packet->flags = ENET_PACKET_FLAG_RELIABLE; - c2sinit = true; - putint(p, SV_INITC2S); - sendstring(player1->name, p); - sendstring(player1->team, p); - putint(p, player1->lifesequence); - }; - loopv(messages) // send messages collected during the previous frames - { - ivector &msg = messages[i]; - if(msg[1]) packet->flags = ENET_PACKET_FLAG_RELIABLE; - loopi(msg[0]) putint(p, msg[i+2]); - }; - messages.setsize(0); - if(lastmillis-lastping>250) - { - putint(p, SV_PING); - putint(p, lastmillis); - lastping = lastmillis; - }; - }; - *(ushort *)start = ENET_HOST_TO_NET_16(p-start); - enet_packet_resize(packet, p-start); - incomingdemodata(start, p-start, true); - if(clienthost) { enet_host_broadcast(clienthost, 0, packet); enet_host_flush(clienthost); } - else localclienttoserver(packet); - lastupdate = lastmillis; - if(serveriteminitdone) loadgamerest(); // hack -}; - -void gets2c() // get updates from the server -{ - ENetEvent event; - if(!clienthost) return; - if(connecting && lastmillis/3000 > connecting/3000) - { - conoutf("attempting to connect..."); - connecting = lastmillis; - ++connattempts; - if(connattempts > 3) - { - conoutf("could not connect to server"); - disconnect(); - return; - }; - }; - while(clienthost!=NULL && enet_host_service(clienthost, &event, 0)>0) - switch(event.type) - { - case ENET_EVENT_TYPE_CONNECT: - conoutf("connected to server"); - connecting = 0; - throttle(); - break; - - case ENET_EVENT_TYPE_RECEIVE: - if(disconnecting) conoutf("attempting to disconnect..."); - else localservertoclient(event.packet->data, event.packet->dataLength); - enet_packet_destroy(event.packet); - break; - - case ENET_EVENT_TYPE_DISCONNECT: - if(disconnecting) disconnect(); - else server_err(); - return; - } -}; - ADDED src/client.cxx Index: src/client.cxx ================================================================== --- /dev/null +++ src/client.cxx @@ -0,0 +1,309 @@ +// client.cpp, mostly network related client game code + +#include "cube.h" + +ENetHost *clienthost = NULL; +int connecting = 0; +int connattempts = 0; +int disconnecting = 0; +int clientnum = -1; // our client id in the game +bool c2sinit = false; // whether we need to tell the other clients our stats + +int getclientnum() { return clientnum; }; + +bool multiplayer() +{ + // check not correct on listen server? + if(clienthost) conoutf("operation not available in multiplayer"); + return clienthost!=NULL; +}; + +bool allowedittoggle() +{ + bool allow = !clienthost || gamemode==1; + if(!allow) conoutf("editing in multiplayer requires coopedit mode (1)"); + return allow; +}; + +VARF(rate, 0, 0, 25000, if(clienthost && (!rate || rate>1000)) enet_host_bandwidth_limit (clienthost, rate, rate)); + +void throttle(); + +VARF(throttle_interval, 0, 5, 30, throttle()); +VARF(throttle_accel, 0, 2, 32, throttle()); +VARF(throttle_decel, 0, 2, 32, throttle()); + +void throttle() +{ + if(!clienthost || connecting) return; + assert(ENET_PEER_PACKET_THROTTLE_SCALE==32); + enet_peer_throttle_configure(clienthost->peers, throttle_interval*1000, throttle_accel, throttle_decel); +}; + +void newname(char *name) { c2sinit = false; strn0cpy(player1->name, name, 16); }; +void newteam(char *name) { c2sinit = false; strn0cpy(player1->team, name, 5); }; + +COMMANDN(team, newteam, ARG_1STR); +COMMANDN(name, newname, ARG_1STR); + +void writeclientinfo(FILE *f) +{ + fprintf(f, "name \"%s\"\nteam \"%s\"\n", player1->name, player1->team); +}; + +void connects(char *servername) +{ + disconnect(1); // reset state + addserver(servername); + + conoutf("attempting to connect to %s", servername); + ENetAddress address = { ENET_HOST_ANY, CUBE_SERVER_PORT }; + if(enet_address_set_host(&address, servername) < 0) + { + conoutf("could not resolve server %s", servername); + return; + }; + + clienthost = enet_host_create(NULL, 1, rate, rate); + + if(clienthost) + { + enet_host_connect(clienthost, &address, 1); + enet_host_flush(clienthost); + connecting = lastmillis; + connattempts = 0; + } + else + { + conoutf("could not connect to server"); + disconnect(); + }; +}; + +void disconnect(int onlyclean, int async) +{ + if(clienthost) + { + if(!connecting && !disconnecting) + { + enet_peer_disconnect(clienthost->peers); + enet_host_flush(clienthost); + disconnecting = lastmillis; + }; + if(clienthost->peers->state != ENET_PEER_STATE_DISCONNECTED) + { + if(async) return; + enet_peer_reset(clienthost->peers); + }; + enet_host_destroy(clienthost); + }; + + if(clienthost && !connecting) conoutf("disconnected"); + clienthost = NULL; + connecting = 0; + connattempts = 0; + disconnecting = 0; + clientnum = -1; + c2sinit = false; + player1->lifesequence = 0; + loopv(players) zapdynent(players[i]); + + localdisconnect(); + + if(!onlyclean) { stop(); localconnect(); }; +}; + +void trydisconnect() +{ + if(!clienthost) + { + conoutf("not connected"); + return; + }; + if(connecting) + { + conoutf("aborting connection attempt"); + disconnect(); + return; + }; + conoutf("attempting to disconnect..."); + disconnect(0, !disconnecting); +}; + +string ctext; +void toserver(char *text) { conoutf("%s:\f %s", player1->name, text); strn0cpy(ctext, text, 80); }; +void echo(char *text) { conoutf("%s", text); }; + +COMMAND(echo, ARG_VARI); +COMMANDN(say, toserver, ARG_VARI); +COMMANDN(connect, connects, ARG_1STR); +COMMANDN(disconnect, trydisconnect, ARG_NONE); + +// collect c2s messages conveniently + +vector messages; + +void addmsg(int rel, int num, int type, ...) +{ + if(demoplayback) return; + if(num!=msgsizelookup(type)) { sprintf_sd(s)("inconsistant msg size for %d (%d != %d)", type, num, msgsizelookup(type)); fatal(s); }; + if(messages.length()==100) { conoutf("command flood protection (type %d)", type); return; }; + ivector &msg = messages.add(); + msg.add(num); + msg.add(rel); + msg.add(type); + va_list marker; + va_start(marker, type); + loopi(num-1) msg.add(va_arg(marker, int)); + va_end(marker); +}; + +void server_err() +{ + conoutf("server network error, disconnecting..."); + disconnect(); +}; + +int lastupdate = 0, lastping = 0; +string toservermap; +bool senditemstoserver = false; // after a map change, since server doesn't have map data + +string clientpassword; +void password(char *p) { strcpy_s(clientpassword, p); }; +COMMAND(password, ARG_1STR); + +bool netmapstart() { senditemstoserver = true; return clienthost!=NULL; }; + +void initclientnet() +{ + ctext[0] = 0; + toservermap[0] = 0; + clientpassword[0] = 0; + newname("unnamed"); + newteam("red"); +}; + +void sendpackettoserv(void *packet) +{ + if(clienthost) { enet_host_broadcast(clienthost, 0, (ENetPacket *)packet); enet_host_flush(clienthost); } + else localclienttoserver((ENetPacket *)packet); +} + +void c2sinfo(dynent *d) // send update to the server +{ + if(clientnum<0) return; // we haven't had a welcome message from the server yet + if(lastmillis-lastupdate<40) return; // don't update faster than 25fps + ENetPacket *packet = enet_packet_create (NULL, MAXTRANS, 0); + uchar *start = packet->data; + uchar *p = start+2; + bool serveriteminitdone = false; + if(toservermap[0]) // suggest server to change map + { // do this exclusively as map change may invalidate rest of update + packet->flags = ENET_PACKET_FLAG_RELIABLE; + putint(p, SV_MAPCHANGE); + sendstring(toservermap, p); + toservermap[0] = 0; + putint(p, nextmode); + } + else + { + putint(p, SV_POS); + putint(p, clientnum); + putint(p, (int)(d->o.x*DMF)); // quantize coordinates to 1/16th of a cube, between 1 and 3 bytes + putint(p, (int)(d->o.y*DMF)); + putint(p, (int)(d->o.z*DMF)); + putint(p, (int)(d->yaw*DAF)); + putint(p, (int)(d->pitch*DAF)); + putint(p, (int)(d->roll*DAF)); + putint(p, (int)(d->vel.x*DVF)); // quantize to 1/100, almost always 1 byte + putint(p, (int)(d->vel.y*DVF)); + putint(p, (int)(d->vel.z*DVF)); + // pack rest in 1 byte: strafe:2, move:2, onfloor:1, state:3 + putint(p, (d->strafe&3) | ((d->move&3)<<2) | (((int)d->onfloor)<<4) | ((editmode ? CS_EDITING : d->state)<<5) ); + + if(senditemstoserver) + { + packet->flags = ENET_PACKET_FLAG_RELIABLE; + putint(p, SV_ITEMLIST); + if(!m_noitems) putitems(p); + putint(p, -1); + senditemstoserver = false; + serveriteminitdone = true; + }; + if(ctext[0]) // player chat, not flood protected for now + { + packet->flags = ENET_PACKET_FLAG_RELIABLE; + putint(p, SV_TEXT); + sendstring(ctext, p); + ctext[0] = 0; + }; + if(!c2sinit) // tell other clients who I am + { + packet->flags = ENET_PACKET_FLAG_RELIABLE; + c2sinit = true; + putint(p, SV_INITC2S); + sendstring(player1->name, p); + sendstring(player1->team, p); + putint(p, player1->lifesequence); + }; + loopv(messages) // send messages collected during the previous frames + { + ivector &msg = messages[i]; + if(msg[1]) packet->flags = ENET_PACKET_FLAG_RELIABLE; + loopi(msg[0]) putint(p, msg[i+2]); + }; + messages.setsize(0); + if(lastmillis-lastping>250) + { + putint(p, SV_PING); + putint(p, lastmillis); + lastping = lastmillis; + }; + }; + *(ushort *)start = ENET_HOST_TO_NET_16(p-start); + enet_packet_resize(packet, p-start); + incomingdemodata(start, p-start, true); + if(clienthost) { enet_host_broadcast(clienthost, 0, packet); enet_host_flush(clienthost); } + else localclienttoserver(packet); + lastupdate = lastmillis; + if(serveriteminitdone) loadgamerest(); // hack +}; + +void gets2c() // get updates from the server +{ + ENetEvent event; + if(!clienthost) return; + if(connecting && lastmillis/3000 > connecting/3000) + { + conoutf("attempting to connect..."); + connecting = lastmillis; + ++connattempts; + if(connattempts > 3) + { + conoutf("could not connect to server"); + disconnect(); + return; + }; + }; + while(clienthost!=NULL && enet_host_service(clienthost, &event, 0)>0) + switch(event.type) + { + case ENET_EVENT_TYPE_CONNECT: + conoutf("connected to server"); + connecting = 0; + throttle(); + break; + + case ENET_EVENT_TYPE_RECEIVE: + if(disconnecting) conoutf("attempting to disconnect..."); + else localservertoclient(event.packet->data, event.packet->dataLength); + enet_packet_destroy(event.packet); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + if(disconnecting) disconnect(); + else server_err(); + return; + } +}; + DELETED src/clientextras.cpp Index: src/clientextras.cpp ================================================================== --- src/clientextras.cpp +++ /dev/null @@ -1,157 +0,0 @@ -// clientextras.cpp: stuff that didn't fit in client.cpp or clientgame.cpp :) - -#include "cube.h" - -// render players & monsters -// very messy ad-hoc handling of animation frames, should be made more configurable - -// D D D D' D D D D' A A' P P' I I' R, R' E L J J' -int frame[] = { 178, 184, 190, 137, 183, 189, 197, 164, 46, 51, 54, 32, 0, 0, 40, 1, 162, 162, 67, 168 }; -int range[] = { 6, 6, 8, 28, 1, 1, 1, 1, 8, 19, 4, 18, 40, 1, 6, 15, 1, 1, 1, 1 }; - -void renderclient(dynent *d, bool team, char *mdlname, bool hellpig, float scale) -{ - int n = 3; - float speed = 100.0f; - float mz = d->o.z-d->eyeheight+1.55f*scale; - int basetime = -((int)d&0xFFF); - if(d->state==CS_DEAD) - { - int r; - if(hellpig) { n = 2; r = range[3]; } else { n = (int)d%3; r = range[n]; }; - basetime = d->lastaction; - int t = lastmillis-d->lastaction; - if(t<0 || t>20000) return; - if(t>(r-1)*100) { n += 4; if(t>(r+10)*100) { t -= (r+10)*100; mz -= t*t/10000000000.0f*t; }; }; - if(mz<-1000) return; - //mdl = (((int)d>>6)&1)+1; - //mz = d->o.z-d->eyeheight+0.2f; - //scale = 1.2f; - } - else if(d->state==CS_EDITING) { n = 16; } - else if(d->state==CS_LAGGED) { n = 17; } - else if(d->monsterstate==M_ATTACKING) { n = 8; } - else if(d->monsterstate==M_PAIN) { n = 10; } - else if((!d->move && !d->strafe) || !d->moving) { n = 12; } - else if(!d->onfloor && d->timeinair>100) { n = 18; } - else { n = 14; speed = 1200/d->maxspeed*scale; if(hellpig) speed = 300/d->maxspeed; }; - if(hellpig) { n++; scale *= 32; mz -= 1.9f; }; - rendermodel(mdlname, frame[n], range[n], 0, 1.5f, d->o.x, mz, d->o.y, d->yaw+90, d->pitch/2, team, scale, speed, 0, basetime); -}; - -extern int democlientnum; - -void renderclients() -{ - dynent *d; - loopv(players) if((d = players[i]) && (!demoplayback || i!=democlientnum)) renderclient(d, isteam(player1->team, d->team), "monster/ogro", false, 1.0f); -}; - -// creation of scoreboard pseudo-menu - -bool scoreson = false; - -void showscores(bool on) -{ - scoreson = on; - menuset(((int)on)-1); -}; - -struct sline { string s; }; -vector scorelines; - -void renderscore(dynent *d) -{ - sprintf_sd(lag)("%d", d->plag); - sprintf_sd(name) ("(%s)", d->name); - sprintf_s(scorelines.add().s)("%d\t%s\t%d\t%s\t%s", d->frags, d->state==CS_LAGGED ? "LAG" : lag, d->ping, d->team, d->state==CS_DEAD ? name : d->name); - menumanual(0, scorelines.length()-1, scorelines.last().s); -}; - -const int maxteams = 4; -char *teamname[maxteams]; -int teamscore[maxteams], teamsused; -string teamscores; -int timeremain = 0; - -void addteamscore(dynent *d) -{ - if(!d) return; - loopi(teamsused) if(strcmp(teamname[i], d->team)==0) { teamscore[i] += d->frags; return; }; - if(teamsused==maxteams) return; - teamname[teamsused] = d->team; - teamscore[teamsused++] = d->frags; -}; - -void renderscores() -{ - if(!scoreson) return; - scorelines.setsize(0); - if(!demoplayback) renderscore(player1); - loopv(players) if(players[i]) renderscore(players[i]); - sortmenu(0, scorelines.length()); - if(m_teammode) - { - teamsused = 0; - loopv(players) addteamscore(players[i]); - if(!demoplayback) addteamscore(player1); - teamscores[0] = 0; - loopj(teamsused) - { - sprintf_sd(sc)("[ %s: %d ]", teamname[j], teamscore[j]); - strcat_s(teamscores, sc); - }; - menumanual(0, scorelines.length(), ""); - menumanual(0, scorelines.length()+1, teamscores); - }; -}; - -// sendmap/getmap commands, should be replaced by more intuitive map downloading - -void sendmap(char *mapname) -{ - if(*mapname) save_world(mapname); - changemap(mapname); - mapname = getclientmap(); - int mapsize; - uchar *mapdata = readmap(mapname, &mapsize); - if(!mapdata) return; - ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + mapsize, ENET_PACKET_FLAG_RELIABLE); - uchar *start = packet->data; - uchar *p = start+2; - putint(p, SV_SENDMAP); - sendstring(mapname, p); - putint(p, mapsize); - if(65535 - (p - start) < mapsize) - { - conoutf("map %s is too large to send", mapname); - free(mapdata); - enet_packet_destroy(packet); - return; - }; - memcpy(p, mapdata, mapsize); - p += mapsize; - free(mapdata); - *(ushort *)start = ENET_HOST_TO_NET_16(p-start); - enet_packet_resize(packet, p-start); - sendpackettoserv(packet); - conoutf("sending map %s to server...", mapname); - sprintf_sd(msg)("[map %s uploaded to server, \"getmap\" to receive it]", mapname); - toserver(msg); -} - -void getmap() -{ - ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE); - uchar *start = packet->data; - uchar *p = start+2; - putint(p, SV_RECVMAP); - *(ushort *)start = ENET_HOST_TO_NET_16(p-start); - enet_packet_resize(packet, p-start); - sendpackettoserv(packet); - conoutf("requesting map from server..."); -} - -COMMAND(sendmap, ARG_1STR); -COMMAND(getmap, ARG_NONE); - ADDED src/clientextras.cxx Index: src/clientextras.cxx ================================================================== --- /dev/null +++ src/clientextras.cxx @@ -0,0 +1,157 @@ +// clientextras.cpp: stuff that didn't fit in client.cpp or clientgame.cpp :) + +#include "cube.h" + +// render players & monsters +// very messy ad-hoc handling of animation frames, should be made more configurable + +// D D D D' D D D D' A A' P P' I I' R, R' E L J J' +int frame[] = { 178, 184, 190, 137, 183, 189, 197, 164, 46, 51, 54, 32, 0, 0, 40, 1, 162, 162, 67, 168 }; +int range[] = { 6, 6, 8, 28, 1, 1, 1, 1, 8, 19, 4, 18, 40, 1, 6, 15, 1, 1, 1, 1 }; + +void renderclient(dynent *d, bool team, char *mdlname, bool hellpig, float scale) +{ + int n = 3; + float speed = 100.0f; + float mz = d->o.z-d->eyeheight+1.55f*scale; + int basetime = -((int)d&0xFFF); + if(d->state==CS_DEAD) + { + int r; + if(hellpig) { n = 2; r = range[3]; } else { n = (int)d%3; r = range[n]; }; + basetime = d->lastaction; + int t = lastmillis-d->lastaction; + if(t<0 || t>20000) return; + if(t>(r-1)*100) { n += 4; if(t>(r+10)*100) { t -= (r+10)*100; mz -= t*t/10000000000.0f*t; }; }; + if(mz<-1000) return; + //mdl = (((int)d>>6)&1)+1; + //mz = d->o.z-d->eyeheight+0.2f; + //scale = 1.2f; + } + else if(d->state==CS_EDITING) { n = 16; } + else if(d->state==CS_LAGGED) { n = 17; } + else if(d->monsterstate==M_ATTACKING) { n = 8; } + else if(d->monsterstate==M_PAIN) { n = 10; } + else if((!d->move && !d->strafe) || !d->moving) { n = 12; } + else if(!d->onfloor && d->timeinair>100) { n = 18; } + else { n = 14; speed = 1200/d->maxspeed*scale; if(hellpig) speed = 300/d->maxspeed; }; + if(hellpig) { n++; scale *= 32; mz -= 1.9f; }; + rendermodel(mdlname, frame[n], range[n], 0, 1.5f, d->o.x, mz, d->o.y, d->yaw+90, d->pitch/2, team, scale, speed, 0, basetime); +}; + +extern int democlientnum; + +void renderclients() +{ + dynent *d; + loopv(players) if((d = players[i]) && (!demoplayback || i!=democlientnum)) renderclient(d, isteam(player1->team, d->team), "monster/ogro", false, 1.0f); +}; + +// creation of scoreboard pseudo-menu + +bool scoreson = false; + +void showscores(bool on) +{ + scoreson = on; + menuset(((int)on)-1); +}; + +struct sline { string s; }; +vector scorelines; + +void renderscore(dynent *d) +{ + sprintf_sd(lag)("%d", d->plag); + sprintf_sd(name) ("(%s)", d->name); + sprintf_s(scorelines.add().s)("%d\t%s\t%d\t%s\t%s", d->frags, d->state==CS_LAGGED ? "LAG" : lag, d->ping, d->team, d->state==CS_DEAD ? name : d->name); + menumanual(0, scorelines.length()-1, scorelines.last().s); +}; + +const int maxteams = 4; +char *teamname[maxteams]; +int teamscore[maxteams], teamsused; +string teamscores; +int timeremain = 0; + +void addteamscore(dynent *d) +{ + if(!d) return; + loopi(teamsused) if(strcmp(teamname[i], d->team)==0) { teamscore[i] += d->frags; return; }; + if(teamsused==maxteams) return; + teamname[teamsused] = d->team; + teamscore[teamsused++] = d->frags; +}; + +void renderscores() +{ + if(!scoreson) return; + scorelines.setsize(0); + if(!demoplayback) renderscore(player1); + loopv(players) if(players[i]) renderscore(players[i]); + sortmenu(0, scorelines.length()); + if(m_teammode) + { + teamsused = 0; + loopv(players) addteamscore(players[i]); + if(!demoplayback) addteamscore(player1); + teamscores[0] = 0; + loopj(teamsused) + { + sprintf_sd(sc)("[ %s: %d ]", teamname[j], teamscore[j]); + strcat_s(teamscores, sc); + }; + menumanual(0, scorelines.length(), ""); + menumanual(0, scorelines.length()+1, teamscores); + }; +}; + +// sendmap/getmap commands, should be replaced by more intuitive map downloading + +void sendmap(char *mapname) +{ + if(*mapname) save_world(mapname); + changemap(mapname); + mapname = getclientmap(); + int mapsize; + uchar *mapdata = readmap(mapname, &mapsize); + if(!mapdata) return; + ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + mapsize, ENET_PACKET_FLAG_RELIABLE); + uchar *start = packet->data; + uchar *p = start+2; + putint(p, SV_SENDMAP); + sendstring(mapname, p); + putint(p, mapsize); + if(65535 - (p - start) < mapsize) + { + conoutf("map %s is too large to send", mapname); + free(mapdata); + enet_packet_destroy(packet); + return; + }; + memcpy(p, mapdata, mapsize); + p += mapsize; + free(mapdata); + *(ushort *)start = ENET_HOST_TO_NET_16(p-start); + enet_packet_resize(packet, p-start); + sendpackettoserv(packet); + conoutf("sending map %s to server...", mapname); + sprintf_sd(msg)("[map %s uploaded to server, \"getmap\" to receive it]", mapname); + toserver(msg); +} + +void getmap() +{ + ENetPacket *packet = enet_packet_create(NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE); + uchar *start = packet->data; + uchar *p = start+2; + putint(p, SV_RECVMAP); + *(ushort *)start = ENET_HOST_TO_NET_16(p-start); + enet_packet_resize(packet, p-start); + sendpackettoserv(packet); + conoutf("requesting map from server..."); +} + +COMMAND(sendmap, ARG_1STR); +COMMAND(getmap, ARG_NONE); + DELETED src/clientgame.cpp Index: src/clientgame.cpp ================================================================== --- src/clientgame.cpp +++ /dev/null @@ -1,455 +0,0 @@ -// clientgame.cpp: core game related stuff - -#include "cube.h" - -int nextmode = 0; // nextmode becomes gamemode after next map load -VAR(gamemode, 1, 0, 0); - -void mode(int n) { addmsg(1, 2, SV_GAMEMODE, nextmode = n); }; -COMMAND(mode, ARG_1INT); - -bool intermission = false; - -dynent *player1 = newdynent(); // our client -dvector players; // other clients - -VARP(sensitivity, 0, 10, 10000); -VARP(sensitivityscale, 1, 1, 10000); -VARP(invmouse, 0, 0, 1); - -int lastmillis = 0; -int curtime = 10; -string clientmap; - -extern int framesinmap; - -char *getclientmap() { return clientmap; }; - -void resetmovement(dynent *d) -{ - d->k_left = false; - d->k_right = false; - d->k_up = false; - d->k_down = false; - d->jumpnext = false; - d->strafe = 0; - d->move = 0; -}; - -void spawnstate(dynent *d) // reset player state not persistent accross spawns -{ - resetmovement(d); - d->vel.x = d->vel.y = d->vel.z = 0; - d->onfloor = false; - d->timeinair = 0; - d->health = 100; - d->armour = 50; - d->armourtype = A_BLUE; - d->quadmillis = 0; - d->lastattackgun = d->gunselect = GUN_SG; - d->gunwait = 0; - d->attacking = false; - d->lastaction = 0; - loopi(NUMGUNS) d->ammo[i] = 0; - d->ammo[GUN_FIST] = 1; - if(m_noitems) - { - d->gunselect = GUN_RIFLE; - d->armour = 0; - if(m_noitemsrail) - { - d->health = 1; - d->ammo[GUN_RIFLE] = 100; - } - else - { - if(gamemode==12) { d->gunselect = GUN_FIST; return; }; // eihrul's secret "instafist" mode - d->health = 256; - if(m_tarena) - { - int gun1 = rnd(4)+1; - baseammo(d->gunselect = gun1); - for(;;) - { - int gun2 = rnd(4)+1; - if(gun1!=gun2) { baseammo(gun2); break; }; - }; - } - else if(m_arena) // insta arena - { - d->ammo[GUN_RIFLE] = 100; - } - else // efficiency - { - loopi(4) baseammo(i+1); - d->gunselect = GUN_CG; - }; - d->ammo[GUN_CG] /= 2; - }; - } - else - { - d->ammo[GUN_SG] = 5; - }; -}; - -dynent *newdynent() // create a new blank player or monster -{ - dynent *d = (dynent *)gp()->alloc(sizeof(dynent)); - d->o.x = 0; - d->o.y = 0; - d->o.z = 0; - d->yaw = 270; - d->pitch = 0; - d->roll = 0; - d->maxspeed = 22; - d->outsidemap = false; - d->inwater = false; - d->radius = 1.1f; - d->eyeheight = 3.2f; - d->aboveeye = 0.7f; - d->frags = 0; - d->plag = 0; - d->ping = 0; - d->lastupdate = lastmillis; - d->enemy = NULL; - d->monsterstate = 0; - d->name[0] = d->team[0] = 0; - d->blocked = false; - d->lifesequence = 0; - d->state = CS_ALIVE; - spawnstate(d); - return d; -}; - -void respawnself() -{ - spawnplayer(player1); - showscores(false); -}; - -void arenacount(dynent *d, int &alive, int &dead, char *&lastteam, bool &oneteam) -{ - if(d->state!=CS_DEAD) - { - alive++; - if(lastteam && strcmp(lastteam, d->team)) oneteam = false; - lastteam = d->team; - } - else - { - dead++; - }; -}; - -int arenarespawnwait = 0; -int arenadetectwait = 0; - -void arenarespawn() -{ - if(arenarespawnwait) - { - if(arenarespawnwait0 && (alive<=1 || (m_teammode && oneteam))) - { - conoutf("arena round is over! next round in 5 seconds..."); - if(alive) conoutf("team %s is last man standing", lastteam); - else conoutf("everyone died!"); - arenarespawnwait = lastmillis+5000; - arenadetectwait = lastmillis+10000; - player1->roll = 0; - }; - }; -}; - -void zapdynent(dynent *&d) -{ - if(d) gp()->dealloc(d, sizeof(dynent)); - d = NULL; -}; - -extern int democlientnum; - -void otherplayers() -{ - loopv(players) if(players[i]) - { - const int lagtime = lastmillis-players[i]->lastupdate; - if(lagtime>1000 && players[i]->state==CS_ALIVE) - { - players[i]->state = CS_LAGGED; - continue; - }; - if(lagtime && players[i]->state != CS_DEAD && (!demoplayback || i!=democlientnum)) moveplayer(players[i], 2, false); // use physics to extrapolate player position - }; -}; - -void respawn() -{ - if(player1->state==CS_DEAD) - { - player1->attacking = false; - if(m_arena) { conoutf("waiting for new round to start..."); return; }; - if(m_sp) { nextmode = gamemode; changemap(clientmap); return; }; // if we die in SP we try the same map again - respawnself(); - }; -}; - -int sleepwait = 0; -string sleepcmd; -void sleepf(char *msec, char *cmd) { sleepwait = atoi(msec)+lastmillis; strcpy_s(sleepcmd, cmd); }; -COMMANDN(sleep, sleepf, ARG_2STR); - -void updateworld(int millis) // main game update loop -{ - if(lastmillis) - { - curtime = millis - lastmillis; - if(sleepwait && lastmillis>sleepwait) { sleepwait = 0; execute(sleepcmd); }; - physicsframe(); - checkquad(curtime); - if(m_arena) arenarespawn(); - moveprojectiles((float)curtime); - demoplaybackstep(); - if(!demoplayback) - { - if(getclientnum()>=0) shoot(player1, worldpos); // only shoot when connected to server - gets2c(); // do this first, so we have most accurate information when our player moves - }; - otherplayers(); - if(!demoplayback) - { - monsterthink(); - if(player1->state==CS_DEAD) - { - if(lastmillis-player1->lastaction<2000) - { - player1->move = player1->strafe = 0; - moveplayer(player1, 10, false); - } - else if(!m_arena && !m_sp && lastmillis-player1->lastaction>10000) respawn(); - } - else if(!intermission) - { - moveplayer(player1, 20, true); - checkitems(); - }; - c2sinfo(player1); // do this last, to reduce the effective frame lag - }; - }; - lastmillis = millis; -}; - -void entinmap(dynent *d) // brute force but effective way to find a free spawn spot in the map -{ - loopi(100) // try max 100 times - { - float dx = (rnd(21)-10)/10.0f*i; // increasing distance - float dy = (rnd(21)-10)/10.0f*i; - d->o.x += dx; - d->o.y += dy; - if(collide(d, true, 0, 0)) return; - d->o.x -= dx; - d->o.y -= dy; - }; - conoutf("can't find entity spawn spot! (%d, %d)", (int)d->o.x, (int)d->o.y); - // leave ent at original pos, possibly stuck -}; - -int spawncycle = -1; -int fixspawn = 2; - -void spawnplayer(dynent *d) // place at random spawn. also used by monsters! -{ - int r = fixspawn-->0 ? 4 : rnd(10)+1; - loopi(r) spawncycle = findentity(PLAYERSTART, spawncycle+1); - if(spawncycle!=-1) - { - d->o.x = ents[spawncycle].x; - d->o.y = ents[spawncycle].y; - d->o.z = ents[spawncycle].z; - d->yaw = ents[spawncycle].attr1; - d->pitch = 0; - d->roll = 0; - } - else - { - d->o.x = d->o.y = (float)ssize/2; - d->o.z = 4; - }; - entinmap(d); - spawnstate(d); - d->state = CS_ALIVE; -}; - -// movement input code - -#define dir(name,v,d,s,os) void name(bool isdown) { player1->s = isdown; player1->v = isdown ? d : (player1->os ? -(d) : 0); player1->lastmove = lastmillis; }; - -dir(backward, move, -1, k_down, k_up); -dir(forward, move, 1, k_up, k_down); -dir(left, strafe, 1, k_left, k_right); -dir(right, strafe, -1, k_right, k_left); - -void attack(bool on) -{ - if(intermission) return; - if(editmode) editdrag(on); - else if(player1->attacking = on) respawn(); -}; - -void jumpn(bool on) { if(!intermission && (player1->jumpnext = on)) respawn(); }; - -COMMAND(backward, ARG_DOWN); -COMMAND(forward, ARG_DOWN); -COMMAND(left, ARG_DOWN); -COMMAND(right, ARG_DOWN); -COMMANDN(jump, jumpn, ARG_DOWN); -COMMAND(attack, ARG_DOWN); -COMMAND(showscores, ARG_DOWN); - -void fixplayer1range() -{ - const float MAXPITCH = 90.0f; - if(player1->pitch>MAXPITCH) player1->pitch = MAXPITCH; - if(player1->pitch<-MAXPITCH) player1->pitch = -MAXPITCH; - while(player1->yaw<0.0f) player1->yaw += 360.0f; - while(player1->yaw>=360.0f) player1->yaw -= 360.0f; -}; - -void mousemove(int dx, int dy) -{ - if(player1->state==CS_DEAD || intermission) return; - const float SENSF = 33.0f; // try match quake sens - player1->yaw += (dx/SENSF)*(sensitivity/(float)sensitivityscale); - player1->pitch -= (dy/SENSF)*(sensitivity/(float)sensitivityscale)*(invmouse ? -1 : 1); - fixplayer1range(); -}; - -// damage arriving from the network, monsters, yourself, all ends up here. - -void selfdamage(int damage, int actor, dynent *act) -{ - if(player1->state!=CS_ALIVE || editmode || intermission) return; - damageblend(damage); - demoblend(damage); - int ad = damage*(player1->armourtype+1)*20/100; // let armour absorb when possible - if(ad>player1->armour) ad = player1->armour; - player1->armour -= ad; - damage -= ad; - float droll = damage/0.5f; - player1->roll += player1->roll>0 ? droll : (player1->roll<0 ? -droll : (rnd(2) ? droll : -droll)); // give player a kick depending on amount of damage - if((player1->health -= damage)<=0) - { - if(actor==-2) - { - conoutf("you got killed by %s!", act->name); - } - else if(actor==-1) - { - actor = getclientnum(); - conoutf("you suicided!"); - addmsg(1, 2, SV_FRAGS, --player1->frags); - } - else - { - dynent *a = getclient(actor); - if(a) - { - if(isteam(a->team, player1->team)) - { - conoutf("you got fragged by a teammate (%s)", a->name); - } - else - { - conoutf("you got fragged by %s", a->name); - }; - }; - }; - showscores(true); - addmsg(1, 2, SV_DIED, actor); - player1->lifesequence++; - player1->attacking = false; - player1->state = CS_DEAD; - player1->pitch = 0; - player1->roll = 60; - playsound(S_DIE1+rnd(2)); - spawnstate(player1); - player1->lastaction = lastmillis; - } - else - { - playsound(S_PAIN6); - }; -}; - -void timeupdate(int timeremain) -{ - if(!timeremain) - { - intermission = true; - player1->attacking = false; - conoutf("intermission:"); - conoutf("game has ended!"); - showscores(true); - } - else - { - conoutf("time remaining: %d minutes", timeremain); - }; -}; - -dynent *getclient(int cn) // ensure valid entity -{ - if(cn<0 || cn>=MAXCLIENTS) - { - neterr("clientnum"); - return NULL; - }; - while(cn>=players.length()) players.add(NULL); - return players[cn] ? players[cn] : (players[cn] = newdynent()); -}; - -void initclient() -{ - clientmap[0] = 0; - initclientnet(); -}; - -void startmap(char *name) // called just after a map load -{ - if(netmapstart() && m_sp) { gamemode = 0; conoutf("coop sp not supported yet"); }; - sleepwait = 0; - monsterclear(); - projreset(); - spawncycle = -1; - spawnplayer(player1); - player1->frags = 0; - loopv(players) if(players[i]) players[i]->frags = 0; - resetspawns(); - strcpy_s(clientmap, name); - if(editmode) toggleedit(); - setvar("gamespeed", 100); - setvar("fog", 180); - setvar("fogcolour", 0x8099B3); - showscores(false); - intermission = false; - framesinmap = 0; - conoutf("game mode is %s", modestr(gamemode)); -}; - -COMMANDN(map, changemap, ARG_1STR); ADDED src/clientgame.cxx Index: src/clientgame.cxx ================================================================== --- /dev/null +++ src/clientgame.cxx @@ -0,0 +1,455 @@ +// clientgame.cpp: core game related stuff + +#include "cube.h" + +int nextmode = 0; // nextmode becomes gamemode after next map load +VAR(gamemode, 1, 0, 0); + +void mode(int n) { addmsg(1, 2, SV_GAMEMODE, nextmode = n); }; +COMMAND(mode, ARG_1INT); + +bool intermission = false; + +dynent *player1 = newdynent(); // our client +dvector players; // other clients + +VARP(sensitivity, 0, 10, 10000); +VARP(sensitivityscale, 1, 1, 10000); +VARP(invmouse, 0, 0, 1); + +int lastmillis = 0; +int curtime = 10; +string clientmap; + +extern int framesinmap; + +char *getclientmap() { return clientmap; }; + +void resetmovement(dynent *d) +{ + d->k_left = false; + d->k_right = false; + d->k_up = false; + d->k_down = false; + d->jumpnext = false; + d->strafe = 0; + d->move = 0; +}; + +void spawnstate(dynent *d) // reset player state not persistent accross spawns +{ + resetmovement(d); + d->vel.x = d->vel.y = d->vel.z = 0; + d->onfloor = false; + d->timeinair = 0; + d->health = 100; + d->armour = 50; + d->armourtype = A_BLUE; + d->quadmillis = 0; + d->lastattackgun = d->gunselect = GUN_SG; + d->gunwait = 0; + d->attacking = false; + d->lastaction = 0; + loopi(NUMGUNS) d->ammo[i] = 0; + d->ammo[GUN_FIST] = 1; + if(m_noitems) + { + d->gunselect = GUN_RIFLE; + d->armour = 0; + if(m_noitemsrail) + { + d->health = 1; + d->ammo[GUN_RIFLE] = 100; + } + else + { + if(gamemode==12) { d->gunselect = GUN_FIST; return; }; // eihrul's secret "instafist" mode + d->health = 256; + if(m_tarena) + { + int gun1 = rnd(4)+1; + baseammo(d->gunselect = gun1); + for(;;) + { + int gun2 = rnd(4)+1; + if(gun1!=gun2) { baseammo(gun2); break; }; + }; + } + else if(m_arena) // insta arena + { + d->ammo[GUN_RIFLE] = 100; + } + else // efficiency + { + loopi(4) baseammo(i+1); + d->gunselect = GUN_CG; + }; + d->ammo[GUN_CG] /= 2; + }; + } + else + { + d->ammo[GUN_SG] = 5; + }; +}; + +dynent *newdynent() // create a new blank player or monster +{ + dynent *d = (dynent *)gp()->alloc(sizeof(dynent)); + d->o.x = 0; + d->o.y = 0; + d->o.z = 0; + d->yaw = 270; + d->pitch = 0; + d->roll = 0; + d->maxspeed = 22; + d->outsidemap = false; + d->inwater = false; + d->radius = 1.1f; + d->eyeheight = 3.2f; + d->aboveeye = 0.7f; + d->frags = 0; + d->plag = 0; + d->ping = 0; + d->lastupdate = lastmillis; + d->enemy = NULL; + d->monsterstate = 0; + d->name[0] = d->team[0] = 0; + d->blocked = false; + d->lifesequence = 0; + d->state = CS_ALIVE; + spawnstate(d); + return d; +}; + +void respawnself() +{ + spawnplayer(player1); + showscores(false); +}; + +void arenacount(dynent *d, int &alive, int &dead, char *&lastteam, bool &oneteam) +{ + if(d->state!=CS_DEAD) + { + alive++; + if(lastteam && strcmp(lastteam, d->team)) oneteam = false; + lastteam = d->team; + } + else + { + dead++; + }; +}; + +int arenarespawnwait = 0; +int arenadetectwait = 0; + +void arenarespawn() +{ + if(arenarespawnwait) + { + if(arenarespawnwait0 && (alive<=1 || (m_teammode && oneteam))) + { + conoutf("arena round is over! next round in 5 seconds..."); + if(alive) conoutf("team %s is last man standing", lastteam); + else conoutf("everyone died!"); + arenarespawnwait = lastmillis+5000; + arenadetectwait = lastmillis+10000; + player1->roll = 0; + }; + }; +}; + +void zapdynent(dynent *&d) +{ + if(d) gp()->dealloc(d, sizeof(dynent)); + d = NULL; +}; + +extern int democlientnum; + +void otherplayers() +{ + loopv(players) if(players[i]) + { + const int lagtime = lastmillis-players[i]->lastupdate; + if(lagtime>1000 && players[i]->state==CS_ALIVE) + { + players[i]->state = CS_LAGGED; + continue; + }; + if(lagtime && players[i]->state != CS_DEAD && (!demoplayback || i!=democlientnum)) moveplayer(players[i], 2, false); // use physics to extrapolate player position + }; +}; + +void respawn() +{ + if(player1->state==CS_DEAD) + { + player1->attacking = false; + if(m_arena) { conoutf("waiting for new round to start..."); return; }; + if(m_sp) { nextmode = gamemode; changemap(clientmap); return; }; // if we die in SP we try the same map again + respawnself(); + }; +}; + +int sleepwait = 0; +string sleepcmd; +void sleepf(char *msec, char *cmd) { sleepwait = atoi(msec)+lastmillis; strcpy_s(sleepcmd, cmd); }; +COMMANDN(sleep, sleepf, ARG_2STR); + +void updateworld(int millis) // main game update loop +{ + if(lastmillis) + { + curtime = millis - lastmillis; + if(sleepwait && lastmillis>sleepwait) { sleepwait = 0; execute(sleepcmd); }; + physicsframe(); + checkquad(curtime); + if(m_arena) arenarespawn(); + moveprojectiles((float)curtime); + demoplaybackstep(); + if(!demoplayback) + { + if(getclientnum()>=0) shoot(player1, worldpos); // only shoot when connected to server + gets2c(); // do this first, so we have most accurate information when our player moves + }; + otherplayers(); + if(!demoplayback) + { + monsterthink(); + if(player1->state==CS_DEAD) + { + if(lastmillis-player1->lastaction<2000) + { + player1->move = player1->strafe = 0; + moveplayer(player1, 10, false); + } + else if(!m_arena && !m_sp && lastmillis-player1->lastaction>10000) respawn(); + } + else if(!intermission) + { + moveplayer(player1, 20, true); + checkitems(); + }; + c2sinfo(player1); // do this last, to reduce the effective frame lag + }; + }; + lastmillis = millis; +}; + +void entinmap(dynent *d) // brute force but effective way to find a free spawn spot in the map +{ + loopi(100) // try max 100 times + { + float dx = (rnd(21)-10)/10.0f*i; // increasing distance + float dy = (rnd(21)-10)/10.0f*i; + d->o.x += dx; + d->o.y += dy; + if(collide(d, true, 0, 0)) return; + d->o.x -= dx; + d->o.y -= dy; + }; + conoutf("can't find entity spawn spot! (%d, %d)", (int)d->o.x, (int)d->o.y); + // leave ent at original pos, possibly stuck +}; + +int spawncycle = -1; +int fixspawn = 2; + +void spawnplayer(dynent *d) // place at random spawn. also used by monsters! +{ + int r = fixspawn-->0 ? 4 : rnd(10)+1; + loopi(r) spawncycle = findentity(PLAYERSTART, spawncycle+1); + if(spawncycle!=-1) + { + d->o.x = ents[spawncycle].x; + d->o.y = ents[spawncycle].y; + d->o.z = ents[spawncycle].z; + d->yaw = ents[spawncycle].attr1; + d->pitch = 0; + d->roll = 0; + } + else + { + d->o.x = d->o.y = (float)ssize/2; + d->o.z = 4; + }; + entinmap(d); + spawnstate(d); + d->state = CS_ALIVE; +}; + +// movement input code + +#define dir(name,v,d,s,os) void name(bool isdown) { player1->s = isdown; player1->v = isdown ? d : (player1->os ? -(d) : 0); player1->lastmove = lastmillis; }; + +dir(backward, move, -1, k_down, k_up); +dir(forward, move, 1, k_up, k_down); +dir(left, strafe, 1, k_left, k_right); +dir(right, strafe, -1, k_right, k_left); + +void attack(bool on) +{ + if(intermission) return; + if(editmode) editdrag(on); + else if(player1->attacking = on) respawn(); +}; + +void jumpn(bool on) { if(!intermission && (player1->jumpnext = on)) respawn(); }; + +COMMAND(backward, ARG_DOWN); +COMMAND(forward, ARG_DOWN); +COMMAND(left, ARG_DOWN); +COMMAND(right, ARG_DOWN); +COMMANDN(jump, jumpn, ARG_DOWN); +COMMAND(attack, ARG_DOWN); +COMMAND(showscores, ARG_DOWN); + +void fixplayer1range() +{ + const float MAXPITCH = 90.0f; + if(player1->pitch>MAXPITCH) player1->pitch = MAXPITCH; + if(player1->pitch<-MAXPITCH) player1->pitch = -MAXPITCH; + while(player1->yaw<0.0f) player1->yaw += 360.0f; + while(player1->yaw>=360.0f) player1->yaw -= 360.0f; +}; + +void mousemove(int dx, int dy) +{ + if(player1->state==CS_DEAD || intermission) return; + const float SENSF = 33.0f; // try match quake sens + player1->yaw += (dx/SENSF)*(sensitivity/(float)sensitivityscale); + player1->pitch -= (dy/SENSF)*(sensitivity/(float)sensitivityscale)*(invmouse ? -1 : 1); + fixplayer1range(); +}; + +// damage arriving from the network, monsters, yourself, all ends up here. + +void selfdamage(int damage, int actor, dynent *act) +{ + if(player1->state!=CS_ALIVE || editmode || intermission) return; + damageblend(damage); + demoblend(damage); + int ad = damage*(player1->armourtype+1)*20/100; // let armour absorb when possible + if(ad>player1->armour) ad = player1->armour; + player1->armour -= ad; + damage -= ad; + float droll = damage/0.5f; + player1->roll += player1->roll>0 ? droll : (player1->roll<0 ? -droll : (rnd(2) ? droll : -droll)); // give player a kick depending on amount of damage + if((player1->health -= damage)<=0) + { + if(actor==-2) + { + conoutf("you got killed by %s!", act->name); + } + else if(actor==-1) + { + actor = getclientnum(); + conoutf("you suicided!"); + addmsg(1, 2, SV_FRAGS, --player1->frags); + } + else + { + dynent *a = getclient(actor); + if(a) + { + if(isteam(a->team, player1->team)) + { + conoutf("you got fragged by a teammate (%s)", a->name); + } + else + { + conoutf("you got fragged by %s", a->name); + }; + }; + }; + showscores(true); + addmsg(1, 2, SV_DIED, actor); + player1->lifesequence++; + player1->attacking = false; + player1->state = CS_DEAD; + player1->pitch = 0; + player1->roll = 60; + playsound(S_DIE1+rnd(2)); + spawnstate(player1); + player1->lastaction = lastmillis; + } + else + { + playsound(S_PAIN6); + }; +}; + +void timeupdate(int timeremain) +{ + if(!timeremain) + { + intermission = true; + player1->attacking = false; + conoutf("intermission:"); + conoutf("game has ended!"); + showscores(true); + } + else + { + conoutf("time remaining: %d minutes", timeremain); + }; +}; + +dynent *getclient(int cn) // ensure valid entity +{ + if(cn<0 || cn>=MAXCLIENTS) + { + neterr("clientnum"); + return NULL; + }; + while(cn>=players.length()) players.add(NULL); + return players[cn] ? players[cn] : (players[cn] = newdynent()); +}; + +void initclient() +{ + clientmap[0] = 0; + initclientnet(); +}; + +void startmap(char *name) // called just after a map load +{ + if(netmapstart() && m_sp) { gamemode = 0; conoutf("coop sp not supported yet"); }; + sleepwait = 0; + monsterclear(); + projreset(); + spawncycle = -1; + spawnplayer(player1); + player1->frags = 0; + loopv(players) if(players[i]) players[i]->frags = 0; + resetspawns(); + strcpy_s(clientmap, name); + if(editmode) toggleedit(); + setvar("gamespeed", 100); + setvar("fog", 180); + setvar("fogcolour", 0x8099B3); + showscores(false); + intermission = false; + framesinmap = 0; + conoutf("game mode is %s", modestr(gamemode)); +}; + +COMMANDN(map, changemap, ARG_1STR); DELETED src/clients2c.cpp Index: src/clients2c.cpp ================================================================== --- src/clients2c.cpp +++ /dev/null @@ -1,355 +0,0 @@ -// client processing of the incoming network stream - -#include "cube.h" - -extern int clientnum; -extern bool c2sinit, senditemstoserver; -extern string toservermap; -extern string clientpassword; - -void neterr(char *s) -{ - conoutf("illegal network message (%s)", s); - disconnect(); -}; - -void changemapserv(char *name, int mode) // forced map change from the server -{ - gamemode = mode; - load_world(name); -}; - -void changemap(char *name) // request map change, server may ignore -{ - strcpy_s(toservermap, name); -}; - -// update the position of other clients in the game in our world -// don't care if he's in the scenery or other players, -// just don't overlap with our client - -void updatepos(dynent *d) -{ - const float r = player1->radius+d->radius; - const float dx = player1->o.x-d->o.x; - const float dy = player1->o.y-d->o.y; - const float dz = player1->o.z-d->o.z; - const float rz = player1->aboveeye+d->eyeheight; - const float fx = (float)fabs(dx), fy = (float)fabs(dy), fz = (float)fabs(dz); - if(fxstate!=CS_DEAD) - { - if(fxo.y += dy<0 ? r-fy : -(r-fy); // push aside - else d->o.x += dx<0 ? r-fx : -(r-fx); - }; - int lagtime = lastmillis-d->lastupdate; - if(lagtime) - { - d->plag = (d->plag*5+lagtime)/6; - d->lastupdate = lastmillis; - }; -}; - -void localservertoclient(uchar *buf, int len) // processes any updates from the server -{ - if(ENET_NET_TO_HOST_16(*(ushort *)buf)!=len) neterr("packet length"); - incomingdemodata(buf, len); - - uchar *end = buf+len; - uchar *p = buf+2; - char text[MAXTRANS]; - int cn = -1, type; - dynent *d = NULL; - bool mapchanged = false; - - while(po.x = getint(p)/DMF; - d->o.y = getint(p)/DMF; - d->o.z = getint(p)/DMF; - d->yaw = getint(p)/DAF; - d->pitch = getint(p)/DAF; - d->roll = getint(p)/DAF; - d->vel.x = getint(p)/DVF; - d->vel.y = getint(p)/DVF; - d->vel.z = getint(p)/DVF; - int f = getint(p); - d->strafe = (f&3)==3 ? -1 : f&3; - f >>= 2; - d->move = (f&3)==3 ? -1 : f&3; - d->onfloor = (f>>2)&1; - int state = f>>3; - if(state==CS_DEAD && d->state!=CS_DEAD) d->lastaction = lastmillis; - d->state = state; - if(!demoplayback) updatepos(d); - break; - }; - - case SV_SOUND: - playsound(getint(p), &d->o); - break; - - case SV_TEXT: - sgetstr(); - conoutf("%s:\f %s", d->name, text); - break; - - case SV_MAPCHANGE: - sgetstr(); - changemapserv(text, getint(p)); - mapchanged = true; - break; - - case SV_ITEMLIST: - { - int n; - if(mapchanged) { senditemstoserver = false; resetspawns(); }; - while((n = getint(p))!=-1) if(mapchanged) setspawn(n, true); - break; - }; - - case SV_MAPRELOAD: // server requests next map - { - getint(p); - sprintf_sd(nextmapalias)("nextmap_%s", getclientmap()); - char *map = getalias(nextmapalias); // look up map in the cycle - changemap(map ? map : getclientmap()); - break; - }; - - case SV_INITC2S: // another client either connected or changed name/team - { - sgetstr(); - if(d->name[0]) // already connected - { - if(strcmp(d->name, text)) - conoutf("%s is now known as %s", d->name, text); - } - else // new client - { - c2sinit = false; // send new players my info again - conoutf("connected: %s", text); - }; - strcpy_s(d->name, text); - sgetstr(); - strcpy_s(d->team, text); - d->lifesequence = getint(p); - break; - }; - - case SV_CDIS: - cn = getint(p); - if(!(d = getclient(cn))) break; - conoutf("player %s disconnected", d->name[0] ? d->name : "[incompatible client]"); - zapdynent(players[cn]); - break; - - case SV_SHOT: - { - int gun = getint(p); - vec s, e; - s.x = getint(p)/DMF; - s.y = getint(p)/DMF; - s.z = getint(p)/DMF; - e.x = getint(p)/DMF; - e.y = getint(p)/DMF; - e.z = getint(p)/DMF; - if(gun==GUN_SG) createrays(s, e); - shootv(gun, s, e, d); - break; - }; - - case SV_DAMAGE: - { - int target = getint(p); - int damage = getint(p); - int ls = getint(p); - if(target==clientnum) { if(ls==player1->lifesequence) selfdamage(damage, cn, d); } - else playsound(S_PAIN1+rnd(5), &getclient(target)->o); - break; - }; - - case SV_DIED: - { - int actor = getint(p); - if(actor==cn) - { - conoutf("%s suicided", d->name); - } - else if(actor==clientnum) - { - int frags; - if(isteam(player1->team, d->team)) - { - frags = -1; - conoutf("you fragged a teammate (%s)", d->name); - } - else - { - frags = 1; - conoutf("you fragged %s", d->name); - }; - addmsg(1, 2, SV_FRAGS, player1->frags += frags); - } - else - { - dynent *a = getclient(actor); - if(a) - { - if(isteam(a->team, d->name)) - { - conoutf("%s fragged his teammate (%s)", a->name, d->name); - } - else - { - conoutf("%s fragged %s", a->name, d->name); - }; - }; - }; - playsound(S_DIE1+rnd(2), &d->o); - d->lifesequence++; - break; - }; - - case SV_FRAGS: - players[cn]->frags = getint(p); - break; - - case SV_ITEMPICKUP: - setspawn(getint(p), false); - getint(p); - break; - - case SV_ITEMSPAWN: - { - uint i = getint(p); - setspawn(i, true); - if(i>=(uint)ents.length()) break; - vec v = { ents[i].x, ents[i].y, ents[i].z }; - playsound(S_ITEMSPAWN, &v); - break; - }; - - case SV_ITEMACC: // server acknowledges that I picked up this item - realpickup(getint(p), player1); - break; - - case SV_EDITH: // coop editing messages, should be extended to include all possible editing ops - case SV_EDITT: - case SV_EDITS: - case SV_EDITD: - case SV_EDITE: - { - int x = getint(p); - int y = getint(p); - int xs = getint(p); - int ys = getint(p); - int v = getint(p); - block b = { x, y, xs, ys }; - switch(type) - { - case SV_EDITH: editheightxy(v!=0, getint(p), b); break; - case SV_EDITT: edittexxy(v, getint(p), b); break; - case SV_EDITS: edittypexy(v, b); break; - case SV_EDITD: setvdeltaxy(v, b); break; - case SV_EDITE: editequalisexy(v!=0, b); break; - }; - break; - }; - - case SV_EDITENT: // coop edit of ent - { - uint i = getint(p); - while((uint)ents.length()<=i) ents.add().type = NOTUSED; - int to = ents[i].type; - ents[i].type = getint(p); - ents[i].x = getint(p); - ents[i].y = getint(p); - ents[i].z = getint(p); - ents[i].attr1 = getint(p); - ents[i].attr2 = getint(p); - ents[i].attr3 = getint(p); - ents[i].attr4 = getint(p); - ents[i].spawned = false; - if(ents[i].type==LIGHT || to==LIGHT) calclight(); - break; - }; - - case SV_PING: - getint(p); - break; - - case SV_PONG: - addmsg(0, 2, SV_CLIENTPING, player1->ping = (player1->ping*5+lastmillis-getint(p))/6); - break; - - case SV_CLIENTPING: - players[cn]->ping = getint(p); - break; - - case SV_GAMEMODE: - nextmode = getint(p); - break; - - case SV_TIMEUP: - timeupdate(getint(p)); - break; - - case SV_RECVMAP: - { - sgetstr(); - conoutf("received map \"%s\" from server, reloading..", text); - int mapsize = getint(p); - writemap(text, mapsize, p); - p += mapsize; - changemapserv(text, gamemode); - break; - }; - - case SV_SERVMSG: - sgetstr(); - conoutf("%s", text); - break; - - case SV_EXT: // so we can messages without breaking previous clients/servers, if necessary - { - for(int n = getint(p); n; n--) getint(p); - break; - }; - - default: - neterr("type"); - return; - }; -}; ADDED src/clients2c.cxx Index: src/clients2c.cxx ================================================================== --- /dev/null +++ src/clients2c.cxx @@ -0,0 +1,355 @@ +// client processing of the incoming network stream + +#include "cube.h" + +extern int clientnum; +extern bool c2sinit, senditemstoserver; +extern string toservermap; +extern string clientpassword; + +void neterr(char *s) +{ + conoutf("illegal network message (%s)", s); + disconnect(); +}; + +void changemapserv(char *name, int mode) // forced map change from the server +{ + gamemode = mode; + load_world(name); +}; + +void changemap(char *name) // request map change, server may ignore +{ + strcpy_s(toservermap, name); +}; + +// update the position of other clients in the game in our world +// don't care if he's in the scenery or other players, +// just don't overlap with our client + +void updatepos(dynent *d) +{ + const float r = player1->radius+d->radius; + const float dx = player1->o.x-d->o.x; + const float dy = player1->o.y-d->o.y; + const float dz = player1->o.z-d->o.z; + const float rz = player1->aboveeye+d->eyeheight; + const float fx = (float)fabs(dx), fy = (float)fabs(dy), fz = (float)fabs(dz); + if(fxstate!=CS_DEAD) + { + if(fxo.y += dy<0 ? r-fy : -(r-fy); // push aside + else d->o.x += dx<0 ? r-fx : -(r-fx); + }; + int lagtime = lastmillis-d->lastupdate; + if(lagtime) + { + d->plag = (d->plag*5+lagtime)/6; + d->lastupdate = lastmillis; + }; +}; + +void localservertoclient(uchar *buf, int len) // processes any updates from the server +{ + if(ENET_NET_TO_HOST_16(*(ushort *)buf)!=len) neterr("packet length"); + incomingdemodata(buf, len); + + uchar *end = buf+len; + uchar *p = buf+2; + char text[MAXTRANS]; + int cn = -1, type; + dynent *d = NULL; + bool mapchanged = false; + + while(po.x = getint(p)/DMF; + d->o.y = getint(p)/DMF; + d->o.z = getint(p)/DMF; + d->yaw = getint(p)/DAF; + d->pitch = getint(p)/DAF; + d->roll = getint(p)/DAF; + d->vel.x = getint(p)/DVF; + d->vel.y = getint(p)/DVF; + d->vel.z = getint(p)/DVF; + int f = getint(p); + d->strafe = (f&3)==3 ? -1 : f&3; + f >>= 2; + d->move = (f&3)==3 ? -1 : f&3; + d->onfloor = (f>>2)&1; + int state = f>>3; + if(state==CS_DEAD && d->state!=CS_DEAD) d->lastaction = lastmillis; + d->state = state; + if(!demoplayback) updatepos(d); + break; + }; + + case SV_SOUND: + playsound(getint(p), &d->o); + break; + + case SV_TEXT: + sgetstr(); + conoutf("%s:\f %s", d->name, text); + break; + + case SV_MAPCHANGE: + sgetstr(); + changemapserv(text, getint(p)); + mapchanged = true; + break; + + case SV_ITEMLIST: + { + int n; + if(mapchanged) { senditemstoserver = false; resetspawns(); }; + while((n = getint(p))!=-1) if(mapchanged) setspawn(n, true); + break; + }; + + case SV_MAPRELOAD: // server requests next map + { + getint(p); + sprintf_sd(nextmapalias)("nextmap_%s", getclientmap()); + char *map = getalias(nextmapalias); // look up map in the cycle + changemap(map ? map : getclientmap()); + break; + }; + + case SV_INITC2S: // another client either connected or changed name/team + { + sgetstr(); + if(d->name[0]) // already connected + { + if(strcmp(d->name, text)) + conoutf("%s is now known as %s", d->name, text); + } + else // new client + { + c2sinit = false; // send new players my info again + conoutf("connected: %s", text); + }; + strcpy_s(d->name, text); + sgetstr(); + strcpy_s(d->team, text); + d->lifesequence = getint(p); + break; + }; + + case SV_CDIS: + cn = getint(p); + if(!(d = getclient(cn))) break; + conoutf("player %s disconnected", d->name[0] ? d->name : "[incompatible client]"); + zapdynent(players[cn]); + break; + + case SV_SHOT: + { + int gun = getint(p); + vec s, e; + s.x = getint(p)/DMF; + s.y = getint(p)/DMF; + s.z = getint(p)/DMF; + e.x = getint(p)/DMF; + e.y = getint(p)/DMF; + e.z = getint(p)/DMF; + if(gun==GUN_SG) createrays(s, e); + shootv(gun, s, e, d); + break; + }; + + case SV_DAMAGE: + { + int target = getint(p); + int damage = getint(p); + int ls = getint(p); + if(target==clientnum) { if(ls==player1->lifesequence) selfdamage(damage, cn, d); } + else playsound(S_PAIN1+rnd(5), &getclient(target)->o); + break; + }; + + case SV_DIED: + { + int actor = getint(p); + if(actor==cn) + { + conoutf("%s suicided", d->name); + } + else if(actor==clientnum) + { + int frags; + if(isteam(player1->team, d->team)) + { + frags = -1; + conoutf("you fragged a teammate (%s)", d->name); + } + else + { + frags = 1; + conoutf("you fragged %s", d->name); + }; + addmsg(1, 2, SV_FRAGS, player1->frags += frags); + } + else + { + dynent *a = getclient(actor); + if(a) + { + if(isteam(a->team, d->name)) + { + conoutf("%s fragged his teammate (%s)", a->name, d->name); + } + else + { + conoutf("%s fragged %s", a->name, d->name); + }; + }; + }; + playsound(S_DIE1+rnd(2), &d->o); + d->lifesequence++; + break; + }; + + case SV_FRAGS: + players[cn]->frags = getint(p); + break; + + case SV_ITEMPICKUP: + setspawn(getint(p), false); + getint(p); + break; + + case SV_ITEMSPAWN: + { + uint i = getint(p); + setspawn(i, true); + if(i>=(uint)ents.length()) break; + vec v = { ents[i].x, ents[i].y, ents[i].z }; + playsound(S_ITEMSPAWN, &v); + break; + }; + + case SV_ITEMACC: // server acknowledges that I picked up this item + realpickup(getint(p), player1); + break; + + case SV_EDITH: // coop editing messages, should be extended to include all possible editing ops + case SV_EDITT: + case SV_EDITS: + case SV_EDITD: + case SV_EDITE: + { + int x = getint(p); + int y = getint(p); + int xs = getint(p); + int ys = getint(p); + int v = getint(p); + block b = { x, y, xs, ys }; + switch(type) + { + case SV_EDITH: editheightxy(v!=0, getint(p), b); break; + case SV_EDITT: edittexxy(v, getint(p), b); break; + case SV_EDITS: edittypexy(v, b); break; + case SV_EDITD: setvdeltaxy(v, b); break; + case SV_EDITE: editequalisexy(v!=0, b); break; + }; + break; + }; + + case SV_EDITENT: // coop edit of ent + { + uint i = getint(p); + while((uint)ents.length()<=i) ents.add().type = NOTUSED; + int to = ents[i].type; + ents[i].type = getint(p); + ents[i].x = getint(p); + ents[i].y = getint(p); + ents[i].z = getint(p); + ents[i].attr1 = getint(p); + ents[i].attr2 = getint(p); + ents[i].attr3 = getint(p); + ents[i].attr4 = getint(p); + ents[i].spawned = false; + if(ents[i].type==LIGHT || to==LIGHT) calclight(); + break; + }; + + case SV_PING: + getint(p); + break; + + case SV_PONG: + addmsg(0, 2, SV_CLIENTPING, player1->ping = (player1->ping*5+lastmillis-getint(p))/6); + break; + + case SV_CLIENTPING: + players[cn]->ping = getint(p); + break; + + case SV_GAMEMODE: + nextmode = getint(p); + break; + + case SV_TIMEUP: + timeupdate(getint(p)); + break; + + case SV_RECVMAP: + { + sgetstr(); + conoutf("received map \"%s\" from server, reloading..", text); + int mapsize = getint(p); + writemap(text, mapsize, p); + p += mapsize; + changemapserv(text, gamemode); + break; + }; + + case SV_SERVMSG: + sgetstr(); + conoutf("%s", text); + break; + + case SV_EXT: // so we can messages without breaking previous clients/servers, if necessary + { + for(int n = getint(p); n; n--) getint(p); + break; + }; + + default: + neterr("type"); + return; + }; +}; DELETED src/command.cpp Index: src/command.cpp ================================================================== --- src/command.cpp +++ /dev/null @@ -1,363 +0,0 @@ -// command.cpp: implements the parsing and execution of a tiny script language which -// is largely backwards compatible with the quake console language. - -#include "cube.h" - -enum { ID_VAR, ID_COMMAND, ID_ALIAS }; - -struct ident -{ - int type; // one of ID_* above - char *name; - int min, max; // ID_VAR - int *storage; // ID_VAR - void (*fun)(); // ID_VAR, ID_COMMAND - int narg; // ID_VAR, ID_COMMAND - char *action; // ID_ALIAS - bool persist; -}; - -void itoa(char *s, int i) { sprintf_s(s)("%d", i); }; -char *exchangestr(char *o, char *n) { gp()->deallocstr(o); return newstring(n); }; - -hashtable *idents = NULL; // contains ALL vars/commands/aliases - -void alias(char *name, char *action) -{ - ident *b = idents->access(name); - if(!b) - { - name = newstring(name); - ident b = { ID_ALIAS, name, 0, 0, 0, 0, 0, newstring(action), true }; - idents->access(name, &b); - } - else - { - if(b->type==ID_ALIAS) b->action = exchangestr(b->action, action); - else conoutf("cannot redefine builtin %s with an alias", name); - }; -}; - -COMMAND(alias, ARG_2STR); - -// variable's and commands are registered through globals, see cube.h - -int variable(char *name, int min, int cur, int max, int *storage, void (*fun)(), bool persist) -{ - if(!idents) idents = new hashtable; - ident v = { ID_VAR, name, min, max, storage, fun, 0, 0, persist }; - idents->access(name, &v); - return cur; -}; - -void setvar(char *name, int i) { *idents->access(name)->storage = i; }; -int getvar(char *name) { return *idents->access(name)->storage; }; -bool identexists(char *name) { return idents->access(name)!=NULL; }; - -char *getalias(char *name) -{ - ident *i = idents->access(name); - return i && i->type==ID_ALIAS ? i->action : NULL; -}; - -bool addcommand(char *name, void (*fun)(), int narg) -{ - if(!idents) idents = new hashtable; - ident c = { ID_COMMAND, name, 0, 0, 0, fun, narg, 0, false }; - idents->access(name, &c); - return false; -}; - -char *parseexp(char *&p, int right) // parse any nested set of () or [] -{ - int left = *p++; - char *word = p; - for(int brak = 1; brak; ) - { - int c = *p++; - if(c=='\r') *(p-1) = ' '; // hack - if(c==left) brak++; - else if(c==right) brak--; - else if(!c) { p--; conoutf("missing \"%c\"", right); return NULL; }; - }; - char *s = newstring(word, p-word-1); - if(left=='(') - { - string t; - itoa(t, execute(s)); // evaluate () exps directly, and substitute result - s = exchangestr(s, t); - }; - return s; -}; - -char *parseword(char *&p) // parse single argument, including expressions -{ - p += strspn(p, " \t\r"); - if(p[0]=='/' && p[1]=='/') p += strcspn(p, "\n\0"); - if(*p=='\"') - { - p++; - char *word = p; - p += strcspn(p, "\"\r\n\0"); - char *s = newstring(word, p-word); - if(*p=='\"') p++; - return s; - }; - if(*p=='(') return parseexp(p, ')'); - if(*p=='[') return parseexp(p, ']'); - char *word = p; - p += strcspn(p, "; \t\r\n\0"); - if(p-word==0) return NULL; - return newstring(word, p-word); -}; - -char *lookup(char *n) // find value of ident referenced with $ in exp -{ - ident *id = idents->access(n+1); - if(id) switch(id->type) - { - case ID_VAR: string t; itoa(t, *(id->storage)); return exchangestr(n, t); - case ID_ALIAS: return exchangestr(n, id->action); - }; - conoutf("unknown alias lookup: %s", n+1); - return n; -}; - -int execute(char *p, bool isdown) // all evaluation happens here, recursively -{ - const int MAXWORDS = 25; // limit, remove - char *w[MAXWORDS]; - int val = 0; - for(bool cont = true; cont;) // for each ; seperated statement - { - int numargs = MAXWORDS; - loopi(MAXWORDS) // collect all argument values - { - w[i] = ""; - if(i>numargs) continue; - char *s = parseword(p); // parse and evaluate exps - if(!s) { numargs = i; s = ""; }; - if(*s=='$') s = lookup(s); // substitute variables - w[i] = s; - }; - - p += strcspn(p, ";\n\0"); - cont = *p++!=0; // more statements if this isn't the end of the string - char *c = w[0]; - if(*c=='/') c++; // strip irc-style command prefix - if(!*c) continue; // empty statement - - ident *id = idents->access(c); - if(!id) - { - val = ATOI(c); - if(!val && *c!='0') conoutf("unknown command: %s", c); - } - else switch(id->type) - { - case ID_COMMAND: // game defined commands - switch(id->narg) // use very ad-hoc function signature, and just call it - { - case ARG_1INT: if(isdown) ((void (__cdecl *)(int))id->fun)(ATOI(w[1])); break; - case ARG_2INT: if(isdown) ((void (__cdecl *)(int, int))id->fun)(ATOI(w[1]), ATOI(w[2])); break; - case ARG_3INT: if(isdown) ((void (__cdecl *)(int, int, int))id->fun)(ATOI(w[1]), ATOI(w[2]), ATOI(w[3])); break; - case ARG_4INT: if(isdown) ((void (__cdecl *)(int, int, int, int))id->fun)(ATOI(w[1]), ATOI(w[2]), ATOI(w[3]), ATOI(w[4])); break; - case ARG_NONE: if(isdown) ((void (__cdecl *)())id->fun)(); break; - case ARG_1STR: if(isdown) ((void (__cdecl *)(char *))id->fun)(w[1]); break; - case ARG_2STR: if(isdown) ((void (__cdecl *)(char *, char *))id->fun)(w[1], w[2]); break; - case ARG_3STR: if(isdown) ((void (__cdecl *)(char *, char *, char*))id->fun)(w[1], w[2], w[3]); break; - case ARG_5STR: if(isdown) ((void (__cdecl *)(char *, char *, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5]); break; - case ARG_DOWN: ((void (__cdecl *)(bool))id->fun)(isdown); break; - case ARG_DWN1: ((void (__cdecl *)(bool, char *))id->fun)(isdown, w[1]); break; - case ARG_1EXP: if(isdown) val = ((int (__cdecl *)(int))id->fun)(execute(w[1])); break; - case ARG_2EXP: if(isdown) val = ((int (__cdecl *)(int, int))id->fun)(execute(w[1]), execute(w[2])); break; - case ARG_1EST: if(isdown) val = ((int (__cdecl *)(char *))id->fun)(w[1]); break; - case ARG_2EST: if(isdown) val = ((int (__cdecl *)(char *, char *))id->fun)(w[1], w[2]); break; - case ARG_VARI: if(isdown) - { - string r; // limit, remove - r[0] = 0; - for(int i = 1; ifun)(r); - break; - } - }; - break; - - case ID_VAR: // game defined variabled - if(isdown) - { - if(!w[1][0]) conoutf("%s = %d", c, *id->storage); // var with no value just prints its current value - else - { - if(id->min>id->max) - { - conoutf("variable is read-only"); - } - else - { - int i1 = ATOI(w[1]); - if(i1min || i1>id->max) - { - i1 = i1min ? id->min : id->max; // clamp to valid range - conoutf("valid range for %s is %d..%d", c, id->min, id->max); - } - *id->storage = i1; - }; - if(id->fun) ((void (__cdecl *)())id->fun)(); // call trigger function if available - }; - }; - break; - - case ID_ALIAS: // alias, also used as functions and (global) variables - for(int i = 1; iaction); // create new string here because alias could rebind itself - val = execute(action, isdown); - gp()->deallocstr(action); - break; - }; - loopj(numargs) gp()->deallocstr(w[j]); - }; - return val; -}; - -// tab-completion of all idents - -int completesize = 0, completeidx = 0; - -void resetcomplete() { completesize = 0; }; - -void complete(char *s) -{ - if(*s!='/') - { - string t; - strcpy_s(t, s); - strcpy_s(s, "/"); - strcat_s(s, t); - }; - if(!s[1]) return; - if(!completesize) { completesize = (int)strlen(s)-1; completeidx = 0; }; - int idx = 0; - enumerate(idents, ident *, id, - if(strncmp(id->name, s+1, completesize)==0 && idx++==completeidx) - { - strcpy_s(s, "/"); - strcat_s(s, id->name); - }; - ); - completeidx++; - if(completeidx>=idx) completeidx = 0; -}; - -bool execfile(char *cfgfile) -{ - string s; - strcpy_s(s, cfgfile); - char *buf = loadfile(path(s), NULL); - if(!buf) return false; - execute(buf); - free(buf); - return true; -}; - -void exec(char *cfgfile) -{ - if(!execfile(cfgfile)) conoutf("could not read \"%s\"", cfgfile); -}; - -void writecfg() -{ - FILE *f = fopen("config.cfg", "w"); - if(!f) return; - fprintf(f, "// automatically written on exit, do not modify\n// delete this file to have defaults.cfg overwrite these settings\n// modify settings in game, or put settings in autoexec.cfg to override anything\n\n"); - writeclientinfo(f); - fprintf(f, "\n"); - enumerate(idents, ident *, id, - if(id->type==ID_VAR && id->persist) - { - fprintf(f, "%s %d\n", id->name, *id->storage); - }; - ); - fprintf(f, "\n"); - writebinds(f); - fprintf(f, "\n"); - enumerate(idents, ident *, id, - if(id->type==ID_ALIAS && !strstr(id->name, "nextmap_")) - { - fprintf(f, "alias \"%s\" [%s]\n", id->name, id->action); - }; - ); - fclose(f); -}; - -COMMAND(writecfg, ARG_NONE); - -// below the commands that implement a small imperative language. thanks to the semantics of -// () and [] expressions, any control construct can be defined trivially. - -void intset(char *name, int v) { string b; itoa(b, v); alias(name, b); }; - -void ifthen(char *cond, char *thenp, char *elsep) { execute(cond[0]!='0' ? thenp : elsep); }; -void loopa(char *times, char *body) { int t = atoi(times); loopi(t) { intset("i", i); execute(body); }; }; -void whilea(char *cond, char *body) { while(execute(cond)) execute(body); }; // can't get any simpler than this :) -void onrelease(bool on, char *body) { if(!on) execute(body); }; - -void concat(char *s) { alias("s", s); }; - -void concatword(char *s) -{ - for(char *a = s, *b = s; *a = *b; b++) if(*a!=' ') a++; - concat(s); -}; - -int listlen(char *a) -{ - if(!*a) return 0; - int n = 0; - while(*a) if(*a++==' ') n++; - return n+1; -}; - -void at(char *s, char *pos) -{ - int n = atoi(pos); - loopi(n) s += strspn(s += strcspn(s, " \0"), " "); - s[strcspn(s, " \0")] = 0; - concat(s); -}; - -COMMANDN(loop, loopa, ARG_2STR); -COMMANDN(while, whilea, ARG_2STR); -COMMANDN(if, ifthen, ARG_3STR); -COMMAND(onrelease, ARG_DWN1); -COMMAND(exec, ARG_1STR); -COMMAND(concat, ARG_VARI); -COMMAND(concatword, ARG_VARI); -COMMAND(at, ARG_2STR); -COMMAND(listlen, ARG_1EST); - -int add(int a, int b) { return a+b; }; COMMANDN(+, add, ARG_2EXP); -int mul(int a, int b) { return a*b; }; COMMANDN(*, mul, ARG_2EXP); -int sub(int a, int b) { return a-b; }; COMMANDN(-, sub, ARG_2EXP); -int divi(int a, int b) { return b ? a/b : 0; }; COMMANDN(div, divi, ARG_2EXP); -int mod(int a, int b) { return b ? a%b : 0; }; COMMAND(mod, ARG_2EXP); -int equal(int a, int b) { return (int)(a==b); }; COMMANDN(=, equal, ARG_2EXP); -int lt(int a, int b) { return (int)(ab); }; COMMANDN(>, gt, ARG_2EXP); - -int strcmpa(char *a, char *b) { return strcmp(a,b)==0; }; COMMANDN(strcmp, strcmpa, ARG_2EST); - -int rndn(int a) { return a>0 ? rnd(a) : 0; }; COMMANDN(rnd, rndn, ARG_1EXP); - -int explastmillis() { return lastmillis; }; COMMANDN(millis, explastmillis, ARG_1EXP); - ADDED src/command.cxx Index: src/command.cxx ================================================================== --- /dev/null +++ src/command.cxx @@ -0,0 +1,363 @@ +// command.cpp: implements the parsing and execution of a tiny script language which +// is largely backwards compatible with the quake console language. + +#include "cube.h" + +enum { ID_VAR, ID_COMMAND, ID_ALIAS }; + +struct ident +{ + int type; // one of ID_* above + char *name; + int min, max; // ID_VAR + int *storage; // ID_VAR + void (*fun)(); // ID_VAR, ID_COMMAND + int narg; // ID_VAR, ID_COMMAND + char *action; // ID_ALIAS + bool persist; +}; + +void itoa(char *s, int i) { sprintf_s(s)("%d", i); }; +char *exchangestr(char *o, char *n) { gp()->deallocstr(o); return newstring(n); }; + +hashtable *idents = NULL; // contains ALL vars/commands/aliases + +void alias(char *name, char *action) +{ + ident *b = idents->access(name); + if(!b) + { + name = newstring(name); + ident b = { ID_ALIAS, name, 0, 0, 0, 0, 0, newstring(action), true }; + idents->access(name, &b); + } + else + { + if(b->type==ID_ALIAS) b->action = exchangestr(b->action, action); + else conoutf("cannot redefine builtin %s with an alias", name); + }; +}; + +COMMAND(alias, ARG_2STR); + +// variable's and commands are registered through globals, see cube.h + +int variable(char *name, int min, int cur, int max, int *storage, void (*fun)(), bool persist) +{ + if(!idents) idents = new hashtable; + ident v = { ID_VAR, name, min, max, storage, fun, 0, 0, persist }; + idents->access(name, &v); + return cur; +}; + +void setvar(char *name, int i) { *idents->access(name)->storage = i; }; +int getvar(char *name) { return *idents->access(name)->storage; }; +bool identexists(char *name) { return idents->access(name)!=NULL; }; + +char *getalias(char *name) +{ + ident *i = idents->access(name); + return i && i->type==ID_ALIAS ? i->action : NULL; +}; + +bool addcommand(char *name, void (*fun)(), int narg) +{ + if(!idents) idents = new hashtable; + ident c = { ID_COMMAND, name, 0, 0, 0, fun, narg, 0, false }; + idents->access(name, &c); + return false; +}; + +char *parseexp(char *&p, int right) // parse any nested set of () or [] +{ + int left = *p++; + char *word = p; + for(int brak = 1; brak; ) + { + int c = *p++; + if(c=='\r') *(p-1) = ' '; // hack + if(c==left) brak++; + else if(c==right) brak--; + else if(!c) { p--; conoutf("missing \"%c\"", right); return NULL; }; + }; + char *s = newstring(word, p-word-1); + if(left=='(') + { + string t; + itoa(t, execute(s)); // evaluate () exps directly, and substitute result + s = exchangestr(s, t); + }; + return s; +}; + +char *parseword(char *&p) // parse single argument, including expressions +{ + p += strspn(p, " \t\r"); + if(p[0]=='/' && p[1]=='/') p += strcspn(p, "\n\0"); + if(*p=='\"') + { + p++; + char *word = p; + p += strcspn(p, "\"\r\n\0"); + char *s = newstring(word, p-word); + if(*p=='\"') p++; + return s; + }; + if(*p=='(') return parseexp(p, ')'); + if(*p=='[') return parseexp(p, ']'); + char *word = p; + p += strcspn(p, "; \t\r\n\0"); + if(p-word==0) return NULL; + return newstring(word, p-word); +}; + +char *lookup(char *n) // find value of ident referenced with $ in exp +{ + ident *id = idents->access(n+1); + if(id) switch(id->type) + { + case ID_VAR: string t; itoa(t, *(id->storage)); return exchangestr(n, t); + case ID_ALIAS: return exchangestr(n, id->action); + }; + conoutf("unknown alias lookup: %s", n+1); + return n; +}; + +int execute(char *p, bool isdown) // all evaluation happens here, recursively +{ + const int MAXWORDS = 25; // limit, remove + char *w[MAXWORDS]; + int val = 0; + for(bool cont = true; cont;) // for each ; seperated statement + { + int numargs = MAXWORDS; + loopi(MAXWORDS) // collect all argument values + { + w[i] = ""; + if(i>numargs) continue; + char *s = parseword(p); // parse and evaluate exps + if(!s) { numargs = i; s = ""; }; + if(*s=='$') s = lookup(s); // substitute variables + w[i] = s; + }; + + p += strcspn(p, ";\n\0"); + cont = *p++!=0; // more statements if this isn't the end of the string + char *c = w[0]; + if(*c=='/') c++; // strip irc-style command prefix + if(!*c) continue; // empty statement + + ident *id = idents->access(c); + if(!id) + { + val = ATOI(c); + if(!val && *c!='0') conoutf("unknown command: %s", c); + } + else switch(id->type) + { + case ID_COMMAND: // game defined commands + switch(id->narg) // use very ad-hoc function signature, and just call it + { + case ARG_1INT: if(isdown) ((void (__cdecl *)(int))id->fun)(ATOI(w[1])); break; + case ARG_2INT: if(isdown) ((void (__cdecl *)(int, int))id->fun)(ATOI(w[1]), ATOI(w[2])); break; + case ARG_3INT: if(isdown) ((void (__cdecl *)(int, int, int))id->fun)(ATOI(w[1]), ATOI(w[2]), ATOI(w[3])); break; + case ARG_4INT: if(isdown) ((void (__cdecl *)(int, int, int, int))id->fun)(ATOI(w[1]), ATOI(w[2]), ATOI(w[3]), ATOI(w[4])); break; + case ARG_NONE: if(isdown) ((void (__cdecl *)())id->fun)(); break; + case ARG_1STR: if(isdown) ((void (__cdecl *)(char *))id->fun)(w[1]); break; + case ARG_2STR: if(isdown) ((void (__cdecl *)(char *, char *))id->fun)(w[1], w[2]); break; + case ARG_3STR: if(isdown) ((void (__cdecl *)(char *, char *, char*))id->fun)(w[1], w[2], w[3]); break; + case ARG_5STR: if(isdown) ((void (__cdecl *)(char *, char *, char*, char*, char*))id->fun)(w[1], w[2], w[3], w[4], w[5]); break; + case ARG_DOWN: ((void (__cdecl *)(bool))id->fun)(isdown); break; + case ARG_DWN1: ((void (__cdecl *)(bool, char *))id->fun)(isdown, w[1]); break; + case ARG_1EXP: if(isdown) val = ((int (__cdecl *)(int))id->fun)(execute(w[1])); break; + case ARG_2EXP: if(isdown) val = ((int (__cdecl *)(int, int))id->fun)(execute(w[1]), execute(w[2])); break; + case ARG_1EST: if(isdown) val = ((int (__cdecl *)(char *))id->fun)(w[1]); break; + case ARG_2EST: if(isdown) val = ((int (__cdecl *)(char *, char *))id->fun)(w[1], w[2]); break; + case ARG_VARI: if(isdown) + { + string r; // limit, remove + r[0] = 0; + for(int i = 1; ifun)(r); + break; + } + }; + break; + + case ID_VAR: // game defined variabled + if(isdown) + { + if(!w[1][0]) conoutf("%s = %d", c, *id->storage); // var with no value just prints its current value + else + { + if(id->min>id->max) + { + conoutf("variable is read-only"); + } + else + { + int i1 = ATOI(w[1]); + if(i1min || i1>id->max) + { + i1 = i1min ? id->min : id->max; // clamp to valid range + conoutf("valid range for %s is %d..%d", c, id->min, id->max); + } + *id->storage = i1; + }; + if(id->fun) ((void (__cdecl *)())id->fun)(); // call trigger function if available + }; + }; + break; + + case ID_ALIAS: // alias, also used as functions and (global) variables + for(int i = 1; iaction); // create new string here because alias could rebind itself + val = execute(action, isdown); + gp()->deallocstr(action); + break; + }; + loopj(numargs) gp()->deallocstr(w[j]); + }; + return val; +}; + +// tab-completion of all idents + +int completesize = 0, completeidx = 0; + +void resetcomplete() { completesize = 0; }; + +void complete(char *s) +{ + if(*s!='/') + { + string t; + strcpy_s(t, s); + strcpy_s(s, "/"); + strcat_s(s, t); + }; + if(!s[1]) return; + if(!completesize) { completesize = (int)strlen(s)-1; completeidx = 0; }; + int idx = 0; + enumerate(idents, ident *, id, + if(strncmp(id->name, s+1, completesize)==0 && idx++==completeidx) + { + strcpy_s(s, "/"); + strcat_s(s, id->name); + }; + ); + completeidx++; + if(completeidx>=idx) completeidx = 0; +}; + +bool execfile(char *cfgfile) +{ + string s; + strcpy_s(s, cfgfile); + char *buf = loadfile(path(s), NULL); + if(!buf) return false; + execute(buf); + free(buf); + return true; +}; + +void exec(char *cfgfile) +{ + if(!execfile(cfgfile)) conoutf("could not read \"%s\"", cfgfile); +}; + +void writecfg() +{ + FILE *f = fopen("config.cfg", "w"); + if(!f) return; + fprintf(f, "// automatically written on exit, do not modify\n// delete this file to have defaults.cfg overwrite these settings\n// modify settings in game, or put settings in autoexec.cfg to override anything\n\n"); + writeclientinfo(f); + fprintf(f, "\n"); + enumerate(idents, ident *, id, + if(id->type==ID_VAR && id->persist) + { + fprintf(f, "%s %d\n", id->name, *id->storage); + }; + ); + fprintf(f, "\n"); + writebinds(f); + fprintf(f, "\n"); + enumerate(idents, ident *, id, + if(id->type==ID_ALIAS && !strstr(id->name, "nextmap_")) + { + fprintf(f, "alias \"%s\" [%s]\n", id->name, id->action); + }; + ); + fclose(f); +}; + +COMMAND(writecfg, ARG_NONE); + +// below the commands that implement a small imperative language. thanks to the semantics of +// () and [] expressions, any control construct can be defined trivially. + +void intset(char *name, int v) { string b; itoa(b, v); alias(name, b); }; + +void ifthen(char *cond, char *thenp, char *elsep) { execute(cond[0]!='0' ? thenp : elsep); }; +void loopa(char *times, char *body) { int t = atoi(times); loopi(t) { intset("i", i); execute(body); }; }; +void whilea(char *cond, char *body) { while(execute(cond)) execute(body); }; // can't get any simpler than this :) +void onrelease(bool on, char *body) { if(!on) execute(body); }; + +void concat(char *s) { alias("s", s); }; + +void concatword(char *s) +{ + for(char *a = s, *b = s; *a = *b; b++) if(*a!=' ') a++; + concat(s); +}; + +int listlen(char *a) +{ + if(!*a) return 0; + int n = 0; + while(*a) if(*a++==' ') n++; + return n+1; +}; + +void at(char *s, char *pos) +{ + int n = atoi(pos); + loopi(n) s += strspn(s += strcspn(s, " \0"), " "); + s[strcspn(s, " \0")] = 0; + concat(s); +}; + +COMMANDN(loop, loopa, ARG_2STR); +COMMANDN(while, whilea, ARG_2STR); +COMMANDN(if, ifthen, ARG_3STR); +COMMAND(onrelease, ARG_DWN1); +COMMAND(exec, ARG_1STR); +COMMAND(concat, ARG_VARI); +COMMAND(concatword, ARG_VARI); +COMMAND(at, ARG_2STR); +COMMAND(listlen, ARG_1EST); + +int add(int a, int b) { return a+b; }; COMMANDN(+, add, ARG_2EXP); +int mul(int a, int b) { return a*b; }; COMMANDN(*, mul, ARG_2EXP); +int sub(int a, int b) { return a-b; }; COMMANDN(-, sub, ARG_2EXP); +int divi(int a, int b) { return b ? a/b : 0; }; COMMANDN(div, divi, ARG_2EXP); +int mod(int a, int b) { return b ? a%b : 0; }; COMMAND(mod, ARG_2EXP); +int equal(int a, int b) { return (int)(a==b); }; COMMANDN(=, equal, ARG_2EXP); +int lt(int a, int b) { return (int)(ab); }; COMMANDN(>, gt, ARG_2EXP); + +int strcmpa(char *a, char *b) { return strcmp(a,b)==0; }; COMMANDN(strcmp, strcmpa, ARG_2EST); + +int rndn(int a) { return a>0 ? rnd(a) : 0; }; COMMANDN(rnd, rndn, ARG_1EXP); + +int explastmillis() { return lastmillis; }; COMMANDN(millis, explastmillis, ARG_1EXP); + DELETED src/console.cpp Index: src/console.cpp ================================================================== --- src/console.cpp +++ /dev/null @@ -1,254 +0,0 @@ -// console.cpp: the console buffer, its display, and command line control - -#include "cube.h" -#include - -struct cline { char *cref; int outtime; }; -vector conlines; - -const int ndraw = 5; -const int WORDWRAP = 80; -int conskip = 0; - -bool saycommandon = false; -string commandbuf; - -void setconskip(int n) -{ - conskip += n; - if(conskip<0) conskip = 0; -}; - -COMMANDN(conskip, setconskip, ARG_1INT); - -void conline(const char *sf, bool highlight) // add a line to the console buffer -{ - cline cl; - cl.cref = conlines.length()>100 ? conlines.pop().cref : newstringbuf(""); // constrain the buffer size - cl.outtime = lastmillis; // for how long to keep line on screen - conlines.insert(0,cl); - if(highlight) // show line in a different colour, for chat etc. - { - cl.cref[0] = '\f'; - cl.cref[1] = 0; - strcat_s(cl.cref, sf); - } - else - { - strcpy_s(cl.cref, sf); - }; - puts(cl.cref); - #ifndef WIN32 - fflush(stdout); - #endif -}; - -void conoutf(const char *s, ...) -{ - sprintf_sdv(sf, s); - s = sf; - int n = 0; - while(strlen(s)>WORDWRAP) // cut strings to fit on screen - { - string t; - strn0cpy(t, s, WORDWRAP+1); - conline(t, n++!=0); - s += WORDWRAP; - }; - conline(s, n!=0); -}; - -void renderconsole() // render buffer taking into account time & scrolling -{ - int nd = 0; - char *refs[ndraw]; - loopv(conlines) if(conskip ? i>=conskip-1 || i>=conlines.length()-ndraw : lastmillis-conlines[i].outtime<20000) - { - refs[nd++] = conlines[i].cref; - if(nd==ndraw) break; - }; - loopj(nd) - { - draw_text(refs[j], FONTH/3, (FONTH/4*5)*(nd-j-1)+FONTH/3, 2); - }; -}; - -// keymap is defined externally in keymap.cfg - -struct keym { int code; char *name; char *action; } keyms[256]; -int numkm = 0; - -void keymap(char *code, char *key, char *action) -{ - keyms[numkm].code = atoi(code); - keyms[numkm].name = newstring(key); - keyms[numkm++].action = newstringbuf(action); -}; - -COMMAND(keymap, ARG_3STR); - -void bindkey(char *key, char *action) -{ - for(char *x = key; *x; x++) *x = toupper(*x); - loopi(numkm) if(strcmp(keyms[i].name, key)==0) - { - strcpy_s(keyms[i].action, action); - return; - }; - conoutf("unknown key \"%s\"", key); -}; - -COMMANDN(bind, bindkey, ARG_2STR); - -void saycommand(char *init) // turns input to the command line on or off -{ - SDL_EnableUNICODE(saycommandon = (init!=NULL)); - if(!editmode) keyrepeat(saycommandon); - if(!init) init = ""; - strcpy_s(commandbuf, init); -}; - -void mapmsg(char *s) { strn0cpy(hdr.maptitle, s, 128); }; - -COMMAND(saycommand, ARG_VARI); -COMMAND(mapmsg, ARG_1STR); - -#ifndef WIN32 -#include -#include -#endif - -void pasteconsole() -{ - #ifdef WIN32 - if(!IsClipboardFormatAvailable(CF_TEXT)) return; - if(!OpenClipboard(NULL)) return; - char *cb = (char *)GlobalLock(GetClipboardData(CF_TEXT)); - strcat_s(commandbuf, cb); - GlobalUnlock(cb); - CloseClipboard(); - #else - SDL_SysWMinfo wminfo; - SDL_VERSION(&wminfo.version); - wminfo.subsystem = SDL_SYSWM_X11; - if(!SDL_GetWMInfo(&wminfo)) return; - int cbsize; - char *cb = XFetchBytes(wminfo.info.x11.display, &cbsize); - if(!cb || !cbsize) return; - int commandlen = strlen(commandbuf); - for(char *cbline = cb, *cbend; commandlen + 1 < _MAXDEFSTR && cbline < &cb[cbsize]; cbline = cbend + 1) - { - cbend = (char *)memchr(cbline, '\0', &cb[cbsize] - cbline); - if(!cbend) cbend = &cb[cbsize]; - if(commandlen + cbend - cbline + 1 > _MAXDEFSTR) cbend = cbline + _MAXDEFSTR - commandlen - 1; - memcpy(&commandbuf[commandlen], cbline, cbend - cbline); - commandlen += cbend - cbline; - commandbuf[commandlen] = '\n'; - if(commandlen + 1 < _MAXDEFSTR && cbend < &cb[cbsize]) ++commandlen; - commandbuf[commandlen] = '\0'; - }; - XFree(cb); - #endif -}; - -cvector vhistory; -int histpos = 0; - -void history(int n) -{ - static bool rec = false; - if(!rec && n>=0 && n + +struct cline { char *cref; int outtime; }; +vector conlines; + +const int ndraw = 5; +const int WORDWRAP = 80; +int conskip = 0; + +bool saycommandon = false; +string commandbuf; + +void setconskip(int n) +{ + conskip += n; + if(conskip<0) conskip = 0; +}; + +COMMANDN(conskip, setconskip, ARG_1INT); + +void conline(const char *sf, bool highlight) // add a line to the console buffer +{ + cline cl; + cl.cref = conlines.length()>100 ? conlines.pop().cref : newstringbuf(""); // constrain the buffer size + cl.outtime = lastmillis; // for how long to keep line on screen + conlines.insert(0,cl); + if(highlight) // show line in a different colour, for chat etc. + { + cl.cref[0] = '\f'; + cl.cref[1] = 0; + strcat_s(cl.cref, sf); + } + else + { + strcpy_s(cl.cref, sf); + }; + puts(cl.cref); + #ifndef _WIN32 + fflush(stdout); + #endif +}; + +void conoutf(const char *s, ...) +{ + sprintf_sdv(sf, s); + s = sf; + int n = 0; + while(strlen(s)>WORDWRAP) // cut strings to fit on screen + { + string t; + strn0cpy(t, s, WORDWRAP+1); + conline(t, n++!=0); + s += WORDWRAP; + }; + conline(s, n!=0); +}; + +void renderconsole() // render buffer taking into account time & scrolling +{ + int nd = 0; + char *refs[ndraw]; + loopv(conlines) if(conskip ? i>=conskip-1 || i>=conlines.length()-ndraw : lastmillis-conlines[i].outtime<20000) + { + refs[nd++] = conlines[i].cref; + if(nd==ndraw) break; + }; + loopj(nd) + { + draw_text(refs[j], FONTH/3, (FONTH/4*5)*(nd-j-1)+FONTH/3, 2); + }; +}; + +// keymap is defined externally in keymap.cfg + +struct keym { int code; char *name; char *action; } keyms[256]; +int numkm = 0; + +void keymap(char *code, char *key, char *action) +{ + keyms[numkm].code = atoi(code); + keyms[numkm].name = newstring(key); + keyms[numkm++].action = newstringbuf(action); +}; + +COMMAND(keymap, ARG_3STR); + +void bindkey(char *key, char *action) +{ + for(char *x = key; *x; x++) *x = toupper(*x); + loopi(numkm) if(strcmp(keyms[i].name, key)==0) + { + strcpy_s(keyms[i].action, action); + return; + }; + conoutf("unknown key \"%s\"", key); +}; + +COMMANDN(bind, bindkey, ARG_2STR); + +void saycommand(char *init) // turns input to the command line on or off +{ + SDL_EnableUNICODE(saycommandon = (init!=NULL)); + if(!editmode) keyrepeat(saycommandon); + if(!init) init = ""; + strcpy_s(commandbuf, init); +}; + +void mapmsg(char *s) { strn0cpy(hdr.maptitle, s, 128); }; + +COMMAND(saycommand, ARG_VARI); +COMMAND(mapmsg, ARG_1STR); + +#ifndef _WIN32 +#include +#include +#endif + +void pasteconsole() +{ + #ifdef _WIN32 + if(!IsClipboardFormatAvailable(CF_TEXT)) return; + if(!OpenClipboard(NULL)) return; + char *cb = (char *)GlobalLock(GetClipboardData(CF_TEXT)); + strcat_s(commandbuf, cb); + GlobalUnlock(cb); + CloseClipboard(); + #else + SDL_SysWMinfo wminfo; + SDL_VERSION(&wminfo.version); + wminfo.subsystem = SDL_SYSWM_X11; + if(!SDL_GetWMInfo(&wminfo)) return; + int cbsize; + char *cb = XFetchBytes(wminfo.info.x11.display, &cbsize); + if(!cb || !cbsize) return; + int commandlen = strlen(commandbuf); + for(char *cbline = cb, *cbend; commandlen + 1 < _MAXDEFSTR && cbline < &cb[cbsize]; cbline = cbend + 1) + { + cbend = (char *)memchr(cbline, '\0', &cb[cbsize] - cbline); + if(!cbend) cbend = &cb[cbsize]; + if(commandlen + cbend - cbline + 1 > _MAXDEFSTR) cbend = cbline + _MAXDEFSTR - commandlen - 1; + memcpy(&commandbuf[commandlen], cbline, cbend - cbline); + commandlen += cbend - cbline; + commandbuf[commandlen] = '\n'; + if(commandlen + 1 < _MAXDEFSTR && cbend < &cb[cbsize]) ++commandlen; + commandbuf[commandlen] = '\0'; + }; + XFree(cb); + #endif +}; + +cvector vhistory; +int histpos = 0; + +void history(int n) +{ + static bool rec = false; + if(!rec && n>=0 && nstate==CS_DEAD) return; // do not allow dead players to edit to avoid state confusion - if(!editmode && !allowedittoggle()) return; // not in most multiplayer modes - if(!(editmode = !editmode)) - { - settagareas(); // reset triggers to allow quick playtesting - entinmap(player1); // find spawn closest to current floating pos - } - else - { - resettagareas(); // clear trigger areas to allow them to be edited - player1->health = 100; - if(m_classicsp) monsterclear(); // all monsters back at their spawns for editing - projreset(); - }; - keyrepeat(editmode); - selset = false; - editing = editmode; -}; - -COMMANDN(edittoggle, toggleedit, ARG_NONE); - -void correctsel() // ensures above invariant -{ - selset = !OUTBORD(sel.x, sel.y); - int bsize = ssize-MINBORD; - if(sel.xs+sel.x>bsize) sel.xs = bsize-sel.x; - if(sel.ys+sel.y>bsize) sel.ys = bsize-sel.y; - if(sel.xs<=0 || sel.ys<=0) selset = false; -}; - -bool noteditmode() -{ - correctsel(); - if(!editmode) conoutf("this function is only allowed in edit mode"); - return !editmode; -}; - -bool noselection() -{ - if(!selset) conoutf("no selection"); - return !selset; -}; - -#define EDITSEL if(noteditmode() || noselection()) return; -#define EDITSELMP if(noteditmode() || noselection() || multiplayer()) return; -#define EDITMP if(noteditmode() || multiplayer()) return; - -void selectpos(int x, int y, int xs, int ys) -{ - block s = { x, y, xs, ys }; - sel = s; - selh = 0; - correctsel(); -}; - -void makesel() -{ - block s = { min(lastx,cx), min(lasty,cy), abs(lastx-cx)+1, abs(lasty-cy)+1 }; - sel = s; - selh = max(lasth,ch); - correctsel(); - if(selset) rtex = *S(sel.x, sel.y); -}; - -VAR(flrceil,0,0,2); - -float sheight(sqr *s, sqr *t, float z) // finds out z height when cursor points at wall -{ - return !flrceil //z-s->floorceil-z - ? (s->type==FHF ? s->floor-t->vdelta/4.0f : (float)s->floor) - : (s->type==CHF ? s->ceil+t->vdelta/4.0f : (float)s->ceil); -}; - -void cursorupdate() // called every frame from hud -{ - flrceil = ((int)(player1->pitch>=0))*2; - - volatile float x = worldpos.x; // volatile needed to prevent msvc7 optimizer bug? - volatile float y = worldpos.y; - volatile float z = worldpos.z; - - cx = (int)x; - cy = (int)y; - - if(OUTBORD(cx, cy)) return; - sqr *s = S(cx,cy); - - if(fabs(sheight(s,s,z)-z)>1) // selected wall - { - x += x>player1->o.x ? 0.5f : -0.5f; // find right wall cube - y += y>player1->o.y ? 0.5f : -0.5f; - - cx = (int)x; - cy = (int)y; - - if(OUTBORD(cx, cy)) return; - }; - - if(dragging) makesel(); - - const int GRIDSIZE = 5; - const float GRIDW = 0.5f; - const float GRID8 = 2.0f; - const float GRIDS = 2.0f; - const int GRIDM = 0x7; - - // render editing grid - - for(int ix = cx-GRIDSIZE; ix<=cx+GRIDSIZE; ix++) for(int iy = cy-GRIDSIZE; iy<=cy+GRIDSIZE; iy++) - { - if(OUTBORD(ix, iy)) continue; - sqr *s = S(ix,iy); - if(SOLID(s)) continue; - float h1 = sheight(s, s, z); - float h2 = sheight(s, SWS(s,1,0,ssize), z); - float h3 = sheight(s, SWS(s,1,1,ssize), z); - float h4 = sheight(s, SWS(s,0,1,ssize), z); - if(s->tag) linestyle(GRIDW, 0xFF, 0x40, 0x40); - else if(s->type==FHF || s->type==CHF) linestyle(GRIDW, 0x80, 0xFF, 0x80); - else linestyle(GRIDW, 0x80, 0x80, 0x80); - block b = { ix, iy, 1, 1 }; - box(b, h1, h2, h3, h4); - linestyle(GRID8, 0x40, 0x40, 0xFF); - if(!(ix&GRIDM)) line(ix, iy, h1, ix, iy+1, h4); - if(!(ix+1&GRIDM)) line(ix+1, iy, h2, ix+1, iy+1, h3); - if(!(iy&GRIDM)) line(ix, iy, h1, ix+1, iy, h2); - if(!(iy+1&GRIDM)) line(ix, iy+1, h4, ix+1, iy+1, h3); - }; - - if(!SOLID(s)) - { - float ih = sheight(s, s, z); - linestyle(GRIDS, 0xFF, 0xFF, 0xFF); - block b = { cx, cy, 1, 1 }; - box(b, ih, sheight(s, SWS(s,1,0,ssize), z), sheight(s, SWS(s,1,1,ssize), z), sheight(s, SWS(s,0,1,ssize), z)); - linestyle(GRIDS, 0xFF, 0x00, 0x00); - dot(cx, cy, ih); - ch = (int)ih; - }; - - if(selset) - { - linestyle(GRIDS, 0xFF, 0x40, 0x40); - box(sel, (float)selh, (float)selh, (float)selh, (float)selh); - }; -}; - -vector undos; // unlimited undo -VARP(undomegs, 0, 1, 10); // bounded by n megs - -void pruneundos(int maxremain) // bound memory -{ - int t = 0; - loopvrev(undos) - { - t += undos[i]->xs*undos[i]->ys*sizeof(sqr); - if(t>maxremain) free(undos.remove(i)); - }; -}; - -void makeundo() -{ - undos.add(blockcopy(sel)); - pruneundos(undomegs<<20); -}; - -void editundo() -{ - EDITMP; - if(undos.empty()) { conoutf("nothing more to undo"); return; }; - block *p = undos.pop(); - blockpaste(*p); - free(p); -}; - -block *copybuf = NULL; - -void copy() -{ - EDITSELMP; - if(copybuf) free(copybuf); - copybuf = blockcopy(sel); -}; - -void paste() -{ - EDITMP; - if(!copybuf) { conoutf("nothing to paste"); return; }; - sel.xs = copybuf->xs; - sel.ys = copybuf->ys; - correctsel(); - if(!selset || sel.xs!=copybuf->xs || sel.ys!=copybuf->ys) { conoutf("incorrect selection"); return; }; - makeundo(); - copybuf->x = sel.x; - copybuf->y = sel.y; - blockpaste(*copybuf); -}; - -void tofronttex() // maintain most recently used of the texture lists when applying texture -{ - loopi(3) - { - int c = curedittex[i]; - if(c>=0) - { - uchar *p = hdr.texlists[i]; - int t = p[c]; - for(int a = c-1; a>=0; a--) p[a+1] = p[a]; - p[0] = t; - curedittex[i] = -1; - }; - }; -}; - -void editdrag(bool isdown) -{ - if(dragging = isdown) - { - lastx = cx; - lasty = cy; - lasth = ch; - selset = false; - tofronttex(); - }; - makesel(); -}; - -// the core editing function. all the *xy functions perform the core operations -// and are also called directly from the network, the function below it is strictly -// triggered locally. They all have very similar structure. - -void editheightxy(bool isfloor, int amount, block &sel) -{ - loopselxy(if(isfloor) - { - s->floor += amount; - if(s->floor>=s->ceil) s->floor = s->ceil-1; - } - else - { - s->ceil += amount; - if(s->ceil<=s->floor) s->ceil = s->floor+1; - }); -}; - -void editheight(int flr, int amount) -{ - EDITSEL; - bool isfloor = flr==0; - editheightxy(isfloor, amount, sel); - addmsg(1, 7, SV_EDITH, sel.x, sel.y, sel.xs, sel.ys, isfloor, amount); -}; - -COMMAND(editheight, ARG_2INT); - -void edittexxy(int type, int t, block &sel) -{ - loopselxy(switch(type) - { - case 0: s->ftex = t; break; - case 1: s->wtex = t; break; - case 2: s->ctex = t; break; - case 3: s->utex = t; break; - }); -}; - -void edittex(int type, int dir) -{ - EDITSEL; - if(type<0 || type>3) return; - if(type!=lasttype) { tofronttex(); lasttype = type; }; - int atype = type==3 ? 1 : type; - int i = curedittex[atype]; - i = i<0 ? 0 : i+dir; - curedittex[atype] = i = min(max(i, 0), 255); - int t = lasttex = hdr.texlists[atype][i]; - edittexxy(type, t, sel); - addmsg(1, 7, SV_EDITT, sel.x, sel.y, sel.xs, sel.ys, type, t); -}; - -void replace() -{ - EDITSELMP; - loop(x,ssize) loop(y,ssize) - { - sqr *s = S(x, y); - switch(lasttype) - { - case 0: if(s->ftex == rtex.ftex) s->ftex = lasttex; break; - case 1: if(s->wtex == rtex.wtex) s->wtex = lasttex; break; - case 2: if(s->ctex == rtex.ctex) s->ctex = lasttex; break; - case 3: if(s->utex == rtex.utex) s->utex = lasttex; break; - }; - }; - block b = { 0, 0, ssize, ssize }; - remip(b); -}; - -void edittypexy(int type, block &sel) -{ - loopselxy(s->type = type); -}; - -void edittype(int type) -{ - EDITSEL; - if(type==CORNER && (sel.xs!=sel.ys || sel.xs==3 || sel.xs>4 && sel.xs!=8 - || sel.x&~-sel.xs || sel.y&~-sel.ys)) - { conoutf("corner selection must be power of 2 aligned"); return; }; - edittypexy(type, sel); - addmsg(1, 6, SV_EDITS, sel.x, sel.y, sel.xs, sel.ys, type); -}; - -void heightfield(int t) { edittype(t==0 ? FHF : CHF); }; -void solid(int t) { edittype(t==0 ? SPACE : SOLID); }; -void corner() { edittype(CORNER); }; - -COMMAND(heightfield, ARG_1INT); -COMMAND(solid, ARG_1INT); -COMMAND(corner, ARG_NONE); - -void editequalisexy(bool isfloor, block &sel) -{ - int low = 127, hi = -128; - loopselxy( - { - if(s->floorfloor; - if(s->ceil>hi) hi = s->ceil; - }); - loopselxy( - { - if(isfloor) s->floor = low; else s->ceil = hi; - if(s->floor>=s->ceil) s->floor = s->ceil-1; - }); -}; - -void equalize(int flr) -{ - bool isfloor = flr==0; - EDITSEL; - editequalisexy(isfloor, sel); - addmsg(1, 6, SV_EDITE, sel.x, sel.y, sel.xs, sel.ys, isfloor); -}; - -COMMAND(equalize, ARG_1INT); - -void setvdeltaxy(int delta, block &sel) -{ - loopselxy(s->vdelta = max(s->vdelta+delta, 0)); - remipmore(sel); -}; - -void setvdelta(int delta) -{ - EDITSEL; - setvdeltaxy(delta, sel); - addmsg(1, 6, SV_EDITD, sel.x, sel.y, sel.xs, sel.ys, delta); -}; - -const int MAXARCHVERT = 50; -int archverts[MAXARCHVERT][MAXARCHVERT]; -bool archvinit = false; - -void archvertex(int span, int vert, int delta) -{ - if(!archvinit) - { - archvinit = true; - loop(s,MAXARCHVERT) loop(v,MAXARCHVERT) archverts[s][v] = 0; - }; - if(span>=MAXARCHVERT || vert>=MAXARCHVERT || span<0 || vert<0) return; - archverts[span][vert] = delta; -}; - -void arch(int sidedelta, int _a) -{ - EDITSELMP; - sel.xs++; - sel.ys++; - if(sel.xs>MAXARCHVERT) sel.xs = MAXARCHVERT; - if(sel.ys>MAXARCHVERT) sel.ys = MAXARCHVERT; - loopselxy(s->vdelta = - sel.xs>sel.ys - ? (archverts[sel.xs-1][x] + (y==0 || y==sel.ys-1 ? sidedelta : 0)) - : (archverts[sel.ys-1][y] + (x==0 || x==sel.xs-1 ? sidedelta : 0))); - remipmore(sel); -}; - -void slope(int xd, int yd) -{ - EDITSELMP; - int off = 0; - if(xd<0) off -= xd*sel.xs; - if(yd<0) off -= yd*sel.ys; - sel.xs++; - sel.ys++; - loopselxy(s->vdelta = xd*x+yd*y+off); - remipmore(sel); -}; - -void perlin(int scale, int seed, int psize) -{ - EDITSELMP; - sel.xs++; - sel.ys++; - makeundo(); - sel.xs--; - sel.ys--; - perlinarea(sel, scale, seed, psize); - sel.xs++; - sel.ys++; - remipmore(sel); - sel.xs--; - sel.ys--; -}; - -VARF(fullbright, 0, 0, 1, - if(fullbright) - { - if(noteditmode()) return; - loopi(mipsize) world[i].r = world[i].g = world[i].b = 176; - }; -); - -void edittag(int tag) -{ - EDITSELMP; - loopselxy(s->tag = tag); -}; - -void newent(char *what, char *a1, char *a2, char *a3, char *a4) -{ - EDITSEL; - newentity(sel.x, sel.y, (int)player1->o.z, what, ATOI(a1), ATOI(a2), ATOI(a3), ATOI(a4)); -}; - -COMMANDN(select, selectpos, ARG_4INT); -COMMAND(edittag, ARG_1INT); -COMMAND(replace, ARG_NONE); -COMMAND(archvertex, ARG_3INT); -COMMAND(arch, ARG_2INT); -COMMAND(slope, ARG_2INT); -COMMANDN(vdelta, setvdelta, ARG_1INT); -COMMANDN(undo, editundo, ARG_NONE); -COMMAND(copy, ARG_NONE); -COMMAND(paste, ARG_NONE); -COMMAND(edittex, ARG_2INT); -COMMAND(newent, ARG_5STR); -COMMAND(perlin, ARG_3INT); - - ADDED src/editing.cxx Index: src/editing.cxx ================================================================== --- /dev/null +++ src/editing.cxx @@ -0,0 +1,487 @@ +// editing.cpp: most map editing commands go here, entity editing commands are in world.cpp + +#include "cube.h" + +bool editmode = false; + +// the current selection, used by almost all editing commands +// invariant: all code assumes that these are kept inside MINBORD distance of the edge of the map + +block sel = +{ + variable("selx", 0, 0, 4096, &sel.x, NULL, false), + variable("sely", 0, 0, 4096, &sel.y, NULL, false), + variable("selxs", 0, 0, 4096, &sel.xs, NULL, false), + variable("selys", 0, 0, 4096, &sel.ys, NULL, false), +}; + +int selh = 0; +bool selset = false; + +#define loopselxy(b) { makeundo(); loop(x,sel.xs) loop(y,sel.ys) { sqr *s = S(sel.x+x, sel.y+y); b; }; remip(sel); } + +int cx, cy, ch; + +int curedittex[] = { -1, -1, -1 }; + +bool dragging = false; +int lastx, lasty, lasth; + +int lasttype = 0, lasttex = 0; +sqr rtex; + +VAR(editing,0,0,1); + +void toggleedit() +{ + if(player1->state==CS_DEAD) return; // do not allow dead players to edit to avoid state confusion + if(!editmode && !allowedittoggle()) return; // not in most multiplayer modes + if(!(editmode = !editmode)) + { + settagareas(); // reset triggers to allow quick playtesting + entinmap(player1); // find spawn closest to current floating pos + } + else + { + resettagareas(); // clear trigger areas to allow them to be edited + player1->health = 100; + if(m_classicsp) monsterclear(); // all monsters back at their spawns for editing + projreset(); + }; + keyrepeat(editmode); + selset = false; + editing = editmode; +}; + +COMMANDN(edittoggle, toggleedit, ARG_NONE); + +void correctsel() // ensures above invariant +{ + selset = !OUTBORD(sel.x, sel.y); + int bsize = ssize-MINBORD; + if(sel.xs+sel.x>bsize) sel.xs = bsize-sel.x; + if(sel.ys+sel.y>bsize) sel.ys = bsize-sel.y; + if(sel.xs<=0 || sel.ys<=0) selset = false; +}; + +bool noteditmode() +{ + correctsel(); + if(!editmode) conoutf("this function is only allowed in edit mode"); + return !editmode; +}; + +bool noselection() +{ + if(!selset) conoutf("no selection"); + return !selset; +}; + +#define EDITSEL if(noteditmode() || noselection()) return; +#define EDITSELMP if(noteditmode() || noselection() || multiplayer()) return; +#define EDITMP if(noteditmode() || multiplayer()) return; + +void selectpos(int x, int y, int xs, int ys) +{ + block s = { x, y, xs, ys }; + sel = s; + selh = 0; + correctsel(); +}; + +void makesel() +{ + block s = { min(lastx,cx), min(lasty,cy), abs(lastx-cx)+1, abs(lasty-cy)+1 }; + sel = s; + selh = max(lasth,ch); + correctsel(); + if(selset) rtex = *S(sel.x, sel.y); +}; + +VAR(flrceil,0,0,2); + +float sheight(sqr *s, sqr *t, float z) // finds out z height when cursor points at wall +{ + return !flrceil //z-s->floorceil-z + ? (s->type==FHF ? s->floor-t->vdelta/4.0f : (float)s->floor) + : (s->type==CHF ? s->ceil+t->vdelta/4.0f : (float)s->ceil); +}; + +void cursorupdate() // called every frame from hud +{ + flrceil = ((int)(player1->pitch>=0))*2; + + volatile float x = worldpos.x; // volatile needed to prevent msvc7 optimizer bug? + volatile float y = worldpos.y; + volatile float z = worldpos.z; + + cx = (int)x; + cy = (int)y; + + if(OUTBORD(cx, cy)) return; + sqr *s = S(cx,cy); + + if(fabs(sheight(s,s,z)-z)>1) // selected wall + { + x += x>player1->o.x ? 0.5f : -0.5f; // find right wall cube + y += y>player1->o.y ? 0.5f : -0.5f; + + cx = (int)x; + cy = (int)y; + + if(OUTBORD(cx, cy)) return; + }; + + if(dragging) makesel(); + + const int GRIDSIZE = 5; + const float GRIDW = 0.5f; + const float GRID8 = 2.0f; + const float GRIDS = 2.0f; + const int GRIDM = 0x7; + + // render editing grid + + for(int ix = cx-GRIDSIZE; ix<=cx+GRIDSIZE; ix++) for(int iy = cy-GRIDSIZE; iy<=cy+GRIDSIZE; iy++) + { + if(OUTBORD(ix, iy)) continue; + sqr *s = S(ix,iy); + if(SOLID(s)) continue; + float h1 = sheight(s, s, z); + float h2 = sheight(s, SWS(s,1,0,ssize), z); + float h3 = sheight(s, SWS(s,1,1,ssize), z); + float h4 = sheight(s, SWS(s,0,1,ssize), z); + if(s->tag) linestyle(GRIDW, 0xFF, 0x40, 0x40); + else if(s->type==FHF || s->type==CHF) linestyle(GRIDW, 0x80, 0xFF, 0x80); + else linestyle(GRIDW, 0x80, 0x80, 0x80); + block b = { ix, iy, 1, 1 }; + box(b, h1, h2, h3, h4); + linestyle(GRID8, 0x40, 0x40, 0xFF); + if(!(ix&GRIDM)) line(ix, iy, h1, ix, iy+1, h4); + if(!(ix+1&GRIDM)) line(ix+1, iy, h2, ix+1, iy+1, h3); + if(!(iy&GRIDM)) line(ix, iy, h1, ix+1, iy, h2); + if(!(iy+1&GRIDM)) line(ix, iy+1, h4, ix+1, iy+1, h3); + }; + + if(!SOLID(s)) + { + float ih = sheight(s, s, z); + linestyle(GRIDS, 0xFF, 0xFF, 0xFF); + block b = { cx, cy, 1, 1 }; + box(b, ih, sheight(s, SWS(s,1,0,ssize), z), sheight(s, SWS(s,1,1,ssize), z), sheight(s, SWS(s,0,1,ssize), z)); + linestyle(GRIDS, 0xFF, 0x00, 0x00); + dot(cx, cy, ih); + ch = (int)ih; + }; + + if(selset) + { + linestyle(GRIDS, 0xFF, 0x40, 0x40); + box(sel, (float)selh, (float)selh, (float)selh, (float)selh); + }; +}; + +vector undos; // unlimited undo +VARP(undomegs, 0, 1, 10); // bounded by n megs + +void pruneundos(int maxremain) // bound memory +{ + int t = 0; + loopvrev(undos) + { + t += undos[i]->xs*undos[i]->ys*sizeof(sqr); + if(t>maxremain) free(undos.remove(i)); + }; +}; + +void makeundo() +{ + undos.add(blockcopy(sel)); + pruneundos(undomegs<<20); +}; + +void editundo() +{ + EDITMP; + if(undos.empty()) { conoutf("nothing more to undo"); return; }; + block *p = undos.pop(); + blockpaste(*p); + free(p); +}; + +block *copybuf = NULL; + +void copy() +{ + EDITSELMP; + if(copybuf) free(copybuf); + copybuf = blockcopy(sel); +}; + +void paste() +{ + EDITMP; + if(!copybuf) { conoutf("nothing to paste"); return; }; + sel.xs = copybuf->xs; + sel.ys = copybuf->ys; + correctsel(); + if(!selset || sel.xs!=copybuf->xs || sel.ys!=copybuf->ys) { conoutf("incorrect selection"); return; }; + makeundo(); + copybuf->x = sel.x; + copybuf->y = sel.y; + blockpaste(*copybuf); +}; + +void tofronttex() // maintain most recently used of the texture lists when applying texture +{ + loopi(3) + { + int c = curedittex[i]; + if(c>=0) + { + uchar *p = hdr.texlists[i]; + int t = p[c]; + for(int a = c-1; a>=0; a--) p[a+1] = p[a]; + p[0] = t; + curedittex[i] = -1; + }; + }; +}; + +void editdrag(bool isdown) +{ + if(dragging = isdown) + { + lastx = cx; + lasty = cy; + lasth = ch; + selset = false; + tofronttex(); + }; + makesel(); +}; + +// the core editing function. all the *xy functions perform the core operations +// and are also called directly from the network, the function below it is strictly +// triggered locally. They all have very similar structure. + +void editheightxy(bool isfloor, int amount, block &sel) +{ + loopselxy(if(isfloor) + { + s->floor += amount; + if(s->floor>=s->ceil) s->floor = s->ceil-1; + } + else + { + s->ceil += amount; + if(s->ceil<=s->floor) s->ceil = s->floor+1; + }); +}; + +void editheight(int flr, int amount) +{ + EDITSEL; + bool isfloor = flr==0; + editheightxy(isfloor, amount, sel); + addmsg(1, 7, SV_EDITH, sel.x, sel.y, sel.xs, sel.ys, isfloor, amount); +}; + +COMMAND(editheight, ARG_2INT); + +void edittexxy(int type, int t, block &sel) +{ + loopselxy(switch(type) + { + case 0: s->ftex = t; break; + case 1: s->wtex = t; break; + case 2: s->ctex = t; break; + case 3: s->utex = t; break; + }); +}; + +void edittex(int type, int dir) +{ + EDITSEL; + if(type<0 || type>3) return; + if(type!=lasttype) { tofronttex(); lasttype = type; }; + int atype = type==3 ? 1 : type; + int i = curedittex[atype]; + i = i<0 ? 0 : i+dir; + curedittex[atype] = i = min(max(i, 0), 255); + int t = lasttex = hdr.texlists[atype][i]; + edittexxy(type, t, sel); + addmsg(1, 7, SV_EDITT, sel.x, sel.y, sel.xs, sel.ys, type, t); +}; + +void replace() +{ + EDITSELMP; + loop(x,ssize) loop(y,ssize) + { + sqr *s = S(x, y); + switch(lasttype) + { + case 0: if(s->ftex == rtex.ftex) s->ftex = lasttex; break; + case 1: if(s->wtex == rtex.wtex) s->wtex = lasttex; break; + case 2: if(s->ctex == rtex.ctex) s->ctex = lasttex; break; + case 3: if(s->utex == rtex.utex) s->utex = lasttex; break; + }; + }; + block b = { 0, 0, ssize, ssize }; + remip(b); +}; + +void edittypexy(int type, block &sel) +{ + loopselxy(s->type = type); +}; + +void edittype(int type) +{ + EDITSEL; + if(type==CORNER && (sel.xs!=sel.ys || sel.xs==3 || sel.xs>4 && sel.xs!=8 + || sel.x&~-sel.xs || sel.y&~-sel.ys)) + { conoutf("corner selection must be power of 2 aligned"); return; }; + edittypexy(type, sel); + addmsg(1, 6, SV_EDITS, sel.x, sel.y, sel.xs, sel.ys, type); +}; + +void heightfield(int t) { edittype(t==0 ? FHF : CHF); }; +void solid(int t) { edittype(t==0 ? SPACE : SOLID); }; +void corner() { edittype(CORNER); }; + +COMMAND(heightfield, ARG_1INT); +COMMAND(solid, ARG_1INT); +COMMAND(corner, ARG_NONE); + +void editequalisexy(bool isfloor, block &sel) +{ + int low = 127, hi = -128; + loopselxy( + { + if(s->floorfloor; + if(s->ceil>hi) hi = s->ceil; + }); + loopselxy( + { + if(isfloor) s->floor = low; else s->ceil = hi; + if(s->floor>=s->ceil) s->floor = s->ceil-1; + }); +}; + +void equalize(int flr) +{ + bool isfloor = flr==0; + EDITSEL; + editequalisexy(isfloor, sel); + addmsg(1, 6, SV_EDITE, sel.x, sel.y, sel.xs, sel.ys, isfloor); +}; + +COMMAND(equalize, ARG_1INT); + +void setvdeltaxy(int delta, block &sel) +{ + loopselxy(s->vdelta = max(s->vdelta+delta, 0)); + remipmore(sel); +}; + +void setvdelta(int delta) +{ + EDITSEL; + setvdeltaxy(delta, sel); + addmsg(1, 6, SV_EDITD, sel.x, sel.y, sel.xs, sel.ys, delta); +}; + +const int MAXARCHVERT = 50; +int archverts[MAXARCHVERT][MAXARCHVERT]; +bool archvinit = false; + +void archvertex(int span, int vert, int delta) +{ + if(!archvinit) + { + archvinit = true; + loop(s,MAXARCHVERT) loop(v,MAXARCHVERT) archverts[s][v] = 0; + }; + if(span>=MAXARCHVERT || vert>=MAXARCHVERT || span<0 || vert<0) return; + archverts[span][vert] = delta; +}; + +void arch(int sidedelta, int _a) +{ + EDITSELMP; + sel.xs++; + sel.ys++; + if(sel.xs>MAXARCHVERT) sel.xs = MAXARCHVERT; + if(sel.ys>MAXARCHVERT) sel.ys = MAXARCHVERT; + loopselxy(s->vdelta = + sel.xs>sel.ys + ? (archverts[sel.xs-1][x] + (y==0 || y==sel.ys-1 ? sidedelta : 0)) + : (archverts[sel.ys-1][y] + (x==0 || x==sel.xs-1 ? sidedelta : 0))); + remipmore(sel); +}; + +void slope(int xd, int yd) +{ + EDITSELMP; + int off = 0; + if(xd<0) off -= xd*sel.xs; + if(yd<0) off -= yd*sel.ys; + sel.xs++; + sel.ys++; + loopselxy(s->vdelta = xd*x+yd*y+off); + remipmore(sel); +}; + +void perlin(int scale, int seed, int psize) +{ + EDITSELMP; + sel.xs++; + sel.ys++; + makeundo(); + sel.xs--; + sel.ys--; + perlinarea(sel, scale, seed, psize); + sel.xs++; + sel.ys++; + remipmore(sel); + sel.xs--; + sel.ys--; +}; + +VARF(fullbright, 0, 0, 1, + if(fullbright) + { + if(noteditmode()) return; + loopi(mipsize) world[i].r = world[i].g = world[i].b = 176; + }; +); + +void edittag(int tag) +{ + EDITSELMP; + loopselxy(s->tag = tag); +}; + +void newent(char *what, char *a1, char *a2, char *a3, char *a4) +{ + EDITSEL; + newentity(sel.x, sel.y, (int)player1->o.z, what, ATOI(a1), ATOI(a2), ATOI(a3), ATOI(a4)); +}; + +COMMANDN(select, selectpos, ARG_4INT); +COMMAND(edittag, ARG_1INT); +COMMAND(replace, ARG_NONE); +COMMAND(archvertex, ARG_3INT); +COMMAND(arch, ARG_2INT); +COMMAND(slope, ARG_2INT); +COMMANDN(vdelta, setvdelta, ARG_1INT); +COMMANDN(undo, editundo, ARG_NONE); +COMMAND(copy, ARG_NONE); +COMMAND(paste, ARG_NONE); +COMMAND(edittex, ARG_2INT); +COMMAND(newent, ARG_5STR); +COMMAND(perlin, ARG_3INT); + + DELETED src/entities.cpp Index: src/entities.cpp ================================================================== --- src/entities.cpp +++ /dev/null @@ -1,242 +0,0 @@ -// entities.cpp: map entity related functions (pickup etc.) - -#include "cube.h" - -vector ents; - -char *entmdlnames[] = -{ - "shells", "bullets", "rockets", "rrounds", "health", "boost", - "g_armour", "y_armour", "quad", "teleporter", -}; - -int triggertime = 0; - -void renderent(entity &e, char *mdlname, float z, float yaw, int frame = 0, int numf = 1, int basetime = 0, float speed = 10.0f) -{ - rendermodel(mdlname, frame, numf, 0, 1.1f, e.x, z+S(e.x, e.y)->floor, e.y, yaw, 0, false, 1.0f, speed, 0, basetime); -}; - -void renderentities() -{ - if(lastmillis>triggertime+1000) triggertime = 0; - loopv(ents) - { - entity &e = ents[i]; - if(e.type==MAPMODEL) - { - mapmodelinfo &mmi = getmminfo(e.attr2); - if(!&mmi) continue; - rendermodel(mmi.name, 0, 1, e.attr4, (float)mmi.rad, e.x, (float)S(e.x, e.y)->floor+mmi.zoff+e.attr3, e.y, (float)((e.attr1+7)-(e.attr1+7)%15), 0, false, 1.0f, 10.0f, mmi.snap); - } - else - { - if(OUTBORD(e.x, e.y)) continue; - if(e.type!=CARROT) - { - if(!e.spawned && e.type!=TELEPORT) continue; - if(e.typeTELEPORT) continue; - renderent(e, entmdlnames[e.type-I_SHELLS], (float)(1+sin(lastmillis/100.0+e.x+e.y)/20), lastmillis/10.0f); - } - else switch(e.attr2) - { - case 1: - case 3: - continue; - - case 2: - case 0: - if(!e.spawned) continue; - renderent(e, "carrot", (float)(1+sin(lastmillis/100.0+e.x+e.y)/20), lastmillis/(e.attr2 ? 1.0f : 10.0f)); - break; - - case 4: renderent(e, "switch2", 3, (float)e.attr3*90, (!e.spawned && !triggertime) ? 1 : 0, (e.spawned || !triggertime) ? 1 : 2, triggertime, 1050.0f); break; - case 5: renderent(e, "switch1", -0.15f, (float)e.attr3*90, (!e.spawned && !triggertime) ? 30 : 0, (e.spawned || !triggertime) ? 1 : 30, triggertime, 35.0f); break; - }; - }; - }; -}; - -struct itemstat { int add, max, sound; } itemstats[] = -{ - 10, 50, S_ITEMAMMO, - 20, 100, S_ITEMAMMO, - 5, 25, S_ITEMAMMO, - 5, 25, S_ITEMAMMO, - 25, 100, S_ITEMHEALTH, - 50, 200, S_ITEMHEALTH, - 100, 100, S_ITEMARMOUR, - 150, 150, S_ITEMARMOUR, - 20000, 30000, S_ITEMPUP, -}; - -void baseammo(int gun) { player1->ammo[gun] = itemstats[gun-1].add*2; }; - -// these two functions are called when the server acknowledges that you really -// picked up the item (in multiplayer someone may grab it before you). - -void radditem(int i, int &v) -{ - itemstat &is = itemstats[ents[i].type-I_SHELLS]; - ents[i].spawned = false; - v += is.add; - if(v>is.max) v = is.max; - playsoundc(is.sound); -}; - -void realpickup(int n, dynent *d) -{ - switch(ents[n].type) - { - case I_SHELLS: radditem(n, d->ammo[1]); break; - case I_BULLETS: radditem(n, d->ammo[2]); break; - case I_ROCKETS: radditem(n, d->ammo[3]); break; - case I_ROUNDS: radditem(n, d->ammo[4]); break; - case I_HEALTH: radditem(n, d->health); break; - case I_BOOST: radditem(n, d->health); break; - - case I_GREENARMOUR: - radditem(n, d->armour); - d->armourtype = A_GREEN; - break; - - case I_YELLOWARMOUR: - radditem(n, d->armour); - d->armourtype = A_YELLOW; - break; - - case I_QUAD: - radditem(n, d->quadmillis); - conoutf("you got the quad!"); - break; - }; -}; - -// these functions are called when the client touches the item - -void additem(int i, int &v, int spawnsec) -{ - if(vo.x = ents[e].x; - d->o.y = ents[e].y; - d->o.z = ents[e].z; - d->yaw = ents[e].attr1; - d->pitch = 0; - d->vel.x = d->vel.y = d->vel.z = 0; - entinmap(d); - playsoundc(S_TELEPORT); - break; - }; - }; -}; - -void pickup(int n, dynent *d) -{ - int np = 1; - loopv(players) if(players[i]) np++; - np = np<3 ? 4 : (np>4 ? 2 : 3); // spawn times are dependent on number of players - int ammo = np*2; - switch(ents[n].type) - { - case I_SHELLS: additem(n, d->ammo[1], ammo); break; - case I_BULLETS: additem(n, d->ammo[2], ammo); break; - case I_ROCKETS: additem(n, d->ammo[3], ammo); break; - case I_ROUNDS: additem(n, d->ammo[4], ammo); break; - case I_HEALTH: additem(n, d->health, np*5); break; - case I_BOOST: additem(n, d->health, 60); break; - - case I_GREENARMOUR: - // (100h/100g only absorbs 166 damage) - if(d->armourtype==A_YELLOW && d->armour>66) break; - additem(n, d->armour, 20); - break; - - case I_YELLOWARMOUR: - additem(n, d->armour, 20); - break; - - case I_QUAD: - additem(n, d->quadmillis, 60); - break; - - case CARROT: - ents[n].spawned = false; - triggertime = lastmillis; - trigger(ents[n].attr1, ents[n].attr2, false); // needs to go over server for multiplayer - break; - - case TELEPORT: - { - static int lastteleport = 0; - if(lastmillis-lastteleport<500) break; - lastteleport = lastmillis; - teleport(n, d); - break; - }; - - case JUMPPAD: - { - static int lastjumppad = 0; - if(lastmillis-lastjumppad<300) break; - lastjumppad = lastmillis; - vec v = { (int)(char)ents[n].attr3/10.0f, (int)(char)ents[n].attr2/10.0f, ents[n].attr1/10.0f }; - player1->vel.z = 0; - vadd(player1->vel, v); - playsoundc(S_JUMPPAD); - break; - }; - }; -}; - -void checkitems() -{ - if(editmode) return; - loopv(ents) - { - entity &e = ents[i]; - if(e.type==NOTUSED) continue; - if(!ents[i].spawned && e.type!=TELEPORT && e.type!=JUMPPAD) continue; - if(OUTBORD(e.x, e.y)) continue; - vec v = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight }; - vdist(dist, t, player1->o, v); - if(dist<(e.type==TELEPORT ? 4 : 2.5)) pickup(i, player1); - }; -}; - -void checkquad(int time) -{ - if(player1->quadmillis && (player1->quadmillis -= time)<0) - { - player1->quadmillis = 0; - playsoundc(S_PUPOUT); - conoutf("quad damage is over"); - }; -}; - -void putitems(uchar *&p) // puts items in network stream and also spawns them locally -{ - loopv(ents) if((ents[i].type>=I_SHELLS && ents[i].type<=I_QUAD) || ents[i].type==CARROT) - { - putint(p, i); - ents[i].spawned = true; - }; -}; - -void resetspawns() { loopv(ents) ents[i].spawned = false; }; -void setspawn(uint i, bool on) { if(i<(uint)ents.length()) ents[i].spawned = on; }; ADDED src/entities.cxx Index: src/entities.cxx ================================================================== --- /dev/null +++ src/entities.cxx @@ -0,0 +1,242 @@ +// entities.cpp: map entity related functions (pickup etc.) + +#include "cube.h" + +vector ents; + +char *entmdlnames[] = +{ + "shells", "bullets", "rockets", "rrounds", "health", "boost", + "g_armour", "y_armour", "quad", "teleporter", +}; + +int triggertime = 0; + +void renderent(entity &e, char *mdlname, float z, float yaw, int frame = 0, int numf = 1, int basetime = 0, float speed = 10.0f) +{ + rendermodel(mdlname, frame, numf, 0, 1.1f, e.x, z+S(e.x, e.y)->floor, e.y, yaw, 0, false, 1.0f, speed, 0, basetime); +}; + +void renderentities() +{ + if(lastmillis>triggertime+1000) triggertime = 0; + loopv(ents) + { + entity &e = ents[i]; + if(e.type==MAPMODEL) + { + mapmodelinfo &mmi = getmminfo(e.attr2); + if(!&mmi) continue; + rendermodel(mmi.name, 0, 1, e.attr4, (float)mmi.rad, e.x, (float)S(e.x, e.y)->floor+mmi.zoff+e.attr3, e.y, (float)((e.attr1+7)-(e.attr1+7)%15), 0, false, 1.0f, 10.0f, mmi.snap); + } + else + { + if(OUTBORD(e.x, e.y)) continue; + if(e.type!=CARROT) + { + if(!e.spawned && e.type!=TELEPORT) continue; + if(e.typeTELEPORT) continue; + renderent(e, entmdlnames[e.type-I_SHELLS], (float)(1+sin(lastmillis/100.0+e.x+e.y)/20), lastmillis/10.0f); + } + else switch(e.attr2) + { + case 1: + case 3: + continue; + + case 2: + case 0: + if(!e.spawned) continue; + renderent(e, "carrot", (float)(1+sin(lastmillis/100.0+e.x+e.y)/20), lastmillis/(e.attr2 ? 1.0f : 10.0f)); + break; + + case 4: renderent(e, "switch2", 3, (float)e.attr3*90, (!e.spawned && !triggertime) ? 1 : 0, (e.spawned || !triggertime) ? 1 : 2, triggertime, 1050.0f); break; + case 5: renderent(e, "switch1", -0.15f, (float)e.attr3*90, (!e.spawned && !triggertime) ? 30 : 0, (e.spawned || !triggertime) ? 1 : 30, triggertime, 35.0f); break; + }; + }; + }; +}; + +struct itemstat { int add, max, sound; } itemstats[] = +{ + 10, 50, S_ITEMAMMO, + 20, 100, S_ITEMAMMO, + 5, 25, S_ITEMAMMO, + 5, 25, S_ITEMAMMO, + 25, 100, S_ITEMHEALTH, + 50, 200, S_ITEMHEALTH, + 100, 100, S_ITEMARMOUR, + 150, 150, S_ITEMARMOUR, + 20000, 30000, S_ITEMPUP, +}; + +void baseammo(int gun) { player1->ammo[gun] = itemstats[gun-1].add*2; }; + +// these two functions are called when the server acknowledges that you really +// picked up the item (in multiplayer someone may grab it before you). + +void radditem(int i, int &v) +{ + itemstat &is = itemstats[ents[i].type-I_SHELLS]; + ents[i].spawned = false; + v += is.add; + if(v>is.max) v = is.max; + playsoundc(is.sound); +}; + +void realpickup(int n, dynent *d) +{ + switch(ents[n].type) + { + case I_SHELLS: radditem(n, d->ammo[1]); break; + case I_BULLETS: radditem(n, d->ammo[2]); break; + case I_ROCKETS: radditem(n, d->ammo[3]); break; + case I_ROUNDS: radditem(n, d->ammo[4]); break; + case I_HEALTH: radditem(n, d->health); break; + case I_BOOST: radditem(n, d->health); break; + + case I_GREENARMOUR: + radditem(n, d->armour); + d->armourtype = A_GREEN; + break; + + case I_YELLOWARMOUR: + radditem(n, d->armour); + d->armourtype = A_YELLOW; + break; + + case I_QUAD: + radditem(n, d->quadmillis); + conoutf("you got the quad!"); + break; + }; +}; + +// these functions are called when the client touches the item + +void additem(int i, int &v, int spawnsec) +{ + if(vo.x = ents[e].x; + d->o.y = ents[e].y; + d->o.z = ents[e].z; + d->yaw = ents[e].attr1; + d->pitch = 0; + d->vel.x = d->vel.y = d->vel.z = 0; + entinmap(d); + playsoundc(S_TELEPORT); + break; + }; + }; +}; + +void pickup(int n, dynent *d) +{ + int np = 1; + loopv(players) if(players[i]) np++; + np = np<3 ? 4 : (np>4 ? 2 : 3); // spawn times are dependent on number of players + int ammo = np*2; + switch(ents[n].type) + { + case I_SHELLS: additem(n, d->ammo[1], ammo); break; + case I_BULLETS: additem(n, d->ammo[2], ammo); break; + case I_ROCKETS: additem(n, d->ammo[3], ammo); break; + case I_ROUNDS: additem(n, d->ammo[4], ammo); break; + case I_HEALTH: additem(n, d->health, np*5); break; + case I_BOOST: additem(n, d->health, 60); break; + + case I_GREENARMOUR: + // (100h/100g only absorbs 166 damage) + if(d->armourtype==A_YELLOW && d->armour>66) break; + additem(n, d->armour, 20); + break; + + case I_YELLOWARMOUR: + additem(n, d->armour, 20); + break; + + case I_QUAD: + additem(n, d->quadmillis, 60); + break; + + case CARROT: + ents[n].spawned = false; + triggertime = lastmillis; + trigger(ents[n].attr1, ents[n].attr2, false); // needs to go over server for multiplayer + break; + + case TELEPORT: + { + static int lastteleport = 0; + if(lastmillis-lastteleport<500) break; + lastteleport = lastmillis; + teleport(n, d); + break; + }; + + case JUMPPAD: + { + static int lastjumppad = 0; + if(lastmillis-lastjumppad<300) break; + lastjumppad = lastmillis; + vec v = { (int)(char)ents[n].attr3/10.0f, (int)(char)ents[n].attr2/10.0f, ents[n].attr1/10.0f }; + player1->vel.z = 0; + vadd(player1->vel, v); + playsoundc(S_JUMPPAD); + break; + }; + }; +}; + +void checkitems() +{ + if(editmode) return; + loopv(ents) + { + entity &e = ents[i]; + if(e.type==NOTUSED) continue; + if(!ents[i].spawned && e.type!=TELEPORT && e.type!=JUMPPAD) continue; + if(OUTBORD(e.x, e.y)) continue; + vec v = { e.x, e.y, S(e.x, e.y)->floor+player1->eyeheight }; + vdist(dist, t, player1->o, v); + if(dist<(e.type==TELEPORT ? 4 : 2.5)) pickup(i, player1); + }; +}; + +void checkquad(int time) +{ + if(player1->quadmillis && (player1->quadmillis -= time)<0) + { + player1->quadmillis = 0; + playsoundc(S_PUPOUT); + conoutf("quad damage is over"); + }; +}; + +void putitems(uchar *&p) // puts items in network stream and also spawns them locally +{ + loopv(ents) if((ents[i].type>=I_SHELLS && ents[i].type<=I_QUAD) || ents[i].type==CARROT) + { + putint(p, i); + ents[i].spawned = true; + }; +}; + +void resetspawns() { loopv(ents) ents[i].spawned = false; }; +void setspawn(uint i, bool on) { if(i<(uint)ents.length()) ents[i].spawned = on; }; DELETED src/main.cpp Index: src/main.cpp ================================================================== --- src/main.cpp +++ /dev/null @@ -1,237 +0,0 @@ -// main.cpp: initialisation & main loop - -#include "cube.h" - -void cleanup(char *msg) // single program exit point; -{ - stop(); - disconnect(true); - writecfg(); - cleangl(); - cleansound(); - cleanupserver(); - SDL_ShowCursor(1); - if(msg) - { - #ifdef WIN32 - MessageBox(NULL, msg, "cube fatal error", MB_OK|MB_SYSTEMMODAL); - #else - printf(msg); - #endif - }; - SDL_Quit(); - exit(1); -}; - -void quit() // normal exit -{ - writeservercfg(); - cleanup(NULL); -}; - -void fatal(char *s, char *o) // failure exit -{ - sprintf_sd(msg)("%s%s (%s)\n", s, o, SDL_GetError()); - cleanup(msg); -}; - -void *alloc(int s) // for some big chunks... most other allocs use the memory pool -{ - void *b = calloc(1,s); - if(!b) fatal("out of memory!"); - return b; -}; - -int scr_w = 640; -int scr_h = 480; - -void screenshot() -{ - SDL_Surface *image; - SDL_Surface *temp; - int idx; - if(image = SDL_CreateRGBSurface(SDL_SWSURFACE, scr_w, scr_h, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0)) - { - if(temp = SDL_CreateRGBSurface(SDL_SWSURFACE, scr_w, scr_h, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0)) - { - glReadPixels(0, 0, scr_w, scr_h, GL_RGB, GL_UNSIGNED_BYTE, image->pixels); - for (idx = 0; idxpixels+3*scr_w*idx; - memcpy(dest, (char *)image->pixels+3*scr_w*(scr_h-1-idx), 3*scr_w); - endianswap(dest, 3, scr_w); - }; - sprintf_sd(buf)("screenshots/screenshot_%d.bmp", lastmillis); - SDL_SaveBMP(temp, path(buf)); - SDL_FreeSurface(temp); - }; - SDL_FreeSurface(image); - }; -}; - -COMMAND(screenshot, ARG_NONE); -COMMAND(quit, ARG_NONE); - -void keyrepeat(bool on) -{ - SDL_EnableKeyRepeat(on ? SDL_DEFAULT_REPEAT_DELAY : 0, - SDL_DEFAULT_REPEAT_INTERVAL); -}; - -VARF(gamespeed, 10, 100, 1000, if(multiplayer()) gamespeed = 100); -VARP(minmillis, 0, 5, 1000); - -int islittleendian = 1; -int framesinmap = 0; - -int main(int argc, char **argv) -{ - bool dedicated = false; - int fs = SDL_FULLSCREEN, par = 0, uprate = 0, maxcl = 4; - char *sdesc = "", *ip = "", *master = NULL, *passwd = ""; - islittleendian = *((char *)&islittleendian); - - #define log(s) conoutf("init: %s", s) - log("sdl"); - - for(int i = 1; i200) lastmillis = millis-200; - else if(millis-lastmillis<1) lastmillis = millis-1; - if(millis-lastmilliso.x, player1->o.y); - readdepth(scr_w, scr_h); - SDL_GL_SwapBuffers(); - extern void updatevol(); updatevol(); - if(framesinmap++<5) // cheap hack to get rid of initial sparklies, even when triple buffering etc. - { - player1->yaw += 5; - gl_drawframe(scr_w, scr_h, fps); - player1->yaw -= 5; - }; - gl_drawframe(scr_w, scr_h, fps); - SDL_Event event; - int lasttype = 0, lastbut = 0; - while(SDL_PollEvent(&event)) - { - switch(event.type) - { - case SDL_QUIT: - quit(); - break; - - case SDL_KEYDOWN: - case SDL_KEYUP: - keypress(event.key.keysym.sym, event.key.state==SDL_PRESSED, event.key.keysym.unicode); - break; - - case SDL_MOUSEMOTION: - if(ignore) { ignore--; break; }; - mousemove(event.motion.xrel, event.motion.yrel); - break; - - case SDL_MOUSEBUTTONDOWN: - case SDL_MOUSEBUTTONUP: - if(lasttype==event.type && lastbut==event.button.button) break; // why?? get event twice without it - keypress(-event.button.button, event.button.state!=0, 0); - lasttype = event.type; - lastbut = event.button.button; - break; - }; - }; - }; - quit(); - return 1; -}; - - ADDED src/main.cxx Index: src/main.cxx ================================================================== --- /dev/null +++ src/main.cxx @@ -0,0 +1,237 @@ +// main.cpp: initialisation & main loop + +#include "cube.h" + +void cleanup(char *msg) // single program exit point; +{ + stop(); + disconnect(true); + writecfg(); + cleangl(); + cleansound(); + cleanupserver(); + SDL_ShowCursor(1); + if(msg) + { + #ifdef _WIN32 + MessageBox(NULL, msg, "cube fatal error", MB_OK|MB_SYSTEMMODAL); + #else + printf(msg); + #endif + }; + SDL_Quit(); + exit(1); +}; + +void quit() // normal exit +{ + writeservercfg(); + cleanup(NULL); +}; + +void fatal(char *s, char *o) // failure exit +{ + sprintf_sd(msg)("%s%s (%s)\n", s, o, SDL_GetError()); + cleanup(msg); +}; + +void *alloc(int s) // for some big chunks... most other allocs use the memory pool +{ + void *b = calloc(1,s); + if(!b) fatal("out of memory!"); + return b; +}; + +int scr_w = 640; +int scr_h = 480; + +void screenshot() +{ + SDL_Surface *image; + SDL_Surface *temp; + int idx; + if(image = SDL_CreateRGBSurface(SDL_SWSURFACE, scr_w, scr_h, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0)) + { + if(temp = SDL_CreateRGBSurface(SDL_SWSURFACE, scr_w, scr_h, 24, 0x0000FF, 0x00FF00, 0xFF0000, 0)) + { + glReadPixels(0, 0, scr_w, scr_h, GL_RGB, GL_UNSIGNED_BYTE, image->pixels); + for (idx = 0; idxpixels+3*scr_w*idx; + memcpy(dest, (char *)image->pixels+3*scr_w*(scr_h-1-idx), 3*scr_w); + endianswap(dest, 3, scr_w); + }; + sprintf_sd(buf)("screenshots/screenshot_%d.bmp", lastmillis); + SDL_SaveBMP(temp, path(buf)); + SDL_FreeSurface(temp); + }; + SDL_FreeSurface(image); + }; +}; + +COMMAND(screenshot, ARG_NONE); +COMMAND(quit, ARG_NONE); + +void keyrepeat(bool on) +{ + SDL_EnableKeyRepeat(on ? SDL_DEFAULT_REPEAT_DELAY : 0, + SDL_DEFAULT_REPEAT_INTERVAL); +}; + +VARF(gamespeed, 10, 100, 1000, if(multiplayer()) gamespeed = 100); +VARP(minmillis, 0, 5, 1000); + +int islittleendian = 1; +int framesinmap = 0; + +int main(int argc, char **argv) +{ + bool dedicated = false; + int fs = SDL_FULLSCREEN, par = 0, uprate = 0, maxcl = 4; + char *sdesc = "", *ip = "", *master = NULL, *passwd = ""; + islittleendian = *((char *)&islittleendian); + + #define log(s) conoutf("init: %s", s) + log("sdl"); + + for(int i = 1; i200) lastmillis = millis-200; + else if(millis-lastmillis<1) lastmillis = millis-1; + if(millis-lastmilliso.x, player1->o.y); + readdepth(scr_w, scr_h); + SDL_GL_SwapBuffers(); + extern void updatevol(); updatevol(); + if(framesinmap++<5) // cheap hack to get rid of initial sparklies, even when triple buffering etc. + { + player1->yaw += 5; + gl_drawframe(scr_w, scr_h, fps); + player1->yaw -= 5; + }; + gl_drawframe(scr_w, scr_h, fps); + SDL_Event event; + int lasttype = 0, lastbut = 0; + while(SDL_PollEvent(&event)) + { + switch(event.type) + { + case SDL_QUIT: + quit(); + break; + + case SDL_KEYDOWN: + case SDL_KEYUP: + keypress(event.key.keysym.sym, event.key.state==SDL_PRESSED, event.key.keysym.unicode); + break; + + case SDL_MOUSEMOTION: + if(ignore) { ignore--; break; }; + mousemove(event.motion.xrel, event.motion.yrel); + break; + + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + if(lasttype==event.type && lastbut==event.button.button) break; // why?? get event twice without it + keypress(-event.button.button, event.button.state!=0, 0); + lasttype = event.type; + lastbut = event.button.button; + break; + }; + }; + }; + quit(); + return 1; +}; + + DELETED src/menus.cpp Index: src/menus.cpp ================================================================== --- src/menus.cpp +++ /dev/null @@ -1,145 +0,0 @@ -// menus.cpp: ingame menu system (also used for scores and serverlist) - -#include "cube.h" - -struct mitem { char *text, *action; }; - -struct gmenu -{ - char *name; - vector items; - int mwidth; - int menusel; -}; - -vector menus; - -int vmenu = -1; - -ivector menustack; - -void menuset(int menu) -{ - if((vmenu = menu)>=1) resetmovement(player1); - if(vmenu==1) menus[1].menusel = 0; -}; - -void showmenu(char *name) -{ - loopv(menus) if(i>1 && strcmp(menus[i].name, name)==0) - { - menuset(i); - return; - }; -}; - -int menucompare(mitem *a, mitem *b) -{ - int x = atoi(a->text); - int y = atoi(b->text); - if(x>y) return -1; - if(x1 ? "[ %s menu ]" : "%s", m.name); - int mdisp = m.items.length(); - int w = 0; - loopi(mdisp) - { - int x = text_width(m.items[i].text); - if(x>w) w = x; - }; - int tw = text_width(title); - if(tw>w) w = tw; - int step = FONTH/4*5; - int h = (mdisp+2)*step; - int y = (VIRTH-h)/2; - int x = (VIRTW-w)/2; - blendbox(x-FONTH/2*3, y-FONTH, x+w+FONTH/2*3, y+h+FONTH, true); - draw_text(title, x, y,2); - y += FONTH*2; - if(vmenu) - { - int bh = y+m.menusel*step; - blendbox(x-FONTH, bh-10, x+w+FONTH, bh+FONTH+10, false); - }; - loopj(mdisp) - { - draw_text(m.items[j].text, x, y, 2); - y += step; - }; - return true; -}; - -void newmenu(char *name) -{ - gmenu &menu = menus.add(); - menu.name = newstring(name); - menu.menusel = 0; -}; - -void menumanual(int m, int n, char *text) -{ - if(!n) menus[m].items.setsize(0); - mitem &mitem = menus[m].items.add(); - mitem.text = text; - mitem.action = ""; -} - -void menuitem(char *text, char *action) -{ - gmenu &menu = menus.last(); - mitem &mi = menu.items.add(); - mi.text = newstring(text); - mi.action = action[0] ? newstring(action) : mi.text; -}; - -COMMAND(menuitem, ARG_2STR); -COMMAND(showmenu, ARG_1STR); -COMMAND(newmenu, ARG_1STR); - -bool menukey(int code, bool isdown) -{ - if(vmenu<=0) return false; - int menusel = menus[vmenu].menusel; - if(isdown) - { - if(code==SDLK_ESCAPE) - { - menuset(-1); - if(!menustack.empty()) menuset(menustack.pop()); - return true; - } - else if(code==SDLK_UP || code==-4) menusel--; - else if(code==SDLK_DOWN || code==-5) menusel++; - int n = menus[vmenu].items.length(); - if(menusel<0) menusel = n-1; - else if(menusel>=n) menusel = 0; - menus[vmenu].menusel = menusel; - } - else - { - if(code==SDLK_RETURN || code==-2) - { - char *action = menus[vmenu].items[menusel].action; - if(vmenu==1) connects(getservername(menusel)); - menustack.add(vmenu); - menuset(-1); - execute(action, true); - }; - }; - return true; -}; ADDED src/menus.cxx Index: src/menus.cxx ================================================================== --- /dev/null +++ src/menus.cxx @@ -0,0 +1,145 @@ +// menus.cpp: ingame menu system (also used for scores and serverlist) + +#include "cube.h" + +struct mitem { char *text, *action; }; + +struct gmenu +{ + char *name; + vector items; + int mwidth; + int menusel; +}; + +vector menus; + +int vmenu = -1; + +ivector menustack; + +void menuset(int menu) +{ + if((vmenu = menu)>=1) resetmovement(player1); + if(vmenu==1) menus[1].menusel = 0; +}; + +void showmenu(char *name) +{ + loopv(menus) if(i>1 && strcmp(menus[i].name, name)==0) + { + menuset(i); + return; + }; +}; + +int menucompare(mitem *a, mitem *b) +{ + int x = atoi(a->text); + int y = atoi(b->text); + if(x>y) return -1; + if(x1 ? "[ %s menu ]" : "%s", m.name); + int mdisp = m.items.length(); + int w = 0; + loopi(mdisp) + { + int x = text_width(m.items[i].text); + if(x>w) w = x; + }; + int tw = text_width(title); + if(tw>w) w = tw; + int step = FONTH/4*5; + int h = (mdisp+2)*step; + int y = (VIRTH-h)/2; + int x = (VIRTW-w)/2; + blendbox(x-FONTH/2*3, y-FONTH, x+w+FONTH/2*3, y+h+FONTH, true); + draw_text(title, x, y,2); + y += FONTH*2; + if(vmenu) + { + int bh = y+m.menusel*step; + blendbox(x-FONTH, bh-10, x+w+FONTH, bh+FONTH+10, false); + }; + loopj(mdisp) + { + draw_text(m.items[j].text, x, y, 2); + y += step; + }; + return true; +}; + +void newmenu(char *name) +{ + gmenu &menu = menus.add(); + menu.name = newstring(name); + menu.menusel = 0; +}; + +void menumanual(int m, int n, char *text) +{ + if(!n) menus[m].items.setsize(0); + mitem &mitem = menus[m].items.add(); + mitem.text = text; + mitem.action = ""; +} + +void menuitem(char *text, char *action) +{ + gmenu &menu = menus.last(); + mitem &mi = menu.items.add(); + mi.text = newstring(text); + mi.action = action[0] ? newstring(action) : mi.text; +}; + +COMMAND(menuitem, ARG_2STR); +COMMAND(showmenu, ARG_1STR); +COMMAND(newmenu, ARG_1STR); + +bool menukey(int code, bool isdown) +{ + if(vmenu<=0) return false; + int menusel = menus[vmenu].menusel; + if(isdown) + { + if(code==SDLK_ESCAPE) + { + menuset(-1); + if(!menustack.empty()) menuset(menustack.pop()); + return true; + } + else if(code==SDLK_UP || code==-4) menusel--; + else if(code==SDLK_DOWN || code==-5) menusel++; + int n = menus[vmenu].items.length(); + if(menusel<0) menusel = n-1; + else if(menusel>=n) menusel = 0; + menus[vmenu].menusel = menusel; + } + else + { + if(code==SDLK_RETURN || code==-2) + { + char *action = menus[vmenu].items[menusel].action; + if(vmenu==1) connects(getservername(menusel)); + menustack.add(vmenu); + menuset(-1); + execute(action, true); + }; + }; + return true; +}; DELETED src/monster.cpp Index: src/monster.cpp ================================================================== --- src/monster.cpp +++ /dev/null @@ -1,338 +0,0 @@ -// monster.cpp: implements AI for single player monsters, currently client only - -#include "cube.h" - -dvector monsters; -int nextmonster, spawnremain, numkilled, monstertotal, mtimestart; - -VARF(skill, 1, 3, 10, conoutf("skill is now %d", skill)); - -dvector &getmonsters() { return monsters; }; -void restoremonsterstate() { loopv(monsters) if(monsters[i]->state==CS_DEAD) numkilled++; }; // for savegames - -#define TOTMFREQ 13 -#define NUMMONSTERTYPES 8 - -struct monstertype // see docs for how these values modify behaviour -{ - short gun, speed, health, freq, lag, rate, pain, loyalty, mscale, bscale; - short painsound, diesound; - char *name, *mdlname; -} - -monstertypes[NUMMONSTERTYPES] = -{ - { GUN_FIREBALL, 15, 100, 3, 0, 100, 800, 1, 10, 10, S_PAINO, S_DIE1, "an ogre", "monster/ogro" }, - { GUN_CG, 18, 70, 2, 70, 10, 400, 2, 8, 9, S_PAINR, S_DEATHR, "a rhino", "monster/rhino" }, - { GUN_SG, 14, 120, 1, 100, 300, 400, 4, 14, 14, S_PAINE, S_DEATHE, "ratamahatta", "monster/rat" }, - { GUN_RIFLE, 15, 200, 1, 80, 300, 300, 4, 18, 18, S_PAINS, S_DEATHS, "a slith", "monster/slith" }, - { GUN_RL, 13, 500, 1, 0, 100, 200, 6, 24, 24, S_PAINB, S_DEATHB, "bauul", "monster/bauul" }, - { GUN_BITE, 22, 50, 3, 0, 100, 400, 1, 12, 15, S_PAINP, S_PIGGR2, "a hellpig", "monster/hellpig" }, - { GUN_ICEBALL, 12, 250, 1, 0, 10, 400, 6, 18, 18, S_PAINH, S_DEATHH, "a knight", "monster/knight" }, - { GUN_SLIMEBALL, 15, 100, 1, 0, 200, 400, 2, 13, 10, S_PAIND, S_DEATHD, "a goblin", "monster/goblin" }, -}; - -dynent *basicmonster(int type, int yaw, int state, int trigger, int move) -{ - if(type>=NUMMONSTERTYPES) - { - conoutf("warning: unknown monster in spawn: %d", type); - type = 0; - }; - dynent *m = newdynent(); - monstertype *t = &monstertypes[m->mtype = type]; - m->eyeheight = 2.0f; - m->aboveeye = 1.9f; - m->radius *= t->bscale/10.0f; - m->eyeheight *= t->bscale/10.0f; - m->aboveeye *= t->bscale/10.0f; - m->monsterstate = state; - if(state!=M_SLEEP) spawnplayer(m); - m->trigger = lastmillis+trigger; - m->targetyaw = m->yaw = (float)yaw; - m->move = move; - m->enemy = player1; - m->gunselect = t->gun; - m->maxspeed = (float)t->speed; - m->health = t->health; - m->armour = 0; - loopi(NUMGUNS) m->ammo[i] = 10000; - m->pitch = 0; - m->roll = 0; - m->state = CS_ALIVE; - m->anger = 0; - strcpy_s(m->name, t->name); - monsters.add(m); - return m; -}; - -void spawnmonster() // spawn a random monster according to freq distribution in DMSP -{ - int n = rnd(TOTMFREQ), type; - for(int i = 0; ; i++) if((n -= monstertypes[i].freq)<0) { type = i; break; }; - basicmonster(type, rnd(360), M_SEARCH, 1000, 1); -}; - -void monsterclear() // called after map start of when toggling edit mode to reset/spawn all monsters to initial state -{ - loopv(monsters) gp()->dealloc(monsters[i], sizeof(dynent)); - monsters.setsize(0); - numkilled = 0; - monstertotal = 0; - spawnremain = 0; - if(m_dmsp) - { - nextmonster = mtimestart = lastmillis+10000; - monstertotal = spawnremain = gamemode<0 ? skill*10 : 0; - } - else if(m_classicsp) - { - mtimestart = lastmillis; - loopv(ents) if(ents[i].type==MONSTER) - { - dynent *m = basicmonster(ents[i].attr2, ents[i].attr1, M_SLEEP, 100, 0); - m->o.x = ents[i].x; - m->o.y = ents[i].y; - m->o.z = ents[i].z; - entinmap(m); - monstertotal++; - }; - }; -}; - -bool los(float lx, float ly, float lz, float bx, float by, float bz, vec &v) // height-correct line of sight for monster shooting/seeing -{ - if(OUTBORD((int)lx, (int)ly) || OUTBORD((int)bx, (int)by)) return false; - float dx = bx-lx; - float dy = by-ly; - int steps = (int)(sqrt(dx*dx+dy*dy)/0.9); - if(!steps) return false; - float x = lx; - float y = ly; - int i = 0; - for(;;) - { - sqr *s = S(fast_f2nat(x), fast_f2nat(y)); - if(SOLID(s)) break; - float floor = s->floor; - if(s->type==FHF) floor -= s->vdelta/4.0f; - float ceil = s->ceil; - if(s->type==CHF) ceil += s->vdelta/4.0f; - float rz = lz-((lz-bz)*(i/(float)steps)); - if(rzceil) break; - v.x = x; - v.y = y; - v.z = rz; - x += dx/(float)steps; - y += dy/(float)steps; - i++; - }; - return i>=steps; -}; - -bool enemylos(dynent *m, vec &v) -{ - v = m->o; - return los(m->o.x, m->o.y, m->o.z, m->enemy->o.x, m->enemy->o.y, m->enemy->o.z, v); -}; - -// monster AI is sequenced using transitions: they are in a particular state where -// they execute a particular behaviour until the trigger time is hit, and then they -// reevaluate their situation based on the current state, the environment etc., and -// transition to the next state. Transition timeframes are parametrized by difficulty -// level (skill), faster transitions means quicker decision making means tougher AI. - -void transition(dynent *m, int state, int moving, int n, int r) // n = at skill 0, n/2 = at skill 10, r = added random factor -{ - m->monsterstate = state; - m->move = moving; - n = n*130/100; - m->trigger = lastmillis+n-skill*(n/16)+rnd(r+1); -}; - -void normalise(dynent *m, float angle) -{ - while(m->yawyaw += 360.0f; - while(m->yaw>angle+180.0f) m->yaw -= 360.0f; -}; - -void monsteraction(dynent *m) // main AI thinking routine, called every frame for every monster -{ - if(m->enemy->state==CS_DEAD) { m->enemy = player1; m->anger = 0; }; - normalise(m, m->targetyaw); - if(m->targetyaw>m->yaw) // slowly turn monster towards his target - { - m->yaw += curtime*0.5f; - if(m->targetyawyaw) m->yaw = m->targetyaw; - } - else - { - m->yaw -= curtime*0.5f; - if(m->targetyaw>m->yaw) m->yaw = m->targetyaw; - }; - - vdist(disttoenemy, vectoenemy, m->o, m->enemy->o); - m->pitch = atan2(m->enemy->o.z-m->o.z, disttoenemy)*180/PI; - - if(m->blocked) // special case: if we run into scenery - { - m->blocked = false; - if(!rnd(20000/monstertypes[m->mtype].speed)) // try to jump over obstackle (rare) - { - m->jumpnext = true; - } - else if(m->triggermonsterstate!=M_HOME || !rnd(5))) // search for a way around (common) - { - m->targetyaw += 180+rnd(180); // patented "random walk" AI pathfinding (tm) ;) - transition(m, M_SEARCH, 1, 400, 1000); - }; - }; - - float enemyyaw = -(float)atan2(m->enemy->o.x - m->o.x, m->enemy->o.y - m->o.y)/PI*180+180; - - switch(m->monsterstate) - { - case M_PAIN: - case M_ATTACKING: - case M_SEARCH: - if(m->triggeryaw); - if(disttoenemy<8 // the better the angle to the player, the further the monster can see/hear - ||(disttoenemy<16 && angle<135) - ||(disttoenemy<32 && angle<90) - ||(disttoenemy<64 && angle<45) - || angle<10) - { - transition(m, M_HOME, 1, 500, 200); - playsound(S_GRUNT1+rnd(2), &m->o); - }; - break; - }; - - case M_AIMING: // this state is the delay between wanting to shoot and actually firing - if(m->triggerlastaction = 0; - m->attacking = true; - shoot(m, m->attacktarget); - transition(m, M_ATTACKING, 0, 600, 0); - }; - break; - - case M_HOME: // monster has visual contact, heads straight for player and may want to shoot at any time - m->targetyaw = enemyyaw; - if(m->triggerenemy->state==CS_ALIVE) // get ready to fire - { - m->attacktarget = target; - transition(m, M_AIMING, 0, monstertypes[m->mtype].lag, 10); - } - else // track player some more - { - transition(m, M_HOME, 1, monstertypes[m->mtype].rate, 0); - }; - }; - }; - break; - }; - - moveplayer(m, 1, false); // use physics to move monster -}; - -void monsterpain(dynent *m, int damage, dynent *d) -{ - if(d->monsterstate) // a monster hit us - { - if(m!=d) // guard for RL guys shooting themselves :) - { - m->anger++; // don't attack straight away, first get angry - int anger = m->mtype==d->mtype ? m->anger/2 : m->anger; - if(anger>=monstertypes[m->mtype].loyalty) m->enemy = d; // monster infight if very angry - }; - } - else // player hit us - { - m->anger = 0; - m->enemy = d; - }; - transition(m, M_PAIN, 0, monstertypes[m->mtype].pain,200); // in this state monster won't attack - if((m->health -= damage)<=0) - { - m->state = CS_DEAD; - m->lastaction = lastmillis; - numkilled++; - player1->frags = numkilled; - playsound(monstertypes[m->mtype].diesound, &m->o); - int remain = monstertotal-numkilled; - if(remain>0 && remain<=5) conoutf("only %d monster(s) remaining", remain); - } - else - { - playsound(monstertypes[m->mtype].painsound, &m->o); - }; -}; - -void endsp(bool allkilled) -{ - conoutf(allkilled ? "you have cleared the map!" : "you reached the exit!"); - conoutf("score: %d kills in %d seconds", numkilled, (lastmillis-mtimestart)/1000); - monstertotal = 0; - startintermission(); -}; - -void monsterthink() -{ - if(m_dmsp && spawnremain && lastmillis>nextmonster) - { - if(spawnremain--==monstertotal) conoutf("The invasion has begun!"); - nextmonster = lastmillis+1000; - spawnmonster(); - }; - - if(monstertotal && !spawnremain && numkilled==monstertotal) endsp(true); - - loopv(ents) // equivalent of player entity touch, but only teleports are used - { - entity &e = ents[i]; - if(e.type!=TELEPORT) continue; - if(OUTBORD(e.x, e.y)) continue; - vec v = { e.x, e.y, S(e.x, e.y)->floor }; - loopv(monsters) if(monsters[i]->state==CS_DEAD) - { - if(lastmillis-monsters[i]->lastaction<2000) - { - monsters[i]->move = 0; - moveplayer(monsters[i], 1, false); - }; - } - else - { - v.z += monsters[i]->eyeheight; - vdist(dist, t, monsters[i]->o, v); - v.z -= monsters[i]->eyeheight; - if(dist<4) teleport((int)(&e-&ents[0]), monsters[i]); - }; - }; - - loopv(monsters) if(monsters[i]->state==CS_ALIVE) monsteraction(monsters[i]); -}; - -void monsterrender() -{ - loopv(monsters) renderclient(monsters[i], false, monstertypes[monsters[i]->mtype].mdlname, monsters[i]->mtype==5, monstertypes[monsters[i]->mtype].mscale/10.0f); -}; ADDED src/monster.cxx Index: src/monster.cxx ================================================================== --- /dev/null +++ src/monster.cxx @@ -0,0 +1,338 @@ +// monster.cpp: implements AI for single player monsters, currently client only + +#include "cube.h" + +dvector monsters; +int nextmonster, spawnremain, numkilled, monstertotal, mtimestart; + +VARF(skill, 1, 3, 10, conoutf("skill is now %d", skill)); + +dvector &getmonsters() { return monsters; }; +void restoremonsterstate() { loopv(monsters) if(monsters[i]->state==CS_DEAD) numkilled++; }; // for savegames + +#define TOTMFREQ 13 +#define NUMMONSTERTYPES 8 + +struct monstertype // see docs for how these values modify behaviour +{ + short gun, speed, health, freq, lag, rate, pain, loyalty, mscale, bscale; + short painsound, diesound; + char *name, *mdlname; +} + +monstertypes[NUMMONSTERTYPES] = +{ + { GUN_FIREBALL, 15, 100, 3, 0, 100, 800, 1, 10, 10, S_PAINO, S_DIE1, "an ogre", "monster/ogro" }, + { GUN_CG, 18, 70, 2, 70, 10, 400, 2, 8, 9, S_PAINR, S_DEATHR, "a rhino", "monster/rhino" }, + { GUN_SG, 14, 120, 1, 100, 300, 400, 4, 14, 14, S_PAINE, S_DEATHE, "ratamahatta", "monster/rat" }, + { GUN_RIFLE, 15, 200, 1, 80, 300, 300, 4, 18, 18, S_PAINS, S_DEATHS, "a slith", "monster/slith" }, + { GUN_RL, 13, 500, 1, 0, 100, 200, 6, 24, 24, S_PAINB, S_DEATHB, "bauul", "monster/bauul" }, + { GUN_BITE, 22, 50, 3, 0, 100, 400, 1, 12, 15, S_PAINP, S_PIGGR2, "a hellpig", "monster/hellpig" }, + { GUN_ICEBALL, 12, 250, 1, 0, 10, 400, 6, 18, 18, S_PAINH, S_DEATHH, "a knight", "monster/knight" }, + { GUN_SLIMEBALL, 15, 100, 1, 0, 200, 400, 2, 13, 10, S_PAIND, S_DEATHD, "a goblin", "monster/goblin" }, +}; + +dynent *basicmonster(int type, int yaw, int state, int trigger, int move) +{ + if(type>=NUMMONSTERTYPES) + { + conoutf("warning: unknown monster in spawn: %d", type); + type = 0; + }; + dynent *m = newdynent(); + monstertype *t = &monstertypes[m->mtype = type]; + m->eyeheight = 2.0f; + m->aboveeye = 1.9f; + m->radius *= t->bscale/10.0f; + m->eyeheight *= t->bscale/10.0f; + m->aboveeye *= t->bscale/10.0f; + m->monsterstate = state; + if(state!=M_SLEEP) spawnplayer(m); + m->trigger = lastmillis+trigger; + m->targetyaw = m->yaw = (float)yaw; + m->move = move; + m->enemy = player1; + m->gunselect = t->gun; + m->maxspeed = (float)t->speed; + m->health = t->health; + m->armour = 0; + loopi(NUMGUNS) m->ammo[i] = 10000; + m->pitch = 0; + m->roll = 0; + m->state = CS_ALIVE; + m->anger = 0; + strcpy_s(m->name, t->name); + monsters.add(m); + return m; +}; + +void spawnmonster() // spawn a random monster according to freq distribution in DMSP +{ + int n = rnd(TOTMFREQ), type; + for(int i = 0; ; i++) if((n -= monstertypes[i].freq)<0) { type = i; break; }; + basicmonster(type, rnd(360), M_SEARCH, 1000, 1); +}; + +void monsterclear() // called after map start of when toggling edit mode to reset/spawn all monsters to initial state +{ + loopv(monsters) gp()->dealloc(monsters[i], sizeof(dynent)); + monsters.setsize(0); + numkilled = 0; + monstertotal = 0; + spawnremain = 0; + if(m_dmsp) + { + nextmonster = mtimestart = lastmillis+10000; + monstertotal = spawnremain = gamemode<0 ? skill*10 : 0; + } + else if(m_classicsp) + { + mtimestart = lastmillis; + loopv(ents) if(ents[i].type==MONSTER) + { + dynent *m = basicmonster(ents[i].attr2, ents[i].attr1, M_SLEEP, 100, 0); + m->o.x = ents[i].x; + m->o.y = ents[i].y; + m->o.z = ents[i].z; + entinmap(m); + monstertotal++; + }; + }; +}; + +bool los(float lx, float ly, float lz, float bx, float by, float bz, vec &v) // height-correct line of sight for monster shooting/seeing +{ + if(OUTBORD((int)lx, (int)ly) || OUTBORD((int)bx, (int)by)) return false; + float dx = bx-lx; + float dy = by-ly; + int steps = (int)(sqrt(dx*dx+dy*dy)/0.9); + if(!steps) return false; + float x = lx; + float y = ly; + int i = 0; + for(;;) + { + sqr *s = S(fast_f2nat(x), fast_f2nat(y)); + if(SOLID(s)) break; + float floor = s->floor; + if(s->type==FHF) floor -= s->vdelta/4.0f; + float ceil = s->ceil; + if(s->type==CHF) ceil += s->vdelta/4.0f; + float rz = lz-((lz-bz)*(i/(float)steps)); + if(rzceil) break; + v.x = x; + v.y = y; + v.z = rz; + x += dx/(float)steps; + y += dy/(float)steps; + i++; + }; + return i>=steps; +}; + +bool enemylos(dynent *m, vec &v) +{ + v = m->o; + return los(m->o.x, m->o.y, m->o.z, m->enemy->o.x, m->enemy->o.y, m->enemy->o.z, v); +}; + +// monster AI is sequenced using transitions: they are in a particular state where +// they execute a particular behaviour until the trigger time is hit, and then they +// reevaluate their situation based on the current state, the environment etc., and +// transition to the next state. Transition timeframes are parametrized by difficulty +// level (skill), faster transitions means quicker decision making means tougher AI. + +void transition(dynent *m, int state, int moving, int n, int r) // n = at skill 0, n/2 = at skill 10, r = added random factor +{ + m->monsterstate = state; + m->move = moving; + n = n*130/100; + m->trigger = lastmillis+n-skill*(n/16)+rnd(r+1); +}; + +void normalise(dynent *m, float angle) +{ + while(m->yawyaw += 360.0f; + while(m->yaw>angle+180.0f) m->yaw -= 360.0f; +}; + +void monsteraction(dynent *m) // main AI thinking routine, called every frame for every monster +{ + if(m->enemy->state==CS_DEAD) { m->enemy = player1; m->anger = 0; }; + normalise(m, m->targetyaw); + if(m->targetyaw>m->yaw) // slowly turn monster towards his target + { + m->yaw += curtime*0.5f; + if(m->targetyawyaw) m->yaw = m->targetyaw; + } + else + { + m->yaw -= curtime*0.5f; + if(m->targetyaw>m->yaw) m->yaw = m->targetyaw; + }; + + vdist(disttoenemy, vectoenemy, m->o, m->enemy->o); + m->pitch = atan2(m->enemy->o.z-m->o.z, disttoenemy)*180/PI; + + if(m->blocked) // special case: if we run into scenery + { + m->blocked = false; + if(!rnd(20000/monstertypes[m->mtype].speed)) // try to jump over obstackle (rare) + { + m->jumpnext = true; + } + else if(m->triggermonsterstate!=M_HOME || !rnd(5))) // search for a way around (common) + { + m->targetyaw += 180+rnd(180); // patented "random walk" AI pathfinding (tm) ;) + transition(m, M_SEARCH, 1, 400, 1000); + }; + }; + + float enemyyaw = -(float)atan2(m->enemy->o.x - m->o.x, m->enemy->o.y - m->o.y)/PI*180+180; + + switch(m->monsterstate) + { + case M_PAIN: + case M_ATTACKING: + case M_SEARCH: + if(m->triggeryaw); + if(disttoenemy<8 // the better the angle to the player, the further the monster can see/hear + ||(disttoenemy<16 && angle<135) + ||(disttoenemy<32 && angle<90) + ||(disttoenemy<64 && angle<45) + || angle<10) + { + transition(m, M_HOME, 1, 500, 200); + playsound(S_GRUNT1+rnd(2), &m->o); + }; + break; + }; + + case M_AIMING: // this state is the delay between wanting to shoot and actually firing + if(m->triggerlastaction = 0; + m->attacking = true; + shoot(m, m->attacktarget); + transition(m, M_ATTACKING, 0, 600, 0); + }; + break; + + case M_HOME: // monster has visual contact, heads straight for player and may want to shoot at any time + m->targetyaw = enemyyaw; + if(m->triggerenemy->state==CS_ALIVE) // get ready to fire + { + m->attacktarget = target; + transition(m, M_AIMING, 0, monstertypes[m->mtype].lag, 10); + } + else // track player some more + { + transition(m, M_HOME, 1, monstertypes[m->mtype].rate, 0); + }; + }; + }; + break; + }; + + moveplayer(m, 1, false); // use physics to move monster +}; + +void monsterpain(dynent *m, int damage, dynent *d) +{ + if(d->monsterstate) // a monster hit us + { + if(m!=d) // guard for RL guys shooting themselves :) + { + m->anger++; // don't attack straight away, first get angry + int anger = m->mtype==d->mtype ? m->anger/2 : m->anger; + if(anger>=monstertypes[m->mtype].loyalty) m->enemy = d; // monster infight if very angry + }; + } + else // player hit us + { + m->anger = 0; + m->enemy = d; + }; + transition(m, M_PAIN, 0, monstertypes[m->mtype].pain,200); // in this state monster won't attack + if((m->health -= damage)<=0) + { + m->state = CS_DEAD; + m->lastaction = lastmillis; + numkilled++; + player1->frags = numkilled; + playsound(monstertypes[m->mtype].diesound, &m->o); + int remain = monstertotal-numkilled; + if(remain>0 && remain<=5) conoutf("only %d monster(s) remaining", remain); + } + else + { + playsound(monstertypes[m->mtype].painsound, &m->o); + }; +}; + +void endsp(bool allkilled) +{ + conoutf(allkilled ? "you have cleared the map!" : "you reached the exit!"); + conoutf("score: %d kills in %d seconds", numkilled, (lastmillis-mtimestart)/1000); + monstertotal = 0; + startintermission(); +}; + +void monsterthink() +{ + if(m_dmsp && spawnremain && lastmillis>nextmonster) + { + if(spawnremain--==monstertotal) conoutf("The invasion has begun!"); + nextmonster = lastmillis+1000; + spawnmonster(); + }; + + if(monstertotal && !spawnremain && numkilled==monstertotal) endsp(true); + + loopv(ents) // equivalent of player entity touch, but only teleports are used + { + entity &e = ents[i]; + if(e.type!=TELEPORT) continue; + if(OUTBORD(e.x, e.y)) continue; + vec v = { e.x, e.y, S(e.x, e.y)->floor }; + loopv(monsters) if(monsters[i]->state==CS_DEAD) + { + if(lastmillis-monsters[i]->lastaction<2000) + { + monsters[i]->move = 0; + moveplayer(monsters[i], 1, false); + }; + } + else + { + v.z += monsters[i]->eyeheight; + vdist(dist, t, monsters[i]->o, v); + v.z -= monsters[i]->eyeheight; + if(dist<4) teleport((int)(&e-&ents[0]), monsters[i]); + }; + }; + + loopv(monsters) if(monsters[i]->state==CS_ALIVE) monsteraction(monsters[i]); +}; + +void monsterrender() +{ + loopv(monsters) renderclient(monsters[i], false, monstertypes[monsters[i]->mtype].mdlname, monsters[i]->mtype==5, monstertypes[monsters[i]->mtype].mscale/10.0f); +}; DELETED src/physics.cpp Index: src/physics.cpp ================================================================== --- src/physics.cpp +++ /dev/null @@ -1,323 +0,0 @@ -// physics.cpp: no physics books were hurt nor consulted in the construction of this code. -// All physics computations and constants were invented on the fly and simply tweaked until -// they "felt right", and have no basis in reality. Collision detection is simplistic but -// very robust (uses discrete steps at fixed fps). - -#include "cube.h" - -bool plcollide(dynent *d, dynent *o, float &headspace, float &hi, float &lo) // collide with player or monster -{ - if(o->state!=CS_ALIVE) return true; - const float r = o->radius+d->radius; - if(fabs(o->o.x-d->o.x)o.y-d->o.y)o.z-d->eyeheighto.z-o->eyeheight) { if(o->o.z-o->eyeheighto.z-o->eyeheight-1; } - else if(o->o.z+o->aboveeye>lo) lo = o->o.z+o->aboveeye+1; - - if(fabs(o->o.z-d->o.z)aboveeye+d->eyeheight) return false; - if(d->monsterstate) return false; // hack - headspace = d->o.z-o->o.z-o->aboveeye-d->eyeheight; - if(headspace<0) headspace = 10; - }; - return true; -}; - -bool cornertest(int mip, int x, int y, int dx, int dy, int &bx, int &by, int &bs) // recursively collide with a mipmapped corner cube -{ - sqr *w = wmip[mip]; - int sz = ssize>>mip; - bool stest = SOLID(SWS(w, x+dx, y, sz)) && SOLID(SWS(w, x, y+dy, sz)); - mip++; - x /= 2; - y /= 2; - if(SWS(wmip[mip], x, y, ssize>>mip)->type==CORNER) - { - bx = x<radius; - if(fabs(e.x-d->o.x)o.y)floor+mmi.zoff+e.attr3); - if(d->o.z-d->eyeheightlo) lo = mmz+mmi.h; - }; - }; -}; - -// all collision happens here -// spawn is a dirty side effect used in spawning -// drop & rise are supplied by the physics below to indicate gravity/push for current mini-timestep - -bool collide(dynent *d, bool spawn, float drop, float rise) -{ - const float fx1 = d->o.x-d->radius; // figure out integer cube rectangle this entity covers in map - const float fy1 = d->o.y-d->radius; - const float fx2 = d->o.x+d->radius; - const float fy2 = d->o.y+d->radius; - const int x1 = fast_f2nat(fx1); - const int y1 = fast_f2nat(fy1); - const int x2 = fast_f2nat(fx2); - const int y2 = fast_f2nat(fy2); - float hi = 127, lo = -128; - float minfloor = (d->monsterstate && !spawn && d->health>100) ? d->o.z-d->eyeheight-4.5f : -1000.0f; // big monsters are afraid of heights, unless angry :) - - for(int x = x1; x<=x2; x++) for(int y = y1; y<=y2; y++) // collide with map - { - if(OUTBORD(x,y)) return false; - sqr *s = S(x,y); - float ceil = s->ceil; - float floor = s->floor; - switch(s->type) - { - case SOLID: - return false; - - case CORNER: - { - int bx = x, by = y, bs = 1; - if(x==x1 && y==y1 && cornertest(0, x, y, -1, -1, bx, by, bs) && fx1-bx+fy1-by<=bs - || x==x2 && y==y1 && cornertest(0, x, y, 1, -1, bx, by, bs) && fx2-bx>=fy1-by - || x==x1 && y==y2 && cornertest(0, x, y, -1, 1, bx, by, bs) && fx1-bx<=fy2-by - || x==x2 && y==y2 && cornertest(0, x, y, 1, 1, bx, by, bs) && fx2-bx+fy2-by>=bs) - return false; - break; - }; - - case FHF: // FIXME: too simplistic collision with slopes, makes it feels like tiny stairs - floor -= (s->vdelta+S(x+1,y)->vdelta+S(x,y+1)->vdelta+S(x+1,y+1)->vdelta)/16.0f; - break; - - case CHF: - ceil += (s->vdelta+S(x+1,y)->vdelta+S(x,y+1)->vdelta+S(x+1,y+1)->vdelta)/16.0f; - - }; - if(ceillo) lo = floor; - if(flooreyeheight+d->aboveeye) return false; - - float headspace = 10; - loopv(players) // collide with other players - { - dynent *o = players[i]; - if(!o || o==d) continue; - if(!plcollide(d, o, headspace, hi, lo)) return false; - }; - if(d!=player1) if(!plcollide(d, player1, headspace, hi, lo)) return false; - dvector &v = getmonsters(); - // this loop can be a performance bottleneck with many monster on a slow cpu, - // should replace with a blockmap but seems mostly fast enough - loopv(v) if(!vreject(d->o, v[i]->o, 7.0f) && d!=v[i] && !plcollide(d, v[i], headspace, hi, lo)) return false; - headspace -= 0.01f; - - mmcollide(d, hi, lo); // collide with map models - - if(spawn) - { - d->o.z = lo+d->eyeheight; // just drop to floor (sideeffect) - d->onfloor = true; - } - else - { - const float space = d->o.z-d->eyeheight-lo; - if(space<0) - { - if(space>-0.01) d->o.z = lo+d->eyeheight; // stick on step - else if(space>-1.26f) d->o.z += rise; // rise thru stair - else return false; - } - else - { - d->o.z -= min(min(drop, space), headspace); // gravity - }; - - const float space2 = hi-(d->o.z+d->aboveeye); - if(space2<0) - { - if(space2<-0.1) return false; // hack alert! - d->o.z = hi-d->aboveeye; // glue to ceiling - d->vel.z = 0; // cancel out jumping velocity - }; - - d->onfloor = d->o.z-d->eyeheight-lo<0.001f; - }; - return true; -} - -float rad(float x) { return x*3.14159f/180; }; - -VARP(maxroll, 0, 3, 20); - -int physicsfraction = 0, physicsrepeat = 0; -const int MINFRAMETIME = 20; // physics always simulated at 50fps or better - -void physicsframe() // optimally schedule physics frames inside the graphics frames -{ - if(curtime>=MINFRAMETIME) - { - int faketime = curtime+physicsfraction; - physicsrepeat = faketime/MINFRAMETIME; - physicsfraction = faketime-physicsrepeat*MINFRAMETIME; - } - else - { - physicsrepeat = 1; - }; -}; - -// main physics routine, moves a player/monster for a curtime step -// moveres indicated the physics precision (which is lower for monsters and multiplayer prediction) -// local is false for multiplayer prediction - -void moveplayer(dynent *pl, int moveres, bool local, int curtime) -{ - const bool water = hdr.waterlevel>pl->o.z-0.5f; - const bool floating = (editmode && local) || pl->state==CS_EDITING; - - vec d; // vector of direction we ideally want to move in - - d.x = (float)(pl->move*cos(rad(pl->yaw-90))); - d.y = (float)(pl->move*sin(rad(pl->yaw-90))); - d.z = 0; - - if(floating || water) - { - d.x *= (float)cos(rad(pl->pitch)); - d.y *= (float)cos(rad(pl->pitch)); - d.z = (float)(pl->move*sin(rad(pl->pitch))); - }; - - d.x += (float)(pl->strafe*cos(rad(pl->yaw-180))); - d.y += (float)(pl->strafe*sin(rad(pl->yaw-180))); - - const float speed = curtime/(water ? 2000.0f : 1000.0f)*pl->maxspeed; - const float friction = water ? 20.0f : (pl->onfloor || floating ? 6.0f : 30.0f); - - const float fpsfric = friction/curtime*20.0f; - - vmul(pl->vel, fpsfric-1); // slowly apply friction and direction to velocity, gives a smooth movement - vadd(pl->vel, d); - vdiv(pl->vel, fpsfric); - d = pl->vel; - vmul(d, speed); // d is now frametime based velocity vector - - pl->blocked = false; - pl->moving = true; - - if(floating) // just apply velocity - { - vadd(pl->o, d); - if(pl->jumpnext) { pl->jumpnext = false; pl->vel.z = 2; } - } - else // apply velocity with collision - { - if(pl->onfloor || water) - { - if(pl->jumpnext) - { - pl->jumpnext = false; - pl->vel.z = 1.7f; // physics impulse upwards - if(water) { pl->vel.x /= 8; pl->vel.y /= 8; }; // dampen velocity change even harder, gives correct water feel - if(local) playsoundc(S_JUMP); - else if(pl->monsterstate) playsound(S_JUMP, &pl->o); - } - else if(pl->timeinair>800) // if we land after long time must have been a high jump, make thud sound - { - if(local) playsoundc(S_LAND); - else if(pl->monsterstate) playsound(S_LAND, &pl->o); - }; - pl->timeinair = 0; - } - else - { - pl->timeinair += curtime; - }; - - const float gravity = 20; - const float f = 1.0f/moveres; - float dropf = ((gravity-1)+pl->timeinair/15.0f); // incorrect, but works fine - if(water) { dropf = 5; pl->timeinair = 0; }; // float slowly down in water - const float drop = dropf*curtime/gravity/100/moveres; // at high fps, gravity kicks in too fast - const float rise = speed/moveres/1.2f; // extra smoothness when lifting up stairs - - loopi(moveres) // discrete steps collision detection & sliding - { - // try move forward - pl->o.x += f*d.x; - pl->o.y += f*d.y; - pl->o.z += f*d.z; - if(collide(pl, false, drop, rise)) continue; - // player stuck, try slide along y axis - pl->blocked = true; - pl->o.x -= f*d.x; - if(collide(pl, false, drop, rise)) { d.x = 0; continue; }; - pl->o.x += f*d.x; - // still stuck, try x axis - pl->o.y -= f*d.y; - if(collide(pl, false, drop, rise)) { d.y = 0; continue; }; - pl->o.y += f*d.y; - // try just dropping down - pl->moving = false; - pl->o.x -= f*d.x; - pl->o.y -= f*d.y; - if(collide(pl, false, drop, rise)) { d.y = d.x = 0; continue; }; - pl->o.z -= f*d.z; - break; - }; - }; - - // detect wether player is outside map, used for skipping zbuffer clear mostly - - if(pl->o.x < 0 || pl->o.x >= ssize || pl->o.y <0 || pl->o.y > ssize) - { - pl->outsidemap = true; - } - else - { - sqr *s = S((int)pl->o.x, (int)pl->o.y); - pl->outsidemap = SOLID(s) - || pl->o.z < s->floor - (s->type==FHF ? s->vdelta/4 : 0) - || pl->o.z > s->ceil + (s->type==CHF ? s->vdelta/4 : 0); - }; - - // automatically apply smooth roll when strafing - - if(pl->strafe==0) - { - pl->roll = pl->roll/(1+(float)sqrt((float)curtime)/25); - } - else - { - pl->roll += pl->strafe*curtime/-30.0f; - if(pl->roll>maxroll) pl->roll = (float)maxroll; - if(pl->roll<-maxroll) pl->roll = (float)-maxroll; - }; - - // play sounds on water transitions - - if(!pl->inwater && water) { playsound(S_SPLASH2, &pl->o); pl->vel.z = 0; } - else if(pl->inwater && !water) playsound(S_SPLASH1, &pl->o); - pl->inwater = water; -}; - -void moveplayer(dynent *pl, int moveres, bool local) -{ - loopi(physicsrepeat) moveplayer(pl, moveres, local, i ? curtime/physicsrepeat : curtime-curtime/physicsrepeat*(physicsrepeat-1)); -}; - ADDED src/physics.cxx Index: src/physics.cxx ================================================================== --- /dev/null +++ src/physics.cxx @@ -0,0 +1,323 @@ +// physics.cpp: no physics books were hurt nor consulted in the construction of this code. +// All physics computations and constants were invented on the fly and simply tweaked until +// they "felt right", and have no basis in reality. Collision detection is simplistic but +// very robust (uses discrete steps at fixed fps). + +#include "cube.h" + +bool plcollide(dynent *d, dynent *o, float &headspace, float &hi, float &lo) // collide with player or monster +{ + if(o->state!=CS_ALIVE) return true; + const float r = o->radius+d->radius; + if(fabs(o->o.x-d->o.x)o.y-d->o.y)o.z-d->eyeheighto.z-o->eyeheight) { if(o->o.z-o->eyeheighto.z-o->eyeheight-1; } + else if(o->o.z+o->aboveeye>lo) lo = o->o.z+o->aboveeye+1; + + if(fabs(o->o.z-d->o.z)aboveeye+d->eyeheight) return false; + if(d->monsterstate) return false; // hack + headspace = d->o.z-o->o.z-o->aboveeye-d->eyeheight; + if(headspace<0) headspace = 10; + }; + return true; +}; + +bool cornertest(int mip, int x, int y, int dx, int dy, int &bx, int &by, int &bs) // recursively collide with a mipmapped corner cube +{ + sqr *w = wmip[mip]; + int sz = ssize>>mip; + bool stest = SOLID(SWS(w, x+dx, y, sz)) && SOLID(SWS(w, x, y+dy, sz)); + mip++; + x /= 2; + y /= 2; + if(SWS(wmip[mip], x, y, ssize>>mip)->type==CORNER) + { + bx = x<radius; + if(fabs(e.x-d->o.x)o.y)floor+mmi.zoff+e.attr3); + if(d->o.z-d->eyeheightlo) lo = mmz+mmi.h; + }; + }; +}; + +// all collision happens here +// spawn is a dirty side effect used in spawning +// drop & rise are supplied by the physics below to indicate gravity/push for current mini-timestep + +bool collide(dynent *d, bool spawn, float drop, float rise) +{ + const float fx1 = d->o.x-d->radius; // figure out integer cube rectangle this entity covers in map + const float fy1 = d->o.y-d->radius; + const float fx2 = d->o.x+d->radius; + const float fy2 = d->o.y+d->radius; + const int x1 = fast_f2nat(fx1); + const int y1 = fast_f2nat(fy1); + const int x2 = fast_f2nat(fx2); + const int y2 = fast_f2nat(fy2); + float hi = 127, lo = -128; + float minfloor = (d->monsterstate && !spawn && d->health>100) ? d->o.z-d->eyeheight-4.5f : -1000.0f; // big monsters are afraid of heights, unless angry :) + + for(int x = x1; x<=x2; x++) for(int y = y1; y<=y2; y++) // collide with map + { + if(OUTBORD(x,y)) return false; + sqr *s = S(x,y); + float ceil = s->ceil; + float floor = s->floor; + switch(s->type) + { + case SOLID: + return false; + + case CORNER: + { + int bx = x, by = y, bs = 1; + if(x==x1 && y==y1 && cornertest(0, x, y, -1, -1, bx, by, bs) && fx1-bx+fy1-by<=bs + || x==x2 && y==y1 && cornertest(0, x, y, 1, -1, bx, by, bs) && fx2-bx>=fy1-by + || x==x1 && y==y2 && cornertest(0, x, y, -1, 1, bx, by, bs) && fx1-bx<=fy2-by + || x==x2 && y==y2 && cornertest(0, x, y, 1, 1, bx, by, bs) && fx2-bx+fy2-by>=bs) + return false; + break; + }; + + case FHF: // FIXME: too simplistic collision with slopes, makes it feels like tiny stairs + floor -= (s->vdelta+S(x+1,y)->vdelta+S(x,y+1)->vdelta+S(x+1,y+1)->vdelta)/16.0f; + break; + + case CHF: + ceil += (s->vdelta+S(x+1,y)->vdelta+S(x,y+1)->vdelta+S(x+1,y+1)->vdelta)/16.0f; + + }; + if(ceillo) lo = floor; + if(flooreyeheight+d->aboveeye) return false; + + float headspace = 10; + loopv(players) // collide with other players + { + dynent *o = players[i]; + if(!o || o==d) continue; + if(!plcollide(d, o, headspace, hi, lo)) return false; + }; + if(d!=player1) if(!plcollide(d, player1, headspace, hi, lo)) return false; + dvector &v = getmonsters(); + // this loop can be a performance bottleneck with many monster on a slow cpu, + // should replace with a blockmap but seems mostly fast enough + loopv(v) if(!vreject(d->o, v[i]->o, 7.0f) && d!=v[i] && !plcollide(d, v[i], headspace, hi, lo)) return false; + headspace -= 0.01f; + + mmcollide(d, hi, lo); // collide with map models + + if(spawn) + { + d->o.z = lo+d->eyeheight; // just drop to floor (sideeffect) + d->onfloor = true; + } + else + { + const float space = d->o.z-d->eyeheight-lo; + if(space<0) + { + if(space>-0.01) d->o.z = lo+d->eyeheight; // stick on step + else if(space>-1.26f) d->o.z += rise; // rise thru stair + else return false; + } + else + { + d->o.z -= min(min(drop, space), headspace); // gravity + }; + + const float space2 = hi-(d->o.z+d->aboveeye); + if(space2<0) + { + if(space2<-0.1) return false; // hack alert! + d->o.z = hi-d->aboveeye; // glue to ceiling + d->vel.z = 0; // cancel out jumping velocity + }; + + d->onfloor = d->o.z-d->eyeheight-lo<0.001f; + }; + return true; +} + +float rad(float x) { return x*3.14159f/180; }; + +VARP(maxroll, 0, 3, 20); + +int physicsfraction = 0, physicsrepeat = 0; +const int MINFRAMETIME = 20; // physics always simulated at 50fps or better + +void physicsframe() // optimally schedule physics frames inside the graphics frames +{ + if(curtime>=MINFRAMETIME) + { + int faketime = curtime+physicsfraction; + physicsrepeat = faketime/MINFRAMETIME; + physicsfraction = faketime-physicsrepeat*MINFRAMETIME; + } + else + { + physicsrepeat = 1; + }; +}; + +// main physics routine, moves a player/monster for a curtime step +// moveres indicated the physics precision (which is lower for monsters and multiplayer prediction) +// local is false for multiplayer prediction + +void moveplayer(dynent *pl, int moveres, bool local, int curtime) +{ + const bool water = hdr.waterlevel>pl->o.z-0.5f; + const bool floating = (editmode && local) || pl->state==CS_EDITING; + + vec d; // vector of direction we ideally want to move in + + d.x = (float)(pl->move*cos(rad(pl->yaw-90))); + d.y = (float)(pl->move*sin(rad(pl->yaw-90))); + d.z = 0; + + if(floating || water) + { + d.x *= (float)cos(rad(pl->pitch)); + d.y *= (float)cos(rad(pl->pitch)); + d.z = (float)(pl->move*sin(rad(pl->pitch))); + }; + + d.x += (float)(pl->strafe*cos(rad(pl->yaw-180))); + d.y += (float)(pl->strafe*sin(rad(pl->yaw-180))); + + const float speed = curtime/(water ? 2000.0f : 1000.0f)*pl->maxspeed; + const float friction = water ? 20.0f : (pl->onfloor || floating ? 6.0f : 30.0f); + + const float fpsfric = friction/curtime*20.0f; + + vmul(pl->vel, fpsfric-1); // slowly apply friction and direction to velocity, gives a smooth movement + vadd(pl->vel, d); + vdiv(pl->vel, fpsfric); + d = pl->vel; + vmul(d, speed); // d is now frametime based velocity vector + + pl->blocked = false; + pl->moving = true; + + if(floating) // just apply velocity + { + vadd(pl->o, d); + if(pl->jumpnext) { pl->jumpnext = false; pl->vel.z = 2; } + } + else // apply velocity with collision + { + if(pl->onfloor || water) + { + if(pl->jumpnext) + { + pl->jumpnext = false; + pl->vel.z = 1.7f; // physics impulse upwards + if(water) { pl->vel.x /= 8; pl->vel.y /= 8; }; // dampen velocity change even harder, gives correct water feel + if(local) playsoundc(S_JUMP); + else if(pl->monsterstate) playsound(S_JUMP, &pl->o); + } + else if(pl->timeinair>800) // if we land after long time must have been a high jump, make thud sound + { + if(local) playsoundc(S_LAND); + else if(pl->monsterstate) playsound(S_LAND, &pl->o); + }; + pl->timeinair = 0; + } + else + { + pl->timeinair += curtime; + }; + + const float gravity = 20; + const float f = 1.0f/moveres; + float dropf = ((gravity-1)+pl->timeinair/15.0f); // incorrect, but works fine + if(water) { dropf = 5; pl->timeinair = 0; }; // float slowly down in water + const float drop = dropf*curtime/gravity/100/moveres; // at high fps, gravity kicks in too fast + const float rise = speed/moveres/1.2f; // extra smoothness when lifting up stairs + + loopi(moveres) // discrete steps collision detection & sliding + { + // try move forward + pl->o.x += f*d.x; + pl->o.y += f*d.y; + pl->o.z += f*d.z; + if(collide(pl, false, drop, rise)) continue; + // player stuck, try slide along y axis + pl->blocked = true; + pl->o.x -= f*d.x; + if(collide(pl, false, drop, rise)) { d.x = 0; continue; }; + pl->o.x += f*d.x; + // still stuck, try x axis + pl->o.y -= f*d.y; + if(collide(pl, false, drop, rise)) { d.y = 0; continue; }; + pl->o.y += f*d.y; + // try just dropping down + pl->moving = false; + pl->o.x -= f*d.x; + pl->o.y -= f*d.y; + if(collide(pl, false, drop, rise)) { d.y = d.x = 0; continue; }; + pl->o.z -= f*d.z; + break; + }; + }; + + // detect wether player is outside map, used for skipping zbuffer clear mostly + + if(pl->o.x < 0 || pl->o.x >= ssize || pl->o.y <0 || pl->o.y > ssize) + { + pl->outsidemap = true; + } + else + { + sqr *s = S((int)pl->o.x, (int)pl->o.y); + pl->outsidemap = SOLID(s) + || pl->o.z < s->floor - (s->type==FHF ? s->vdelta/4 : 0) + || pl->o.z > s->ceil + (s->type==CHF ? s->vdelta/4 : 0); + }; + + // automatically apply smooth roll when strafing + + if(pl->strafe==0) + { + pl->roll = pl->roll/(1+(float)sqrt((float)curtime)/25); + } + else + { + pl->roll += pl->strafe*curtime/-30.0f; + if(pl->roll>maxroll) pl->roll = (float)maxroll; + if(pl->roll<-maxroll) pl->roll = (float)-maxroll; + }; + + // play sounds on water transitions + + if(!pl->inwater && water) { playsound(S_SPLASH2, &pl->o); pl->vel.z = 0; } + else if(pl->inwater && !water) playsound(S_SPLASH1, &pl->o); + pl->inwater = water; +}; + +void moveplayer(dynent *pl, int moveres, bool local) +{ + loopi(physicsrepeat) moveplayer(pl, moveres, local, i ? curtime/physicsrepeat : curtime-curtime/physicsrepeat*(physicsrepeat-1)); +}; + DELETED src/rendercubes.cpp Index: src/rendercubes.cpp ================================================================== --- src/rendercubes.cpp +++ /dev/null @@ -1,359 +0,0 @@ -// rendercubes.cpp: sits in between worldrender.cpp and rendergl.cpp and fills the vertex array for different cube surfaces. - -#include "cube.h" - -vertex *verts = NULL; -int curvert; -int curmaxverts = 10000; - -void setarraypointers() -{ - glVertexPointer(3, GL_FLOAT, sizeof(vertex), &verts[0].x); - glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex), &verts[0].r); - glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), &verts[0].u); -}; - -void reallocv() -{ - verts = (vertex *)realloc(verts, (curmaxverts *= 2)*sizeof(vertex)); - curmaxverts -= 10; - if(!verts) fatal("no vertex memory!"); - setarraypointers(); -}; - -// generating the actual vertices is done dynamically every frame and sits at the -// leaves of all these functions, and are part of the cpu bottleneck on really slow -// machines, hence the macros. - -#define vertcheck() { if(curvert>=curmaxverts) reallocv(); } - -#define vertf(v1, v2, v3, ls, t1, t2) { vertex &v = verts[curvert++]; \ - v.u = t1; v.v = t2; \ - v.x = v1; v.y = v2; v.z = v3; \ - v.r = ls->r; v.g = ls->g; v.b = ls->b; v.a = 255; }; - -#define vert(v1, v2, v3, ls, t1, t2) { vertf((float)(v1), (float)(v2), (float)(v3), ls, t1, t2); } - -int nquads; -const float TEXTURESCALE = 32.0f; -bool floorstrip = false, deltastrip = false; -int oh, oy, ox, ogltex; // the o* vars are used by the stripification -int ol3r, ol3g, ol3b, ol4r, ol4g, ol4b; -int firstindex; -bool showm = false; - -void showmip() { showm = !showm; }; -void mipstats(int a, int b, int c) { if(showm) conoutf("1x1/2x2/4x4: %d / %d / %d", a, b, c); }; - -COMMAND(showmip, ARG_NONE); - -#define stripend() { if(floorstrip || deltastrip) { addstrip(ogltex, firstindex, curvert-firstindex); floorstrip = deltastrip = false; }; }; -void finishstrips() { stripend(); }; - -sqr sbright, sdark; -VAR(lighterror,1,8,100); - -void render_flat(int wtex, int x, int y, int size, int h, sqr *l1, sqr *l2, sqr *l3, sqr *l4, bool isceil) // floor/ceil quads -{ - vertcheck(); - if(showm) { l3 = l1 = &sbright; l4 = l2 = &sdark; }; - - int sx, sy; - int gltex = lookuptexture(wtex, sx, sy); - float xf = TEXTURESCALE/sx; - float yf = TEXTURESCALE/sy; - float xs = size*xf; - float ys = size*yf; - float xo = xf*x; - float yo = yf*y; - - bool first = !floorstrip || y!=oy+size || ogltex!=gltex || h!=oh || x!=ox; - - if(first) // start strip here - { - stripend(); - firstindex = curvert; - ogltex = gltex; - oh = h; - ox = x; - floorstrip = true; - if(isceil) - { - vert(x+size, h, y, l2, xo+xs, yo); - vert(x, h, y, l1, xo, yo); - } - else - { - vert(x, h, y, l1, xo, yo); - vert(x+size, h, y, l2, xo+xs, yo); - }; - ol3r = l1->r; - ol3g = l1->g; - ol3b = l1->b; - ol4r = l2->r; - ol4g = l2->g; - ol4b = l2->b; - } - else // continue strip - { - int lighterr = lighterror*2; - if((abs(ol3r-l3->r)r)g)g)b)b)r; - ol3g = l1->g; - ol3b = l1->b; - ol4r = l2->r; - ol4g = l2->g; - ol4b = l2->b; - }; - - if(isceil) - { - vertf((float)x+size, h3, (float)y+size, l3, xo+xs, yo+ys); - vertf((float)x, h4, (float)y+size, l4, xo, yo+ys); - } - else - { - vertf((float)x, h4, (float)y+size, l4, xo, yo+ys); - vertf((float)x+size, h3, (float)y+size, l3, xo+xs, yo+ys); - }; - - oy = y; - nquads++; -}; - -void render_2tris(sqr *h, sqr *s, int x1, int y1, int x2, int y2, int x3, int y3, sqr *l1, sqr *l2, sqr *l3) // floor/ceil tris on a corner cube -{ - stripend(); - vertcheck(); - - int sx, sy; - int gltex = lookuptexture(h->ftex, sx, sy); - float xf = TEXTURESCALE/sx; - float yf = TEXTURESCALE/sy; - - vertf((float)x1, h->floor, (float)y1, l1, xf*x1, yf*y1); - vertf((float)x2, h->floor, (float)y2, l2, xf*x2, yf*y2); - vertf((float)x3, h->floor, (float)y3, l3, xf*x3, yf*y3); - addstrip(gltex, curvert-3, 3); - - gltex = lookuptexture(h->ctex, sx, sy); - xf = TEXTURESCALE/sx; - yf = TEXTURESCALE/sy; - - vertf((float)x3, h->ceil, (float)y3, l3, xf*x3, yf*y3); - vertf((float)x2, h->ceil, (float)y2, l2, xf*x2, yf*y2); - vertf((float)x1, h->ceil, (float)y1, l1, xf*x1, yf*y1); - addstrip(gltex, curvert-3, 3); - nquads++; -}; - -void render_tris(int x, int y, int size, bool topleft, - sqr *h1, sqr *h2, sqr *s, sqr *t, sqr *u, sqr *v) -{ - if(topleft) - { - if(h1) render_2tris(h1, s, x+size, y+size, x, y+size, x, y, u, v, s); - if(h2) render_2tris(h2, s, x, y, x+size, y, x+size, y+size, s, t, v); - } - else - { - if(h1) render_2tris(h1, s, x, y, x+size, y, x, y+size, s, t, u); - if(h2) render_2tris(h2, s, x+size, y, x+size, y+size, x, y+size, t, u, v); - }; -}; - -void render_square(int wtex, float floor1, float floor2, float ceil1, float ceil2, int x1, int y1, int x2, int y2, int size, sqr *l1, sqr *l2, bool flip) // wall quads -{ - stripend(); - vertcheck(); - if(showm) { l1 = &sbright; l2 = &sdark; }; - - int sx, sy; - int gltex = lookuptexture(wtex, sx, sy); - float xf = TEXTURESCALE/sx; - float yf = TEXTURESCALE/sy; - float xs = size*xf; - float xo = xf*(x1==x2 ? min(y1,y2) : min(x1,x2)); - - if(!flip) - { - vertf((float)x2, ceil2, (float)y2, l2, xo+xs, -yf*ceil2); - vertf((float)x1, ceil1, (float)y1, l1, xo, -yf*ceil1); - vertf((float)x2, floor2, (float)y2, l2, xo+xs, -floor2*yf); - vertf((float)x1, floor1, (float)y1, l1, xo, -floor1*yf); - } - else - { - vertf((float)x1, ceil1, (float)y1, l1, xo, -yf*ceil1); - vertf((float)x2, ceil2, (float)y2, l2, xo+xs, -yf*ceil2); - vertf((float)x1, floor1, (float)y1, l1, xo, -floor1*yf); - vertf((float)x2, floor2, (float)y2, l2, xo+xs, -floor2*yf); - }; - - nquads++; - addstrip(gltex, curvert-4, 4); -}; - -int wx1, wy1, wx2, wy2; - -VAR(watersubdiv, 1, 4, 64); -VARF(waterlevel, -128, -128, 127, if(!noteditmode()) hdr.waterlevel = waterlevel); - -inline void vertw(int v1, float v2, int v3, sqr *c, float t1, float t2, float t) -{ - vertcheck(); - vertf((float)v1, v2-(float)sin(v1*v3*0.1+t)*0.2f, (float)v3, c, t1, t2); -}; - -inline float dx(float x) { return x+(float)sin(x*2+lastmillis/1000.0f)*0.04f; }; -inline float dy(float x) { return x+(float)sin(x*2+lastmillis/900.0f+PI/5)*0.05f; }; - -// renders water for bounding rect area that contains water... simple but very inefficient - -int renderwater(float hf) -{ - if(wx1<0) return nquads; - - glDepthMask(GL_FALSE); - glEnable(GL_BLEND); - glBlendFunc(GL_ONE, GL_SRC_COLOR); - int sx, sy; - glBindTexture(GL_TEXTURE_2D, lookuptexture(DEFAULT_LIQUID, sx, sy)); - - wx1 &= ~(watersubdiv-1); - wy1 &= ~(watersubdiv-1); - - float xf = TEXTURESCALE/sx; - float yf = TEXTURESCALE/sy; - float xs = watersubdiv*xf; - float ys = watersubdiv*yf; - float t1 = lastmillis/300.0f; - float t2 = lastmillis/4000.0f; - - sqr dl; - dl.r = dl.g = dl.b = 255; - - for(int xx = wx1; xxwx2) wx2 = x2; - if(y2>wy2) wy2 = y2; - }; -}; - -void resetcubes() -{ - if(!verts) reallocv(); - floorstrip = deltastrip = false; - wx1 = -1; - nquads = 0; - sbright.r = sbright.g = sbright.b = 255; - sdark.r = sdark.g = sdark.b = 0; -}; - - ADDED src/rendercubes.cxx Index: src/rendercubes.cxx ================================================================== --- /dev/null +++ src/rendercubes.cxx @@ -0,0 +1,359 @@ +// rendercubes.cpp: sits in between worldrender.cpp and rendergl.cpp and fills the vertex array for different cube surfaces. + +#include "cube.h" + +vertex *verts = NULL; +int curvert; +int curmaxverts = 10000; + +void setarraypointers() +{ + glVertexPointer(3, GL_FLOAT, sizeof(vertex), &verts[0].x); + glColorPointer(4, GL_UNSIGNED_BYTE, sizeof(vertex), &verts[0].r); + glTexCoordPointer(2, GL_FLOAT, sizeof(vertex), &verts[0].u); +}; + +void reallocv() +{ + verts = (vertex *)realloc(verts, (curmaxverts *= 2)*sizeof(vertex)); + curmaxverts -= 10; + if(!verts) fatal("no vertex memory!"); + setarraypointers(); +}; + +// generating the actual vertices is done dynamically every frame and sits at the +// leaves of all these functions, and are part of the cpu bottleneck on really slow +// machines, hence the macros. + +#define vertcheck() { if(curvert>=curmaxverts) reallocv(); } + +#define vertf(v1, v2, v3, ls, t1, t2) { vertex &v = verts[curvert++]; \ + v.u = t1; v.v = t2; \ + v.x = v1; v.y = v2; v.z = v3; \ + v.r = ls->r; v.g = ls->g; v.b = ls->b; v.a = 255; }; + +#define vert(v1, v2, v3, ls, t1, t2) { vertf((float)(v1), (float)(v2), (float)(v3), ls, t1, t2); } + +int nquads; +const float TEXTURESCALE = 32.0f; +bool floorstrip = false, deltastrip = false; +int oh, oy, ox, ogltex; // the o* vars are used by the stripification +int ol3r, ol3g, ol3b, ol4r, ol4g, ol4b; +int firstindex; +bool showm = false; + +void showmip() { showm = !showm; }; +void mipstats(int a, int b, int c) { if(showm) conoutf("1x1/2x2/4x4: %d / %d / %d", a, b, c); }; + +COMMAND(showmip, ARG_NONE); + +#define stripend() { if(floorstrip || deltastrip) { addstrip(ogltex, firstindex, curvert-firstindex); floorstrip = deltastrip = false; }; }; +void finishstrips() { stripend(); }; + +sqr sbright, sdark; +VAR(lighterror,1,8,100); + +void render_flat(int wtex, int x, int y, int size, int h, sqr *l1, sqr *l2, sqr *l3, sqr *l4, bool isceil) // floor/ceil quads +{ + vertcheck(); + if(showm) { l3 = l1 = &sbright; l4 = l2 = &sdark; }; + + int sx, sy; + int gltex = lookuptexture(wtex, sx, sy); + float xf = TEXTURESCALE/sx; + float yf = TEXTURESCALE/sy; + float xs = size*xf; + float ys = size*yf; + float xo = xf*x; + float yo = yf*y; + + bool first = !floorstrip || y!=oy+size || ogltex!=gltex || h!=oh || x!=ox; + + if(first) // start strip here + { + stripend(); + firstindex = curvert; + ogltex = gltex; + oh = h; + ox = x; + floorstrip = true; + if(isceil) + { + vert(x+size, h, y, l2, xo+xs, yo); + vert(x, h, y, l1, xo, yo); + } + else + { + vert(x, h, y, l1, xo, yo); + vert(x+size, h, y, l2, xo+xs, yo); + }; + ol3r = l1->r; + ol3g = l1->g; + ol3b = l1->b; + ol4r = l2->r; + ol4g = l2->g; + ol4b = l2->b; + } + else // continue strip + { + int lighterr = lighterror*2; + if((abs(ol3r-l3->r)r)g)g)b)b)r; + ol3g = l1->g; + ol3b = l1->b; + ol4r = l2->r; + ol4g = l2->g; + ol4b = l2->b; + }; + + if(isceil) + { + vertf((float)x+size, h3, (float)y+size, l3, xo+xs, yo+ys); + vertf((float)x, h4, (float)y+size, l4, xo, yo+ys); + } + else + { + vertf((float)x, h4, (float)y+size, l4, xo, yo+ys); + vertf((float)x+size, h3, (float)y+size, l3, xo+xs, yo+ys); + }; + + oy = y; + nquads++; +}; + +void render_2tris(sqr *h, sqr *s, int x1, int y1, int x2, int y2, int x3, int y3, sqr *l1, sqr *l2, sqr *l3) // floor/ceil tris on a corner cube +{ + stripend(); + vertcheck(); + + int sx, sy; + int gltex = lookuptexture(h->ftex, sx, sy); + float xf = TEXTURESCALE/sx; + float yf = TEXTURESCALE/sy; + + vertf((float)x1, h->floor, (float)y1, l1, xf*x1, yf*y1); + vertf((float)x2, h->floor, (float)y2, l2, xf*x2, yf*y2); + vertf((float)x3, h->floor, (float)y3, l3, xf*x3, yf*y3); + addstrip(gltex, curvert-3, 3); + + gltex = lookuptexture(h->ctex, sx, sy); + xf = TEXTURESCALE/sx; + yf = TEXTURESCALE/sy; + + vertf((float)x3, h->ceil, (float)y3, l3, xf*x3, yf*y3); + vertf((float)x2, h->ceil, (float)y2, l2, xf*x2, yf*y2); + vertf((float)x1, h->ceil, (float)y1, l1, xf*x1, yf*y1); + addstrip(gltex, curvert-3, 3); + nquads++; +}; + +void render_tris(int x, int y, int size, bool topleft, + sqr *h1, sqr *h2, sqr *s, sqr *t, sqr *u, sqr *v) +{ + if(topleft) + { + if(h1) render_2tris(h1, s, x+size, y+size, x, y+size, x, y, u, v, s); + if(h2) render_2tris(h2, s, x, y, x+size, y, x+size, y+size, s, t, v); + } + else + { + if(h1) render_2tris(h1, s, x, y, x+size, y, x, y+size, s, t, u); + if(h2) render_2tris(h2, s, x+size, y, x+size, y+size, x, y+size, t, u, v); + }; +}; + +void render_square(int wtex, float floor1, float floor2, float ceil1, float ceil2, int x1, int y1, int x2, int y2, int size, sqr *l1, sqr *l2, bool flip) // wall quads +{ + stripend(); + vertcheck(); + if(showm) { l1 = &sbright; l2 = &sdark; }; + + int sx, sy; + int gltex = lookuptexture(wtex, sx, sy); + float xf = TEXTURESCALE/sx; + float yf = TEXTURESCALE/sy; + float xs = size*xf; + float xo = xf*(x1==x2 ? min(y1,y2) : min(x1,x2)); + + if(!flip) + { + vertf((float)x2, ceil2, (float)y2, l2, xo+xs, -yf*ceil2); + vertf((float)x1, ceil1, (float)y1, l1, xo, -yf*ceil1); + vertf((float)x2, floor2, (float)y2, l2, xo+xs, -floor2*yf); + vertf((float)x1, floor1, (float)y1, l1, xo, -floor1*yf); + } + else + { + vertf((float)x1, ceil1, (float)y1, l1, xo, -yf*ceil1); + vertf((float)x2, ceil2, (float)y2, l2, xo+xs, -yf*ceil2); + vertf((float)x1, floor1, (float)y1, l1, xo, -floor1*yf); + vertf((float)x2, floor2, (float)y2, l2, xo+xs, -floor2*yf); + }; + + nquads++; + addstrip(gltex, curvert-4, 4); +}; + +int wx1, wy1, wx2, wy2; + +VAR(watersubdiv, 1, 4, 64); +VARF(waterlevel, -128, -128, 127, if(!noteditmode()) hdr.waterlevel = waterlevel); + +inline void vertw(int v1, float v2, int v3, sqr *c, float t1, float t2, float t) +{ + vertcheck(); + vertf((float)v1, v2-(float)sin(v1*v3*0.1+t)*0.2f, (float)v3, c, t1, t2); +}; + +inline float dx(float x) { return x+(float)sin(x*2+lastmillis/1000.0f)*0.04f; }; +inline float dy(float x) { return x+(float)sin(x*2+lastmillis/900.0f+PI/5)*0.05f; }; + +// renders water for bounding rect area that contains water... simple but very inefficient + +int renderwater(float hf) +{ + if(wx1<0) return nquads; + + glDepthMask(GL_FALSE); + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_SRC_COLOR); + int sx, sy; + glBindTexture(GL_TEXTURE_2D, lookuptexture(DEFAULT_LIQUID, sx, sy)); + + wx1 &= ~(watersubdiv-1); + wy1 &= ~(watersubdiv-1); + + float xf = TEXTURESCALE/sx; + float yf = TEXTURESCALE/sy; + float xs = watersubdiv*xf; + float ys = watersubdiv*yf; + float t1 = lastmillis/300.0f; + float t2 = lastmillis/4000.0f; + + sqr dl; + dl.r = dl.g = dl.b = 255; + + for(int xx = wx1; xxwx2) wx2 = x2; + if(y2>wy2) wy2 = y2; + }; +}; + +void resetcubes() +{ + if(!verts) reallocv(); + floorstrip = deltastrip = false; + wx1 = -1; + nquads = 0; + sbright.r = sbright.g = sbright.b = 255; + sdark.r = sdark.g = sdark.b = 0; +}; + + DELETED src/renderextras.cpp Index: src/renderextras.cpp ================================================================== --- src/renderextras.cpp +++ /dev/null @@ -1,372 +0,0 @@ -// renderextras.cpp: misc gl render code and the HUD - -#include "cube.h" - -void line(int x1, int y1, float z1, int x2, int y2, float z2) -{ - glBegin(GL_POLYGON); - glVertex3f((float)x1, z1, (float)y1); - glVertex3f((float)x1, z1, y1+0.01f); - glVertex3f((float)x2, z2, y2+0.01f); - glVertex3f((float)x2, z2, (float)y2); - glEnd(); - xtraverts += 4; -}; - -void linestyle(float width, int r, int g, int b) -{ - glLineWidth(width); - glColor3ub(r,g,b); -}; - -void box(block &b, float z1, float z2, float z3, float z4) -{ - glBegin(GL_POLYGON); - glVertex3f((float)b.x, z1, (float)b.y); - glVertex3f((float)b.x+b.xs, z2, (float)b.y); - glVertex3f((float)b.x+b.xs, z3, (float)b.y+b.ys); - glVertex3f((float)b.x, z4, (float)b.y+b.ys); - glEnd(); - xtraverts += 4; -}; - -void dot(int x, int y, float z) -{ - const float DOF = 0.1f; - glBegin(GL_POLYGON); - glVertex3f(x-DOF, (float)z, y-DOF); - glVertex3f(x+DOF, (float)z, y-DOF); - glVertex3f(x+DOF, (float)z, y+DOF); - glVertex3f(x-DOF, (float)z, y+DOF); - glEnd(); - xtraverts += 4; -}; - -void blendbox(int x1, int y1, int x2, int y2, bool border) -{ - glDepthMask(GL_FALSE); - glDisable(GL_TEXTURE_2D); - glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); - glBegin(GL_QUADS); - if(border) glColor3d(0.5, 0.3, 0.4); - else glColor3d(1.0, 1.0, 1.0); - glVertex2i(x1, y1); - glVertex2i(x2, y1); - glVertex2i(x2, y2); - glVertex2i(x1, y2); - glEnd(); - glDisable(GL_BLEND); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - glBegin(GL_POLYGON); - glColor3d(0.2, 0.7, 0.4); - glVertex2i(x1, y1); - glVertex2i(x2, y1); - glVertex2i(x2, y2); - glVertex2i(x1, y2); - glEnd(); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - xtraverts += 8; - glEnable(GL_BLEND); - glEnable(GL_TEXTURE_2D); - glDepthMask(GL_TRUE); -}; - -const int MAXSPHERES = 50; -struct sphere { vec o; float size, max; int type; sphere *next; }; -sphere spheres[MAXSPHERES], *slist = NULL, *sempty = NULL; -bool sinit = false; - -void newsphere(vec &o, float max, int type) -{ - if(!sinit) - { - loopi(MAXSPHERES) - { - spheres[i].next = sempty; - sempty = &spheres[i]; - }; - sinit = true; - }; - if(sempty) - { - sphere *p = sempty; - sempty = p->next; - p->o = o; - p->max = max; - p->size = 1; - p->type = type; - p->next = slist; - slist = p; - }; -}; - -void renderspheres(int time) -{ - glDepthMask(GL_FALSE); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE); - glBindTexture(GL_TEXTURE_2D, 4); - - for(sphere *p, **pp = &slist; p = *pp;) - { - glPushMatrix(); - float size = p->size/p->max; - glColor4f(1.0f, 1.0f, 1.0f, 1.0f-size); - glTranslatef(p->o.x, p->o.z, p->o.y); - glRotatef(lastmillis/5.0f, 1, 1, 1); - glScalef(p->size, p->size, p->size); - glCallList(1); - glScalef(0.8f, 0.8f, 0.8f); - glCallList(1); - glPopMatrix(); - xtraverts += 12*6*2; - - if(p->size>p->max) - { - *pp = p->next; - p->next = sempty; - sempty = p; - } - else - { - p->size += time/100.0f; - pp = &p->next; - }; - }; - - glDisable(GL_BLEND); - glDepthMask(GL_TRUE); -}; - -string closeent; -char *entnames[] = -{ - "none?", "light", "playerstart", - "shells", "bullets", "rockets", "riflerounds", - "health", "healthboost", "greenarmour", "yellowarmour", "quaddamage", - "teleport", "teledest", - "mapmodel", "monster", "trigger", "jumppad", - "?", "?", "?", "?", "?", -}; - -void renderents() // show sparkly thingies for map entities in edit mode -{ - closeent[0] = 0; - if(!editmode) return; - loopv(ents) - { - entity &e = ents[i]; - if(e.type==NOTUSED) continue; - vec v = { e.x, e.y, e.z }; - particle_splash(2, 2, 40, v); - }; - int e = closestent(); - if(e>=0) - { - entity &c = ents[e]; - sprintf_s(closeent)("closest entity = %s (%d, %d, %d, %d), selection = (%d, %d)", entnames[c.type], c.attr1, c.attr2, c.attr3, c.attr4, getvar("selxs"), getvar("selys")); - }; -}; - -void loadsky(char *basename) -{ - static string lastsky = ""; - if(strcmp(lastsky, basename)==0) return; - char *side[] = { "ft", "bk", "lf", "rt", "dn", "up" }; - int texnum = 14; - loopi(6) - { - sprintf_sd(name)("packages/%s_%s.jpg", basename, side[i]); - int xs, ys; - if(!installtex(texnum+i, path(name), xs, ys, true)) conoutf("could not load sky textures"); - }; - strcpy_s(lastsky, basename); -}; - -COMMAND(loadsky, ARG_1STR); - -float cursordepth = 0.9f; -GLint viewport[4]; -GLdouble mm[16], pm[16]; -vec worldpos; - -void readmatrices() -{ - glGetIntegerv(GL_VIEWPORT, viewport); - glGetDoublev(GL_MODELVIEW_MATRIX, mm); - glGetDoublev(GL_PROJECTION_MATRIX, pm); -}; - -// stupid function to cater for stupid ATI linux drivers that return incorrect depth values - -float depthcorrect(float d) -{ - return (d<=1/256.0f) ? d*256 : d; -}; - -// find out the 3d target of the crosshair in the world easily and very acurately. -// sadly many very old cards and drivers appear to fuck up on glReadPixels() and give false -// coordinates, making shooting and such impossible. -// also hits map entities which is unwanted. -// could be replaced by a more acurate version of monster.cpp los() if needed - -void readdepth(int w, int h) -{ - glReadPixels(w/2, h/2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &cursordepth); - double worldx = 0, worldy = 0, worldz = 0; - gluUnProject(w/2, h/2, depthcorrect(cursordepth), mm, pm, viewport, &worldx, &worldz, &worldy); - worldpos.x = (float)worldx; - worldpos.y = (float)worldy; - worldpos.z = (float)worldz; - vec r = { (float)mm[0], (float)mm[4], (float)mm[8] }; - vec u = { (float)mm[1], (float)mm[5], (float)mm[9] }; - setorient(r, u); -}; - -void drawicon(float tx, float ty, int x, int y) -{ - glBindTexture(GL_TEXTURE_2D, 5); - glBegin(GL_QUADS); - tx /= 192; - ty /= 192; - float o = 1/3.0f; - int s = 120; - glTexCoord2f(tx, ty); glVertex2i(x, y); - glTexCoord2f(tx+o, ty); glVertex2i(x+s, y); - glTexCoord2f(tx+o, ty+o); glVertex2i(x+s, y+s); - glTexCoord2f(tx, ty+o); glVertex2i(x, y+s); - glEnd(); - xtraverts += 4; -}; - -void invertperspective() -{ - // This only generates a valid inverse matrix for matrices generated by gluPerspective() - GLdouble inv[16]; - memset(inv, 0, sizeof(inv)); - - inv[0*4+0] = 1.0/pm[0*4+0]; - inv[1*4+1] = 1.0/pm[1*4+1]; - inv[2*4+3] = 1.0/pm[3*4+2]; - inv[3*4+2] = -1.0; - inv[3*4+3] = pm[2*4+2]/pm[3*4+2]; - - glLoadMatrixd(inv); -}; - -VARP(crosshairsize, 0, 15, 50); - -int dblend = 0; -void damageblend(int n) { dblend += n; }; - -VAR(hidestats, 0, 0, 1); -VARP(crosshairfx, 0, 1, 1); - -void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwater) -{ - readmatrices(); - if(editmode) - { - if(cursordepth==1.0f) worldpos = player1->o; - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - cursorupdate(); - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - }; - - glDisable(GL_DEPTH_TEST); - invertperspective(); - glPushMatrix(); - glOrtho(0, VIRTW, VIRTH, 0, -1, 1); - glEnable(GL_BLEND); - - glDepthMask(GL_FALSE); - - if(dblend || underwater) - { - glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); - glBegin(GL_QUADS); - if(dblend) glColor3d(0.0f, 0.9f, 0.9f); - else glColor3d(0.9f, 0.5f, 0.0f); - glVertex2i(0, 0); - glVertex2i(VIRTW, 0); - glVertex2i(VIRTW, VIRTH); - glVertex2i(0, VIRTH); - glEnd(); - dblend -= curtime/3; - if(dblend<0) dblend = 0; - }; - - glEnable(GL_TEXTURE_2D); - - char *command = getcurcommand(); - char *player = playerincrosshair(); - if(command) draw_textf("> %s_", 20, 1570, 2, command); - else if(closeent[0]) draw_text(closeent, 20, 1570, 2); - else if(player) draw_text(player, 20, 1570, 2); - - renderscores(); - if(!rendermenu()) - { - glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA); - glBindTexture(GL_TEXTURE_2D, 1); - glBegin(GL_QUADS); - glColor3ub(255,255,255); - if(crosshairfx) - { - if(player1->gunwait) glColor3ub(128,128,128); - else if(player1->health<=25) glColor3ub(255,0,0); - else if(player1->health<=50) glColor3ub(255,128,0); - }; - float chsize = (float)crosshairsize; - glTexCoord2d(0.0, 0.0); glVertex2f(VIRTW/2 - chsize, VIRTH/2 - chsize); - glTexCoord2d(1.0, 0.0); glVertex2f(VIRTW/2 + chsize, VIRTH/2 - chsize); - glTexCoord2d(1.0, 1.0); glVertex2f(VIRTW/2 + chsize, VIRTH/2 + chsize); - glTexCoord2d(0.0, 1.0); glVertex2f(VIRTW/2 - chsize, VIRTH/2 + chsize); - glEnd(); - }; - - glPopMatrix(); - - glPushMatrix(); - glOrtho(0, VIRTW*4/3, VIRTH*4/3, 0, -1, 1); - renderconsole(); - - if(!hidestats) - { - glPopMatrix(); - glPushMatrix(); - glOrtho(0, VIRTW*3/2, VIRTH*3/2, 0, -1, 1); - draw_textf("fps %d", 3200, 2390, 2, curfps); - draw_textf("wqd %d", 3200, 2460, 2, nquads); - draw_textf("wvt %d", 3200, 2530, 2, curvert); - draw_textf("evt %d", 3200, 2600, 2, xtraverts); - }; - - glPopMatrix(); - - if(player1->state==CS_ALIVE) - { - glPushMatrix(); - glOrtho(0, VIRTW/2, VIRTH/2, 0, -1, 1); - draw_textf("%d", 90, 827, 2, player1->health); - if(player1->armour) draw_textf("%d", 390, 827, 2, player1->armour); - draw_textf("%d", 690, 827, 2, player1->ammo[player1->gunselect]); - glPopMatrix(); - glPushMatrix(); - glOrtho(0, VIRTW, VIRTH, 0, -1, 1); - glDisable(GL_BLEND); - drawicon(128, 128, 20, 1650); - if(player1->armour) drawicon((float)(player1->armourtype*64), 0, 620, 1650); - int g = player1->gunselect; - int r = 64; - if(g>2) { g -= 3; r = 128; }; - drawicon((float)(g*64), (float)r, 1220, 1650); - glPopMatrix(); - }; - - glDepthMask(GL_TRUE); - glDisable(GL_BLEND); - glDisable(GL_TEXTURE_2D); - glEnable(GL_DEPTH_TEST); -}; - ADDED src/renderextras.cxx Index: src/renderextras.cxx ================================================================== --- /dev/null +++ src/renderextras.cxx @@ -0,0 +1,372 @@ +// renderextras.cpp: misc gl render code and the HUD + +#include "cube.h" + +void line(int x1, int y1, float z1, int x2, int y2, float z2) +{ + glBegin(GL_POLYGON); + glVertex3f((float)x1, z1, (float)y1); + glVertex3f((float)x1, z1, y1+0.01f); + glVertex3f((float)x2, z2, y2+0.01f); + glVertex3f((float)x2, z2, (float)y2); + glEnd(); + xtraverts += 4; +}; + +void linestyle(float width, int r, int g, int b) +{ + glLineWidth(width); + glColor3ub(r,g,b); +}; + +void box(block &b, float z1, float z2, float z3, float z4) +{ + glBegin(GL_POLYGON); + glVertex3f((float)b.x, z1, (float)b.y); + glVertex3f((float)b.x+b.xs, z2, (float)b.y); + glVertex3f((float)b.x+b.xs, z3, (float)b.y+b.ys); + glVertex3f((float)b.x, z4, (float)b.y+b.ys); + glEnd(); + xtraverts += 4; +}; + +void dot(int x, int y, float z) +{ + const float DOF = 0.1f; + glBegin(GL_POLYGON); + glVertex3f(x-DOF, (float)z, y-DOF); + glVertex3f(x+DOF, (float)z, y-DOF); + glVertex3f(x+DOF, (float)z, y+DOF); + glVertex3f(x-DOF, (float)z, y+DOF); + glEnd(); + xtraverts += 4; +}; + +void blendbox(int x1, int y1, int x2, int y2, bool border) +{ + glDepthMask(GL_FALSE); + glDisable(GL_TEXTURE_2D); + glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + glBegin(GL_QUADS); + if(border) glColor3d(0.5, 0.3, 0.4); + else glColor3d(1.0, 1.0, 1.0); + glVertex2i(x1, y1); + glVertex2i(x2, y1); + glVertex2i(x2, y2); + glVertex2i(x1, y2); + glEnd(); + glDisable(GL_BLEND); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + glBegin(GL_POLYGON); + glColor3d(0.2, 0.7, 0.4); + glVertex2i(x1, y1); + glVertex2i(x2, y1); + glVertex2i(x2, y2); + glVertex2i(x1, y2); + glEnd(); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + xtraverts += 8; + glEnable(GL_BLEND); + glEnable(GL_TEXTURE_2D); + glDepthMask(GL_TRUE); +}; + +const int MAXSPHERES = 50; +struct sphere { vec o; float size, max; int type; sphere *next; }; +sphere spheres[MAXSPHERES], *slist = NULL, *sempty = NULL; +bool sinit = false; + +void newsphere(vec &o, float max, int type) +{ + if(!sinit) + { + loopi(MAXSPHERES) + { + spheres[i].next = sempty; + sempty = &spheres[i]; + }; + sinit = true; + }; + if(sempty) + { + sphere *p = sempty; + sempty = p->next; + p->o = o; + p->max = max; + p->size = 1; + p->type = type; + p->next = slist; + slist = p; + }; +}; + +void renderspheres(int time) +{ + glDepthMask(GL_FALSE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE); + glBindTexture(GL_TEXTURE_2D, 4); + + for(sphere *p, **pp = &slist; p = *pp;) + { + glPushMatrix(); + float size = p->size/p->max; + glColor4f(1.0f, 1.0f, 1.0f, 1.0f-size); + glTranslatef(p->o.x, p->o.z, p->o.y); + glRotatef(lastmillis/5.0f, 1, 1, 1); + glScalef(p->size, p->size, p->size); + glCallList(1); + glScalef(0.8f, 0.8f, 0.8f); + glCallList(1); + glPopMatrix(); + xtraverts += 12*6*2; + + if(p->size>p->max) + { + *pp = p->next; + p->next = sempty; + sempty = p; + } + else + { + p->size += time/100.0f; + pp = &p->next; + }; + }; + + glDisable(GL_BLEND); + glDepthMask(GL_TRUE); +}; + +string closeent; +char *entnames[] = +{ + "none?", "light", "playerstart", + "shells", "bullets", "rockets", "riflerounds", + "health", "healthboost", "greenarmour", "yellowarmour", "quaddamage", + "teleport", "teledest", + "mapmodel", "monster", "trigger", "jumppad", + "?", "?", "?", "?", "?", +}; + +void renderents() // show sparkly thingies for map entities in edit mode +{ + closeent[0] = 0; + if(!editmode) return; + loopv(ents) + { + entity &e = ents[i]; + if(e.type==NOTUSED) continue; + vec v = { e.x, e.y, e.z }; + particle_splash(2, 2, 40, v); + }; + int e = closestent(); + if(e>=0) + { + entity &c = ents[e]; + sprintf_s(closeent)("closest entity = %s (%d, %d, %d, %d), selection = (%d, %d)", entnames[c.type], c.attr1, c.attr2, c.attr3, c.attr4, getvar("selxs"), getvar("selys")); + }; +}; + +void loadsky(char *basename) +{ + static string lastsky = ""; + if(strcmp(lastsky, basename)==0) return; + char *side[] = { "ft", "bk", "lf", "rt", "dn", "up" }; + int texnum = 14; + loopi(6) + { + sprintf_sd(name)("packages/%s_%s.jpg", basename, side[i]); + int xs, ys; + if(!installtex(texnum+i, path(name), xs, ys, true)) conoutf("could not load sky textures"); + }; + strcpy_s(lastsky, basename); +}; + +COMMAND(loadsky, ARG_1STR); + +float cursordepth = 0.9f; +GLint viewport[4]; +GLdouble mm[16], pm[16]; +vec worldpos; + +void readmatrices() +{ + glGetIntegerv(GL_VIEWPORT, viewport); + glGetDoublev(GL_MODELVIEW_MATRIX, mm); + glGetDoublev(GL_PROJECTION_MATRIX, pm); +}; + +// stupid function to cater for stupid ATI linux drivers that return incorrect depth values + +float depthcorrect(float d) +{ + return (d<=1/256.0f) ? d*256 : d; +}; + +// find out the 3d target of the crosshair in the world easily and very acurately. +// sadly many very old cards and drivers appear to fuck up on glReadPixels() and give false +// coordinates, making shooting and such impossible. +// also hits map entities which is unwanted. +// could be replaced by a more acurate version of monster.cpp los() if needed + +void readdepth(int w, int h) +{ + glReadPixels(w/2, h/2, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &cursordepth); + double worldx = 0, worldy = 0, worldz = 0; + gluUnProject(w/2, h/2, depthcorrect(cursordepth), mm, pm, viewport, &worldx, &worldz, &worldy); + worldpos.x = (float)worldx; + worldpos.y = (float)worldy; + worldpos.z = (float)worldz; + vec r = { (float)mm[0], (float)mm[4], (float)mm[8] }; + vec u = { (float)mm[1], (float)mm[5], (float)mm[9] }; + setorient(r, u); +}; + +void drawicon(float tx, float ty, int x, int y) +{ + glBindTexture(GL_TEXTURE_2D, 5); + glBegin(GL_QUADS); + tx /= 192; + ty /= 192; + float o = 1/3.0f; + int s = 120; + glTexCoord2f(tx, ty); glVertex2i(x, y); + glTexCoord2f(tx+o, ty); glVertex2i(x+s, y); + glTexCoord2f(tx+o, ty+o); glVertex2i(x+s, y+s); + glTexCoord2f(tx, ty+o); glVertex2i(x, y+s); + glEnd(); + xtraverts += 4; +}; + +void invertperspective() +{ + // This only generates a valid inverse matrix for matrices generated by gluPerspective() + GLdouble inv[16]; + memset(inv, 0, sizeof(inv)); + + inv[0*4+0] = 1.0/pm[0*4+0]; + inv[1*4+1] = 1.0/pm[1*4+1]; + inv[2*4+3] = 1.0/pm[3*4+2]; + inv[3*4+2] = -1.0; + inv[3*4+3] = pm[2*4+2]/pm[3*4+2]; + + glLoadMatrixd(inv); +}; + +VARP(crosshairsize, 0, 15, 50); + +int dblend = 0; +void damageblend(int n) { dblend += n; }; + +VAR(hidestats, 0, 0, 1); +VARP(crosshairfx, 0, 1, 1); + +void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwater) +{ + readmatrices(); + if(editmode) + { + if(cursordepth==1.0f) worldpos = player1->o; + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + cursorupdate(); + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + }; + + glDisable(GL_DEPTH_TEST); + invertperspective(); + glPushMatrix(); + glOrtho(0, VIRTW, VIRTH, 0, -1, 1); + glEnable(GL_BLEND); + + glDepthMask(GL_FALSE); + + if(dblend || underwater) + { + glBlendFunc(GL_ZERO, GL_ONE_MINUS_SRC_COLOR); + glBegin(GL_QUADS); + if(dblend) glColor3d(0.0f, 0.9f, 0.9f); + else glColor3d(0.9f, 0.5f, 0.0f); + glVertex2i(0, 0); + glVertex2i(VIRTW, 0); + glVertex2i(VIRTW, VIRTH); + glVertex2i(0, VIRTH); + glEnd(); + dblend -= curtime/3; + if(dblend<0) dblend = 0; + }; + + glEnable(GL_TEXTURE_2D); + + char *command = getcurcommand(); + char *player = playerincrosshair(); + if(command) draw_textf("> %s_", 20, 1570, 2, command); + else if(closeent[0]) draw_text(closeent, 20, 1570, 2); + else if(player) draw_text(player, 20, 1570, 2); + + renderscores(); + if(!rendermenu()) + { + glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA); + glBindTexture(GL_TEXTURE_2D, 1); + glBegin(GL_QUADS); + glColor3ub(255,255,255); + if(crosshairfx) + { + if(player1->gunwait) glColor3ub(128,128,128); + else if(player1->health<=25) glColor3ub(255,0,0); + else if(player1->health<=50) glColor3ub(255,128,0); + }; + float chsize = (float)crosshairsize; + glTexCoord2d(0.0, 0.0); glVertex2f(VIRTW/2 - chsize, VIRTH/2 - chsize); + glTexCoord2d(1.0, 0.0); glVertex2f(VIRTW/2 + chsize, VIRTH/2 - chsize); + glTexCoord2d(1.0, 1.0); glVertex2f(VIRTW/2 + chsize, VIRTH/2 + chsize); + glTexCoord2d(0.0, 1.0); glVertex2f(VIRTW/2 - chsize, VIRTH/2 + chsize); + glEnd(); + }; + + glPopMatrix(); + + glPushMatrix(); + glOrtho(0, VIRTW*4/3, VIRTH*4/3, 0, -1, 1); + renderconsole(); + + if(!hidestats) + { + glPopMatrix(); + glPushMatrix(); + glOrtho(0, VIRTW*3/2, VIRTH*3/2, 0, -1, 1); + draw_textf("fps %d", 3200, 2390, 2, curfps); + draw_textf("wqd %d", 3200, 2460, 2, nquads); + draw_textf("wvt %d", 3200, 2530, 2, curvert); + draw_textf("evt %d", 3200, 2600, 2, xtraverts); + }; + + glPopMatrix(); + + if(player1->state==CS_ALIVE) + { + glPushMatrix(); + glOrtho(0, VIRTW/2, VIRTH/2, 0, -1, 1); + draw_textf("%d", 90, 827, 2, player1->health); + if(player1->armour) draw_textf("%d", 390, 827, 2, player1->armour); + draw_textf("%d", 690, 827, 2, player1->ammo[player1->gunselect]); + glPopMatrix(); + glPushMatrix(); + glOrtho(0, VIRTW, VIRTH, 0, -1, 1); + glDisable(GL_BLEND); + drawicon(128, 128, 20, 1650); + if(player1->armour) drawicon((float)(player1->armourtype*64), 0, 620, 1650); + int g = player1->gunselect; + int r = 64; + if(g>2) { g -= 3; r = 128; }; + drawicon((float)(g*64), (float)r, 1220, 1650); + glPopMatrix(); + }; + + glDepthMask(GL_TRUE); + glDisable(GL_BLEND); + glDisable(GL_TEXTURE_2D); + glEnable(GL_DEPTH_TEST); +}; + DELETED src/rendergl.cpp Index: src/rendergl.cpp ================================================================== --- src/rendergl.cpp +++ /dev/null @@ -1,398 +0,0 @@ -// rendergl.cpp: core opengl rendering stuff - -#include "cube.h" - -#ifdef DARWIN -#define GL_COMBINE_EXT GL_COMBINE_ARB -#define GL_COMBINE_RGB_EXT GL_COMBINE_RGB_ARB -#define GL_SOURCE0_RBG_EXT GL_SOURCE0_RGB_ARB -#define GL_SOURCE1_RBG_EXT GL_SOURCE1_RGB_ARB -#define GL_RGB_SCALE_EXT GL_RGB_SCALE_ARB -#endif - -extern int curvert; - -bool hasoverbright = false; - -void purgetextures(); - -GLUquadricObj *qsphere = NULL; -int glmaxtexsize = 256; - -void gl_init(int w, int h) -{ - //#define fogvalues 0.5f, 0.6f, 0.7f, 1.0f - - glViewport(0, 0, w, h); - glClearDepth(1.0); - glDepthFunc(GL_LESS); - glEnable(GL_DEPTH_TEST); - glShadeModel(GL_SMOOTH); - - - glEnable(GL_FOG); - glFogi(GL_FOG_MODE, GL_LINEAR); - glFogf(GL_FOG_DENSITY, 0.25); - glHint(GL_FOG_HINT, GL_NICEST); - - - glEnable(GL_LINE_SMOOTH); - glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); - glEnable(GL_POLYGON_OFFSET_LINE); - glPolygonOffset(-3.0, -3.0); - - glCullFace(GL_FRONT); - glEnable(GL_CULL_FACE); - - char *exts = (char *)glGetString(GL_EXTENSIONS); - - if(strstr(exts, "GL_EXT_texture_env_combine")) hasoverbright = true; - else conoutf("WARNING: cannot use overbright lighting, using old lighting model!"); - - glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glmaxtexsize); - - purgetextures(); - - if(!(qsphere = gluNewQuadric())) fatal("glu sphere"); - gluQuadricDrawStyle(qsphere, GLU_FILL); - gluQuadricOrientation(qsphere, GLU_INSIDE); - gluQuadricTexture(qsphere, GL_TRUE); - glNewList(1, GL_COMPILE); - gluSphere(qsphere, 1, 12, 6); - glEndList(); -}; - -void cleangl() -{ - if(qsphere) gluDeleteQuadric(qsphere); -}; - -bool installtex(int tnum, char *texname, int &xs, int &ys, bool clamp) -{ - SDL_Surface *s = IMG_Load(texname); - if(!s) { conoutf("couldn't load texture %s", texname); return false; }; - if(s->format->BitsPerPixel!=24) { conoutf("texture must be 24bpp: %s", texname); return false; }; - // loopi(s->w*s->h*3) { uchar *p = (uchar *)s->pixels+i; *p = 255-*p; }; - glBindTexture(GL_TEXTURE_2D, tnum); - glPixelStorei(GL_UNPACK_ALIGNMENT, 1); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); //NEAREST); - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); - xs = s->w; - ys = s->h; - while(xs>glmaxtexsize || ys>glmaxtexsize) { xs /= 2; ys /= 2; }; - void *scaledimg = s->pixels; - if(xs!=s->w) - { - conoutf("warning: quality loss: scaling %s", texname); // for voodoo cards under linux - scaledimg = alloc(xs*ys*3); - gluScaleImage(GL_RGB, s->w, s->h, GL_UNSIGNED_BYTE, s->pixels, xs, ys, GL_UNSIGNED_BYTE, scaledimg); - }; - if(gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, xs, ys, GL_RGB, GL_UNSIGNED_BYTE, scaledimg)) fatal("could not build mipmaps"); - if(xs!=s->w) free(scaledimg); - SDL_FreeSurface(s); - return true; -}; - -// management of texture slots -// each texture slot can have multople texture frames, of which currently only the first is used -// additional frames can be used for various shaders - -const int MAXTEX = 1000; -int texx[MAXTEX]; // ( loaded texture ) -> ( name, size ) -int texy[MAXTEX]; -string texname[MAXTEX]; -int curtex = 0; -const int FIRSTTEX = 1000; // opengl id = loaded id + FIRSTTEX -// std 1+, sky 14+, mdls 20+ - -const int MAXFRAMES = 2; // increase to allow more complex shader defs -int mapping[256][MAXFRAMES]; // ( cube texture, frame ) -> ( opengl id, name ) -string mapname[256][MAXFRAMES]; - -void purgetextures() -{ - loopi(256) loop(j,MAXFRAMES) mapping[i][j] = 0; -}; - -int curtexnum = 0; - -void texturereset() { curtexnum = 0; }; - -void texture(char *aframe, char *name) -{ - int num = curtexnum++, frame = atoi(aframe); - if(num<0 || num>=256 || frame<0 || frame>=MAXFRAMES) return; - mapping[num][frame] = 1; - char *n = mapname[num][frame]; - strcpy_s(n, name); - path(n); -}; - -COMMAND(texturereset, ARG_NONE); -COMMAND(texture, ARG_2STR); - -int lookuptexture(int tex, int &xs, int &ys) -{ - int frame = 0; // other frames? - int tid = mapping[tex][frame]; - - if(tid>=FIRSTTEX) - { - xs = texx[tid-FIRSTTEX]; - ys = texy[tid-FIRSTTEX]; - return tid; - }; - - xs = ys = 16; - if(!tid) return 1; // crosshair :) - - loopi(curtex) // lazily happens once per "texture" command, basically - { - if(strcmp(mapname[tex][frame], texname[i])==0) - { - mapping[tex][frame] = tid = i+FIRSTTEX; - xs = texx[i]; - ys = texy[i]; - return tid; - }; - }; - - if(curtex==MAXTEX) fatal("loaded too many textures"); - - int tnum = curtex+FIRSTTEX; - strcpy_s(texname[curtex], mapname[tex][frame]); - - sprintf_sd(name)("packages%c%s", PATHDIV, texname[curtex]); - - if(installtex(tnum, name, xs, ys)) - { - mapping[tex][frame] = tnum; - texx[curtex] = xs; - texy[curtex] = ys; - curtex++; - return tnum; - } - else - { - return mapping[tex][frame] = FIRSTTEX; // temp fix - }; -}; - -void setupworld() -{ - glEnableClientState(GL_VERTEX_ARRAY); - glEnableClientState(GL_COLOR_ARRAY); - glEnableClientState(GL_TEXTURE_COORD_ARRAY); - setarraypointers(); - - if(hasoverbright) - { - glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); - glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); - glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT); - }; -}; - -int skyoglid; - -struct strip { int tex, start, num; }; -vector strips; - -void renderstripssky() -{ - glBindTexture(GL_TEXTURE_2D, skyoglid); - loopv(strips) if(strips[i].tex==skyoglid) glDrawArrays(GL_TRIANGLE_STRIP, strips[i].start, strips[i].num); -}; - -void renderstrips() -{ - int lasttex = -1; - loopv(strips) if(strips[i].tex!=skyoglid) - { - if(strips[i].tex!=lasttex) - { - glBindTexture(GL_TEXTURE_2D, strips[i].tex); - lasttex = strips[i].tex; - }; - glDrawArrays(GL_TRIANGLE_STRIP, strips[i].start, strips[i].num); - }; -}; - -void overbright(float amount) { if(hasoverbright) glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, amount ); }; - -void addstrip(int tex, int start, int n) -{ - strip &s = strips.add(); - s.tex = tex; - s.start = start; - s.num = n; -}; - -VARFP(gamma, 30, 100, 300, -{ - float f = gamma/100.0f; - if(SDL_SetGamma(f,f,f)==-1) - { - conoutf("Could not set gamma (card/driver doesn't support it?)"); - conoutf("sdl: %s", SDL_GetError()); - }; -}); - -void transplayer() -{ - glLoadIdentity(); - - glRotated(player1->roll,0.0,0.0,1.0); - glRotated(player1->pitch,-1.0,0.0,0.0); - glRotated(player1->yaw,0.0,1.0,0.0); - - glTranslated(-player1->o.x, (player1->state==CS_DEAD ? player1->eyeheight-0.2f : 0)-player1->o.z, -player1->o.y); -}; - -VARP(fov, 10, 105, 120); - -int xtraverts; - -VAR(fog, 64, 180, 1024); -VAR(fogcolour, 0, 0x8099B3, 0xFFFFFF); - -VARP(hudgun,0,1,1); - -char *hudgunnames[] = { "hudguns/fist", "hudguns/shotg", "hudguns/chaing", "hudguns/rocket", "hudguns/rifle" }; - -void drawhudmodel(int start, int end, float speed, int base) -{ - rendermodel(hudgunnames[player1->gunselect], start, end, 0, 1.0f, player1->o.x, player1->o.z, player1->o.y, player1->yaw+90, player1->pitch, false, 1.0f, speed, 0, base); -}; - -void drawhudgun(float fovy, float aspect, int farplane) -{ - if(!hudgun /*|| !player1->gunselect*/) return; - - glEnable(GL_CULL_FACE); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(fovy, aspect, 0.3f, farplane); - glMatrixMode(GL_MODELVIEW); - - //glClear(GL_DEPTH_BUFFER_BIT); - int rtime = reloadtime(player1->gunselect); - if(player1->lastaction && player1->lastattackgun==player1->gunselect && lastmillis-player1->lastactionlastaction); - } - else - { - drawhudmodel(6, 1, 100, 0); - }; - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - gluPerspective(fovy, aspect, 0.15f, farplane); - glMatrixMode(GL_MODELVIEW); - - glDisable(GL_CULL_FACE); -}; - -void gl_drawframe(int w, int h, float curfps) -{ - float hf = hdr.waterlevel-0.3f; - float fovy = (float)fov*h/w; - float aspect = w/(float)h; - bool underwater = player1->o.z>16)/256.0f, ((fogcolour>>8)&255)/256.0f, (fogcolour&255)/256.0f, 1.0f }; - glFogfv(GL_FOG_COLOR, fogc); - glClearColor(fogc[0], fogc[1], fogc[2], 1.0f); - - if(underwater) - { - fovy += (float)sin(lastmillis/1000.0)*2.0f; - aspect += (float)sin(lastmillis/1000.0+PI)*0.1f; - glFogi(GL_FOG_START, 0); - glFogi(GL_FOG_END, (fog+96)/8); - }; - - glClear((player1->outsidemap ? GL_COLOR_BUFFER_BIT : 0) | GL_DEPTH_BUFFER_BIT); - - glMatrixMode(GL_PROJECTION); - glLoadIdentity(); - int farplane = fog*5/2; - gluPerspective(fovy, aspect, 0.15f, farplane); - glMatrixMode(GL_MODELVIEW); - - transplayer(); - - glEnable(GL_TEXTURE_2D); - - int xs, ys; - skyoglid = lookuptexture(DEFAULT_SKY, xs, ys); - - resetcubes(); - - curvert = 0; - strips.setsize(0); - - render_world(player1->o.x, player1->o.y, player1->o.z, - (int)player1->yaw, (int)player1->pitch, (float)fov, w, h); - finishstrips(); - - setupworld(); - - renderstripssky(); - - glLoadIdentity(); - glRotated(player1->pitch, -1.0, 0.0, 0.0); - glRotated(player1->yaw, 0.0, 1.0, 0.0); - glRotated(90.0, 1.0, 0.0, 0.0); - glColor3f(1.0f, 1.0f, 1.0f); - glDisable(GL_FOG); - glDepthFunc(GL_GREATER); - draw_envbox(14, fog*4/3); - glDepthFunc(GL_LESS); - glEnable(GL_FOG); - - transplayer(); - - overbright(2); - - renderstrips(); - - xtraverts = 0; - - renderclients(); - monsterrender(); - - renderentities(); - - renderspheres(curtime); - renderents(); - - glDisable(GL_CULL_FACE); - - drawhudgun(fovy, aspect, farplane); - - overbright(1); - int nquads = renderwater(hf); - - overbright(2); - render_particles(curtime); - overbright(1); - - glDisable(GL_FOG); - - glDisable(GL_TEXTURE_2D); - - gl_drawhud(w, h, (int)curfps, nquads, curvert, underwater); - - glEnable(GL_CULL_FACE); - glEnable(GL_FOG); -}; - ADDED src/rendergl.cxx Index: src/rendergl.cxx ================================================================== --- /dev/null +++ src/rendergl.cxx @@ -0,0 +1,398 @@ +// rendergl.cpp: core opengl rendering stuff + +#include "cube.h" + +#ifdef DARWIN +#define GL_COMBINE_EXT GL_COMBINE_ARB +#define GL_COMBINE_RGB_EXT GL_COMBINE_RGB_ARB +#define GL_SOURCE0_RBG_EXT GL_SOURCE0_RGB_ARB +#define GL_SOURCE1_RBG_EXT GL_SOURCE1_RGB_ARB +#define GL_RGB_SCALE_EXT GL_RGB_SCALE_ARB +#endif + +extern int curvert; + +bool hasoverbright = false; + +void purgetextures(); + +GLUquadricObj *qsphere = NULL; +int glmaxtexsize = 256; + +void gl_init(int w, int h) +{ + //#define fogvalues 0.5f, 0.6f, 0.7f, 1.0f + + glViewport(0, 0, w, h); + glClearDepth(1.0); + glDepthFunc(GL_LESS); + glEnable(GL_DEPTH_TEST); + glShadeModel(GL_SMOOTH); + + + glEnable(GL_FOG); + glFogi(GL_FOG_MODE, GL_LINEAR); + glFogf(GL_FOG_DENSITY, 0.25); + glHint(GL_FOG_HINT, GL_NICEST); + + + glEnable(GL_LINE_SMOOTH); + glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); + glEnable(GL_POLYGON_OFFSET_LINE); + glPolygonOffset(-3.0, -3.0); + + glCullFace(GL_FRONT); + glEnable(GL_CULL_FACE); + + char *exts = (char *)glGetString(GL_EXTENSIONS); + + if(strstr(exts, "GL_EXT_texture_env_combine")) hasoverbright = true; + else conoutf("WARNING: cannot use overbright lighting, using old lighting model!"); + + glGetIntegerv(GL_MAX_TEXTURE_SIZE, &glmaxtexsize); + + purgetextures(); + + if(!(qsphere = gluNewQuadric())) fatal("glu sphere"); + gluQuadricDrawStyle(qsphere, GLU_FILL); + gluQuadricOrientation(qsphere, GLU_INSIDE); + gluQuadricTexture(qsphere, GL_TRUE); + glNewList(1, GL_COMPILE); + gluSphere(qsphere, 1, 12, 6); + glEndList(); +}; + +void cleangl() +{ + if(qsphere) gluDeleteQuadric(qsphere); +}; + +bool installtex(int tnum, char *texname, int &xs, int &ys, bool clamp) +{ + SDL_Surface *s = IMG_Load(texname); + if(!s) { conoutf("couldn't load texture %s", texname); return false; }; + if(s->format->BitsPerPixel!=24) { conoutf("texture must be 24bpp: %s", texname); return false; }; + // loopi(s->w*s->h*3) { uchar *p = (uchar *)s->pixels+i; *p = 255-*p; }; + glBindTexture(GL_TEXTURE_2D, tnum); + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clamp ? GL_CLAMP_TO_EDGE : GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); //NEAREST); + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); + xs = s->w; + ys = s->h; + while(xs>glmaxtexsize || ys>glmaxtexsize) { xs /= 2; ys /= 2; }; + void *scaledimg = s->pixels; + if(xs!=s->w) + { + conoutf("warning: quality loss: scaling %s", texname); // for voodoo cards under linux + scaledimg = alloc(xs*ys*3); + gluScaleImage(GL_RGB, s->w, s->h, GL_UNSIGNED_BYTE, s->pixels, xs, ys, GL_UNSIGNED_BYTE, scaledimg); + }; + if(gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGB, xs, ys, GL_RGB, GL_UNSIGNED_BYTE, scaledimg)) fatal("could not build mipmaps"); + if(xs!=s->w) free(scaledimg); + SDL_FreeSurface(s); + return true; +}; + +// management of texture slots +// each texture slot can have multople texture frames, of which currently only the first is used +// additional frames can be used for various shaders + +const int MAXTEX = 1000; +int texx[MAXTEX]; // ( loaded texture ) -> ( name, size ) +int texy[MAXTEX]; +string texname[MAXTEX]; +int curtex = 0; +const int FIRSTTEX = 1000; // opengl id = loaded id + FIRSTTEX +// std 1+, sky 14+, mdls 20+ + +const int MAXFRAMES = 2; // increase to allow more complex shader defs +int mapping[256][MAXFRAMES]; // ( cube texture, frame ) -> ( opengl id, name ) +string mapname[256][MAXFRAMES]; + +void purgetextures() +{ + loopi(256) loop(j,MAXFRAMES) mapping[i][j] = 0; +}; + +int curtexnum = 0; + +void texturereset() { curtexnum = 0; }; + +void texture(char *aframe, char *name) +{ + int num = curtexnum++, frame = atoi(aframe); + if(num<0 || num>=256 || frame<0 || frame>=MAXFRAMES) return; + mapping[num][frame] = 1; + char *n = mapname[num][frame]; + strcpy_s(n, name); + path(n); +}; + +COMMAND(texturereset, ARG_NONE); +COMMAND(texture, ARG_2STR); + +int lookuptexture(int tex, int &xs, int &ys) +{ + int frame = 0; // other frames? + int tid = mapping[tex][frame]; + + if(tid>=FIRSTTEX) + { + xs = texx[tid-FIRSTTEX]; + ys = texy[tid-FIRSTTEX]; + return tid; + }; + + xs = ys = 16; + if(!tid) return 1; // crosshair :) + + loopi(curtex) // lazily happens once per "texture" command, basically + { + if(strcmp(mapname[tex][frame], texname[i])==0) + { + mapping[tex][frame] = tid = i+FIRSTTEX; + xs = texx[i]; + ys = texy[i]; + return tid; + }; + }; + + if(curtex==MAXTEX) fatal("loaded too many textures"); + + int tnum = curtex+FIRSTTEX; + strcpy_s(texname[curtex], mapname[tex][frame]); + + sprintf_sd(name)("packages%c%s", PATHDIV, texname[curtex]); + + if(installtex(tnum, name, xs, ys)) + { + mapping[tex][frame] = tnum; + texx[curtex] = xs; + texy[curtex] = ys; + curtex++; + return tnum; + } + else + { + return mapping[tex][frame] = FIRSTTEX; // temp fix + }; +}; + +void setupworld() +{ + glEnableClientState(GL_VERTEX_ARRAY); + glEnableClientState(GL_COLOR_ARRAY); + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + setarraypointers(); + + if(hasoverbright) + { + glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); + glTexEnvi(GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_MODULATE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE0_RGB_EXT, GL_TEXTURE); + glTexEnvi(GL_TEXTURE_ENV, GL_SOURCE1_RGB_EXT, GL_PRIMARY_COLOR_EXT); + }; +}; + +int skyoglid; + +struct strip { int tex, start, num; }; +vector strips; + +void renderstripssky() +{ + glBindTexture(GL_TEXTURE_2D, skyoglid); + loopv(strips) if(strips[i].tex==skyoglid) glDrawArrays(GL_TRIANGLE_STRIP, strips[i].start, strips[i].num); +}; + +void renderstrips() +{ + int lasttex = -1; + loopv(strips) if(strips[i].tex!=skyoglid) + { + if(strips[i].tex!=lasttex) + { + glBindTexture(GL_TEXTURE_2D, strips[i].tex); + lasttex = strips[i].tex; + }; + glDrawArrays(GL_TRIANGLE_STRIP, strips[i].start, strips[i].num); + }; +}; + +void overbright(float amount) { if(hasoverbright) glTexEnvf(GL_TEXTURE_ENV, GL_RGB_SCALE_EXT, amount ); }; + +void addstrip(int tex, int start, int n) +{ + strip &s = strips.add(); + s.tex = tex; + s.start = start; + s.num = n; +}; + +VARFP(gamma, 30, 100, 300, +{ + float f = gamma/100.0f; + if(SDL_SetGamma(f,f,f)==-1) + { + conoutf("Could not set gamma (card/driver doesn't support it?)"); + conoutf("sdl: %s", SDL_GetError()); + }; +}); + +void transplayer() +{ + glLoadIdentity(); + + glRotated(player1->roll,0.0,0.0,1.0); + glRotated(player1->pitch,-1.0,0.0,0.0); + glRotated(player1->yaw,0.0,1.0,0.0); + + glTranslated(-player1->o.x, (player1->state==CS_DEAD ? player1->eyeheight-0.2f : 0)-player1->o.z, -player1->o.y); +}; + +VARP(fov, 10, 105, 120); + +int xtraverts; + +VAR(fog, 64, 180, 1024); +VAR(fogcolour, 0, 0x8099B3, 0xFFFFFF); + +VARP(hudgun,0,1,1); + +char *hudgunnames[] = { "hudguns/fist", "hudguns/shotg", "hudguns/chaing", "hudguns/rocket", "hudguns/rifle" }; + +void drawhudmodel(int start, int end, float speed, int base) +{ + rendermodel(hudgunnames[player1->gunselect], start, end, 0, 1.0f, player1->o.x, player1->o.z, player1->o.y, player1->yaw+90, player1->pitch, false, 1.0f, speed, 0, base); +}; + +void drawhudgun(float fovy, float aspect, int farplane) +{ + if(!hudgun /*|| !player1->gunselect*/) return; + + glEnable(GL_CULL_FACE); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(fovy, aspect, 0.3f, farplane); + glMatrixMode(GL_MODELVIEW); + + //glClear(GL_DEPTH_BUFFER_BIT); + int rtime = reloadtime(player1->gunselect); + if(player1->lastaction && player1->lastattackgun==player1->gunselect && lastmillis-player1->lastactionlastaction); + } + else + { + drawhudmodel(6, 1, 100, 0); + }; + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + gluPerspective(fovy, aspect, 0.15f, farplane); + glMatrixMode(GL_MODELVIEW); + + glDisable(GL_CULL_FACE); +}; + +void gl_drawframe(int w, int h, float curfps) +{ + float hf = hdr.waterlevel-0.3f; + float fovy = (float)fov*h/w; + float aspect = w/(float)h; + bool underwater = player1->o.z>16)/256.0f, ((fogcolour>>8)&255)/256.0f, (fogcolour&255)/256.0f, 1.0f }; + glFogfv(GL_FOG_COLOR, fogc); + glClearColor(fogc[0], fogc[1], fogc[2], 1.0f); + + if(underwater) + { + fovy += (float)sin(lastmillis/1000.0)*2.0f; + aspect += (float)sin(lastmillis/1000.0+PI)*0.1f; + glFogi(GL_FOG_START, 0); + glFogi(GL_FOG_END, (fog+96)/8); + }; + + glClear((player1->outsidemap ? GL_COLOR_BUFFER_BIT : 0) | GL_DEPTH_BUFFER_BIT); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + int farplane = fog*5/2; + gluPerspective(fovy, aspect, 0.15f, farplane); + glMatrixMode(GL_MODELVIEW); + + transplayer(); + + glEnable(GL_TEXTURE_2D); + + int xs, ys; + skyoglid = lookuptexture(DEFAULT_SKY, xs, ys); + + resetcubes(); + + curvert = 0; + strips.setsize(0); + + render_world(player1->o.x, player1->o.y, player1->o.z, + (int)player1->yaw, (int)player1->pitch, (float)fov, w, h); + finishstrips(); + + setupworld(); + + renderstripssky(); + + glLoadIdentity(); + glRotated(player1->pitch, -1.0, 0.0, 0.0); + glRotated(player1->yaw, 0.0, 1.0, 0.0); + glRotated(90.0, 1.0, 0.0, 0.0); + glColor3f(1.0f, 1.0f, 1.0f); + glDisable(GL_FOG); + glDepthFunc(GL_GREATER); + draw_envbox(14, fog*4/3); + glDepthFunc(GL_LESS); + glEnable(GL_FOG); + + transplayer(); + + overbright(2); + + renderstrips(); + + xtraverts = 0; + + renderclients(); + monsterrender(); + + renderentities(); + + renderspheres(curtime); + renderents(); + + glDisable(GL_CULL_FACE); + + drawhudgun(fovy, aspect, farplane); + + overbright(1); + int nquads = renderwater(hf); + + overbright(2); + render_particles(curtime); + overbright(1); + + glDisable(GL_FOG); + + glDisable(GL_TEXTURE_2D); + + gl_drawhud(w, h, (int)curfps, nquads, curvert, underwater); + + glEnable(GL_CULL_FACE); + glEnable(GL_FOG); +}; + DELETED src/rendermd2.cpp Index: src/rendermd2.cpp ================================================================== --- src/rendermd2.cpp +++ /dev/null @@ -1,275 +0,0 @@ -// rendermd2.cpp: loader code adapted from a nehe tutorial - -#include "cube.h" - -struct md2_header -{ - int magic; - int version; - int skinWidth, skinHeight; - int frameSize; - int numSkins, numVertices, numTexcoords; - int numTriangles, numGlCommands, numFrames; - int offsetSkins, offsetTexcoords, offsetTriangles; - int offsetFrames, offsetGlCommands, offsetEnd; -}; - -struct md2_vertex -{ - uchar vertex[3], lightNormalIndex; -}; - -struct md2_frame -{ - float scale[3]; - float translate[3]; - char name[16]; - md2_vertex vertices[1]; -}; - -struct md2 -{ - int numGlCommands; - int* glCommands; - int numTriangles; - int frameSize; - int numFrames; - int numVerts; - char* frames; - vec **mverts; - int displaylist; - int displaylistverts; - - mapmodelinfo mmi; - char *loadname; - int mdlnum; - bool loaded; - - bool load(char* filename); - void render(vec &light, int numFrame, int range, float x, float y, float z, float yaw, float pitch, float scale, float speed, int snap, int basetime); - void scale(int frame, float scale, int sn); - - md2() : numGlCommands(0), frameSize(0), numFrames(0), displaylist(0), loaded(false) {}; - - ~md2() - { - if(glCommands) - delete [] glCommands; - if(frames) - delete [] frames; - } -}; - - -bool md2::load(char* filename) -{ - FILE* file; - md2_header header; - - if((file= fopen(filename, "rb"))==NULL) return false; - - fread(&header, sizeof(md2_header), 1, file); - endianswap(&header, sizeof(int), sizeof(md2_header)/sizeof(int)); - - if(header.magic!= 844121161 || header.version!=8) return false; - - frames = new char[header.frameSize*header.numFrames]; - if(frames==NULL) return false; - - fseek(file, header.offsetFrames, SEEK_SET); - fread(frames, header.frameSize*header.numFrames, 1, file); - - for(int i = 0; i < header.numFrames; ++i) - { - endianswap(frames + i * header.frameSize, sizeof(float), 6); - } - - glCommands = new int[header.numGlCommands]; - if(glCommands==NULL) return false; - - fseek(file, header.offsetGlCommands, SEEK_SET); - fread(glCommands, header.numGlCommands*sizeof(int), 1, file); - - endianswap(glCommands, sizeof(int), header.numGlCommands); - - numFrames = header.numFrames; - numGlCommands= header.numGlCommands; - frameSize = header.frameSize; - numTriangles = header.numTriangles; - numVerts = header.numVertices; - - fclose(file); - - mverts = new vec*[numFrames]; - loopj(numFrames) mverts[j] = NULL; - - return true; -}; - -float snap(int sn, float f) { return sn ? (float)(((int)(f+sn*0.5f))&(~(sn-1))) : f; }; - -void md2::scale(int frame, float scale, int sn) -{ - mverts[frame] = new vec[numVerts]; - md2_frame *cf = (md2_frame *) ((char*)frames+frameSize*frame); - float sc = 16.0f/scale; - loop(vi, numVerts) - { - uchar *cv = (uchar *)&cf->vertices[vi].vertex; - vec *v = &(mverts[frame])[vi]; - v->x = (snap(sn, cv[0]*cf->scale[0])+cf->translate[0])/sc; - v->y = -(snap(sn, cv[1]*cf->scale[1])+cf->translate[1])/sc; - v->z = (snap(sn, cv[2]*cf->scale[2])+cf->translate[2])/sc; - }; -}; - -void md2::render(vec &light, int frame, int range, float x, float y, float z, float yaw, float pitch, float sc, float speed, int snap, int basetime) -{ - loopi(range) if(!mverts[frame+i]) scale(frame+i, sc, snap); - - glPushMatrix (); - glTranslatef(x, y, z); - glRotatef(yaw+180, 0, -1, 0); - glRotatef(pitch, 0, 0, 1); - - glColor3fv((float *)&light); - - if(displaylist && frame==0 && range==1) - { - glCallList(displaylist); - xtraverts += displaylistverts; - } - else - { - if(frame==0 && range==1) - { - static int displaylistn = 10; - glNewList(displaylist = displaylistn++, GL_COMPILE); - displaylistverts = xtraverts; - }; - - int time = lastmillis-basetime; - int fr1 = (int)(time/speed); - float frac1 = (time-fr1*speed)/speed; - float frac2 = 1-frac1; - fr1 = fr1%range+frame; - int fr2 = fr1+1; - if(fr2>=frame+range) fr2 = frame; - vec *verts1 = mverts[fr1]; - vec *verts2 = mverts[fr2]; - - for(int *command = glCommands; (*command)!=0;) - { - int numVertex = *command++; - if(numVertex>0) { glBegin(GL_TRIANGLE_STRIP); } - else { glBegin(GL_TRIANGLE_FAN); numVertex = -numVertex; }; - - loopi(numVertex) - { - float tu = *((float*)command++); - float tv = *((float*)command++); - glTexCoord2f(tu, tv); - int vn = *command++; - vec &v1 = verts1[vn]; - vec &v2 = verts2[vn]; - #define ip(c) v1.c*frac2+v2.c*frac1 - glVertex3f(ip(x), ip(z), ip(y)); - }; - - xtraverts += numVertex; - - glEnd(); - }; - - if(displaylist) - { - glEndList(); - displaylistverts = xtraverts-displaylistverts; - }; - }; - - glPopMatrix(); -} - -hashtable *mdllookup = NULL; -vector mapmodels; -const int FIRSTMDL = 20; - -void delayedload(md2 *m) -{ - if(!m->loaded) - { - sprintf_sd(name1)("packages/models/%s/tris.md2", m->loadname); - if(!m->load(path(name1))) fatal("loadmodel: ", name1); - sprintf_sd(name2)("packages/models/%s/skin.jpg", m->loadname); - int xs, ys; - installtex(FIRSTMDL+m->mdlnum, path(name2), xs, ys); - m->loaded = true; - }; -}; - -int modelnum = 0; - -md2 *loadmodel(char *name) -{ - if(!mdllookup) mdllookup = new hashtable; - md2 **mm = mdllookup->access(name); - if(mm) return *mm; - md2 *m = new md2(); - m->mdlnum = modelnum++; - mapmodelinfo mmi = { 2, 2, 0, 0, "" }; - m->mmi = mmi; - m->loadname = newstring(name); - mdllookup->access(m->loadname, &m); - return m; -}; - -void mapmodel(char *rad, char *h, char *zoff, char *snap, char *name) -{ - md2 *m = loadmodel(name); - mapmodelinfo mmi = { atoi(rad), atoi(h), atoi(zoff), atoi(snap), m->loadname }; - m->mmi = mmi; - mapmodels.add(m); -}; - -void mapmodelreset() { mapmodels.setsize(0); }; - -mapmodelinfo &getmminfo(int i) { return immi : *(mapmodelinfo *)0; }; - -COMMAND(mapmodel, ARG_5STR); -COMMAND(mapmodelreset, ARG_NONE); - -void rendermodel(char *mdl, int frame, int range, int tex, float rad, float x, float y, float z, float yaw, float pitch, bool teammate, float scale, float speed, int snap, int basetime) -{ - md2 *m = loadmodel(mdl); - - if(isoccluded(player1->o.x, player1->o.y, x-rad, z-rad, rad*2)) return; - - delayedload(m); - - int xs, ys; - glBindTexture(GL_TEXTURE_2D, tex ? lookuptexture(tex, xs, ys) : FIRSTMDL+m->mdlnum); - - int ix = (int)x; - int iy = (int)z; - vec light = { 1.0f, 1.0f, 1.0f }; - - if(!OUTBORD(ix, iy)) - { - sqr *s = S(ix,iy); - float ll = 256.0f; // 0.96f; - float of = 0.0f; // 0.1f; - light.x = s->r/ll+of; - light.y = s->g/ll+of; - light.z = s->b/ll+of; - }; - - if(teammate) - { - light.x *= 0.6f; - light.y *= 0.7f; - light.z *= 1.2f; - }; - - m->render(light, frame, range, x, y, z, yaw, pitch, scale, speed, snap, basetime); -}; ADDED src/rendermd2.cxx Index: src/rendermd2.cxx ================================================================== --- /dev/null +++ src/rendermd2.cxx @@ -0,0 +1,275 @@ +// rendermd2.cpp: loader code adapted from a nehe tutorial + +#include "cube.h" + +struct md2_header +{ + int magic; + int version; + int skinWidth, skinHeight; + int frameSize; + int numSkins, numVertices, numTexcoords; + int numTriangles, numGlCommands, numFrames; + int offsetSkins, offsetTexcoords, offsetTriangles; + int offsetFrames, offsetGlCommands, offsetEnd; +}; + +struct md2_vertex +{ + uchar vertex[3], lightNormalIndex; +}; + +struct md2_frame +{ + float scale[3]; + float translate[3]; + char name[16]; + md2_vertex vertices[1]; +}; + +struct md2 +{ + int numGlCommands; + int* glCommands; + int numTriangles; + int frameSize; + int numFrames; + int numVerts; + char* frames; + vec **mverts; + int displaylist; + int displaylistverts; + + mapmodelinfo mmi; + char *loadname; + int mdlnum; + bool loaded; + + bool load(char* filename); + void render(vec &light, int numFrame, int range, float x, float y, float z, float yaw, float pitch, float scale, float speed, int snap, int basetime); + void scale(int frame, float scale, int sn); + + md2() : numGlCommands(0), frameSize(0), numFrames(0), displaylist(0), loaded(false) {}; + + ~md2() + { + if(glCommands) + delete [] glCommands; + if(frames) + delete [] frames; + } +}; + + +bool md2::load(char* filename) +{ + FILE* file; + md2_header header; + + if((file= fopen(filename, "rb"))==NULL) return false; + + fread(&header, sizeof(md2_header), 1, file); + endianswap(&header, sizeof(int), sizeof(md2_header)/sizeof(int)); + + if(header.magic!= 844121161 || header.version!=8) return false; + + frames = new char[header.frameSize*header.numFrames]; + if(frames==NULL) return false; + + fseek(file, header.offsetFrames, SEEK_SET); + fread(frames, header.frameSize*header.numFrames, 1, file); + + for(int i = 0; i < header.numFrames; ++i) + { + endianswap(frames + i * header.frameSize, sizeof(float), 6); + } + + glCommands = new int[header.numGlCommands]; + if(glCommands==NULL) return false; + + fseek(file, header.offsetGlCommands, SEEK_SET); + fread(glCommands, header.numGlCommands*sizeof(int), 1, file); + + endianswap(glCommands, sizeof(int), header.numGlCommands); + + numFrames = header.numFrames; + numGlCommands= header.numGlCommands; + frameSize = header.frameSize; + numTriangles = header.numTriangles; + numVerts = header.numVertices; + + fclose(file); + + mverts = new vec*[numFrames]; + loopj(numFrames) mverts[j] = NULL; + + return true; +}; + +float snap(int sn, float f) { return sn ? (float)(((int)(f+sn*0.5f))&(~(sn-1))) : f; }; + +void md2::scale(int frame, float scale, int sn) +{ + mverts[frame] = new vec[numVerts]; + md2_frame *cf = (md2_frame *) ((char*)frames+frameSize*frame); + float sc = 16.0f/scale; + loop(vi, numVerts) + { + uchar *cv = (uchar *)&cf->vertices[vi].vertex; + vec *v = &(mverts[frame])[vi]; + v->x = (snap(sn, cv[0]*cf->scale[0])+cf->translate[0])/sc; + v->y = -(snap(sn, cv[1]*cf->scale[1])+cf->translate[1])/sc; + v->z = (snap(sn, cv[2]*cf->scale[2])+cf->translate[2])/sc; + }; +}; + +void md2::render(vec &light, int frame, int range, float x, float y, float z, float yaw, float pitch, float sc, float speed, int snap, int basetime) +{ + loopi(range) if(!mverts[frame+i]) scale(frame+i, sc, snap); + + glPushMatrix (); + glTranslatef(x, y, z); + glRotatef(yaw+180, 0, -1, 0); + glRotatef(pitch, 0, 0, 1); + + glColor3fv((float *)&light); + + if(displaylist && frame==0 && range==1) + { + glCallList(displaylist); + xtraverts += displaylistverts; + } + else + { + if(frame==0 && range==1) + { + static int displaylistn = 10; + glNewList(displaylist = displaylistn++, GL_COMPILE); + displaylistverts = xtraverts; + }; + + int time = lastmillis-basetime; + int fr1 = (int)(time/speed); + float frac1 = (time-fr1*speed)/speed; + float frac2 = 1-frac1; + fr1 = fr1%range+frame; + int fr2 = fr1+1; + if(fr2>=frame+range) fr2 = frame; + vec *verts1 = mverts[fr1]; + vec *verts2 = mverts[fr2]; + + for(int *command = glCommands; (*command)!=0;) + { + int numVertex = *command++; + if(numVertex>0) { glBegin(GL_TRIANGLE_STRIP); } + else { glBegin(GL_TRIANGLE_FAN); numVertex = -numVertex; }; + + loopi(numVertex) + { + float tu = *((float*)command++); + float tv = *((float*)command++); + glTexCoord2f(tu, tv); + int vn = *command++; + vec &v1 = verts1[vn]; + vec &v2 = verts2[vn]; + #define ip(c) v1.c*frac2+v2.c*frac1 + glVertex3f(ip(x), ip(z), ip(y)); + }; + + xtraverts += numVertex; + + glEnd(); + }; + + if(displaylist) + { + glEndList(); + displaylistverts = xtraverts-displaylistverts; + }; + }; + + glPopMatrix(); +} + +hashtable *mdllookup = NULL; +vector mapmodels; +const int FIRSTMDL = 20; + +void delayedload(md2 *m) +{ + if(!m->loaded) + { + sprintf_sd(name1)("packages/models/%s/tris.md2", m->loadname); + if(!m->load(path(name1))) fatal("loadmodel: ", name1); + sprintf_sd(name2)("packages/models/%s/skin.jpg", m->loadname); + int xs, ys; + installtex(FIRSTMDL+m->mdlnum, path(name2), xs, ys); + m->loaded = true; + }; +}; + +int modelnum = 0; + +md2 *loadmodel(char *name) +{ + if(!mdllookup) mdllookup = new hashtable; + md2 **mm = mdllookup->access(name); + if(mm) return *mm; + md2 *m = new md2(); + m->mdlnum = modelnum++; + mapmodelinfo mmi = { 2, 2, 0, 0, "" }; + m->mmi = mmi; + m->loadname = newstring(name); + mdllookup->access(m->loadname, &m); + return m; +}; + +void mapmodel(char *rad, char *h, char *zoff, char *snap, char *name) +{ + md2 *m = loadmodel(name); + mapmodelinfo mmi = { atoi(rad), atoi(h), atoi(zoff), atoi(snap), m->loadname }; + m->mmi = mmi; + mapmodels.add(m); +}; + +void mapmodelreset() { mapmodels.setsize(0); }; + +mapmodelinfo &getmminfo(int i) { return immi : *(mapmodelinfo *)0; }; + +COMMAND(mapmodel, ARG_5STR); +COMMAND(mapmodelreset, ARG_NONE); + +void rendermodel(char *mdl, int frame, int range, int tex, float rad, float x, float y, float z, float yaw, float pitch, bool teammate, float scale, float speed, int snap, int basetime) +{ + md2 *m = loadmodel(mdl); + + if(isoccluded(player1->o.x, player1->o.y, x-rad, z-rad, rad*2)) return; + + delayedload(m); + + int xs, ys; + glBindTexture(GL_TEXTURE_2D, tex ? lookuptexture(tex, xs, ys) : FIRSTMDL+m->mdlnum); + + int ix = (int)x; + int iy = (int)z; + vec light = { 1.0f, 1.0f, 1.0f }; + + if(!OUTBORD(ix, iy)) + { + sqr *s = S(ix,iy); + float ll = 256.0f; // 0.96f; + float of = 0.0f; // 0.1f; + light.x = s->r/ll+of; + light.y = s->g/ll+of; + light.z = s->b/ll+of; + }; + + if(teammate) + { + light.x *= 0.6f; + light.y *= 0.7f; + light.z *= 1.2f; + }; + + m->render(light, frame, range, x, y, z, yaw, pitch, scale, speed, snap, basetime); +}; DELETED src/renderparticles.cpp Index: src/renderparticles.cpp ================================================================== --- src/renderparticles.cpp +++ /dev/null @@ -1,142 +0,0 @@ -// renderparticles.cpp - -#include "cube.h" - -const int MAXPARTICLES = 10500; -const int NUMPARTCUTOFF = 20; -struct particle { vec o, d; int fade, type; int millis; particle *next; }; -particle particles[MAXPARTICLES], *parlist = NULL, *parempty = NULL; -bool parinit = false; - -VARP(maxparticles, 100, 2000, MAXPARTICLES-500); - -void newparticle(vec &o, vec &d, int fade, int type) -{ - if(!parinit) - { - loopi(MAXPARTICLES) - { - particles[i].next = parempty; - parempty = &particles[i]; - }; - parinit = true; - }; - if(parempty) - { - particle *p = parempty; - parempty = p->next; - p->o = o; - p->d = d; - p->fade = fade; - p->type = type; - p->millis = lastmillis; - p->next = parlist; - parlist = p; - }; -}; - -VAR(demotracking, 0, 0, 1); -VARP(particlesize, 20, 100, 500); - -vec right, up; - -void setorient(vec &r, vec &u) { right = r; up = u; }; - -void render_particles(int time) -{ - if(demoplayback && demotracking) - { - vec nom = { 0, 0, 0 }; - newparticle(player1->o, nom, 100000000, 8); - }; - - glDepthMask(GL_FALSE); - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA); - glDisable(GL_FOG); - - struct parttype { float r, g, b; int gr, tex; float sz; } parttypes[] = - { - { 0.7f, 0.6f, 0.3f, 2, 3, 0.06f }, // yellow: sparks - { 0.5f, 0.5f, 0.5f, 20, 7, 0.15f }, // grey: small smoke - { 0.2f, 0.2f, 1.0f, 20, 3, 0.08f }, // blue: edit mode entities - { 1.0f, 0.1f, 0.1f, 1, 7, 0.06f }, // red: blood spats - { 1.0f, 0.8f, 0.8f, 20, 6, 1.2f }, // yellow: fireball1 - { 0.5f, 0.5f, 0.5f, 20, 7, 0.6f }, // grey: big smoke - { 1.0f, 1.0f, 1.0f, 20, 8, 1.2f }, // blue: fireball2 - { 1.0f, 1.0f, 1.0f, 20, 9, 1.2f }, // green: fireball3 - { 1.0f, 0.1f, 0.1f, 0, 7, 0.2f }, // red: demotrack - }; - - int numrender = 0; - - for(particle *p, **pp = &parlist; p = *pp;) - { - parttype *pt = &parttypes[p->type]; - - glBindTexture(GL_TEXTURE_2D, pt->tex); - glBegin(GL_QUADS); - - glColor3d(pt->r, pt->g, pt->b); - float sz = pt->sz*particlesize/100.0f; - // perf varray? - glTexCoord2f(0.0, 1.0); glVertex3d(p->o.x+(-right.x+up.x)*sz, p->o.z+(-right.y+up.y)*sz, p->o.y+(-right.z+up.z)*sz); - glTexCoord2f(1.0, 1.0); glVertex3d(p->o.x+( right.x+up.x)*sz, p->o.z+( right.y+up.y)*sz, p->o.y+( right.z+up.z)*sz); - glTexCoord2f(1.0, 0.0); glVertex3d(p->o.x+( right.x-up.x)*sz, p->o.z+( right.y-up.y)*sz, p->o.y+( right.z-up.z)*sz); - glTexCoord2f(0.0, 0.0); glVertex3d(p->o.x+(-right.x-up.x)*sz, p->o.z+(-right.y-up.y)*sz, p->o.y+(-right.z-up.z)*sz); - glEnd(); - xtraverts += 4; - - if(numrender++>maxparticles || (p->fade -= time)<0) - { - *pp = p->next; - p->next = parempty; - parempty = p; - } - else - { - if(pt->gr) p->o.z -= ((lastmillis-p->millis)/3.0f)*curtime/(pt->gr*10000); - vec a = p->d; - vmul(a,time); - vdiv(a,20000.0f); - vadd(p->o, a); - pp = &p->next; - }; - }; - - glEnable(GL_FOG); - glDisable(GL_BLEND); - glDepthMask(GL_TRUE); -}; - -void particle_splash(int type, int num, int fade, vec &p) -{ - loopi(num) - { - const int radius = type==5 ? 50 : 150; - int x, y, z; - do - { - x = rnd(radius*2)-radius; - y = rnd(radius*2)-radius; - z = rnd(radius*2)-radius; - } - while(x*x+y*y+z*z>radius*radius); - vec d = { (float)x, (float)y, (float)z }; - newparticle(p, d, rnd(fade*3), type); - }; -}; - -void particle_trail(int type, int fade, vec &s, vec &e) -{ - vdist(d, v, s, e); - vdiv(v, d*2+0.1f); - vec p = s; - loopi((int)d*2) - { - vadd(p, v); - vec d = { float(rnd(11)-5), float(rnd(11)-5), float(rnd(11)-5) }; - newparticle(p, d, rnd(fade)+fade, type); - }; -}; - ADDED src/renderparticles.cxx Index: src/renderparticles.cxx ================================================================== --- /dev/null +++ src/renderparticles.cxx @@ -0,0 +1,142 @@ +// renderparticles.cpp + +#include "cube.h" + +const int MAXPARTICLES = 10500; +const int NUMPARTCUTOFF = 20; +struct particle { vec o, d; int fade, type; int millis; particle *next; }; +particle particles[MAXPARTICLES], *parlist = NULL, *parempty = NULL; +bool parinit = false; + +VARP(maxparticles, 100, 2000, MAXPARTICLES-500); + +void newparticle(vec &o, vec &d, int fade, int type) +{ + if(!parinit) + { + loopi(MAXPARTICLES) + { + particles[i].next = parempty; + parempty = &particles[i]; + }; + parinit = true; + }; + if(parempty) + { + particle *p = parempty; + parempty = p->next; + p->o = o; + p->d = d; + p->fade = fade; + p->type = type; + p->millis = lastmillis; + p->next = parlist; + parlist = p; + }; +}; + +VAR(demotracking, 0, 0, 1); +VARP(particlesize, 20, 100, 500); + +vec right, up; + +void setorient(vec &r, vec &u) { right = r; up = u; }; + +void render_particles(int time) +{ + if(demoplayback && demotracking) + { + vec nom = { 0, 0, 0 }; + newparticle(player1->o, nom, 100000000, 8); + }; + + glDepthMask(GL_FALSE); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_SRC_ALPHA); + glDisable(GL_FOG); + + struct parttype { float r, g, b; int gr, tex; float sz; } parttypes[] = + { + { 0.7f, 0.6f, 0.3f, 2, 3, 0.06f }, // yellow: sparks + { 0.5f, 0.5f, 0.5f, 20, 7, 0.15f }, // grey: small smoke + { 0.2f, 0.2f, 1.0f, 20, 3, 0.08f }, // blue: edit mode entities + { 1.0f, 0.1f, 0.1f, 1, 7, 0.06f }, // red: blood spats + { 1.0f, 0.8f, 0.8f, 20, 6, 1.2f }, // yellow: fireball1 + { 0.5f, 0.5f, 0.5f, 20, 7, 0.6f }, // grey: big smoke + { 1.0f, 1.0f, 1.0f, 20, 8, 1.2f }, // blue: fireball2 + { 1.0f, 1.0f, 1.0f, 20, 9, 1.2f }, // green: fireball3 + { 1.0f, 0.1f, 0.1f, 0, 7, 0.2f }, // red: demotrack + }; + + int numrender = 0; + + for(particle *p, **pp = &parlist; p = *pp;) + { + parttype *pt = &parttypes[p->type]; + + glBindTexture(GL_TEXTURE_2D, pt->tex); + glBegin(GL_QUADS); + + glColor3d(pt->r, pt->g, pt->b); + float sz = pt->sz*particlesize/100.0f; + // perf varray? + glTexCoord2f(0.0, 1.0); glVertex3d(p->o.x+(-right.x+up.x)*sz, p->o.z+(-right.y+up.y)*sz, p->o.y+(-right.z+up.z)*sz); + glTexCoord2f(1.0, 1.0); glVertex3d(p->o.x+( right.x+up.x)*sz, p->o.z+( right.y+up.y)*sz, p->o.y+( right.z+up.z)*sz); + glTexCoord2f(1.0, 0.0); glVertex3d(p->o.x+( right.x-up.x)*sz, p->o.z+( right.y-up.y)*sz, p->o.y+( right.z-up.z)*sz); + glTexCoord2f(0.0, 0.0); glVertex3d(p->o.x+(-right.x-up.x)*sz, p->o.z+(-right.y-up.y)*sz, p->o.y+(-right.z-up.z)*sz); + glEnd(); + xtraverts += 4; + + if(numrender++>maxparticles || (p->fade -= time)<0) + { + *pp = p->next; + p->next = parempty; + parempty = p; + } + else + { + if(pt->gr) p->o.z -= ((lastmillis-p->millis)/3.0f)*curtime/(pt->gr*10000); + vec a = p->d; + vmul(a,time); + vdiv(a,20000.0f); + vadd(p->o, a); + pp = &p->next; + }; + }; + + glEnable(GL_FOG); + glDisable(GL_BLEND); + glDepthMask(GL_TRUE); +}; + +void particle_splash(int type, int num, int fade, vec &p) +{ + loopi(num) + { + const int radius = type==5 ? 50 : 150; + int x, y, z; + do + { + x = rnd(radius*2)-radius; + y = rnd(radius*2)-radius; + z = rnd(radius*2)-radius; + } + while(x*x+y*y+z*z>radius*radius); + vec d = { (float)x, (float)y, (float)z }; + newparticle(p, d, rnd(fade*3), type); + }; +}; + +void particle_trail(int type, int fade, vec &s, vec &e) +{ + vdist(d, v, s, e); + vdiv(v, d*2+0.1f); + vec p = s; + loopi((int)d*2) + { + vadd(p, v); + vec d = { float(rnd(11)-5), float(rnd(11)-5), float(rnd(11)-5) }; + newparticle(p, d, rnd(fade)+fade, type); + }; +}; + DELETED src/rendertext.cpp Index: src/rendertext.cpp ================================================================== --- src/rendertext.cpp +++ /dev/null @@ -1,222 +0,0 @@ -// rendertext.cpp: based on Don's gl_text.cpp - -#include "cube.h" - -short char_coords[96][4] = -{ - {0,0,25,64}, //! - {25,0,54,64}, //" - {54,0,107,64}, //# - {107,0,148,64}, //$ - {148,0,217,64}, //% - {217,0,263,64}, //& - {263,0,280,64}, //' - {280,0,309,64}, //( - {309,0,338,64}, //) - {338,0,379,64}, //* - {379,0,432,64}, //+ - {432,0,455,64}, //, - {455,0,484,64}, //- - {0,64,21,128}, //. - {23,64,52,128}, /// - {52,64,93,128}, //0 - {93,64,133,128}, //1 - {133,64,174,128}, //2 - {174,64,215,128}, //3 - {215,64,256,128}, //4 - {256,64,296,128}, //5 - {296,64,337,128}, //6 - {337,64,378,128}, //7 - {378,64,419,128}, //8 - {419,64,459,128}, //9 - {459,64,488,128}, //: - {0,128,29,192}, //; - {29,128,81,192}, //< - {81,128,134,192}, //= - {134,128,186,192}, //> - {186,128,221,192}, //? - {221,128,285,192}, //@ - {285,128,329,192}, //A - {329,128,373,192}, //B - {373,128,418,192}, //C - {418,128,467,192}, //D - {0,192,40,256}, //E - {40,192,77,256}, //F - {77,192,127,256}, //G - {127,192,175,256}, //H - {175,192,202,256}, //I - {202,192,231,256}, //J - {231,192,275,256}, //K - {275,192,311,256}, //L - {311,192,365,256}, //M - {365,192,413,256}, //N - {413,192,463,256}, //O - {1,256,38,320}, //P - {38,256,89,320}, //Q - {89,256,133,320}, //R - {133,256,176,320}, //S - {177,256,216,320}, //T - {217,256,263,320}, //U - {263,256,307,320}, //V - {307,256,370,320}, //W - {370,256,414,320}, //X - {414,256,453,320}, //Y - {453,256,497,320}, //Z - {0,320,29,384}, //[ - {29,320,58,384}, //"\" - {59,320,87,384}, //] - {87,320,139,384}, //^ - {139,320,180,384}, //_ - {180,320,221,384}, //` - {221,320,259,384}, //a - {259,320,299,384}, //b - {299,320,332,384}, //c - {332,320,372,384}, //d - {372,320,411,384}, //e - {411,320,433,384}, //f - {435,320,473,384}, //g - {0,384,40,448}, //h - {40,384,56,448}, //i - {58,384,80,448}, //j - {80,384,118,448}, //k - {118,384,135,448}, //l - {135,384,197,448}, //m - {197,384,238,448}, //n - {238,384,277,448}, //o - {277,384,317,448}, //p - {317,384,356,448}, //q - {357,384,384,448}, //r - {385,384,417,448}, //s - {417,384,442,448}, //t - {443,384,483,448}, //u - {0,448,38,512}, //v - {38,448,90,512}, //w - {90,448,128,512}, //x - {128,448,166,512}, //y - {166,448,200,512}, //z - {200,448,241,512}, //{ - {241,448,270,512}, //| - {270,448,310,512}, //} - {310,448,363,512}, //~ -}; - -int text_width(char *str) -{ - int x = 0; - for (int i = 0; str[i] != 0; i++) - { - int c = str[i]; - if(c=='\t') { x = (x+PIXELTAB)/PIXELTAB*PIXELTAB; continue; }; - if(c=='\f') continue; - if(c==' ') { x += FONTH/2; continue; }; - c -= 33; - if(c<0 || c>=95) continue; - int in_width = char_coords[c][2] - char_coords[c][0]; - x += in_width + 1; - } - return x; -} - - -void draw_textf(char *fstr, int left, int top, int gl_num, ...) -{ - sprintf_sdlv(str, gl_num, fstr); - draw_text(str, left, top, gl_num); -}; - -void draw_text(char *str, int left, int top, int gl_num) -{ - glBlendFunc(GL_ONE, GL_ONE); - glBindTexture(GL_TEXTURE_2D, gl_num); - glColor3ub(255,255,255); - - int x = left; - int y = top; - - int i; - float in_left, in_top, in_right, in_bottom; - int in_width, in_height; - - for (i = 0; str[i] != 0; i++) - { - int c = str[i]; - if(c=='\t') { x = (x-left+PIXELTAB)/PIXELTAB*PIXELTAB+left; continue; }; - if(c=='\f') { glColor3ub(64,255,128); continue; }; - if(c==' ') { x += FONTH/2; continue; }; - c -= 33; - if(c<0 || c>=95) continue; - - in_left = ((float) char_coords[c][0]) / 512.0f; - in_top = ((float) char_coords[c][1]+2) / 512.0f; - in_right = ((float) char_coords[c][2]) / 512.0f; - in_bottom = ((float) char_coords[c][3]-2) / 512.0f; - - in_width = char_coords[c][2] - char_coords[c][0]; - in_height = char_coords[c][3] - char_coords[c][1]; - - glBegin(GL_QUADS); - glTexCoord2f(in_left, in_top ); glVertex2i(x, y); - glTexCoord2f(in_right, in_top ); glVertex2i(x + in_width, y); - glTexCoord2f(in_right, in_bottom); glVertex2i(x + in_width, y + in_height); - glTexCoord2f(in_left, in_bottom); glVertex2i(x, y + in_height); - glEnd(); - - xtraverts += 4; - x += in_width + 1; - } -} - -// also Don's code, so goes in here too :) - -void draw_envbox_aux(float s0, float t0, int x0, int y0, int z0, - float s1, float t1, int x1, int y1, int z1, - float s2, float t2, int x2, int y2, int z2, - float s3, float t3, int x3, int y3, int z3, - int texture) -{ - glBindTexture(GL_TEXTURE_2D, texture); - glBegin(GL_QUADS); - glTexCoord2f(s3, t3); glVertex3d(x3, y3, z3); - glTexCoord2f(s2, t2); glVertex3d(x2, y2, z2); - glTexCoord2f(s1, t1); glVertex3d(x1, y1, z1); - glTexCoord2f(s0, t0); glVertex3d(x0, y0, z0); - glEnd(); - xtraverts += 4; -} - -void draw_envbox(int t, int w) -{ - glDepthMask(GL_FALSE); - - draw_envbox_aux(1.0f, 1.0f, -w, -w, w, - 0.0f, 1.0f, w, -w, w, - 0.0f, 0.0f, w, -w, -w, - 1.0f, 0.0f, -w, -w, -w, t); - - draw_envbox_aux(1.0f, 1.0f, +w, w, w, - 0.0f, 1.0f, -w, w, w, - 0.0f, 0.0f, -w, w, -w, - 1.0f, 0.0f, +w, w, -w, t+1); - - draw_envbox_aux(0.0f, 0.0f, -w, -w, -w, - 1.0f, 0.0f, -w, w, -w, - 1.0f, 1.0f, -w, w, w, - 0.0f, 1.0f, -w, -w, w, t+2); - - draw_envbox_aux(1.0f, 1.0f, +w, -w, w, - 0.0f, 1.0f, +w, w, w, - 0.0f, 0.0f, +w, w, -w, - 1.0f, 0.0f, +w, -w, -w, t+3); - - draw_envbox_aux(0.0f, 1.0f, -w, w, w, - 0.0f, 0.0f, +w, w, w, - 1.0f, 0.0f, +w, -w, w, - 1.0f, 1.0f, -w, -w, w, t+4); - - draw_envbox_aux(0.0f, 1.0f, +w, w, -w, - 0.0f, 0.0f, -w, w, -w, - 1.0f, 0.0f, -w, -w, -w, - 1.0f, 1.0f, +w, -w, -w, t+5); - - glDepthMask(GL_TRUE); -} ADDED src/rendertext.cxx Index: src/rendertext.cxx ================================================================== --- /dev/null +++ src/rendertext.cxx @@ -0,0 +1,222 @@ +// rendertext.cpp: based on Don's gl_text.cpp + +#include "cube.h" + +short char_coords[96][4] = +{ + {0,0,25,64}, //! + {25,0,54,64}, //" + {54,0,107,64}, //# + {107,0,148,64}, //$ + {148,0,217,64}, //% + {217,0,263,64}, //& + {263,0,280,64}, //' + {280,0,309,64}, //( + {309,0,338,64}, //) + {338,0,379,64}, //* + {379,0,432,64}, //+ + {432,0,455,64}, //, + {455,0,484,64}, //- + {0,64,21,128}, //. + {23,64,52,128}, /// + {52,64,93,128}, //0 + {93,64,133,128}, //1 + {133,64,174,128}, //2 + {174,64,215,128}, //3 + {215,64,256,128}, //4 + {256,64,296,128}, //5 + {296,64,337,128}, //6 + {337,64,378,128}, //7 + {378,64,419,128}, //8 + {419,64,459,128}, //9 + {459,64,488,128}, //: + {0,128,29,192}, //; + {29,128,81,192}, //< + {81,128,134,192}, //= + {134,128,186,192}, //> + {186,128,221,192}, //? + {221,128,285,192}, //@ + {285,128,329,192}, //A + {329,128,373,192}, //B + {373,128,418,192}, //C + {418,128,467,192}, //D + {0,192,40,256}, //E + {40,192,77,256}, //F + {77,192,127,256}, //G + {127,192,175,256}, //H + {175,192,202,256}, //I + {202,192,231,256}, //J + {231,192,275,256}, //K + {275,192,311,256}, //L + {311,192,365,256}, //M + {365,192,413,256}, //N + {413,192,463,256}, //O + {1,256,38,320}, //P + {38,256,89,320}, //Q + {89,256,133,320}, //R + {133,256,176,320}, //S + {177,256,216,320}, //T + {217,256,263,320}, //U + {263,256,307,320}, //V + {307,256,370,320}, //W + {370,256,414,320}, //X + {414,256,453,320}, //Y + {453,256,497,320}, //Z + {0,320,29,384}, //[ + {29,320,58,384}, //"\" + {59,320,87,384}, //] + {87,320,139,384}, //^ + {139,320,180,384}, //_ + {180,320,221,384}, //` + {221,320,259,384}, //a + {259,320,299,384}, //b + {299,320,332,384}, //c + {332,320,372,384}, //d + {372,320,411,384}, //e + {411,320,433,384}, //f + {435,320,473,384}, //g + {0,384,40,448}, //h + {40,384,56,448}, //i + {58,384,80,448}, //j + {80,384,118,448}, //k + {118,384,135,448}, //l + {135,384,197,448}, //m + {197,384,238,448}, //n + {238,384,277,448}, //o + {277,384,317,448}, //p + {317,384,356,448}, //q + {357,384,384,448}, //r + {385,384,417,448}, //s + {417,384,442,448}, //t + {443,384,483,448}, //u + {0,448,38,512}, //v + {38,448,90,512}, //w + {90,448,128,512}, //x + {128,448,166,512}, //y + {166,448,200,512}, //z + {200,448,241,512}, //{ + {241,448,270,512}, //| + {270,448,310,512}, //} + {310,448,363,512}, //~ +}; + +int text_width(char *str) +{ + int x = 0; + for (int i = 0; str[i] != 0; i++) + { + int c = str[i]; + if(c=='\t') { x = (x+PIXELTAB)/PIXELTAB*PIXELTAB; continue; }; + if(c=='\f') continue; + if(c==' ') { x += FONTH/2; continue; }; + c -= 33; + if(c<0 || c>=95) continue; + int in_width = char_coords[c][2] - char_coords[c][0]; + x += in_width + 1; + } + return x; +} + + +void draw_textf(char *fstr, int left, int top, int gl_num, ...) +{ + sprintf_sdlv(str, gl_num, fstr); + draw_text(str, left, top, gl_num); +}; + +void draw_text(char *str, int left, int top, int gl_num) +{ + glBlendFunc(GL_ONE, GL_ONE); + glBindTexture(GL_TEXTURE_2D, gl_num); + glColor3ub(255,255,255); + + int x = left; + int y = top; + + int i; + float in_left, in_top, in_right, in_bottom; + int in_width, in_height; + + for (i = 0; str[i] != 0; i++) + { + int c = str[i]; + if(c=='\t') { x = (x-left+PIXELTAB)/PIXELTAB*PIXELTAB+left; continue; }; + if(c=='\f') { glColor3ub(64,255,128); continue; }; + if(c==' ') { x += FONTH/2; continue; }; + c -= 33; + if(c<0 || c>=95) continue; + + in_left = ((float) char_coords[c][0]) / 512.0f; + in_top = ((float) char_coords[c][1]+2) / 512.0f; + in_right = ((float) char_coords[c][2]) / 512.0f; + in_bottom = ((float) char_coords[c][3]-2) / 512.0f; + + in_width = char_coords[c][2] - char_coords[c][0]; + in_height = char_coords[c][3] - char_coords[c][1]; + + glBegin(GL_QUADS); + glTexCoord2f(in_left, in_top ); glVertex2i(x, y); + glTexCoord2f(in_right, in_top ); glVertex2i(x + in_width, y); + glTexCoord2f(in_right, in_bottom); glVertex2i(x + in_width, y + in_height); + glTexCoord2f(in_left, in_bottom); glVertex2i(x, y + in_height); + glEnd(); + + xtraverts += 4; + x += in_width + 1; + } +} + +// also Don's code, so goes in here too :) + +void draw_envbox_aux(float s0, float t0, int x0, int y0, int z0, + float s1, float t1, int x1, int y1, int z1, + float s2, float t2, int x2, int y2, int z2, + float s3, float t3, int x3, int y3, int z3, + int texture) +{ + glBindTexture(GL_TEXTURE_2D, texture); + glBegin(GL_QUADS); + glTexCoord2f(s3, t3); glVertex3d(x3, y3, z3); + glTexCoord2f(s2, t2); glVertex3d(x2, y2, z2); + glTexCoord2f(s1, t1); glVertex3d(x1, y1, z1); + glTexCoord2f(s0, t0); glVertex3d(x0, y0, z0); + glEnd(); + xtraverts += 4; +} + +void draw_envbox(int t, int w) +{ + glDepthMask(GL_FALSE); + + draw_envbox_aux(1.0f, 1.0f, -w, -w, w, + 0.0f, 1.0f, w, -w, w, + 0.0f, 0.0f, w, -w, -w, + 1.0f, 0.0f, -w, -w, -w, t); + + draw_envbox_aux(1.0f, 1.0f, +w, w, w, + 0.0f, 1.0f, -w, w, w, + 0.0f, 0.0f, -w, w, -w, + 1.0f, 0.0f, +w, w, -w, t+1); + + draw_envbox_aux(0.0f, 0.0f, -w, -w, -w, + 1.0f, 0.0f, -w, w, -w, + 1.0f, 1.0f, -w, w, w, + 0.0f, 1.0f, -w, -w, w, t+2); + + draw_envbox_aux(1.0f, 1.0f, +w, -w, w, + 0.0f, 1.0f, +w, w, w, + 0.0f, 0.0f, +w, w, -w, + 1.0f, 0.0f, +w, -w, -w, t+3); + + draw_envbox_aux(0.0f, 1.0f, -w, w, w, + 0.0f, 0.0f, +w, w, w, + 1.0f, 0.0f, +w, -w, w, + 1.0f, 1.0f, -w, -w, w, t+4); + + draw_envbox_aux(0.0f, 1.0f, +w, w, -w, + 0.0f, 0.0f, -w, w, -w, + 1.0f, 0.0f, -w, -w, -w, + 1.0f, 1.0f, +w, -w, -w, t+5); + + glDepthMask(GL_TRUE); +} DELETED src/rndmap.cpp Index: src/rndmap.cpp ================================================================== --- src/rndmap.cpp +++ /dev/null @@ -1,70 +0,0 @@ -// rndmap.cpp: perlin noise landscape generation and some experimental random map stuff, currently not used - -#include "cube.h" - -float noise(int x, int y, int seed) -{ - int n = x+y*57; - n = (n<<13)^n; - return 1.0f-((n*(n*n*15731+789221)+1376312589)&0x7fffffff)/1073741824.0f; -} - -float smoothednoise(int x, int y, int seed) -{ - float corners = (noise(x-1, y-1, seed)+noise(x+1, y-1, seed)+noise(x-1, y+1, seed)+noise(x+1, y+1, seed))/16; - float sides = (noise(x-1, y, seed)+noise(x+1, y, seed)+noise(x, y-1, seed)+noise(x, y+1, seed))/8; - float center = noise(x, y, seed)/4; - return corners+sides+center; -} - -float interpolate(float a, float b, float x) -{ - float ft = x*3.1415927f; - float f = (1.0f-(float)cos(ft))*0.5f; - return a*(1-f)+b*f; -} - -float interpolatednoise(float x, float y, int seed) -{ - int ix = (int)x; - float fx = x-ix; - int iy = (int)y; - float fy = y-iy; - float v1 = smoothednoise(ix, iy, seed); - float v2 = smoothednoise(ix+1, iy, seed); - float v3 = smoothednoise(ix, iy+1, seed); - float v4 = smoothednoise(ix+1, iy+1, seed); - float i1 = interpolate(v1, v2, fx); - float i2 = interpolate(v3, v4, fy); - return interpolate(i1, i2, fy); -} - -float perlinnoise_2D(float x, float y, int seedstep, float pers) -{ - float total = 0; - int seed = 0; - for(int i = 0; i<7; i++) - { - float frequency = (float)(2^i); - float amplitude = (float)pow(pers, i); - total += interpolatednoise(x*frequency, y*frequency, seed)*amplitude; - seed += seedstep; - } - return total; -} - -void perlinarea(block &b, int scale, int seed, int psize) -{ - srand(seed); - seed = rnd(10000); - if(!scale) scale = 10; - for(int x = b.x; x<=b.x+b.xs; x++) for(int y = b.y; y<=b.y+b.ys; y++) - { - sqr *s = S(x,y); - if(!SOLID(s) && x!=b.x+b.xs && y!=b.y+b.ys) s->type = FHF; - s->vdelta = (int)(perlinnoise_2D(x/((float)scale)+seed, y/((float)scale)+seed, 1000, 0.01f)*50+25); - if(s->vdelta>128) s->vdelta = 0; - }; -}; - - ADDED src/rndmap.cxx Index: src/rndmap.cxx ================================================================== --- /dev/null +++ src/rndmap.cxx @@ -0,0 +1,70 @@ +// rndmap.cpp: perlin noise landscape generation and some experimental random map stuff, currently not used + +#include "cube.h" + +float noise(int x, int y, int seed) +{ + int n = x+y*57; + n = (n<<13)^n; + return 1.0f-((n*(n*n*15731+789221)+1376312589)&0x7fffffff)/1073741824.0f; +} + +float smoothednoise(int x, int y, int seed) +{ + float corners = (noise(x-1, y-1, seed)+noise(x+1, y-1, seed)+noise(x-1, y+1, seed)+noise(x+1, y+1, seed))/16; + float sides = (noise(x-1, y, seed)+noise(x+1, y, seed)+noise(x, y-1, seed)+noise(x, y+1, seed))/8; + float center = noise(x, y, seed)/4; + return corners+sides+center; +} + +float interpolate(float a, float b, float x) +{ + float ft = x*3.1415927f; + float f = (1.0f-(float)cos(ft))*0.5f; + return a*(1-f)+b*f; +} + +float interpolatednoise(float x, float y, int seed) +{ + int ix = (int)x; + float fx = x-ix; + int iy = (int)y; + float fy = y-iy; + float v1 = smoothednoise(ix, iy, seed); + float v2 = smoothednoise(ix+1, iy, seed); + float v3 = smoothednoise(ix, iy+1, seed); + float v4 = smoothednoise(ix+1, iy+1, seed); + float i1 = interpolate(v1, v2, fx); + float i2 = interpolate(v3, v4, fy); + return interpolate(i1, i2, fy); +} + +float perlinnoise_2D(float x, float y, int seedstep, float pers) +{ + float total = 0; + int seed = 0; + for(int i = 0; i<7; i++) + { + float frequency = (float)(2^i); + float amplitude = (float)pow(pers, i); + total += interpolatednoise(x*frequency, y*frequency, seed)*amplitude; + seed += seedstep; + } + return total; +} + +void perlinarea(block &b, int scale, int seed, int psize) +{ + srand(seed); + seed = rnd(10000); + if(!scale) scale = 10; + for(int x = b.x; x<=b.x+b.xs; x++) for(int y = b.y; y<=b.y+b.ys; y++) + { + sqr *s = S(x,y); + if(!SOLID(s) && x!=b.x+b.xs && y!=b.y+b.ys) s->type = FHF; + s->vdelta = (int)(perlinnoise_2D(x/((float)scale)+seed, y/((float)scale)+seed, 1000, 0.01f)*50+25); + if(s->vdelta>128) s->vdelta = 0; + }; +}; + + DELETED src/savegamedemo.cpp Index: src/savegamedemo.cpp ================================================================== --- src/savegamedemo.cpp +++ /dev/null @@ -1,360 +0,0 @@ -// loading and saving of savegames & demos, dumps the spawn state of all mapents, the full state of all dynents (monsters + player) - -#include "cube.h" - -extern int islittleendian; - -gzFile f = NULL; -bool demorecording = false; -bool demoplayback = false; -bool demoloading = false; -dvector playerhistory; -int democlientnum = 0; - -void startdemo(); - -void gzput(int i) { gzputc(f, i); }; -void gzputi(int i) { gzwrite(f, &i, sizeof(int)); }; -void gzputv(vec &v) { gzwrite(f, &v, sizeof(vec)); }; - -void gzcheck(int a, int b) { if(a!=b) fatal("savegame file corrupt (short)"); }; -int gzget() { char c = gzgetc(f); return c; }; -int gzgeti() { int i; gzcheck(gzread(f, &i, sizeof(int)), sizeof(int)); return i; }; -void gzgetv(vec &v) { gzcheck(gzread(f, &v, sizeof(vec)), sizeof(vec)); }; - -void stop() -{ - if(f) - { - if(demorecording) gzputi(-1); - gzclose(f); - }; - f = NULL; - demorecording = false; - demoplayback = false; - demoloading = false; - loopv(playerhistory) zapdynent(playerhistory[i]); - playerhistory.setsize(0); -}; - -void stopifrecording() { if(demorecording) stop(); }; - -void savestate(char *fn) -{ - stop(); - f = gzopen(fn, "wb9"); - if(!f) { conoutf("could not write %s", fn); return; }; - gzwrite(f, (void *)"CUBESAVE", 8); - gzputc(f, islittleendian); - gzputi(SAVEGAMEVERSION); - gzputi(sizeof(dynent)); - gzwrite(f, getclientmap(), _MAXDEFSTR); - gzputi(gamemode); - gzputi(ents.length()); - loopv(ents) gzputc(f, ents[i].spawned); - gzwrite(f, player1, sizeof(dynent)); - dvector &monsters = getmonsters(); - gzputi(monsters.length()); - loopv(monsters) gzwrite(f, monsters[i], sizeof(dynent)); - gzputi(players.length()); - loopv(players) - { - gzput(players[i]==NULL); - gzwrite(f, players[i], sizeof(dynent)); - }; -}; - -void savegame(char *name) -{ - if(!m_classicsp) { conoutf("can only save classic sp games"); return; }; - sprintf_sd(fn)("savegames/%s.csgz", name); - savestate(fn); - stop(); - conoutf("wrote %s", fn); -}; - -void loadstate(char *fn) -{ - stop(); - if(multiplayer()) return; - f = gzopen(fn, "rb9"); - if(!f) { conoutf("could not open %s", fn); return; }; - - string buf; - gzread(f, buf, 8); - if(strncmp(buf, "CUBESAVE", 8)) goto out; - if(gzgetc(f)!=islittleendian) goto out; // not supporting save->load accross incompatible architectures simpifies things a LOT - if(gzgeti()!=SAVEGAMEVERSION || gzgeti()!=sizeof(dynent)) goto out; - string mapname; - gzread(f, mapname, _MAXDEFSTR); - nextmode = gzgeti(); - changemap(mapname); // continue below once map has been loaded and client & server have updated - return; - out: - conoutf("aborting: savegame/demo from a different version of cube or cpu architecture"); - stop(); -}; - -void loadgame(char *name) -{ - sprintf_sd(fn)("savegames/%s.csgz", name); - loadstate(fn); -}; - -void loadgameout() -{ - stop(); - conoutf("loadgame incomplete: savegame from a different version of this map"); -}; - -void loadgamerest() -{ - if(demoplayback || !f) return; - - if(gzgeti()!=ents.length()) return loadgameout(); - loopv(ents) - { - ents[i].spawned = gzgetc(f)!=0; - if(ents[i].type==CARROT && !ents[i].spawned) trigger(ents[i].attr1, ents[i].attr2, true); - }; - restoreserverstate(ents); - - gzread(f, player1, sizeof(dynent)); - player1->lastaction = lastmillis; - - int nmonsters = gzgeti(); - dvector &monsters = getmonsters(); - if(nmonsters!=monsters.length()) return loadgameout(); - loopv(monsters) - { - gzread(f, monsters[i], sizeof(dynent)); - monsters[i]->enemy = player1; // lazy, could save id of enemy instead - monsters[i]->lastaction = monsters[i]->trigger = lastmillis+500; // also lazy, but no real noticable effect on game - if(monsters[i]->state==CS_DEAD) monsters[i]->lastaction = 0; - }; - restoremonsterstate(); - - int nplayers = gzgeti(); - loopi(nplayers) if(!gzget()) - { - dynent *d = getclient(i); - assert(d); - gzread(f, d, sizeof(dynent)); - }; - - conoutf("savegame restored"); - if(demoloading) startdemo(); else stop(); -}; - -// demo functions - -int starttime = 0; -int playbacktime = 0; -int ddamage, bdamage; -vec dorig; - -void record(char *name) -{ - if(m_sp) { conoutf("cannot record singleplayer games"); return; }; - int cn = getclientnum(); - if(cn<0) return; - sprintf_sd(fn)("demos/%s.cdgz", name); - savestate(fn); - gzputi(cn); - conoutf("started recording demo to %s", fn); - demorecording = true; - starttime = lastmillis; - ddamage = bdamage = 0; -}; - -void demodamage(int damage, vec &o) { ddamage = damage; dorig = o; }; -void demoblend(int damage) { bdamage = damage; }; - -void incomingdemodata(uchar *buf, int len, bool extras) -{ - if(!demorecording) return; - gzputi(lastmillis-starttime); - gzputi(len); - gzwrite(f, buf, len); - gzput(extras); - if(extras) - { - gzput(player1->gunselect); - gzput(player1->lastattackgun); - gzputi(player1->lastaction-starttime); - gzputi(player1->gunwait); - gzputi(player1->health); - gzputi(player1->armour); - gzput(player1->armourtype); - loopi(NUMGUNS) gzput(player1->ammo[i]); - gzput(player1->state); - gzputi(bdamage); - bdamage = 0; - gzputi(ddamage); - if(ddamage) { gzputv(dorig); ddamage = 0; }; - // FIXME: add all other client state which is not send through the network - }; -}; - -void demo(char *name) -{ - sprintf_sd(fn)("demos/%s.cdgz", name); - loadstate(fn); - demoloading = true; -}; - -void stopreset() -{ - conoutf("demo stopped (%d msec elapsed)", lastmillis-starttime); - stop(); - loopv(players) zapdynent(players[i]); - disconnect(0, 0); -}; - -VAR(demoplaybackspeed, 10, 100, 1000); -int scaletime(int t) { return (int)(t*(100.0f/demoplaybackspeed))+starttime; }; - -void readdemotime() -{ - if(gzeof(f) || (playbacktime = gzgeti())==-1) - { - stopreset(); - return; - }; - playbacktime = scaletime(playbacktime); -}; - -void startdemo() -{ - democlientnum = gzgeti(); - demoplayback = true; - starttime = lastmillis; - conoutf("now playing demo"); - dynent *d = getclient(democlientnum); - assert(d); - *d = *player1; - readdemotime(); -}; - -VAR(demodelaymsec, 0, 120, 500); - -void catmulrom(vec &z, vec &a, vec &b, vec &c, float s, vec &dest) // spline interpolation -{ - vec t1 = b, t2 = c; - - vsub(t1, z); vmul(t1, 0.5f) - vsub(t2, a); vmul(t2, 0.5f); - - float s2 = s*s; - float s3 = s*s2; - - dest = a; - vec t = b; - - vmul(dest, 2*s3 - 3*s2 + 1); - vmul(t, -2*s3 + 3*s2); vadd(dest, t); - vmul(t1, s3 - 2*s2 + s); vadd(dest, t1); - vmul(t2, s3 - s2); vadd(dest, t2); -}; - -void fixwrap(dynent *a, dynent *b) -{ - while(b->yaw-a->yaw>180) a->yaw += 360; - while(b->yaw-a->yaw<-180) a->yaw -= 360; -}; - -void demoplaybackstep() -{ - while(demoplayback && lastmillis>=playbacktime) - { - int len = gzgeti(); - if(len<1 || len>MAXTRANS) - { - conoutf("error: huge packet during demo play (%d)", len); - stopreset(); - return; - }; - uchar buf[MAXTRANS]; - gzread(f, buf, len); - localservertoclient(buf, len); // update game state - - dynent *target = players[democlientnum]; - assert(target); - - int extras; - if(extras = gzget()) // read additional client side state not present in normal network stream - { - target->gunselect = gzget(); - target->lastattackgun = gzget(); - target->lastaction = scaletime(gzgeti()); - target->gunwait = gzgeti(); - target->health = gzgeti(); - target->armour = gzgeti(); - target->armourtype = gzget(); - loopi(NUMGUNS) target->ammo[i] = gzget(); - target->state = gzget(); - target->lastmove = playbacktime; - if(bdamage = gzgeti()) damageblend(bdamage); - if(ddamage = gzgeti()) { gzgetv(dorig); particle_splash(3, ddamage, 1000, dorig); }; - // FIXME: set more client state here - }; - - // insert latest copy of player into history - if(extras && (playerhistory.empty() || playerhistory.last()->lastupdate!=playbacktime)) - { - dynent *d = newdynent(); - *d = *target; - d->lastupdate = playbacktime; - playerhistory.add(d); - if(playerhistory.length()>20) - { - zapdynent(playerhistory[0]); - playerhistory.remove(0); - }; - }; - - readdemotime(); - }; - - if(demoplayback) - { - int itime = lastmillis-demodelaymsec; - loopvrev(playerhistory) if(playerhistory[i]->lastupdate=0) z = playerhistory[i-1]; - //if(a==z || b==c) printf("* %d\n", lastmillis); - float bf = (itime-a->lastupdate)/(float)(b->lastupdate-a->lastupdate); - fixwrap(a, player1); - fixwrap(c, player1); - fixwrap(z, player1); - vdist(dist, v, z->o, c->o); - if(dist<16) // if teleport or spawn, dont't interpolate - { - catmulrom(z->o, a->o, b->o, c->o, bf, player1->o); - catmulrom(*(vec *)&z->yaw, *(vec *)&a->yaw, *(vec *)&b->yaw, *(vec *)&c->yaw, bf, *(vec *)&player1->yaw); - }; - fixplayer1range(); - }; - break; - }; - //if(player1->state!=CS_DEAD) showscores(false); - }; -}; - -void stopn() { if(demoplayback) stopreset(); else stop(); conoutf("demo stopped"); }; - -COMMAND(record, ARG_1STR); -COMMAND(demo, ARG_1STR); -COMMANDN(stop, stopn, ARG_NONE); - -COMMAND(savegame, ARG_1STR); -COMMAND(loadgame, ARG_1STR); ADDED src/savegamedemo.cxx Index: src/savegamedemo.cxx ================================================================== --- /dev/null +++ src/savegamedemo.cxx @@ -0,0 +1,360 @@ +// loading and saving of savegames & demos, dumps the spawn state of all mapents, the full state of all dynents (monsters + player) + +#include "cube.h" + +extern int islittleendian; + +gzFile f = NULL; +bool demorecording = false; +bool demoplayback = false; +bool demoloading = false; +dvector playerhistory; +int democlientnum = 0; + +void startdemo(); + +void gzput(int i) { gzputc(f, i); }; +void gzputi(int i) { gzwrite(f, &i, sizeof(int)); }; +void gzputv(vec &v) { gzwrite(f, &v, sizeof(vec)); }; + +void gzcheck(int a, int b) { if(a!=b) fatal("savegame file corrupt (short)"); }; +int gzget() { char c = gzgetc(f); return c; }; +int gzgeti() { int i; gzcheck(gzread(f, &i, sizeof(int)), sizeof(int)); return i; }; +void gzgetv(vec &v) { gzcheck(gzread(f, &v, sizeof(vec)), sizeof(vec)); }; + +void stop() +{ + if(f) + { + if(demorecording) gzputi(-1); + gzclose(f); + }; + f = NULL; + demorecording = false; + demoplayback = false; + demoloading = false; + loopv(playerhistory) zapdynent(playerhistory[i]); + playerhistory.setsize(0); +}; + +void stopifrecording() { if(demorecording) stop(); }; + +void savestate(char *fn) +{ + stop(); + f = gzopen(fn, "wb9"); + if(!f) { conoutf("could not write %s", fn); return; }; + gzwrite(f, (void *)"CUBESAVE", 8); + gzputc(f, islittleendian); + gzputi(SAVEGAMEVERSION); + gzputi(sizeof(dynent)); + gzwrite(f, getclientmap(), _MAXDEFSTR); + gzputi(gamemode); + gzputi(ents.length()); + loopv(ents) gzputc(f, ents[i].spawned); + gzwrite(f, player1, sizeof(dynent)); + dvector &monsters = getmonsters(); + gzputi(monsters.length()); + loopv(monsters) gzwrite(f, monsters[i], sizeof(dynent)); + gzputi(players.length()); + loopv(players) + { + gzput(players[i]==NULL); + gzwrite(f, players[i], sizeof(dynent)); + }; +}; + +void savegame(char *name) +{ + if(!m_classicsp) { conoutf("can only save classic sp games"); return; }; + sprintf_sd(fn)("savegames/%s.csgz", name); + savestate(fn); + stop(); + conoutf("wrote %s", fn); +}; + +void loadstate(char *fn) +{ + stop(); + if(multiplayer()) return; + f = gzopen(fn, "rb9"); + if(!f) { conoutf("could not open %s", fn); return; }; + + string buf; + gzread(f, buf, 8); + if(strncmp(buf, "CUBESAVE", 8)) goto out; + if(gzgetc(f)!=islittleendian) goto out; // not supporting save->load accross incompatible architectures simpifies things a LOT + if(gzgeti()!=SAVEGAMEVERSION || gzgeti()!=sizeof(dynent)) goto out; + string mapname; + gzread(f, mapname, _MAXDEFSTR); + nextmode = gzgeti(); + changemap(mapname); // continue below once map has been loaded and client & server have updated + return; + out: + conoutf("aborting: savegame/demo from a different version of cube or cpu architecture"); + stop(); +}; + +void loadgame(char *name) +{ + sprintf_sd(fn)("savegames/%s.csgz", name); + loadstate(fn); +}; + +void loadgameout() +{ + stop(); + conoutf("loadgame incomplete: savegame from a different version of this map"); +}; + +void loadgamerest() +{ + if(demoplayback || !f) return; + + if(gzgeti()!=ents.length()) return loadgameout(); + loopv(ents) + { + ents[i].spawned = gzgetc(f)!=0; + if(ents[i].type==CARROT && !ents[i].spawned) trigger(ents[i].attr1, ents[i].attr2, true); + }; + restoreserverstate(ents); + + gzread(f, player1, sizeof(dynent)); + player1->lastaction = lastmillis; + + int nmonsters = gzgeti(); + dvector &monsters = getmonsters(); + if(nmonsters!=monsters.length()) return loadgameout(); + loopv(monsters) + { + gzread(f, monsters[i], sizeof(dynent)); + monsters[i]->enemy = player1; // lazy, could save id of enemy instead + monsters[i]->lastaction = monsters[i]->trigger = lastmillis+500; // also lazy, but no real noticable effect on game + if(monsters[i]->state==CS_DEAD) monsters[i]->lastaction = 0; + }; + restoremonsterstate(); + + int nplayers = gzgeti(); + loopi(nplayers) if(!gzget()) + { + dynent *d = getclient(i); + assert(d); + gzread(f, d, sizeof(dynent)); + }; + + conoutf("savegame restored"); + if(demoloading) startdemo(); else stop(); +}; + +// demo functions + +int starttime = 0; +int playbacktime = 0; +int ddamage, bdamage; +vec dorig; + +void record(char *name) +{ + if(m_sp) { conoutf("cannot record singleplayer games"); return; }; + int cn = getclientnum(); + if(cn<0) return; + sprintf_sd(fn)("demos/%s.cdgz", name); + savestate(fn); + gzputi(cn); + conoutf("started recording demo to %s", fn); + demorecording = true; + starttime = lastmillis; + ddamage = bdamage = 0; +}; + +void demodamage(int damage, vec &o) { ddamage = damage; dorig = o; }; +void demoblend(int damage) { bdamage = damage; }; + +void incomingdemodata(uchar *buf, int len, bool extras) +{ + if(!demorecording) return; + gzputi(lastmillis-starttime); + gzputi(len); + gzwrite(f, buf, len); + gzput(extras); + if(extras) + { + gzput(player1->gunselect); + gzput(player1->lastattackgun); + gzputi(player1->lastaction-starttime); + gzputi(player1->gunwait); + gzputi(player1->health); + gzputi(player1->armour); + gzput(player1->armourtype); + loopi(NUMGUNS) gzput(player1->ammo[i]); + gzput(player1->state); + gzputi(bdamage); + bdamage = 0; + gzputi(ddamage); + if(ddamage) { gzputv(dorig); ddamage = 0; }; + // FIXME: add all other client state which is not send through the network + }; +}; + +void demo(char *name) +{ + sprintf_sd(fn)("demos/%s.cdgz", name); + loadstate(fn); + demoloading = true; +}; + +void stopreset() +{ + conoutf("demo stopped (%d msec elapsed)", lastmillis-starttime); + stop(); + loopv(players) zapdynent(players[i]); + disconnect(0, 0); +}; + +VAR(demoplaybackspeed, 10, 100, 1000); +int scaletime(int t) { return (int)(t*(100.0f/demoplaybackspeed))+starttime; }; + +void readdemotime() +{ + if(gzeof(f) || (playbacktime = gzgeti())==-1) + { + stopreset(); + return; + }; + playbacktime = scaletime(playbacktime); +}; + +void startdemo() +{ + democlientnum = gzgeti(); + demoplayback = true; + starttime = lastmillis; + conoutf("now playing demo"); + dynent *d = getclient(democlientnum); + assert(d); + *d = *player1; + readdemotime(); +}; + +VAR(demodelaymsec, 0, 120, 500); + +void catmulrom(vec &z, vec &a, vec &b, vec &c, float s, vec &dest) // spline interpolation +{ + vec t1 = b, t2 = c; + + vsub(t1, z); vmul(t1, 0.5f) + vsub(t2, a); vmul(t2, 0.5f); + + float s2 = s*s; + float s3 = s*s2; + + dest = a; + vec t = b; + + vmul(dest, 2*s3 - 3*s2 + 1); + vmul(t, -2*s3 + 3*s2); vadd(dest, t); + vmul(t1, s3 - 2*s2 + s); vadd(dest, t1); + vmul(t2, s3 - s2); vadd(dest, t2); +}; + +void fixwrap(dynent *a, dynent *b) +{ + while(b->yaw-a->yaw>180) a->yaw += 360; + while(b->yaw-a->yaw<-180) a->yaw -= 360; +}; + +void demoplaybackstep() +{ + while(demoplayback && lastmillis>=playbacktime) + { + int len = gzgeti(); + if(len<1 || len>MAXTRANS) + { + conoutf("error: huge packet during demo play (%d)", len); + stopreset(); + return; + }; + uchar buf[MAXTRANS]; + gzread(f, buf, len); + localservertoclient(buf, len); // update game state + + dynent *target = players[democlientnum]; + assert(target); + + int extras; + if(extras = gzget()) // read additional client side state not present in normal network stream + { + target->gunselect = gzget(); + target->lastattackgun = gzget(); + target->lastaction = scaletime(gzgeti()); + target->gunwait = gzgeti(); + target->health = gzgeti(); + target->armour = gzgeti(); + target->armourtype = gzget(); + loopi(NUMGUNS) target->ammo[i] = gzget(); + target->state = gzget(); + target->lastmove = playbacktime; + if(bdamage = gzgeti()) damageblend(bdamage); + if(ddamage = gzgeti()) { gzgetv(dorig); particle_splash(3, ddamage, 1000, dorig); }; + // FIXME: set more client state here + }; + + // insert latest copy of player into history + if(extras && (playerhistory.empty() || playerhistory.last()->lastupdate!=playbacktime)) + { + dynent *d = newdynent(); + *d = *target; + d->lastupdate = playbacktime; + playerhistory.add(d); + if(playerhistory.length()>20) + { + zapdynent(playerhistory[0]); + playerhistory.remove(0); + }; + }; + + readdemotime(); + }; + + if(demoplayback) + { + int itime = lastmillis-demodelaymsec; + loopvrev(playerhistory) if(playerhistory[i]->lastupdate=0) z = playerhistory[i-1]; + //if(a==z || b==c) printf("* %d\n", lastmillis); + float bf = (itime-a->lastupdate)/(float)(b->lastupdate-a->lastupdate); + fixwrap(a, player1); + fixwrap(c, player1); + fixwrap(z, player1); + vdist(dist, v, z->o, c->o); + if(dist<16) // if teleport or spawn, dont't interpolate + { + catmulrom(z->o, a->o, b->o, c->o, bf, player1->o); + catmulrom(*(vec *)&z->yaw, *(vec *)&a->yaw, *(vec *)&b->yaw, *(vec *)&c->yaw, bf, *(vec *)&player1->yaw); + }; + fixplayer1range(); + }; + break; + }; + //if(player1->state!=CS_DEAD) showscores(false); + }; +}; + +void stopn() { if(demoplayback) stopreset(); else stop(); conoutf("demo stopped"); }; + +COMMAND(record, ARG_1STR); +COMMAND(demo, ARG_1STR); +COMMANDN(stop, stopn, ARG_NONE); + +COMMAND(savegame, ARG_1STR); +COMMAND(loadgame, ARG_1STR); DELETED src/server.cpp Index: src/server.cpp ================================================================== --- src/server.cpp +++ /dev/null @@ -1,468 +0,0 @@ -// server.cpp: little more than enhanced multicaster -// runs dedicated or as client coroutine - -#include "cube.h" - -enum { ST_EMPTY, ST_LOCAL, ST_TCPIP }; - -struct client // server side version of "dynent" type -{ - int type; - ENetPeer *peer; - string hostname; - string mapvote; - string name; - int modevote; -}; - -vector clients; - -int maxclients = 8; -string smapname; - -struct server_entity // server side version of "entity" type -{ - bool spawned; - int spawnsecs; -}; - -vector sents; - -bool notgotitems = true; // true when map has changed and waiting for clients to send item -int mode = 0; - -void restoreserverstate(vector &ents) // hack: called from savegame code, only works in SP -{ - loopv(sents) - { - sents[i].spawned = ents[i].spawned; - sents[i].spawnsecs = 0; - }; -}; - -int interm = 0, minremain = 0, mapend = 0; -bool mapreload = false; - -char *serverpassword = ""; - -bool isdedicated; -ENetHost * serverhost = NULL; -int bsend = 0, brec = 0, laststatus = 0, lastsec = 0; - -#define MAXOBUF 100000 - -void process(ENetPacket *packet, int sender); -void multicast(ENetPacket *packet, int sender); -void disconnect_client(int n, char *reason); - -void send(int n, ENetPacket *packet) -{ - if(!packet) return; - switch(clients[n].type) - { - case ST_TCPIP: - { - enet_peer_send(clients[n].peer, 0, packet); - bsend += packet->dataLength; - break; - }; - - case ST_LOCAL: - localservertoclient(packet->data, packet->dataLength); - break; - - }; -}; - -void send2(bool rel, int cn, int a, int b) -{ - ENetPacket *packet = enet_packet_create(NULL, 32, rel ? ENET_PACKET_FLAG_RELIABLE : 0); - uchar *start = packet->data; - uchar *p = start+2; - putint(p, a); - putint(p, b); - *(ushort *)start = ENET_HOST_TO_NET_16(p-start); - enet_packet_resize(packet, p-start); - if(cn<0) process(packet, -1); - else send(cn, packet); - if(packet->referenceCount==0) enet_packet_destroy(packet); -}; - -void sendservmsg(char *msg) -{ - ENetPacket *packet = enet_packet_create(NULL, _MAXDEFSTR+10, ENET_PACKET_FLAG_RELIABLE); - uchar *start = packet->data; - uchar *p = start+2; - putint(p, SV_SERVMSG); - sendstring(msg, p); - *(ushort *)start = ENET_HOST_TO_NET_16(p-start); - enet_packet_resize(packet, p-start); - multicast(packet, -1); - if(packet->referenceCount==0) enet_packet_destroy(packet); -}; - -void disconnect_client(int n, char *reason) -{ - printf("disconnecting client (%s) [%s]\n", clients[n].hostname, reason); - enet_peer_disconnect(clients[n].peer); - clients[n].type = ST_EMPTY; - send2(true, -1, SV_CDIS, n); -}; - -void resetitems() { sents.setsize(0); notgotitems = true; }; - -void pickup(uint i, int sec, int sender) // server side item pickup, acknowledge first client that gets it -{ - if(i>=(uint)sents.length()) return; - if(sents[i].spawned) - { - sents[i].spawned = false; - sents[i].spawnsecs = sec; - send2(true, sender, SV_ITEMACC, i); - }; -}; - -void resetvotes() -{ - loopv(clients) clients[i].mapvote[0] = 0; -}; - -bool vote(char *map, int reqmode, int sender) -{ - strcpy_s(clients[sender].mapvote, map); - clients[sender].modevote = reqmode; - int yes = 0, no = 0; - loopv(clients) if(clients[i].type!=ST_EMPTY) - { - if(clients[i].mapvote[0]) { if(strcmp(clients[i].mapvote, map)==0 && clients[i].modevote==reqmode) yes++; else no++; } - else no++; - }; - if(yes==1 && no==0) return true; // single player - sprintf_sd(msg)("%s suggests %s on map %s (set map to vote)", clients[sender].name, modestr(reqmode), map); - sendservmsg(msg); - if(yes/(float)(yes+no) <= 0.5f) return false; - sendservmsg("vote passed"); - resetvotes(); - return true; -}; - -// server side processing of updates: does very little and most state is tracked client only -// could be extended to move more gameplay to server (at expense of lag) - -void process(ENetPacket * packet, int sender) // sender may be -1 -{ - if(ENET_NET_TO_HOST_16(*(ushort *)packet->data)!=packet->dataLength) - { - disconnect_client(sender, "packet length"); - return; - }; - - uchar *end = packet->data+packet->dataLength; - uchar *p = packet->data+2; - char text[MAXTRANS]; - int cn = -1, type; - - while(p=clients.length() || clients[cn].type==ST_EMPTY) - { - disconnect_client(sender, "client num"); - return; - }; - int size = msgsizelookup(type); - assert(size!=-1); - loopi(size-2) getint(p); - break; - }; - - case SV_SENDMAP: - { - sgetstr(); - int mapsize = getint(p); - sendmaps(sender, text, mapsize, p); - return; - } - - case SV_RECVMAP: - send(sender, recvmap(sender)); - return; - - case SV_EXT: // allows for new features that require no server updates - { - for(int n = getint(p); n; n--) getint(p); - break; - }; - - default: - { - int size = msgsizelookup(type); - if(size==-1) { disconnect_client(sender, "tag type"); return; }; - loopi(size-1) getint(p); - }; - }; - - if(p>end) { disconnect_client(sender, "end of packet"); return; }; - multicast(packet, sender); -}; - -void send_welcome(int n) -{ - ENetPacket * packet = enet_packet_create (NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE); - uchar *start = packet->data; - uchar *p = start+2; - putint(p, SV_INITS2C); - putint(p, n); - putint(p, PROTOCOL_VERSION); - putint(p, smapname[0]); - sendstring(serverpassword, p); - putint(p, clients.length()>maxclients); - if(smapname[0]) - { - putint(p, SV_MAPCHANGE); - sendstring(smapname, p); - putint(p, mode); - putint(p, SV_ITEMLIST); - loopv(sents) if(sents[i].spawned) putint(p, i); - putint(p, -1); - }; - *(ushort *)start = ENET_HOST_TO_NET_16(p-start); - enet_packet_resize(packet, p-start); - send(n, packet); -}; - -void multicast(ENetPacket *packet, int sender) -{ - loopv(clients) - { - if(i==sender) continue; - send(i, packet); - }; -}; - -void localclienttoserver(ENetPacket *packet) -{ - process(packet, 0); - if(!packet->referenceCount) enet_packet_destroy (packet); -}; - -client &addclient() -{ - loopv(clients) if(clients[i].type==ST_EMPTY) return clients[i]; - return clients.add(); -}; - -void checkintermission() -{ - if(!minremain) - { - interm = lastsec+10; - mapend = lastsec+1000; - }; - send2(true, -1, SV_TIMEUP, minremain--); -}; - -void startintermission() { minremain = 0; checkintermission(); }; - -void resetserverifempty() -{ - loopv(clients) if(clients[i].type!=ST_EMPTY) return; - clients.setsize(0); - smapname[0] = 0; - resetvotes(); - resetitems(); - mode = 0; - mapreload = false; - minremain = 10; - mapend = lastsec+minremain*60; - interm = 0; -}; - -int nonlocalclients = 0; -int lastconnect = 0; - -void serverslice(int seconds, unsigned int timeout) // main server update, called from cube main loop in sp, or dedicated server loop -{ - loopv(sents) // spawn entities when timer reached - { - if(sents[i].spawnsecs && (sents[i].spawnsecs -= seconds-lastsec)<=0) - { - sents[i].spawnsecs = 0; - sents[i].spawned = true; - send2(true, -1, SV_ITEMSPAWN, i); - }; - }; - - lastsec = seconds; - - if((mode>1 || (mode==0 && nonlocalclients)) && seconds>mapend-minremain*60) checkintermission(); - if(interm && seconds>interm) - { - interm = 0; - loopv(clients) if(clients[i].type!=ST_EMPTY) - { - send2(true, i, SV_MAPRELOAD, 0); // ask a client to trigger map reload - mapreload = true; - break; - }; - }; - - resetserverifempty(); - - if(!isdedicated) return; // below is network only - - int numplayers = 0; - loopv(clients) if(clients[i].type!=ST_EMPTY) ++numplayers; - serverms(mode, numplayers, minremain, smapname, seconds, clients.length()>=maxclients); - - if(seconds-laststatus>60) // display bandwidth stats, useful for server ops - { - nonlocalclients = 0; - loopv(clients) if(clients[i].type==ST_TCPIP) nonlocalclients++; - laststatus = seconds; - if(nonlocalclients || bsend || brec) printf("status: %d remote clients, %.1f send, %.1f rec (K/sec)\n", nonlocalclients, bsend/60.0f/1024, brec/60.0f/1024); - bsend = brec = 0; - }; - - ENetEvent event; - if(enet_host_service(serverhost, &event, timeout) > 0) - { - switch(event.type) - { - case ENET_EVENT_TYPE_CONNECT: - { - client &c = addclient(); - c.type = ST_TCPIP; - c.peer = event.peer; - c.peer->data = (void *)(&c-&clients[0]); - char hn[1024]; - strcpy_s(c.hostname, (enet_address_get_host(&c.peer->address, hn, sizeof(hn))==0) ? hn : "localhost"); - printf("client connected (%s)\n", c.hostname); - send_welcome(lastconnect = &c-&clients[0]); - break; - } - case ENET_EVENT_TYPE_RECEIVE: - brec += event.packet->dataLength; - process(event.packet, (int)event.peer->data); - if(event.packet->referenceCount==0) enet_packet_destroy(event.packet); - break; - - case ENET_EVENT_TYPE_DISCONNECT: - if((int)event.peer->data<0) break; - printf("disconnected client (%s)\n", clients[(int)event.peer->data].hostname); - clients[(int)event.peer->data].type = ST_EMPTY; - send2(true, -1, SV_CDIS, (int)event.peer->data); - event.peer->data = (void *)-1; - break; - }; - - if(numplayers>maxclients) - { - disconnect_client(lastconnect, "maxclients reached"); - }; - }; - #ifndef WIN32 - fflush(stdout); - #endif -}; - -void cleanupserver() -{ - if(serverhost) enet_host_destroy(serverhost); -}; - -void localdisconnect() -{ - loopv(clients) if(clients[i].type==ST_LOCAL) clients[i].type = ST_EMPTY; -}; - -void localconnect() -{ - client &c = addclient(); - c.type = ST_LOCAL; - strcpy_s(c.hostname, "local"); - send_welcome(&c-&clients[0]); -}; - -void initserver(bool dedicated, int uprate, char *sdesc, char *ip, char *master, char *passwd, int maxcl) -{ - serverpassword = passwd; - maxclients = maxcl; - servermsinit(master ? master : "wouter.fov120.com/cube/masterserver/", sdesc, dedicated); - - if(isdedicated = dedicated) - { - ENetAddress address = { ENET_HOST_ANY, CUBE_SERVER_PORT }; - if(*ip && enet_address_set_host(&address, ip)<0) printf("WARNING: server ip not resolved"); - serverhost = enet_host_create(&address, MAXCLIENTS, 0, uprate); - if(!serverhost) fatal("could not create server host\n"); - loopi(MAXCLIENTS) serverhost->peers[i].data = (void *)-1; - }; - - resetserverifempty(); - - if(isdedicated) // do not return, this becomes main loop - { - #ifdef WIN32 - SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); - #endif - printf("dedicated server started, waiting for clients...\nCtrl-C to exit\n\n"); - atexit(cleanupserver); - atexit(enet_deinitialize); - for(;;) serverslice(/*enet_time_get_sec()*/time(NULL), 5); - }; -}; ADDED src/server.cxx Index: src/server.cxx ================================================================== --- /dev/null +++ src/server.cxx @@ -0,0 +1,468 @@ +// server.cpp: little more than enhanced multicaster +// runs dedicated or as client coroutine + +#include "cube.h" + +enum { ST_EMPTY, ST_LOCAL, ST_TCPIP }; + +struct client // server side version of "dynent" type +{ + int type; + ENetPeer *peer; + string hostname; + string mapvote; + string name; + int modevote; +}; + +vector clients; + +int maxclients = 8; +string smapname; + +struct server_entity // server side version of "entity" type +{ + bool spawned; + int spawnsecs; +}; + +vector sents; + +bool notgotitems = true; // true when map has changed and waiting for clients to send item +int mode = 0; + +void restoreserverstate(vector &ents) // hack: called from savegame code, only works in SP +{ + loopv(sents) + { + sents[i].spawned = ents[i].spawned; + sents[i].spawnsecs = 0; + }; +}; + +int interm = 0, minremain = 0, mapend = 0; +bool mapreload = false; + +char *serverpassword = ""; + +bool isdedicated; +ENetHost * serverhost = NULL; +int bsend = 0, brec = 0, laststatus = 0, lastsec = 0; + +#define MAXOBUF 100000 + +void process(ENetPacket *packet, int sender); +void multicast(ENetPacket *packet, int sender); +void disconnect_client(int n, char *reason); + +void send(int n, ENetPacket *packet) +{ + if(!packet) return; + switch(clients[n].type) + { + case ST_TCPIP: + { + enet_peer_send(clients[n].peer, 0, packet); + bsend += packet->dataLength; + break; + }; + + case ST_LOCAL: + localservertoclient(packet->data, packet->dataLength); + break; + + }; +}; + +void send2(bool rel, int cn, int a, int b) +{ + ENetPacket *packet = enet_packet_create(NULL, 32, rel ? ENET_PACKET_FLAG_RELIABLE : 0); + uchar *start = packet->data; + uchar *p = start+2; + putint(p, a); + putint(p, b); + *(ushort *)start = ENET_HOST_TO_NET_16(p-start); + enet_packet_resize(packet, p-start); + if(cn<0) process(packet, -1); + else send(cn, packet); + if(packet->referenceCount==0) enet_packet_destroy(packet); +}; + +void sendservmsg(char *msg) +{ + ENetPacket *packet = enet_packet_create(NULL, _MAXDEFSTR+10, ENET_PACKET_FLAG_RELIABLE); + uchar *start = packet->data; + uchar *p = start+2; + putint(p, SV_SERVMSG); + sendstring(msg, p); + *(ushort *)start = ENET_HOST_TO_NET_16(p-start); + enet_packet_resize(packet, p-start); + multicast(packet, -1); + if(packet->referenceCount==0) enet_packet_destroy(packet); +}; + +void disconnect_client(int n, char *reason) +{ + printf("disconnecting client (%s) [%s]\n", clients[n].hostname, reason); + enet_peer_disconnect(clients[n].peer); + clients[n].type = ST_EMPTY; + send2(true, -1, SV_CDIS, n); +}; + +void resetitems() { sents.setsize(0); notgotitems = true; }; + +void pickup(uint i, int sec, int sender) // server side item pickup, acknowledge first client that gets it +{ + if(i>=(uint)sents.length()) return; + if(sents[i].spawned) + { + sents[i].spawned = false; + sents[i].spawnsecs = sec; + send2(true, sender, SV_ITEMACC, i); + }; +}; + +void resetvotes() +{ + loopv(clients) clients[i].mapvote[0] = 0; +}; + +bool vote(char *map, int reqmode, int sender) +{ + strcpy_s(clients[sender].mapvote, map); + clients[sender].modevote = reqmode; + int yes = 0, no = 0; + loopv(clients) if(clients[i].type!=ST_EMPTY) + { + if(clients[i].mapvote[0]) { if(strcmp(clients[i].mapvote, map)==0 && clients[i].modevote==reqmode) yes++; else no++; } + else no++; + }; + if(yes==1 && no==0) return true; // single player + sprintf_sd(msg)("%s suggests %s on map %s (set map to vote)", clients[sender].name, modestr(reqmode), map); + sendservmsg(msg); + if(yes/(float)(yes+no) <= 0.5f) return false; + sendservmsg("vote passed"); + resetvotes(); + return true; +}; + +// server side processing of updates: does very little and most state is tracked client only +// could be extended to move more gameplay to server (at expense of lag) + +void process(ENetPacket * packet, int sender) // sender may be -1 +{ + if(ENET_NET_TO_HOST_16(*(ushort *)packet->data)!=packet->dataLength) + { + disconnect_client(sender, "packet length"); + return; + }; + + uchar *end = packet->data+packet->dataLength; + uchar *p = packet->data+2; + char text[MAXTRANS]; + int cn = -1, type; + + while(p=clients.length() || clients[cn].type==ST_EMPTY) + { + disconnect_client(sender, "client num"); + return; + }; + int size = msgsizelookup(type); + assert(size!=-1); + loopi(size-2) getint(p); + break; + }; + + case SV_SENDMAP: + { + sgetstr(); + int mapsize = getint(p); + sendmaps(sender, text, mapsize, p); + return; + } + + case SV_RECVMAP: + send(sender, recvmap(sender)); + return; + + case SV_EXT: // allows for new features that require no server updates + { + for(int n = getint(p); n; n--) getint(p); + break; + }; + + default: + { + int size = msgsizelookup(type); + if(size==-1) { disconnect_client(sender, "tag type"); return; }; + loopi(size-1) getint(p); + }; + }; + + if(p>end) { disconnect_client(sender, "end of packet"); return; }; + multicast(packet, sender); +}; + +void send_welcome(int n) +{ + ENetPacket * packet = enet_packet_create (NULL, MAXTRANS, ENET_PACKET_FLAG_RELIABLE); + uchar *start = packet->data; + uchar *p = start+2; + putint(p, SV_INITS2C); + putint(p, n); + putint(p, PROTOCOL_VERSION); + putint(p, smapname[0]); + sendstring(serverpassword, p); + putint(p, clients.length()>maxclients); + if(smapname[0]) + { + putint(p, SV_MAPCHANGE); + sendstring(smapname, p); + putint(p, mode); + putint(p, SV_ITEMLIST); + loopv(sents) if(sents[i].spawned) putint(p, i); + putint(p, -1); + }; + *(ushort *)start = ENET_HOST_TO_NET_16(p-start); + enet_packet_resize(packet, p-start); + send(n, packet); +}; + +void multicast(ENetPacket *packet, int sender) +{ + loopv(clients) + { + if(i==sender) continue; + send(i, packet); + }; +}; + +void localclienttoserver(ENetPacket *packet) +{ + process(packet, 0); + if(!packet->referenceCount) enet_packet_destroy (packet); +}; + +client &addclient() +{ + loopv(clients) if(clients[i].type==ST_EMPTY) return clients[i]; + return clients.add(); +}; + +void checkintermission() +{ + if(!minremain) + { + interm = lastsec+10; + mapend = lastsec+1000; + }; + send2(true, -1, SV_TIMEUP, minremain--); +}; + +void startintermission() { minremain = 0; checkintermission(); }; + +void resetserverifempty() +{ + loopv(clients) if(clients[i].type!=ST_EMPTY) return; + clients.setsize(0); + smapname[0] = 0; + resetvotes(); + resetitems(); + mode = 0; + mapreload = false; + minremain = 10; + mapend = lastsec+minremain*60; + interm = 0; +}; + +int nonlocalclients = 0; +int lastconnect = 0; + +void serverslice(int seconds, unsigned int timeout) // main server update, called from cube main loop in sp, or dedicated server loop +{ + loopv(sents) // spawn entities when timer reached + { + if(sents[i].spawnsecs && (sents[i].spawnsecs -= seconds-lastsec)<=0) + { + sents[i].spawnsecs = 0; + sents[i].spawned = true; + send2(true, -1, SV_ITEMSPAWN, i); + }; + }; + + lastsec = seconds; + + if((mode>1 || (mode==0 && nonlocalclients)) && seconds>mapend-minremain*60) checkintermission(); + if(interm && seconds>interm) + { + interm = 0; + loopv(clients) if(clients[i].type!=ST_EMPTY) + { + send2(true, i, SV_MAPRELOAD, 0); // ask a client to trigger map reload + mapreload = true; + break; + }; + }; + + resetserverifempty(); + + if(!isdedicated) return; // below is network only + + int numplayers = 0; + loopv(clients) if(clients[i].type!=ST_EMPTY) ++numplayers; + serverms(mode, numplayers, minremain, smapname, seconds, clients.length()>=maxclients); + + if(seconds-laststatus>60) // display bandwidth stats, useful for server ops + { + nonlocalclients = 0; + loopv(clients) if(clients[i].type==ST_TCPIP) nonlocalclients++; + laststatus = seconds; + if(nonlocalclients || bsend || brec) printf("status: %d remote clients, %.1f send, %.1f rec (K/sec)\n", nonlocalclients, bsend/60.0f/1024, brec/60.0f/1024); + bsend = brec = 0; + }; + + ENetEvent event; + if(enet_host_service(serverhost, &event, timeout) > 0) + { + switch(event.type) + { + case ENET_EVENT_TYPE_CONNECT: + { + client &c = addclient(); + c.type = ST_TCPIP; + c.peer = event.peer; + c.peer->data = (void *)(&c-&clients[0]); + char hn[1024]; + strcpy_s(c.hostname, (enet_address_get_host(&c.peer->address, hn, sizeof(hn))==0) ? hn : "localhost"); + printf("client connected (%s)\n", c.hostname); + send_welcome(lastconnect = &c-&clients[0]); + break; + } + case ENET_EVENT_TYPE_RECEIVE: + brec += event.packet->dataLength; + process(event.packet, (int)event.peer->data); + if(event.packet->referenceCount==0) enet_packet_destroy(event.packet); + break; + + case ENET_EVENT_TYPE_DISCONNECT: + if((int)event.peer->data<0) break; + printf("disconnected client (%s)\n", clients[(int)event.peer->data].hostname); + clients[(int)event.peer->data].type = ST_EMPTY; + send2(true, -1, SV_CDIS, (int)event.peer->data); + event.peer->data = (void *)-1; + break; + }; + + if(numplayers>maxclients) + { + disconnect_client(lastconnect, "maxclients reached"); + }; + }; + #ifndef _WIN32 + fflush(stdout); + #endif +}; + +void cleanupserver() +{ + if(serverhost) enet_host_destroy(serverhost); +}; + +void localdisconnect() +{ + loopv(clients) if(clients[i].type==ST_LOCAL) clients[i].type = ST_EMPTY; +}; + +void localconnect() +{ + client &c = addclient(); + c.type = ST_LOCAL; + strcpy_s(c.hostname, "local"); + send_welcome(&c-&clients[0]); +}; + +void initserver(bool dedicated, int uprate, char *sdesc, char *ip, char *master, char *passwd, int maxcl) +{ + serverpassword = passwd; + maxclients = maxcl; + servermsinit(master ? master : "wouter.fov120.com/cube/masterserver/", sdesc, dedicated); + + if(isdedicated = dedicated) + { + ENetAddress address = { ENET_HOST_ANY, CUBE_SERVER_PORT }; + if(*ip && enet_address_set_host(&address, ip)<0) printf("WARNING: server ip not resolved"); + serverhost = enet_host_create(&address, MAXCLIENTS, 0, uprate); + if(!serverhost) fatal("could not create server host\n"); + loopi(MAXCLIENTS) serverhost->peers[i].data = (void *)-1; + }; + + resetserverifempty(); + + if(isdedicated) // do not return, this becomes main loop + { + #ifdef _WIN32 + SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS); + #endif + printf("dedicated server started, waiting for clients...\nCtrl-C to exit\n\n"); + atexit(cleanupserver); + atexit(enet_deinitialize); + for(;;) serverslice(/*enet_time_get_sec()*/time(NULL), 5); + }; +}; DELETED src/serverbrowser.cpp Index: src/serverbrowser.cpp ================================================================== --- src/serverbrowser.cpp +++ /dev/null @@ -1,303 +0,0 @@ -// serverbrowser.cpp: eihrul's concurrent resolver, and server browser window management - -#include "cube.h" -#include "SDL_thread.h" - -struct resolverthread -{ - SDL_Thread *thread; - char *query; - int starttime; -}; - -struct resolverresult -{ - char *query; - ENetAddress address; -}; - -vector resolverthreads; -vector resolverqueries; -vector resolverresults; -SDL_mutex *resolvermutex; -SDL_sem *resolversem; -int resolverlimit = 1000; - -int resolverloop(void * data) -{ - resolverthread *rt = (resolverthread *)data; - for(;;) - { - SDL_SemWait(resolversem); - SDL_LockMutex(resolvermutex); - if(resolverqueries.empty()) - { - SDL_UnlockMutex(resolvermutex); - continue; - } - rt->query = resolverqueries.pop(); - rt->starttime = lastmillis; - SDL_UnlockMutex(resolvermutex); - ENetAddress address = { ENET_HOST_ANY, CUBE_SERVINFO_PORT }; - enet_address_set_host(&address, rt->query); - SDL_LockMutex(resolvermutex); - resolverresult &rr = resolverresults.add(); - rr.query = rt->query; - rr.address = address; - rt->query = NULL; - rt->starttime = 0; - SDL_UnlockMutex(resolvermutex); - }; - return 0; -}; - -void resolverinit(int threads, int limit) -{ - resolverlimit = limit; - resolversem = SDL_CreateSemaphore(0); - resolvermutex = SDL_CreateMutex(); - - while(threads > 0) - { - resolverthread &rt = resolverthreads.add(); - rt.query = NULL; - rt.starttime = 0; - rt.thread = SDL_CreateThread(resolverloop, &rt); - --threads; - }; -}; - -void resolverstop(resolverthread &rt, bool restart) -{ - SDL_LockMutex(resolvermutex); - SDL_KillThread(rt.thread); - rt.query = NULL; - rt.starttime = 0; - rt.thread = NULL; - if(restart) rt.thread = SDL_CreateThread(resolverloop, &rt); - SDL_UnlockMutex(resolvermutex); -}; - -void resolverclear() -{ - SDL_LockMutex(resolvermutex); - resolverqueries.setsize(0); - resolverresults.setsize(0); - while (SDL_SemTryWait(resolversem) == 0); - loopv(resolverthreads) - { - resolverthread &rt = resolverthreads[i]; - resolverstop(rt, true); - }; - SDL_UnlockMutex(resolvermutex); -}; - -void resolverquery(char *name) -{ - SDL_LockMutex(resolvermutex); - resolverqueries.add(name); - SDL_SemPost(resolversem); - SDL_UnlockMutex(resolvermutex); -}; - -bool resolvercheck(char **name, ENetAddress *address) -{ - SDL_LockMutex(resolvermutex); - if(!resolverresults.empty()) - { - resolverresult &rr = resolverresults.pop(); - *name = rr.query; - *address = rr.address; - SDL_UnlockMutex(resolvermutex); - return true; - } - loopv(resolverthreads) - { - resolverthread &rt = resolverthreads[i]; - if(rt.query) - { - if(lastmillis - rt.starttime > resolverlimit) - { - resolverstop(rt, true); - *name = rt.query; - SDL_UnlockMutex(resolvermutex); - return true; - }; - }; - }; - SDL_UnlockMutex(resolvermutex); - return false; -}; - -struct serverinfo -{ - string name; - string full; - string map; - string sdesc; - int mode, numplayers, ping, protocol, minremain; - ENetAddress address; -}; - -vector servers; -ENetSocket pingsock = ENET_SOCKET_NULL; -int lastinfo = 0; - -char *getservername(int n) { return servers[n].name; }; - -void addserver(char *servername) -{ - loopv(servers) if(strcmp(servers[i].name, servername)==0) return; - serverinfo &si = servers.insert(0, serverinfo()); - strcpy_s(si.name, servername); - si.full[0] = 0; - si.mode = 0; - si.numplayers = 0; - si.ping = 9999; - si.protocol = 0; - si.minremain = 0; - si.map[0] = 0; - si.sdesc[0] = 0; - si.address.host = ENET_HOST_ANY; - si.address.port = CUBE_SERVINFO_PORT; -}; - -void pingservers() -{ - ENetBuffer buf; - uchar ping[MAXTRANS]; - uchar *p; - loopv(servers) - { - serverinfo &si = servers[i]; - if(si.address.host == ENET_HOST_ANY) continue; - p = ping; - putint(p, lastmillis); - buf.data = ping; - buf.dataLength = p - ping; - enet_socket_send(pingsock, &si.address, &buf, 1); - }; - lastinfo = lastmillis; -}; - -void checkresolver() -{ - char *name = NULL; - ENetAddress addr = { ENET_HOST_ANY, CUBE_SERVINFO_PORT }; - while(resolvercheck(&name, &addr)) - { - if(addr.host == ENET_HOST_ANY) continue; - loopv(servers) - { - serverinfo &si = servers[i]; - if(name == si.name) - { - si.address = addr; - addr.host = ENET_HOST_ANY; - break; - } - } - } -} - -void checkpings() -{ - enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE; - ENetBuffer buf; - ENetAddress addr; - uchar ping[MAXTRANS], *p; - char text[MAXTRANS]; - buf.data = ping; - buf.dataLength = sizeof(ping); - while(enet_socket_wait(pingsock, &events, 0) >= 0 && events) - { - if(enet_socket_receive(pingsock, &addr, &buf, 1) <= 0) return; - loopv(servers) - { - serverinfo &si = servers[i]; - if(addr.host == si.address.host) - { - p = ping; - si.ping = lastmillis - getint(p); - si.protocol = getint(p); - if(si.protocol!=PROTOCOL_VERSION) si.ping = 9998; - si.mode = getint(p); - si.numplayers = getint(p); - si.minremain = getint(p); - sgetstr(); - strcpy_s(si.map, text); - sgetstr(); - strcpy_s(si.sdesc, text); - break; - }; - }; - }; -}; - -int sicompare(const serverinfo *a, const serverinfo *b) -{ - return a->ping>b->ping ? 1 : (a->pingping ? -1 : strcmp(a->name, b->name)); -}; - -void refreshservers() -{ - checkresolver(); - checkpings(); - if(lastmillis - lastinfo >= 5000) pingservers(); - servers.sort((void *)sicompare); - int maxmenu = 16; - loopv(servers) - { - serverinfo &si = servers[i]; - if(si.address.host != ENET_HOST_ANY && si.ping != 9999) - { - if(si.protocol!=PROTOCOL_VERSION) sprintf_s(si.full)("%s [different cube protocol]", si.name); - else sprintf_s(si.full)("%d\t%d\t%s, %s: %s %s", si.ping, si.numplayers, si.map[0] ? si.map : "[unknown]", modestr(si.mode), si.name, si.sdesc); - } - else - { - sprintf_s(si.full)(si.address.host != ENET_HOST_ANY ? "%s [waiting for server response]" : "%s [unknown host]\t", si.name); - } - si.full[50] = 0; // cut off too long server descriptions - menumanual(1, i, si.full); - if(!--maxmenu) return; - }; -}; - -void servermenu() -{ - if(pingsock == ENET_SOCKET_NULL) - { - pingsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, NULL); - resolverinit(1, 1000); - }; - resolverclear(); - loopv(servers) resolverquery(servers[i].name); - refreshservers(); - menuset(1); -}; - -void updatefrommaster() -{ - const int MAXUPD = 32000; - uchar buf[MAXUPD]; - uchar *reply = retrieveservers(buf, MAXUPD); - if(!*reply || strstr((char *)reply, "") || strstr((char *)reply, "")) conoutf("master server not replying"); - else { servers.setsize(0); execute((char *)reply); }; - servermenu(); -}; - -COMMAND(addserver, ARG_1STR); -COMMAND(servermenu, ARG_NONE); -COMMAND(updatefrommaster, ARG_NONE); - -void writeservercfg() -{ - FILE *f = fopen("servers.cfg", "w"); - if(!f) return; - fprintf(f, "// servers connected to are added here automatically\n\n"); - loopvrev(servers) fprintf(f, "addserver %s\n", servers[i].name); - fclose(f); -}; - - ADDED src/serverbrowser.cxx Index: src/serverbrowser.cxx ================================================================== --- /dev/null +++ src/serverbrowser.cxx @@ -0,0 +1,303 @@ +// serverbrowser.cpp: eihrul's concurrent resolver, and server browser window management + +#include "cube.h" +#include "SDL_thread.h" + +struct resolverthread +{ + SDL_Thread *thread; + char *query; + int starttime; +}; + +struct resolverresult +{ + char *query; + ENetAddress address; +}; + +vector resolverthreads; +vector resolverqueries; +vector resolverresults; +SDL_mutex *resolvermutex; +SDL_sem *resolversem; +int resolverlimit = 1000; + +int resolverloop(void * data) +{ + resolverthread *rt = (resolverthread *)data; + for(;;) + { + SDL_SemWait(resolversem); + SDL_LockMutex(resolvermutex); + if(resolverqueries.empty()) + { + SDL_UnlockMutex(resolvermutex); + continue; + } + rt->query = resolverqueries.pop(); + rt->starttime = lastmillis; + SDL_UnlockMutex(resolvermutex); + ENetAddress address = { ENET_HOST_ANY, CUBE_SERVINFO_PORT }; + enet_address_set_host(&address, rt->query); + SDL_LockMutex(resolvermutex); + resolverresult &rr = resolverresults.add(); + rr.query = rt->query; + rr.address = address; + rt->query = NULL; + rt->starttime = 0; + SDL_UnlockMutex(resolvermutex); + }; + return 0; +}; + +void resolverinit(int threads, int limit) +{ + resolverlimit = limit; + resolversem = SDL_CreateSemaphore(0); + resolvermutex = SDL_CreateMutex(); + + while(threads > 0) + { + resolverthread &rt = resolverthreads.add(); + rt.query = NULL; + rt.starttime = 0; + rt.thread = SDL_CreateThread(resolverloop, &rt); + --threads; + }; +}; + +void resolverstop(resolverthread &rt, bool restart) +{ + SDL_LockMutex(resolvermutex); + SDL_KillThread(rt.thread); + rt.query = NULL; + rt.starttime = 0; + rt.thread = NULL; + if(restart) rt.thread = SDL_CreateThread(resolverloop, &rt); + SDL_UnlockMutex(resolvermutex); +}; + +void resolverclear() +{ + SDL_LockMutex(resolvermutex); + resolverqueries.setsize(0); + resolverresults.setsize(0); + while (SDL_SemTryWait(resolversem) == 0); + loopv(resolverthreads) + { + resolverthread &rt = resolverthreads[i]; + resolverstop(rt, true); + }; + SDL_UnlockMutex(resolvermutex); +}; + +void resolverquery(char *name) +{ + SDL_LockMutex(resolvermutex); + resolverqueries.add(name); + SDL_SemPost(resolversem); + SDL_UnlockMutex(resolvermutex); +}; + +bool resolvercheck(char **name, ENetAddress *address) +{ + SDL_LockMutex(resolvermutex); + if(!resolverresults.empty()) + { + resolverresult &rr = resolverresults.pop(); + *name = rr.query; + *address = rr.address; + SDL_UnlockMutex(resolvermutex); + return true; + } + loopv(resolverthreads) + { + resolverthread &rt = resolverthreads[i]; + if(rt.query) + { + if(lastmillis - rt.starttime > resolverlimit) + { + resolverstop(rt, true); + *name = rt.query; + SDL_UnlockMutex(resolvermutex); + return true; + }; + }; + }; + SDL_UnlockMutex(resolvermutex); + return false; +}; + +struct serverinfo +{ + string name; + string full; + string map; + string sdesc; + int mode, numplayers, ping, protocol, minremain; + ENetAddress address; +}; + +vector servers; +ENetSocket pingsock = ENET_SOCKET_NULL; +int lastinfo = 0; + +char *getservername(int n) { return servers[n].name; }; + +void addserver(char *servername) +{ + loopv(servers) if(strcmp(servers[i].name, servername)==0) return; + serverinfo &si = servers.insert(0, serverinfo()); + strcpy_s(si.name, servername); + si.full[0] = 0; + si.mode = 0; + si.numplayers = 0; + si.ping = 9999; + si.protocol = 0; + si.minremain = 0; + si.map[0] = 0; + si.sdesc[0] = 0; + si.address.host = ENET_HOST_ANY; + si.address.port = CUBE_SERVINFO_PORT; +}; + +void pingservers() +{ + ENetBuffer buf; + uchar ping[MAXTRANS]; + uchar *p; + loopv(servers) + { + serverinfo &si = servers[i]; + if(si.address.host == ENET_HOST_ANY) continue; + p = ping; + putint(p, lastmillis); + buf.data = ping; + buf.dataLength = p - ping; + enet_socket_send(pingsock, &si.address, &buf, 1); + }; + lastinfo = lastmillis; +}; + +void checkresolver() +{ + char *name = NULL; + ENetAddress addr = { ENET_HOST_ANY, CUBE_SERVINFO_PORT }; + while(resolvercheck(&name, &addr)) + { + if(addr.host == ENET_HOST_ANY) continue; + loopv(servers) + { + serverinfo &si = servers[i]; + if(name == si.name) + { + si.address = addr; + addr.host = ENET_HOST_ANY; + break; + } + } + } +} + +void checkpings() +{ + enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE; + ENetBuffer buf; + ENetAddress addr; + uchar ping[MAXTRANS], *p; + char text[MAXTRANS]; + buf.data = ping; + buf.dataLength = sizeof(ping); + while(enet_socket_wait(pingsock, &events, 0) >= 0 && events) + { + if(enet_socket_receive(pingsock, &addr, &buf, 1) <= 0) return; + loopv(servers) + { + serverinfo &si = servers[i]; + if(addr.host == si.address.host) + { + p = ping; + si.ping = lastmillis - getint(p); + si.protocol = getint(p); + if(si.protocol!=PROTOCOL_VERSION) si.ping = 9998; + si.mode = getint(p); + si.numplayers = getint(p); + si.minremain = getint(p); + sgetstr(); + strcpy_s(si.map, text); + sgetstr(); + strcpy_s(si.sdesc, text); + break; + }; + }; + }; +}; + +int sicompare(const serverinfo *a, const serverinfo *b) +{ + return a->ping>b->ping ? 1 : (a->pingping ? -1 : strcmp(a->name, b->name)); +}; + +void refreshservers() +{ + checkresolver(); + checkpings(); + if(lastmillis - lastinfo >= 5000) pingservers(); + servers.sort((void *)sicompare); + int maxmenu = 16; + loopv(servers) + { + serverinfo &si = servers[i]; + if(si.address.host != ENET_HOST_ANY && si.ping != 9999) + { + if(si.protocol!=PROTOCOL_VERSION) sprintf_s(si.full)("%s [different cube protocol]", si.name); + else sprintf_s(si.full)("%d\t%d\t%s, %s: %s %s", si.ping, si.numplayers, si.map[0] ? si.map : "[unknown]", modestr(si.mode), si.name, si.sdesc); + } + else + { + sprintf_s(si.full)(si.address.host != ENET_HOST_ANY ? "%s [waiting for server response]" : "%s [unknown host]\t", si.name); + } + si.full[50] = 0; // cut off too long server descriptions + menumanual(1, i, si.full); + if(!--maxmenu) return; + }; +}; + +void servermenu() +{ + if(pingsock == ENET_SOCKET_NULL) + { + pingsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, NULL); + resolverinit(1, 1000); + }; + resolverclear(); + loopv(servers) resolverquery(servers[i].name); + refreshservers(); + menuset(1); +}; + +void updatefrommaster() +{ + const int MAXUPD = 32000; + uchar buf[MAXUPD]; + uchar *reply = retrieveservers(buf, MAXUPD); + if(!*reply || strstr((char *)reply, "") || strstr((char *)reply, "")) conoutf("master server not replying"); + else { servers.setsize(0); execute((char *)reply); }; + servermenu(); +}; + +COMMAND(addserver, ARG_1STR); +COMMAND(servermenu, ARG_NONE); +COMMAND(updatefrommaster, ARG_NONE); + +void writeservercfg() +{ + FILE *f = fopen("servers.cfg", "w"); + if(!f) return; + fprintf(f, "// servers connected to are added here automatically\n\n"); + loopvrev(servers) fprintf(f, "addserver %s\n", servers[i].name); + fclose(f); +}; + + DELETED src/serverms.cpp Index: src/serverms.cpp ================================================================== --- src/serverms.cpp +++ /dev/null @@ -1,141 +0,0 @@ -// all server side masterserver and pinging functionality - -#include "cube.h" - -ENetSocket mssock = ENET_SOCKET_NULL; - -void httpgetsend(ENetAddress &ad, char *hostname, char *req, char *ref, char *agent) -{ - if(ad.host==ENET_HOST_ANY) - { - printf("looking up %s...\n", hostname); - enet_address_set_host(&ad, hostname); - if(ad.host==ENET_HOST_ANY) return; - }; - if(mssock!=ENET_SOCKET_NULL) enet_socket_destroy(mssock); - mssock = enet_socket_create(ENET_SOCKET_TYPE_STREAM, NULL); - if(mssock==ENET_SOCKET_NULL) { printf("could not open socket\n"); return; }; - if(enet_socket_connect(mssock, &ad)<0) { printf("could not connect\n"); return; }; - ENetBuffer buf; - sprintf_sd(httpget)("GET %s HTTP/1.0\nHost: %s\nReferer: %s\nUser-Agent: %s\n\n", req, hostname, ref, agent); - buf.data = httpget; - buf.dataLength = strlen((char *)buf.data); - printf("sending request to %s...\n", hostname); - enet_socket_send(mssock, NULL, &buf, 1); -}; - -void httpgetrecieve(ENetBuffer &buf) -{ - if(mssock==ENET_SOCKET_NULL) return; - enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE; - if(enet_socket_wait(mssock, &events, 0) >= 0 && events) - { - int len = enet_socket_receive(mssock, NULL, &buf, 1); - if(len<=0) - { - enet_socket_destroy(mssock); - mssock = ENET_SOCKET_NULL; - return; - }; - buf.data = ((char *)buf.data)+len; - ((char*)buf.data)[0] = 0; - buf.dataLength -= len; - }; -}; - -uchar *stripheader(uchar *b) -{ - char *s = strstr((char *)b, "\n\r\n"); - if(!s) s = strstr((char *)b, "\n\n"); - return s ? (uchar *)s : b; -}; - -ENetAddress masterserver = { ENET_HOST_ANY, 80 }; -int updmaster = 0; -string masterbase; -string masterpath; -uchar masterrep[MAXTRANS]; -ENetBuffer masterb; - -void updatemasterserver(int seconds) -{ - if(seconds>updmaster) // send alive signal to masterserver every hour of uptime - { - sprintf_sd(path)("%sregister.do?action=add", masterpath); - httpgetsend(masterserver, masterbase, path, "cubeserver", "Cube Server"); - masterrep[0] = 0; - masterb.data = masterrep; - masterb.dataLength = MAXTRANS-1; - updmaster = seconds+60*60; - }; -}; - -void checkmasterreply() -{ - bool busy = mssock!=ENET_SOCKET_NULL; - httpgetrecieve(masterb); - if(busy && mssock==ENET_SOCKET_NULL) printf("masterserver reply: %s\n", stripheader(masterrep)); -}; - -uchar *retrieveservers(uchar *buf, int buflen) -{ - sprintf_sd(path)("%sretrieve.do?item=list", masterpath); - httpgetsend(masterserver, masterbase, path, "cubeserver", "Cube Server"); - ENetBuffer eb; - buf[0] = 0; - eb.data = buf; - eb.dataLength = buflen-1; - while(mssock!=ENET_SOCKET_NULL) httpgetrecieve(eb); - return stripheader(buf); -}; - -ENetSocket pongsock = ENET_SOCKET_NULL; -string serverdesc; - -void serverms(int mode, int numplayers, int minremain, char *smapname, int seconds, bool isfull) -{ - checkmasterreply(); - updatemasterserver(seconds); - - // reply all server info requests - ENetBuffer buf; - ENetAddress addr; - uchar pong[MAXTRANS], *p; - int len; - enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE; - buf.data = pong; - while(enet_socket_wait(pongsock, &events, 0) >= 0 && events) - { - buf.dataLength = sizeof(pong); - len = enet_socket_receive(pongsock, &addr, &buf, 1); - if(len < 0) return; - p = &pong[len]; - putint(p, PROTOCOL_VERSION); - putint(p, mode); - putint(p, numplayers); - putint(p, minremain); - string mname; - strcpy_s(mname, isfull ? "[FULL] " : ""); - strcat_s(mname, smapname); - sendstring(mname, p); - sendstring(serverdesc, p); - buf.dataLength = p - pong; - enet_socket_send(pongsock, &addr, &buf, 1); - }; -}; - -void servermsinit(const char *master, char *sdesc, bool listen) -{ - const char *mid = strstr(master, "/"); - if(!mid) mid = master; - strcpy_s(masterpath, mid); - strn0cpy(masterbase, master, mid-master+1); - strcpy_s(serverdesc, sdesc); - - if(listen) - { - ENetAddress address = { ENET_HOST_ANY, CUBE_SERVINFO_PORT }; - pongsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, &address); - if(pongsock == ENET_SOCKET_NULL) fatal("could not create server info socket\n"); - }; -}; ADDED src/serverms.cxx Index: src/serverms.cxx ================================================================== --- /dev/null +++ src/serverms.cxx @@ -0,0 +1,141 @@ +// all server side masterserver and pinging functionality + +#include "cube.h" + +ENetSocket mssock = ENET_SOCKET_NULL; + +void httpgetsend(ENetAddress &ad, char *hostname, char *req, char *ref, char *agent) +{ + if(ad.host==ENET_HOST_ANY) + { + printf("looking up %s...\n", hostname); + enet_address_set_host(&ad, hostname); + if(ad.host==ENET_HOST_ANY) return; + }; + if(mssock!=ENET_SOCKET_NULL) enet_socket_destroy(mssock); + mssock = enet_socket_create(ENET_SOCKET_TYPE_STREAM, NULL); + if(mssock==ENET_SOCKET_NULL) { printf("could not open socket\n"); return; }; + if(enet_socket_connect(mssock, &ad)<0) { printf("could not connect\n"); return; }; + ENetBuffer buf; + sprintf_sd(httpget)("GET %s HTTP/1.0\nHost: %s\nReferer: %s\nUser-Agent: %s\n\n", req, hostname, ref, agent); + buf.data = httpget; + buf.dataLength = strlen((char *)buf.data); + printf("sending request to %s...\n", hostname); + enet_socket_send(mssock, NULL, &buf, 1); +}; + +void httpgetrecieve(ENetBuffer &buf) +{ + if(mssock==ENET_SOCKET_NULL) return; + enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE; + if(enet_socket_wait(mssock, &events, 0) >= 0 && events) + { + int len = enet_socket_receive(mssock, NULL, &buf, 1); + if(len<=0) + { + enet_socket_destroy(mssock); + mssock = ENET_SOCKET_NULL; + return; + }; + buf.data = ((char *)buf.data)+len; + ((char*)buf.data)[0] = 0; + buf.dataLength -= len; + }; +}; + +uchar *stripheader(uchar *b) +{ + char *s = strstr((char *)b, "\n\r\n"); + if(!s) s = strstr((char *)b, "\n\n"); + return s ? (uchar *)s : b; +}; + +ENetAddress masterserver = { ENET_HOST_ANY, 80 }; +int updmaster = 0; +string masterbase; +string masterpath; +uchar masterrep[MAXTRANS]; +ENetBuffer masterb; + +void updatemasterserver(int seconds) +{ + if(seconds>updmaster) // send alive signal to masterserver every hour of uptime + { + sprintf_sd(path)("%sregister.do?action=add", masterpath); + httpgetsend(masterserver, masterbase, path, "cubeserver", "Cube Server"); + masterrep[0] = 0; + masterb.data = masterrep; + masterb.dataLength = MAXTRANS-1; + updmaster = seconds+60*60; + }; +}; + +void checkmasterreply() +{ + bool busy = mssock!=ENET_SOCKET_NULL; + httpgetrecieve(masterb); + if(busy && mssock==ENET_SOCKET_NULL) printf("masterserver reply: %s\n", stripheader(masterrep)); +}; + +uchar *retrieveservers(uchar *buf, int buflen) +{ + sprintf_sd(path)("%sretrieve.do?item=list", masterpath); + httpgetsend(masterserver, masterbase, path, "cubeserver", "Cube Server"); + ENetBuffer eb; + buf[0] = 0; + eb.data = buf; + eb.dataLength = buflen-1; + while(mssock!=ENET_SOCKET_NULL) httpgetrecieve(eb); + return stripheader(buf); +}; + +ENetSocket pongsock = ENET_SOCKET_NULL; +string serverdesc; + +void serverms(int mode, int numplayers, int minremain, char *smapname, int seconds, bool isfull) +{ + checkmasterreply(); + updatemasterserver(seconds); + + // reply all server info requests + ENetBuffer buf; + ENetAddress addr; + uchar pong[MAXTRANS], *p; + int len; + enet_uint32 events = ENET_SOCKET_WAIT_RECEIVE; + buf.data = pong; + while(enet_socket_wait(pongsock, &events, 0) >= 0 && events) + { + buf.dataLength = sizeof(pong); + len = enet_socket_receive(pongsock, &addr, &buf, 1); + if(len < 0) return; + p = &pong[len]; + putint(p, PROTOCOL_VERSION); + putint(p, mode); + putint(p, numplayers); + putint(p, minremain); + string mname; + strcpy_s(mname, isfull ? "[FULL] " : ""); + strcat_s(mname, smapname); + sendstring(mname, p); + sendstring(serverdesc, p); + buf.dataLength = p - pong; + enet_socket_send(pongsock, &addr, &buf, 1); + }; +}; + +void servermsinit(const char *master, char *sdesc, bool listen) +{ + const char *mid = strstr(master, "/"); + if(!mid) mid = master; + strcpy_s(masterpath, mid); + strn0cpy(masterbase, master, mid-master+1); + strcpy_s(serverdesc, sdesc); + + if(listen) + { + ENetAddress address = { ENET_HOST_ANY, CUBE_SERVINFO_PORT }; + pongsock = enet_socket_create(ENET_SOCKET_TYPE_DATAGRAM, &address); + if(pongsock == ENET_SOCKET_NULL) fatal("could not create server info socket\n"); + }; +}; DELETED src/serverutil.cpp Index: src/serverutil.cpp ================================================================== --- src/serverutil.cpp +++ /dev/null @@ -1,123 +0,0 @@ -// misc useful functions used by the server - -#include "cube.h" - -// all network traffic is in 32bit ints, which are then compressed using the following simple scheme (assumes that most values are small). - -void putint(uchar *&p, int n) -{ - if(n<128 && n>-127) { *p++ = n; } - else if(n<0x8000 && n>=-0x8000) { *p++ = 0x80; *p++ = n; *p++ = n>>8; } - else { *p++ = 0x81; *p++ = n; *p++ = n>>8; *p++ = n>>16; *p++ = n>>24; }; -}; - -int getint(uchar *&p) -{ - int c = *((char *)p); - p++; - if(c==-128) { int n = *p++; n |= *((char *)p)<<8; p++; return n;} - else if(c==-127) { int n = *p++; n |= *p++<<8; n |= *p++<<16; return n|(*p++<<24); } - else return c; -}; - -void sendstring(char *t, uchar *&p) -{ - while(*t) putint(p, *t++); - putint(p, 0); -}; - -const char *modenames[] = -{ - "SP", "DMSP", "ffa/default", "coopedit", "ffa/duel", "teamplay", - "instagib", "instagib team", "efficiency", "efficiency team", - "insta arena", "insta clan arena", "tactics arena", "tactics clan arena", -}; - -const char *modestr(int n) { return (n>=-2 && n<12) ? modenames[n+2] : "unknown"; }; - -char msgsizesl[] = // size inclusive message token, 0 for variable or not-checked sizes -{ - SV_INITS2C, 4, SV_INITC2S, 0, SV_POS, 12, SV_TEXT, 0, SV_SOUND, 2, SV_CDIS, 2, - SV_EDITH, 7, SV_EDITT, 7, SV_EDITS, 6, SV_EDITD, 6, SV_EDITE, 6, - SV_DIED, 2, SV_DAMAGE, 4, SV_SHOT, 8, SV_FRAGS, 2, - SV_MAPCHANGE, 0, SV_ITEMSPAWN, 2, SV_ITEMPICKUP, 3, SV_DENIED, 2, - SV_PING, 2, SV_PONG, 2, SV_CLIENTPING, 2, SV_GAMEMODE, 2, - SV_TIMEUP, 2, SV_EDITENT, 10, SV_MAPRELOAD, 2, SV_ITEMACC, 2, - SV_SENDMAP, 0, SV_RECVMAP, 1, SV_SERVMSG, 0, SV_ITEMLIST, 0, - SV_EXT, 0, - -1 -}; - -char msgsizelookup(int msg) -{ - for(char *p = msgsizesl; *p>=0; p += 2) if(*p==msg) return p[1]; - return -1; -}; - -// sending of maps between clients - -string copyname; -int copysize; -uchar *copydata = NULL; - -void sendmaps(int n, string mapname, int mapsize, uchar *mapdata) -{ - if(mapsize <= 0 || mapsize > 256*256) return; - strcpy_s(copyname, mapname); - copysize = mapsize; - if(copydata) free(copydata); - copydata = (uchar *)alloc(mapsize); - memcpy(copydata, mapdata, mapsize); -} - -ENetPacket *recvmap(int n) -{ - if(!copydata) return NULL; - ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + copysize, ENET_PACKET_FLAG_RELIABLE); - uchar *start = packet->data; - uchar *p = start+2; - putint(p, SV_RECVMAP); - sendstring(copyname, p); - putint(p, copysize); - memcpy(p, copydata, copysize); - p += copysize; - *(ushort *)start = ENET_HOST_TO_NET_16(p-start); - enet_packet_resize(packet, p-start); - return packet; -} - - -#ifdef STANDALONE - -void localservertoclient(uchar *buf, int len) {}; -void fatal(char *s, char *o) { cleanupserver(); printf("servererror: %s\n", s); exit(1); }; -void *alloc(int s) { void *b = calloc(1,s); if(!b) fatal("no memory!"); return b; }; - -int main(int argc, char* argv[]) -{ - int uprate = 0, maxcl = 4; - char *sdesc = "", *ip = "", *master = NULL, *passwd = ""; - - for(int i = 1; i-127) { *p++ = n; } + else if(n<0x8000 && n>=-0x8000) { *p++ = 0x80; *p++ = n; *p++ = n>>8; } + else { *p++ = 0x81; *p++ = n; *p++ = n>>8; *p++ = n>>16; *p++ = n>>24; }; +}; + +int getint(uchar *&p) +{ + int c = *((char *)p); + p++; + if(c==-128) { int n = *p++; n |= *((char *)p)<<8; p++; return n;} + else if(c==-127) { int n = *p++; n |= *p++<<8; n |= *p++<<16; return n|(*p++<<24); } + else return c; +}; + +void sendstring(char *t, uchar *&p) +{ + while(*t) putint(p, *t++); + putint(p, 0); +}; + +const char *modenames[] = +{ + "SP", "DMSP", "ffa/default", "coopedit", "ffa/duel", "teamplay", + "instagib", "instagib team", "efficiency", "efficiency team", + "insta arena", "insta clan arena", "tactics arena", "tactics clan arena", +}; + +const char *modestr(int n) { return (n>=-2 && n<12) ? modenames[n+2] : "unknown"; }; + +char msgsizesl[] = // size inclusive message token, 0 for variable or not-checked sizes +{ + SV_INITS2C, 4, SV_INITC2S, 0, SV_POS, 12, SV_TEXT, 0, SV_SOUND, 2, SV_CDIS, 2, + SV_EDITH, 7, SV_EDITT, 7, SV_EDITS, 6, SV_EDITD, 6, SV_EDITE, 6, + SV_DIED, 2, SV_DAMAGE, 4, SV_SHOT, 8, SV_FRAGS, 2, + SV_MAPCHANGE, 0, SV_ITEMSPAWN, 2, SV_ITEMPICKUP, 3, SV_DENIED, 2, + SV_PING, 2, SV_PONG, 2, SV_CLIENTPING, 2, SV_GAMEMODE, 2, + SV_TIMEUP, 2, SV_EDITENT, 10, SV_MAPRELOAD, 2, SV_ITEMACC, 2, + SV_SENDMAP, 0, SV_RECVMAP, 1, SV_SERVMSG, 0, SV_ITEMLIST, 0, + SV_EXT, 0, + -1 +}; + +char msgsizelookup(int msg) +{ + for(char *p = msgsizesl; *p>=0; p += 2) if(*p==msg) return p[1]; + return -1; +}; + +// sending of maps between clients + +string copyname; +int copysize; +uchar *copydata = NULL; + +void sendmaps(int n, string mapname, int mapsize, uchar *mapdata) +{ + if(mapsize <= 0 || mapsize > 256*256) return; + strcpy_s(copyname, mapname); + copysize = mapsize; + if(copydata) free(copydata); + copydata = (uchar *)alloc(mapsize); + memcpy(copydata, mapdata, mapsize); +} + +ENetPacket *recvmap(int n) +{ + if(!copydata) return NULL; + ENetPacket *packet = enet_packet_create(NULL, MAXTRANS + copysize, ENET_PACKET_FLAG_RELIABLE); + uchar *start = packet->data; + uchar *p = start+2; + putint(p, SV_RECVMAP); + sendstring(copyname, p); + putint(p, copysize); + memcpy(p, copydata, copysize); + p += copysize; + *(ushort *)start = ENET_HOST_TO_NET_16(p-start); + enet_packet_resize(packet, p-start); + return packet; +} + + +#ifdef STANDALONE + +void localservertoclient(uchar *buf, int len) {}; +void fatal(char *s, char *o) { cleanupserver(); printf("servererror: %s\n", s); exit(1); }; +void *alloc(int s) { void *b = calloc(1,s); if(!b) fatal("no memory!"); return b; }; + +int main(int argc, char* argv[]) +{ + int uprate = 0, maxcl = 4; + char *sdesc = "", *ip = "", *master = NULL, *passwd = ""; + + for(int i = 1; i=0) { FSOUND_SetVolume(chan, (musicvol*MAXVOL)/255); FSOUND_SetPaused(chan, false); }; - } - else - { - conoutf("could not play music: %s", sn); - }; - #endif - }; -}; - -COMMAND(music, ARG_1STR); - -#ifdef USE_MIXER -vector samples; -#else -vector samples; -#endif - -cvector snames; - -int registersound(char *name) -{ - loopv(snames) if(strcmp(snames[i], name)==0) return i; - snames.add(newstring(name)); - samples.add(NULL); - return samples.length()-1; -}; - -COMMAND(registersound, ARG_1EST); - -void cleansound() -{ - if(nosound) return; - stopsound(); - #ifdef USE_MIXER - Mix_CloseAudio(); - #else - FSOUND_Close(); - #endif -}; - -VAR(stereo, 0, 1, 1); - -void updatechanvol(int chan, vec *loc) -{ - int vol = soundvol, pan = 255/2; - if(loc) - { - vdist(dist, v, *loc, player1->o); - vol -= (int)(dist*3*soundvol/255); // simple mono distance attenuation - if(stereo && (v.x != 0 || v.y != 0)) - { - float yaw = -atan2(v.x, v.y) - player1->yaw*(PI / 180.0f); // relative angle of sound along X-Y axis - pan = int(255.9f*(0.5*sin(yaw)+0.5f)); // range is from 0 (left) to 255 (right) - }; - }; - vol = (vol*MAXVOL)/255; - #ifdef USE_MIXER - Mix_Volume(chan, vol); - Mix_SetPanning(chan, 255-pan, pan); - #else - FSOUND_SetVolume(chan, vol); - FSOUND_SetPan(chan, pan); - #endif -}; - -void newsoundloc(int chan, vec *loc) -{ - assert(chan>=0 && chan5) return; // avoid bursts of sounds with heavy packetloss and in sp - if(n<0 || n>=samples.length()) { conoutf("unregistered sound: %d", n); return; }; - - if(!samples[n]) - { - sprintf_sd(buf)("packages/sounds/%s.wav", snames[n]); - - #ifdef USE_MIXER - samples[n] = Mix_LoadWAV(path(buf)); - #else - samples[n] = FSOUND_Sample_Load(n, path(buf), FSOUND_LOOP_OFF, 0, 0); - #endif - - if(!samples[n]) { conoutf("failed to load sample: %s", buf); return; }; - }; - - #ifdef USE_MIXER - int chan = Mix_PlayChannel(-1, samples[n], 0); - #else - int chan = FSOUND_PlaySoundEx(FSOUND_FREE, samples[n], NULL, true); - #endif - if(chan<0) return; - if(loc) newsoundloc(chan, loc); - updatechanvol(chan, loc); - #ifndef USE_MIXER - FSOUND_SetPaused(chan, false); - #endif -}; - -void sound(int n) { playsound(n, NULL); }; -COMMAND(sound, ARG_1INT); ADDED src/sound.cxx Index: src/sound.cxx ================================================================== --- /dev/null +++ src/sound.cxx @@ -0,0 +1,226 @@ +// sound.cpp: uses fmod on windows and sdl_mixer on unix (both had problems on the other platform) + +#include "cube.h" + +//#ifndef _WIN32 // NOTE: fmod not being supported for the moment as it does not allow stereo pan/vol updating during playback +#define USE_MIXER +//#endif + +VARP(soundvol, 0, 255, 255); +VARP(musicvol, 0, 128, 255); +bool nosound = false; + +#define MAXCHAN 32 +#define SOUNDFREQ 22050 + +struct soundloc { vec loc; bool inuse; } soundlocs[MAXCHAN]; + +#ifdef USE_MIXER + #include "SDL_mixer.h" + #define MAXVOL MIX_MAX_VOLUME + Mix_Music *mod = NULL; + void *stream = NULL; +#else + #include "fmod.h" + #define MAXVOL 255 + FMUSIC_MODULE *mod = NULL; + FSOUND_STREAM *stream = NULL; +#endif + +void stopsound() +{ + if(nosound) return; + if(mod) + { + #ifdef USE_MIXER + Mix_HaltMusic(); + Mix_FreeMusic(mod); + #else + FMUSIC_FreeSong(mod); + #endif + mod = NULL; + }; + if(stream) + { + #ifndef USE_MIXER + FSOUND_Stream_Close(stream); + #endif + stream = NULL; + }; +}; + +VAR(soundbufferlen, 128, 1024, 4096); + +void initsound() +{ + memset(soundlocs, 0, sizeof(soundloc)*MAXCHAN); + #ifdef USE_MIXER + if(Mix_OpenAudio(SOUNDFREQ, MIX_DEFAULT_FORMAT, 2, soundbufferlen)<0) + { + conoutf("sound init failed (SDL_mixer): %s", (size_t)Mix_GetError()); + nosound = true; + }; + Mix_AllocateChannels(MAXCHAN); + #else + if(FSOUND_GetVersion()=0) { FSOUND_SetVolume(chan, (musicvol*MAXVOL)/255); FSOUND_SetPaused(chan, false); }; + } + else + { + conoutf("could not play music: %s", sn); + }; + #endif + }; +}; + +COMMAND(music, ARG_1STR); + +#ifdef USE_MIXER +vector samples; +#else +vector samples; +#endif + +cvector snames; + +int registersound(char *name) +{ + loopv(snames) if(strcmp(snames[i], name)==0) return i; + snames.add(newstring(name)); + samples.add(NULL); + return samples.length()-1; +}; + +COMMAND(registersound, ARG_1EST); + +void cleansound() +{ + if(nosound) return; + stopsound(); + #ifdef USE_MIXER + Mix_CloseAudio(); + #else + FSOUND_Close(); + #endif +}; + +VAR(stereo, 0, 1, 1); + +void updatechanvol(int chan, vec *loc) +{ + int vol = soundvol, pan = 255/2; + if(loc) + { + vdist(dist, v, *loc, player1->o); + vol -= (int)(dist*3*soundvol/255); // simple mono distance attenuation + if(stereo && (v.x != 0 || v.y != 0)) + { + float yaw = -atan2(v.x, v.y) - player1->yaw*(PI / 180.0f); // relative angle of sound along X-Y axis + pan = int(255.9f*(0.5*sin(yaw)+0.5f)); // range is from 0 (left) to 255 (right) + }; + }; + vol = (vol*MAXVOL)/255; + #ifdef USE_MIXER + Mix_Volume(chan, vol); + Mix_SetPanning(chan, 255-pan, pan); + #else + FSOUND_SetVolume(chan, vol); + FSOUND_SetPan(chan, pan); + #endif +}; + +void newsoundloc(int chan, vec *loc) +{ + assert(chan>=0 && chan5) return; // avoid bursts of sounds with heavy packetloss and in sp + if(n<0 || n>=samples.length()) { conoutf("unregistered sound: %d", n); return; }; + + if(!samples[n]) + { + sprintf_sd(buf)("packages/sounds/%s.wav", snames[n]); + + #ifdef USE_MIXER + samples[n] = Mix_LoadWAV(path(buf)); + #else + samples[n] = FSOUND_Sample_Load(n, path(buf), FSOUND_LOOP_OFF, 0, 0); + #endif + + if(!samples[n]) { conoutf("failed to load sample: %s", buf); return; }; + }; + + #ifdef USE_MIXER + int chan = Mix_PlayChannel(-1, samples[n], 0); + #else + int chan = FSOUND_PlaySoundEx(FSOUND_FREE, samples[n], NULL, true); + #endif + if(chan<0) return; + if(loc) newsoundloc(chan, loc); + updatechanvol(chan, loc); + #ifndef USE_MIXER + FSOUND_SetPaused(chan, false); + #endif +}; + +void sound(int n) { playsound(n, NULL); }; +COMMAND(sound, ARG_1INT); DELETED src/tools.cpp Index: src/tools.cpp ================================================================== --- src/tools.cpp +++ /dev/null @@ -1,140 +0,0 @@ -// implementation of generic tools - -#include "tools.h" -#include - -//////////////////////////// pool /////////////////////////// - -pool::pool() -{ - blocks = 0; - allocnext(POOLSIZE); - for(int i = 0; iMAXREUSESIZE) - { - return malloc(size); - } - else - { - size = bucket(size); - void **r = (void **)reuse[size]; - if(r) - { - reuse[size] = *r; - return (void *)r; - } - else - { - size <<= PTRBITS; - if(leftMAXREUSESIZE) - { - free(p); - } - else - { - size = bucket(size); - if(size) // only needed for 0-size free, are there any? - { - *((void **)p) = reuse[size]; - reuse[size] = p; - }; - }; -}; - -void *pool::realloc(void *p, size_t oldsize, size_t newsize) -{ - void *np = alloc(newsize); - if(!oldsize) return np; - memcpy(np, p, newsize>oldsize ? oldsize : newsize); - dealloc(p, oldsize); - return np; -}; - -void pool::dealloc_block(void *b) -{ - if(b) - { - dealloc_block(*((char **)b)); - free(b); - }; -} - -void pool::allocnext(size_t allocsize) -{ - char *b = (char *)malloc(allocsize+PTRSIZE); - *((char **)b) = blocks; - blocks = b; - p = b+PTRSIZE; - left = allocsize; -}; - -char *pool::string(char *s, size_t l) -{ - char *b = (char *)alloc(l+1); - strncpy(b,s,l); - b[l] = 0; - return b; -}; - -pool *gp() // useful for global buffers that need to be initialisation order independant -{ - static pool *p = NULL; - return p ? p : (p = new pool()); -}; - - -///////////////////////// misc tools /////////////////////// - -char *path(char *s) -{ - for(char *t = s; t = strpbrk(t, "/\\"); *t++ = PATHDIV); - return s; -}; - -char *loadfile(char *fn, int *size) -{ - FILE *f = fopen(fn, "rb"); - if(!f) return NULL; - fseek(f, 0, SEEK_END); - int len = ftell(f); - fseek(f, 0, SEEK_SET); - char *buf = (char *)malloc(len+1); - if(!buf) return NULL; - buf[len] = 0; - size_t rlen = fread(buf, 1, len, f); - fclose(f); - if(len!=rlen || len<=0) - { - free(buf); - return NULL; - }; - if(size!=NULL) *size = len; - return buf; -}; - -void endianswap(void *memory, int stride, int length) // little indians as storage format -{ - if(*((char *)&stride)) return; - loop(w, length) loop(i, stride/2) - { - uchar *p = (uchar *)memory+w*stride; - uchar t = p[i]; - p[i] = p[stride-i-1]; - p[stride-i-1] = t; - }; -} ADDED src/tools.cxx Index: src/tools.cxx ================================================================== --- /dev/null +++ src/tools.cxx @@ -0,0 +1,140 @@ +// implementation of generic tools + +#include "tools.h" +#include + +//////////////////////////// pool /////////////////////////// + +pool::pool() +{ + blocks = 0; + allocnext(POOLSIZE); + for(int i = 0; iMAXREUSESIZE) + { + return malloc(size); + } + else + { + size = bucket(size); + void **r = (void **)reuse[size]; + if(r) + { + reuse[size] = *r; + return (void *)r; + } + else + { + size <<= PTRBITS; + if(leftMAXREUSESIZE) + { + free(p); + } + else + { + size = bucket(size); + if(size) // only needed for 0-size free, are there any? + { + *((void **)p) = reuse[size]; + reuse[size] = p; + }; + }; +}; + +void *pool::realloc(void *p, size_t oldsize, size_t newsize) +{ + void *np = alloc(newsize); + if(!oldsize) return np; + memcpy(np, p, newsize>oldsize ? oldsize : newsize); + dealloc(p, oldsize); + return np; +}; + +void pool::dealloc_block(void *b) +{ + if(b) + { + dealloc_block(*((char **)b)); + free(b); + }; +} + +void pool::allocnext(size_t allocsize) +{ + char *b = (char *)malloc(allocsize+PTRSIZE); + *((char **)b) = blocks; + blocks = b; + p = b+PTRSIZE; + left = allocsize; +}; + +char *pool::string(char *s, size_t l) +{ + char *b = (char *)alloc(l+1); + strncpy(b,s,l); + b[l] = 0; + return b; +}; + +pool *gp() // useful for global buffers that need to be initialisation order independant +{ + static pool *p = NULL; + return p ? p : (p = new pool()); +}; + + +///////////////////////// misc tools /////////////////////// + +char *path(char *s) +{ + for(char *t = s; t = strpbrk(t, "/\\"); *t++ = PATHDIV); + return s; +}; + +char *loadfile(char *fn, int *size) +{ + FILE *f = fopen(fn, "rb"); + if(!f) return NULL; + fseek(f, 0, SEEK_END); + int len = ftell(f); + fseek(f, 0, SEEK_SET); + char *buf = (char *)malloc(len+1); + if(!buf) return NULL; + buf[len] = 0; + size_t rlen = fread(buf, 1, len, f); + fclose(f); + if(len!=rlen || len<=0) + { + free(buf); + return NULL; + }; + if(size!=NULL) *size = len; + return buf; +}; + +void endianswap(void *memory, int stride, int length) // little indians as storage format +{ + if(*((char *)&stride)) return; + loop(w, length) loop(i, stride/2) + { + uchar *p = (uchar *)memory+w*stride; + uchar t = p[i]; + p[i] = p[stride-i-1]; + p[stride-i-1] = t; + }; +} DELETED src/weapon.cpp Index: src/weapon.cpp ================================================================== --- src/weapon.cpp +++ /dev/null @@ -1,342 +0,0 @@ -// weapon.cpp: all shooting and effects code - -#include "cube.h" - -struct guninfo { short sound, attackdelay, damage, projspeed, part, kickamount; char *name; }; - -const int MONSTERDAMAGEFACTOR = 4; -const int SGRAYS = 20; -const float SGSPREAD = 2; -vec sg[SGRAYS]; - -guninfo guns[NUMGUNS] = -{ - { S_PUNCH1, 250, 50, 0, 0, 1, "fist" }, - { S_SG, 1400, 10, 0, 0, 20, "shotgun" }, // *SGRAYS - { S_CG, 100, 30, 0, 0, 7, "chaingun" }, - { S_RLFIRE, 800, 120, 80, 0, 10, "rocketlauncher" }, - { S_RIFLE, 1500, 100, 0, 0, 30, "rifle" }, - { S_FLAUNCH, 200, 20, 50, 4, 1, "fireball" }, - { S_ICEBALL, 200, 40, 30, 6, 1, "iceball" }, - { S_SLIMEBALL, 200, 30, 160, 7, 1, "slimeball" }, - { S_PIGR1, 250, 50, 0, 0, 1, "bite" }, -}; - -void selectgun(int a, int b, int c) -{ - if(a<-1 || b<-1 || c<-1 || a>=NUMGUNS || b>=NUMGUNS || c>=NUMGUNS) return; - int s = player1->gunselect; - if(a>=0 && s!=a && player1->ammo[a]) s = a; - else if(b>=0 && s!=b && player1->ammo[b]) s = b; - else if(c>=0 && s!=c && player1->ammo[c]) s = c; - else if(s!=GUN_RL && player1->ammo[GUN_RL]) s = GUN_RL; - else if(s!=GUN_CG && player1->ammo[GUN_CG]) s = GUN_CG; - else if(s!=GUN_SG && player1->ammo[GUN_SG]) s = GUN_SG; - else if(s!=GUN_RIFLE && player1->ammo[GUN_RIFLE]) s = GUN_RIFLE; - else s = GUN_FIST; - if(s!=player1->gunselect) playsoundc(S_WEAPLOAD); - player1->gunselect = s; - //conoutf("%s selected", (int)guns[s].name); -}; - -int reloadtime(int gun) { return guns[gun].attackdelay; }; - -void weapon(char *a1, char *a2, char *a3) -{ - selectgun(a1[0] ? atoi(a1) : -1, - a2[0] ? atoi(a2) : -1, - a3[0] ? atoi(a3) : -1); -}; - -COMMAND(weapon, ARG_3STR); - -void createrays(vec &from, vec &to) // create random spread of rays for the shotgun -{ - vdist(dist, dvec, from, to); - float f = dist*SGSPREAD/1000; - loopi(SGRAYS) - { - #define RNDD (rnd(101)-50)*f - vec r = { RNDD, RNDD, RNDD }; - sg[i] = to; - vadd(sg[i], r); - }; -}; - -bool intersect(dynent *d, vec &from, vec &to) // if lineseg hits entity bounding box -{ - vec v = to, w = d->o, *p; - vsub(v, from); - vsub(w, from); - float c1 = dotprod(w, v); - - if(c1<=0) p = &from; - else - { - float c2 = dotprod(v, v); - if(c2<=c1) p = &to; - else - { - float f = c1/c2; - vmul(v, f); - vadd(v, from); - p = &v; - }; - }; - - return p->x <= d->o.x+d->radius - && p->x >= d->o.x-d->radius - && p->y <= d->o.y+d->radius - && p->y >= d->o.y-d->radius - && p->z <= d->o.z+d->aboveeye - && p->z >= d->o.z-d->eyeheight; -}; - -char *playerincrosshair() -{ - if(demoplayback) return NULL; - loopv(players) - { - dynent *o = players[i]; - if(!o) continue; - if(intersect(o, player1->o, worldpos)) return o->name; - }; - return NULL; -}; - -const int MAXPROJ = 100; -struct projectile { vec o, to; float speed; dynent *owner; int gun; bool inuse, local; }; -projectile projs[MAXPROJ]; - -void projreset() { loopi(MAXPROJ) projs[i].inuse = false; }; - -void newprojectile(vec &from, vec &to, float speed, bool local, dynent *owner, int gun) -{ - loopi(MAXPROJ) - { - projectile *p = &projs[i]; - if(p->inuse) continue; - p->inuse = true; - p->o = from; - p->to = to; - p->speed = speed; - p->local = local; - p->owner = owner; - p->gun = gun; - return; - }; -}; - -void hit(int target, int damage, dynent *d, dynent *at) -{ - if(d==player1) selfdamage(damage, at==player1 ? -1 : -2, at); - else if(d->monsterstate) monsterpain(d, damage, at); - else { addmsg(1, 4, SV_DAMAGE, target, damage, d->lifesequence); playsound(S_PAIN1+rnd(5), &d->o); }; - particle_splash(3, damage, 1000, d->o); - demodamage(damage, d->o); -}; - -const float RL_RADIUS = 5; -const float RL_DAMRAD = 7; // hack - -void radialeffect(dynent *o, vec &v, int cn, int qdam, dynent *at) -{ - if(o->state!=CS_ALIVE) return; - vdist(dist, temp, v, o->o); - dist -= 2; // account for eye distance imprecision - if(distvel, temp); - }; -}; - -void splash(projectile *p, vec &v, vec &vold, int notthisplayer, int notthismonster, int qdam) -{ - particle_splash(0, 50, 300, v); - p->inuse = false; - if(p->gun!=GUN_RL) - { - playsound(S_FEXPLODE, &v); - // no push? - } - else - { - playsound(S_RLHIT, &v); - newsphere(v, RL_RADIUS, 0); - dodynlight(vold, v, 0, 0, p->owner); - if(!p->local) return; - radialeffect(player1, v, -1, qdam, p->owner); - loopv(players) - { - if(i==notthisplayer) continue; - dynent *o = players[i]; - if(!o) continue; - radialeffect(o, v, i, qdam, p->owner); - }; - dvector &mv = getmonsters(); - loopv(mv) if(i!=notthismonster) radialeffect(mv[i], v, i, qdam, p->owner); - }; -}; - -inline void projdamage(dynent *o, projectile *p, vec &v, int i, int im, int qdam) -{ - if(o->state!=CS_ALIVE) return; - if(intersect(o, p->o, v)) - { - splash(p, v, p->o, i, im, qdam); - hit(i, qdam, o, p->owner); - }; -}; - -void moveprojectiles(float time) -{ - loopi(MAXPROJ) - { - projectile *p = &projs[i]; - if(!p->inuse) continue; - int qdam = guns[p->gun].damage*(p->owner->quadmillis ? 4 : 1); - if(p->owner->monsterstate) qdam /= MONSTERDAMAGEFACTOR; - vdist(dist, v, p->o, p->to); - float dtime = dist*1000/p->speed; - if(time>dtime) dtime = time; - vmul(v, time/dtime); - vadd(v, p->o) - if(p->local) - { - loopv(players) - { - dynent *o = players[i]; - if(!o) continue; - projdamage(o, p, v, i, -1, qdam); - }; - if(p->owner!=player1) projdamage(player1, p, v, -1, -1, qdam); - dvector &mv = getmonsters(); - loopv(mv) if(!vreject(mv[i]->o, v, 10.0f) && mv[i]!=p->owner) projdamage(mv[i], p, v, -1, i, qdam); - }; - if(p->inuse) - { - if(time==dtime) splash(p, v, p->o, -1, -1, qdam); - else - { - if(p->gun==GUN_RL) { dodynlight(p->o, v, 0, 255, p->owner); particle_splash(5, 2, 200, v); } - else { particle_splash(1, 1, 200, v); particle_splash(guns[p->gun].part, 1, 1, v); }; - }; - }; - p->o = v; - }; -}; - -void shootv(int gun, vec &from, vec &to, dynent *d, bool local) // create visual effect from a shot -{ - playsound(guns[gun].sound, d==player1 ? NULL : &d->o); - int pspeed = 25; - switch(gun) - { - case GUN_FIST: - break; - - case GUN_SG: - { - loopi(SGRAYS) particle_splash(0, 5, 200, sg[i]); - break; - }; - - case GUN_CG: - particle_splash(0, 100, 250, to); - //particle_trail(1, 10, from, to); - break; - - case GUN_RL: - case GUN_FIREBALL: - case GUN_ICEBALL: - case GUN_SLIMEBALL: - pspeed = guns[gun].projspeed; - if(d->monsterstate) pspeed /= 2; - newprojectile(from, to, (float)pspeed, local, d, gun); - break; - - case GUN_RIFLE: - particle_splash(0, 50, 200, to); - particle_trail(1, 500, from, to); - break; - }; -}; - -void hitpush(int target, int damage, dynent *d, dynent *at, vec &from, vec &to) -{ - hit(target, damage, d, at); - vdist(dist, v, from, to); - vmul(v, damage/dist/50); - vadd(d->vel, v); -}; - -void raydamage(dynent *o, vec &from, vec &to, dynent *d, int i) -{ - if(o->state!=CS_ALIVE) return; - int qdam = guns[d->gunselect].damage; - if(d->quadmillis) qdam *= 4; - if(d->monsterstate) qdam /= MONSTERDAMAGEFACTOR; - if(d->gunselect==GUN_SG) - { - int damage = 0; - loop(r, SGRAYS) if(intersect(o, from, sg[r])) damage += qdam; - if(damage) hitpush(i, damage, o, d, from, to); - } - else if(intersect(o, from, to)) hitpush(i, qdam, o, d, from, to); -}; - -void shoot(dynent *d, vec &targ) -{ - int attacktime = lastmillis-d->lastaction; - if(attacktimegunwait) return; - d->gunwait = 0; - if(!d->attacking) return; - d->lastaction = lastmillis; - d->lastattackgun = d->gunselect; - if(!d->ammo[d->gunselect]) { playsoundc(S_NOAMMO); d->gunwait = 250; d->lastattackgun = -1; return; }; - if(d->gunselect) d->ammo[d->gunselect]--; - vec from = d->o; - vec to = targ; - from.z -= 0.2f; // below eye - - vdist(dist, unitv, from, to); - vdiv(unitv, dist); - vec kickback = unitv; - vmul(kickback, guns[d->gunselect].kickamount*-0.01f); - vadd(d->vel, kickback); - if(d->pitch<80.0f) d->pitch += guns[d->gunselect].kickamount*0.05f; - - - if(d->gunselect==GUN_FIST || d->gunselect==GUN_BITE) - { - vmul(unitv, 3); // punch range - to = from; - vadd(to, unitv); - }; - if(d->gunselect==GUN_SG) createrays(from, to); - - if(d->quadmillis && attacktime>200) playsoundc(S_ITEMPUP); - shootv(d->gunselect, from, to, d, true); - if(!d->monsterstate) addmsg(1, 8, SV_SHOT, d->gunselect, (int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF), (int)(to.x*DMF), (int)(to.y*DMF), (int)(to.z*DMF)); - d->gunwait = guns[d->gunselect].attackdelay; - - if(guns[d->gunselect].projspeed) return; - - loopv(players) - { - dynent *o = players[i]; - if(!o) continue; - raydamage(o, from, to, d, i); - }; - - dvector &v = getmonsters(); - loopv(v) if(v[i]!=d) raydamage(v[i], from, to, d, -2); - - if(d->monsterstate) raydamage(player1, from, to, d, -1); -}; - - ADDED src/weapon.cxx Index: src/weapon.cxx ================================================================== --- /dev/null +++ src/weapon.cxx @@ -0,0 +1,342 @@ +// weapon.cpp: all shooting and effects code + +#include "cube.h" + +struct guninfo { short sound, attackdelay, damage, projspeed, part, kickamount; char *name; }; + +const int MONSTERDAMAGEFACTOR = 4; +const int SGRAYS = 20; +const float SGSPREAD = 2; +vec sg[SGRAYS]; + +guninfo guns[NUMGUNS] = +{ + { S_PUNCH1, 250, 50, 0, 0, 1, "fist" }, + { S_SG, 1400, 10, 0, 0, 20, "shotgun" }, // *SGRAYS + { S_CG, 100, 30, 0, 0, 7, "chaingun" }, + { S_RLFIRE, 800, 120, 80, 0, 10, "rocketlauncher" }, + { S_RIFLE, 1500, 100, 0, 0, 30, "rifle" }, + { S_FLAUNCH, 200, 20, 50, 4, 1, "fireball" }, + { S_ICEBALL, 200, 40, 30, 6, 1, "iceball" }, + { S_SLIMEBALL, 200, 30, 160, 7, 1, "slimeball" }, + { S_PIGR1, 250, 50, 0, 0, 1, "bite" }, +}; + +void selectgun(int a, int b, int c) +{ + if(a<-1 || b<-1 || c<-1 || a>=NUMGUNS || b>=NUMGUNS || c>=NUMGUNS) return; + int s = player1->gunselect; + if(a>=0 && s!=a && player1->ammo[a]) s = a; + else if(b>=0 && s!=b && player1->ammo[b]) s = b; + else if(c>=0 && s!=c && player1->ammo[c]) s = c; + else if(s!=GUN_RL && player1->ammo[GUN_RL]) s = GUN_RL; + else if(s!=GUN_CG && player1->ammo[GUN_CG]) s = GUN_CG; + else if(s!=GUN_SG && player1->ammo[GUN_SG]) s = GUN_SG; + else if(s!=GUN_RIFLE && player1->ammo[GUN_RIFLE]) s = GUN_RIFLE; + else s = GUN_FIST; + if(s!=player1->gunselect) playsoundc(S_WEAPLOAD); + player1->gunselect = s; + //conoutf("%s selected", (int)guns[s].name); +}; + +int reloadtime(int gun) { return guns[gun].attackdelay; }; + +void weapon(char *a1, char *a2, char *a3) +{ + selectgun(a1[0] ? atoi(a1) : -1, + a2[0] ? atoi(a2) : -1, + a3[0] ? atoi(a3) : -1); +}; + +COMMAND(weapon, ARG_3STR); + +void createrays(vec &from, vec &to) // create random spread of rays for the shotgun +{ + vdist(dist, dvec, from, to); + float f = dist*SGSPREAD/1000; + loopi(SGRAYS) + { + #define RNDD (rnd(101)-50)*f + vec r = { RNDD, RNDD, RNDD }; + sg[i] = to; + vadd(sg[i], r); + }; +}; + +bool intersect(dynent *d, vec &from, vec &to) // if lineseg hits entity bounding box +{ + vec v = to, w = d->o, *p; + vsub(v, from); + vsub(w, from); + float c1 = dotprod(w, v); + + if(c1<=0) p = &from; + else + { + float c2 = dotprod(v, v); + if(c2<=c1) p = &to; + else + { + float f = c1/c2; + vmul(v, f); + vadd(v, from); + p = &v; + }; + }; + + return p->x <= d->o.x+d->radius + && p->x >= d->o.x-d->radius + && p->y <= d->o.y+d->radius + && p->y >= d->o.y-d->radius + && p->z <= d->o.z+d->aboveeye + && p->z >= d->o.z-d->eyeheight; +}; + +char *playerincrosshair() +{ + if(demoplayback) return NULL; + loopv(players) + { + dynent *o = players[i]; + if(!o) continue; + if(intersect(o, player1->o, worldpos)) return o->name; + }; + return NULL; +}; + +const int MAXPROJ = 100; +struct projectile { vec o, to; float speed; dynent *owner; int gun; bool inuse, local; }; +projectile projs[MAXPROJ]; + +void projreset() { loopi(MAXPROJ) projs[i].inuse = false; }; + +void newprojectile(vec &from, vec &to, float speed, bool local, dynent *owner, int gun) +{ + loopi(MAXPROJ) + { + projectile *p = &projs[i]; + if(p->inuse) continue; + p->inuse = true; + p->o = from; + p->to = to; + p->speed = speed; + p->local = local; + p->owner = owner; + p->gun = gun; + return; + }; +}; + +void hit(int target, int damage, dynent *d, dynent *at) +{ + if(d==player1) selfdamage(damage, at==player1 ? -1 : -2, at); + else if(d->monsterstate) monsterpain(d, damage, at); + else { addmsg(1, 4, SV_DAMAGE, target, damage, d->lifesequence); playsound(S_PAIN1+rnd(5), &d->o); }; + particle_splash(3, damage, 1000, d->o); + demodamage(damage, d->o); +}; + +const float RL_RADIUS = 5; +const float RL_DAMRAD = 7; // hack + +void radialeffect(dynent *o, vec &v, int cn, int qdam, dynent *at) +{ + if(o->state!=CS_ALIVE) return; + vdist(dist, temp, v, o->o); + dist -= 2; // account for eye distance imprecision + if(distvel, temp); + }; +}; + +void splash(projectile *p, vec &v, vec &vold, int notthisplayer, int notthismonster, int qdam) +{ + particle_splash(0, 50, 300, v); + p->inuse = false; + if(p->gun!=GUN_RL) + { + playsound(S_FEXPLODE, &v); + // no push? + } + else + { + playsound(S_RLHIT, &v); + newsphere(v, RL_RADIUS, 0); + dodynlight(vold, v, 0, 0, p->owner); + if(!p->local) return; + radialeffect(player1, v, -1, qdam, p->owner); + loopv(players) + { + if(i==notthisplayer) continue; + dynent *o = players[i]; + if(!o) continue; + radialeffect(o, v, i, qdam, p->owner); + }; + dvector &mv = getmonsters(); + loopv(mv) if(i!=notthismonster) radialeffect(mv[i], v, i, qdam, p->owner); + }; +}; + +inline void projdamage(dynent *o, projectile *p, vec &v, int i, int im, int qdam) +{ + if(o->state!=CS_ALIVE) return; + if(intersect(o, p->o, v)) + { + splash(p, v, p->o, i, im, qdam); + hit(i, qdam, o, p->owner); + }; +}; + +void moveprojectiles(float time) +{ + loopi(MAXPROJ) + { + projectile *p = &projs[i]; + if(!p->inuse) continue; + int qdam = guns[p->gun].damage*(p->owner->quadmillis ? 4 : 1); + if(p->owner->monsterstate) qdam /= MONSTERDAMAGEFACTOR; + vdist(dist, v, p->o, p->to); + float dtime = dist*1000/p->speed; + if(time>dtime) dtime = time; + vmul(v, time/dtime); + vadd(v, p->o) + if(p->local) + { + loopv(players) + { + dynent *o = players[i]; + if(!o) continue; + projdamage(o, p, v, i, -1, qdam); + }; + if(p->owner!=player1) projdamage(player1, p, v, -1, -1, qdam); + dvector &mv = getmonsters(); + loopv(mv) if(!vreject(mv[i]->o, v, 10.0f) && mv[i]!=p->owner) projdamage(mv[i], p, v, -1, i, qdam); + }; + if(p->inuse) + { + if(time==dtime) splash(p, v, p->o, -1, -1, qdam); + else + { + if(p->gun==GUN_RL) { dodynlight(p->o, v, 0, 255, p->owner); particle_splash(5, 2, 200, v); } + else { particle_splash(1, 1, 200, v); particle_splash(guns[p->gun].part, 1, 1, v); }; + }; + }; + p->o = v; + }; +}; + +void shootv(int gun, vec &from, vec &to, dynent *d, bool local) // create visual effect from a shot +{ + playsound(guns[gun].sound, d==player1 ? NULL : &d->o); + int pspeed = 25; + switch(gun) + { + case GUN_FIST: + break; + + case GUN_SG: + { + loopi(SGRAYS) particle_splash(0, 5, 200, sg[i]); + break; + }; + + case GUN_CG: + particle_splash(0, 100, 250, to); + //particle_trail(1, 10, from, to); + break; + + case GUN_RL: + case GUN_FIREBALL: + case GUN_ICEBALL: + case GUN_SLIMEBALL: + pspeed = guns[gun].projspeed; + if(d->monsterstate) pspeed /= 2; + newprojectile(from, to, (float)pspeed, local, d, gun); + break; + + case GUN_RIFLE: + particle_splash(0, 50, 200, to); + particle_trail(1, 500, from, to); + break; + }; +}; + +void hitpush(int target, int damage, dynent *d, dynent *at, vec &from, vec &to) +{ + hit(target, damage, d, at); + vdist(dist, v, from, to); + vmul(v, damage/dist/50); + vadd(d->vel, v); +}; + +void raydamage(dynent *o, vec &from, vec &to, dynent *d, int i) +{ + if(o->state!=CS_ALIVE) return; + int qdam = guns[d->gunselect].damage; + if(d->quadmillis) qdam *= 4; + if(d->monsterstate) qdam /= MONSTERDAMAGEFACTOR; + if(d->gunselect==GUN_SG) + { + int damage = 0; + loop(r, SGRAYS) if(intersect(o, from, sg[r])) damage += qdam; + if(damage) hitpush(i, damage, o, d, from, to); + } + else if(intersect(o, from, to)) hitpush(i, qdam, o, d, from, to); +}; + +void shoot(dynent *d, vec &targ) +{ + int attacktime = lastmillis-d->lastaction; + if(attacktimegunwait) return; + d->gunwait = 0; + if(!d->attacking) return; + d->lastaction = lastmillis; + d->lastattackgun = d->gunselect; + if(!d->ammo[d->gunselect]) { playsoundc(S_NOAMMO); d->gunwait = 250; d->lastattackgun = -1; return; }; + if(d->gunselect) d->ammo[d->gunselect]--; + vec from = d->o; + vec to = targ; + from.z -= 0.2f; // below eye + + vdist(dist, unitv, from, to); + vdiv(unitv, dist); + vec kickback = unitv; + vmul(kickback, guns[d->gunselect].kickamount*-0.01f); + vadd(d->vel, kickback); + if(d->pitch<80.0f) d->pitch += guns[d->gunselect].kickamount*0.05f; + + + if(d->gunselect==GUN_FIST || d->gunselect==GUN_BITE) + { + vmul(unitv, 3); // punch range + to = from; + vadd(to, unitv); + }; + if(d->gunselect==GUN_SG) createrays(from, to); + + if(d->quadmillis && attacktime>200) playsoundc(S_ITEMPUP); + shootv(d->gunselect, from, to, d, true); + if(!d->monsterstate) addmsg(1, 8, SV_SHOT, d->gunselect, (int)(from.x*DMF), (int)(from.y*DMF), (int)(from.z*DMF), (int)(to.x*DMF), (int)(to.y*DMF), (int)(to.z*DMF)); + d->gunwait = guns[d->gunselect].attackdelay; + + if(guns[d->gunselect].projspeed) return; + + loopv(players) + { + dynent *o = players[i]; + if(!o) continue; + raydamage(o, from, to, d, i); + }; + + dvector &v = getmonsters(); + loopv(v) if(v[i]!=d) raydamage(v[i], from, to, d, -2); + + if(d->monsterstate) raydamage(player1, from, to, d, -1); +}; + + DELETED src/world.cpp Index: src/world.cpp ================================================================== --- src/world.cpp +++ /dev/null @@ -1,372 +0,0 @@ -// world.cpp: core map management stuff - -#include "cube.h" - -extern char *entnames[]; // lookup from map entities above to strings - -sqr *world = NULL; -int sfactor, ssize, cubicsize, mipsize; - -header hdr; - -void settag(int tag, int type) // set all cubes with "tag" to space, if tag is 0 then reset ALL tagged cubes according to type -{ - int maxx = 0, maxy = 0, minx = ssize, miny = ssize; - loop(x, ssize) loop(y, ssize) - { - sqr *s = S(x, y); - if(s->tag) - { - if(tag) - { - if(tag==s->tag) s->type = SPACE; - else continue; - } - else - { - s->type = type ? SOLID : SPACE; - }; - if(x>maxx) maxx = x; - if(y>maxy) maxy = y; - if(x=SMALLEST_FACTOR) return; - int lighterr = getvar("lighterror")*3; - sqr *w = wmip[level]; - sqr *v = wmip[level+1]; - int ws = ssize>>level; - int vs = ssize>>(level+1); - block s = b; - if(s.x&1) { s.x--; s.xs++; }; - if(s.y&1) { s.y--; s.ys++; }; - s.xs = (s.xs+1)&~1; - s.ys = (s.ys+1)&~1; - for(int x = s.x; xtype]++; - r->type = SEMISOLID; // cube contains both solid and space, treated specially in the renderer - loopk(MAXTYPE) if(nums[k]==4) r->type = k; - if(!SOLID(r)) - { - int floor = 127, ceil = -128, num = 0; - loopi(4) if(!SOLID(o[i])) - { - num++; - int fh = o[i]->floor; - int ch = o[i]->ceil; - if(r->type==SEMISOLID) - { - if(o[i]->type==FHF) fh -= o[i]->vdelta/4+2; // crap hack, needed for rendering large mips next to hfs - if(o[i]->type==CHF) ch += o[i]->vdelta/4+2; // FIXME: needs to somehow take into account middle vertices on higher mips - }; - if(fhceil) ceil = ch; - }; - r->floor = floor; - r->ceil = ceil; - }; - if(r->type==CORNER) goto mip; // special case: don't ever split even if textures etc are different - r->defer = 1; - if(SOLID(r)) - { - loopi(3) - { - if(o[i]->wtex!=o[3]->wtex) goto c; // on an all solid cube, only thing that needs to be equal for a perfect mip is the wall texture - }; - } - else - { - loopi(3) - { - if(o[i]->type!=o[3]->type - || o[i]->floor!=o[3]->floor - || o[i]->ceil!=o[3]->ceil - || o[i]->ftex!=o[3]->ftex - || o[i]->ctex!=o[3]->ctex - || abs(o[i+1]->r-o[0]->r)>lighterr // perfect mip even if light is not exactly equal - || abs(o[i+1]->g-o[0]->g)>lighterr - || abs(o[i+1]->b-o[0]->b)>lighterr - || o[i]->utex!=o[3]->utex - || o[i]->wtex!=o[3]->wtex) goto c; - }; - if(r->type==CHF || r->type==FHF) // can make a perfect mip out of a hf if slopes lie on one line - { - if(o[0]->vdelta-o[1]->vdelta != o[1]->vdelta-SWS(w,x+2,y,ws)->vdelta - || o[0]->vdelta-o[2]->vdelta != o[2]->vdelta-SWS(w,x+2,y+2,ws)->vdelta - || o[0]->vdelta-o[3]->vdelta != o[3]->vdelta-SWS(w,x,y+2,ws)->vdelta - || o[3]->vdelta-o[2]->vdelta != o[2]->vdelta-SWS(w,x+2,y+1,ws)->vdelta - || o[1]->vdelta-o[2]->vdelta != o[2]->vdelta-SWS(w,x+1,y+2,ws)->vdelta) goto c; - }; - }; - { loopi(4) if(o[i]->defer) goto c; }; // if any of the constituents is not perfect, then this one isn't either - mip: - r->defer = 0; - c:; - }; - s.x /= 2; - s.y /= 2; - s.xs /= 2; - s.ys /= 2; - remip(s, level+1); -}; - -void remipmore(block &b, int level) -{ - block bb = b; - if(bb.x>1) bb.x--; - if(bb.y>1) bb.y--; - if(bb.xso, v); - if(dist32) v1 = 32; - if(!v1) e.attr1 = 16; - if(!v2 && !v3 && !v4) e.attr2 = 255; - break; - - case MAPMODEL: - e.attr4 = e.attr3; - e.attr3 = e.attr2; - case MONSTER: - case TELEDEST: - e.attr2 = (uchar)e.attr1; - case PLAYERSTART: - e.attr1 = (int)player1->yaw; - break; - }; - addmsg(1, 10, SV_EDITENT, ents.length(), type, e.x, e.y, e.z, e.attr1, e.attr2, e.attr3, e.attr4); - ents.add(*((entity *)&e)); // unsafe! - if(type==LIGHT) calclight(); - return &ents.last(); -}; - -void clearents(char *name) -{ - int type = findtype(name); - if(noteditmode() || multiplayer()) return; - loopv(ents) - { - entity &e = ents[i]; - if(e.type==type) e.type = NOTUSED; - }; - if(type==LIGHT) calclight(); -}; - -COMMAND(clearents, ARG_1STR); - -void scalecomp(uchar &c, int intens) -{ - int n = c*intens/100; - if(n>255) n = 255; - c = n; -}; - -void scalelights(int f, int intens) -{ - loopv(ents) - { - entity &e = ents[i]; - if(e.type!=LIGHT) continue; - e.attr1 = e.attr1*f/100; - if(e.attr1<2) e.attr1 = 2; - if(e.attr1>32) e.attr1 = 32; - if(intens) - { - scalecomp(e.attr2, intens); - scalecomp(e.attr3, intens); - scalecomp(e.attr4, intens); - }; - }; - calclight(); -}; - -COMMAND(scalelights, ARG_2INT); - -int findentity(int type, int index) -{ - for(int i = index; i>(i*2); }; -}; - -void empty_world(int factor, bool force) // main empty world creation routine, if passed factor -1 will enlarge old world by 1 -{ - if(!force && noteditmode()) return; - cleardlights(); - pruneundos(); - sqr *oldworld = world; - bool copy = false; - if(oldworld && factor<0) { factor = sfactor+1; copy = true; }; - if(factorLARGEST_FACTOR) factor = LARGEST_FACTOR; - setupworld(factor); - - loop(x,ssize) loop(y,ssize) - { - sqr *s = S(x,y); - s->r = s->g = s->b = 150; - s->ftex = DEFAULT_FLOOR; - s->ctex = DEFAULT_CEIL; - s->wtex = s->utex = DEFAULT_WALL; - s->type = SOLID; - s->floor = 0; - s->ceil = 16; - s->vdelta = 0; - s->defer = 0; - }; - - strncpy(hdr.head, "CUBE", 4); - hdr.version = MAPVERSION; - hdr.headersize = sizeof(header); - hdr.sfactor = sfactor; - - if(copy) - { - loop(x,ssize/2) loop(y,ssize/2) - { - *S(x+ssize/4, y+ssize/4) = *SWS(oldworld, x, y, ssize/2); - }; - loopv(ents) - { - ents[i].x += ssize/4; - ents[i].y += ssize/4; - }; - } - else - { - strn0cpy(hdr.maptitle, "Untitled Map by Unknown", 128); - hdr.waterlevel = -100000; - loopi(15) hdr.reserved[i] = 0; - loopk(3) loopi(256) hdr.texlists[k][i] = i; - ents.setsize(0); - block b = { 8, 8, ssize-16, ssize-16 }; - edittypexy(SPACE, b); - }; - - calclight(); - startmap("base/unnamed"); - if(oldworld) - { - free(oldworld); - toggleedit(); - execute("fullbright 1"); - }; -}; - -void mapenlarge() { empty_world(-1, false); }; -void newmap(int i) { empty_world(i, false); }; - -COMMAND(mapenlarge, ARG_NONE); -COMMAND(newmap, ARG_1INT); -COMMANDN(recalc, calclight, ARG_NONE); -COMMAND(delent, ARG_NONE); -COMMAND(entproperty, ARG_2INT); - ADDED src/world.cxx Index: src/world.cxx ================================================================== --- /dev/null +++ src/world.cxx @@ -0,0 +1,372 @@ +// world.cpp: core map management stuff + +#include "cube.h" + +extern char *entnames[]; // lookup from map entities above to strings + +sqr *world = NULL; +int sfactor, ssize, cubicsize, mipsize; + +header hdr; + +void settag(int tag, int type) // set all cubes with "tag" to space, if tag is 0 then reset ALL tagged cubes according to type +{ + int maxx = 0, maxy = 0, minx = ssize, miny = ssize; + loop(x, ssize) loop(y, ssize) + { + sqr *s = S(x, y); + if(s->tag) + { + if(tag) + { + if(tag==s->tag) s->type = SPACE; + else continue; + } + else + { + s->type = type ? SOLID : SPACE; + }; + if(x>maxx) maxx = x; + if(y>maxy) maxy = y; + if(x=SMALLEST_FACTOR) return; + int lighterr = getvar("lighterror")*3; + sqr *w = wmip[level]; + sqr *v = wmip[level+1]; + int ws = ssize>>level; + int vs = ssize>>(level+1); + block s = b; + if(s.x&1) { s.x--; s.xs++; }; + if(s.y&1) { s.y--; s.ys++; }; + s.xs = (s.xs+1)&~1; + s.ys = (s.ys+1)&~1; + for(int x = s.x; xtype]++; + r->type = SEMISOLID; // cube contains both solid and space, treated specially in the renderer + loopk(MAXTYPE) if(nums[k]==4) r->type = k; + if(!SOLID(r)) + { + int floor = 127, ceil = -128, num = 0; + loopi(4) if(!SOLID(o[i])) + { + num++; + int fh = o[i]->floor; + int ch = o[i]->ceil; + if(r->type==SEMISOLID) + { + if(o[i]->type==FHF) fh -= o[i]->vdelta/4+2; // crap hack, needed for rendering large mips next to hfs + if(o[i]->type==CHF) ch += o[i]->vdelta/4+2; // FIXME: needs to somehow take into account middle vertices on higher mips + }; + if(fhceil) ceil = ch; + }; + r->floor = floor; + r->ceil = ceil; + }; + if(r->type==CORNER) goto mip; // special case: don't ever split even if textures etc are different + r->defer = 1; + if(SOLID(r)) + { + loopi(3) + { + if(o[i]->wtex!=o[3]->wtex) goto c; // on an all solid cube, only thing that needs to be equal for a perfect mip is the wall texture + }; + } + else + { + loopi(3) + { + if(o[i]->type!=o[3]->type + || o[i]->floor!=o[3]->floor + || o[i]->ceil!=o[3]->ceil + || o[i]->ftex!=o[3]->ftex + || o[i]->ctex!=o[3]->ctex + || abs(o[i+1]->r-o[0]->r)>lighterr // perfect mip even if light is not exactly equal + || abs(o[i+1]->g-o[0]->g)>lighterr + || abs(o[i+1]->b-o[0]->b)>lighterr + || o[i]->utex!=o[3]->utex + || o[i]->wtex!=o[3]->wtex) goto c; + }; + if(r->type==CHF || r->type==FHF) // can make a perfect mip out of a hf if slopes lie on one line + { + if(o[0]->vdelta-o[1]->vdelta != o[1]->vdelta-SWS(w,x+2,y,ws)->vdelta + || o[0]->vdelta-o[2]->vdelta != o[2]->vdelta-SWS(w,x+2,y+2,ws)->vdelta + || o[0]->vdelta-o[3]->vdelta != o[3]->vdelta-SWS(w,x,y+2,ws)->vdelta + || o[3]->vdelta-o[2]->vdelta != o[2]->vdelta-SWS(w,x+2,y+1,ws)->vdelta + || o[1]->vdelta-o[2]->vdelta != o[2]->vdelta-SWS(w,x+1,y+2,ws)->vdelta) goto c; + }; + }; + { loopi(4) if(o[i]->defer) goto c; }; // if any of the constituents is not perfect, then this one isn't either + mip: + r->defer = 0; + c:; + }; + s.x /= 2; + s.y /= 2; + s.xs /= 2; + s.ys /= 2; + remip(s, level+1); +}; + +void remipmore(block &b, int level) +{ + block bb = b; + if(bb.x>1) bb.x--; + if(bb.y>1) bb.y--; + if(bb.xso, v); + if(dist32) v1 = 32; + if(!v1) e.attr1 = 16; + if(!v2 && !v3 && !v4) e.attr2 = 255; + break; + + case MAPMODEL: + e.attr4 = e.attr3; + e.attr3 = e.attr2; + case MONSTER: + case TELEDEST: + e.attr2 = (uchar)e.attr1; + case PLAYERSTART: + e.attr1 = (int)player1->yaw; + break; + }; + addmsg(1, 10, SV_EDITENT, ents.length(), type, e.x, e.y, e.z, e.attr1, e.attr2, e.attr3, e.attr4); + ents.add(*((entity *)&e)); // unsafe! + if(type==LIGHT) calclight(); + return &ents.last(); +}; + +void clearents(char *name) +{ + int type = findtype(name); + if(noteditmode() || multiplayer()) return; + loopv(ents) + { + entity &e = ents[i]; + if(e.type==type) e.type = NOTUSED; + }; + if(type==LIGHT) calclight(); +}; + +COMMAND(clearents, ARG_1STR); + +void scalecomp(uchar &c, int intens) +{ + int n = c*intens/100; + if(n>255) n = 255; + c = n; +}; + +void scalelights(int f, int intens) +{ + loopv(ents) + { + entity &e = ents[i]; + if(e.type!=LIGHT) continue; + e.attr1 = e.attr1*f/100; + if(e.attr1<2) e.attr1 = 2; + if(e.attr1>32) e.attr1 = 32; + if(intens) + { + scalecomp(e.attr2, intens); + scalecomp(e.attr3, intens); + scalecomp(e.attr4, intens); + }; + }; + calclight(); +}; + +COMMAND(scalelights, ARG_2INT); + +int findentity(int type, int index) +{ + for(int i = index; i>(i*2); }; +}; + +void empty_world(int factor, bool force) // main empty world creation routine, if passed factor -1 will enlarge old world by 1 +{ + if(!force && noteditmode()) return; + cleardlights(); + pruneundos(); + sqr *oldworld = world; + bool copy = false; + if(oldworld && factor<0) { factor = sfactor+1; copy = true; }; + if(factorLARGEST_FACTOR) factor = LARGEST_FACTOR; + setupworld(factor); + + loop(x,ssize) loop(y,ssize) + { + sqr *s = S(x,y); + s->r = s->g = s->b = 150; + s->ftex = DEFAULT_FLOOR; + s->ctex = DEFAULT_CEIL; + s->wtex = s->utex = DEFAULT_WALL; + s->type = SOLID; + s->floor = 0; + s->ceil = 16; + s->vdelta = 0; + s->defer = 0; + }; + + strncpy(hdr.head, "CUBE", 4); + hdr.version = MAPVERSION; + hdr.headersize = sizeof(header); + hdr.sfactor = sfactor; + + if(copy) + { + loop(x,ssize/2) loop(y,ssize/2) + { + *S(x+ssize/4, y+ssize/4) = *SWS(oldworld, x, y, ssize/2); + }; + loopv(ents) + { + ents[i].x += ssize/4; + ents[i].y += ssize/4; + }; + } + else + { + strn0cpy(hdr.maptitle, "Untitled Map by Unknown", 128); + hdr.waterlevel = -100000; + loopi(15) hdr.reserved[i] = 0; + loopk(3) loopi(256) hdr.texlists[k][i] = i; + ents.setsize(0); + block b = { 8, 8, ssize-16, ssize-16 }; + edittypexy(SPACE, b); + }; + + calclight(); + startmap("base/unnamed"); + if(oldworld) + { + free(oldworld); + toggleedit(); + execute("fullbright 1"); + }; +}; + +void mapenlarge() { empty_world(-1, false); }; +void newmap(int i) { empty_world(i, false); }; + +COMMAND(mapenlarge, ARG_NONE); +COMMAND(newmap, ARG_1INT); +COMMANDN(recalc, calclight, ARG_NONE); +COMMAND(delent, ARG_NONE); +COMMAND(entproperty, ARG_2INT); + DELETED src/worldio.cpp Index: src/worldio.cpp ================================================================== --- src/worldio.cpp +++ /dev/null @@ -1,328 +0,0 @@ -// worldio.cpp: loading & saving of maps and savegames - -#include "cube.h" - -void backup(char *name, char *backupname) -{ - remove(backupname); - rename(name, backupname); -}; - -string cgzname, bakname, pcfname, mcfname; - -void setnames(char *name) -{ - string pakname, mapname; - char *slash = strpbrk(name, "/\\"); - if(slash) - { - strn0cpy(pakname, name, slash-name+1); - strcpy_s(mapname, slash+1); - } - else - { - strcpy_s(pakname, "base"); - strcpy_s(mapname, name); - }; - sprintf_s(cgzname)("packages/%s/%s.cgz", pakname, mapname); - sprintf_s(bakname)("packages/%s/%s_%d.BAK", pakname, mapname, lastmillis); - sprintf_s(pcfname)("packages/%s/package.cfg", pakname); - sprintf_s(mcfname)("packages/%s/%s.cfg", pakname, mapname); - - path(cgzname); - path(bakname); -}; - -// the optimize routines below are here to reduce the detrimental effects of messy mapping by -// setting certain properties (vdeltas and textures) to neighbouring values wherever there is no -// visible difference. This allows the mipmapper to generate more efficient mips. -// the reason it is done on save is to reduce the amount spend in the mipmapper (as that is done -// in realtime). - -inline bool nhf(sqr *s) { return s->type!=FHF && s->type!=CHF; }; - -void voptimize() // reset vdeltas on non-hf cubes -{ - loop(x, ssize) loop(y, ssize) - { - sqr *s = S(x, y); - if(x && y) { if(nhf(s) && nhf(S(x-1, y)) && nhf(S(x-1, y-1)) && nhf(S(x, y-1))) s->vdelta = 0; } - else s->vdelta = 0; - }; -}; - -void topt(sqr *s, bool &wf, bool &uf, int &wt, int &ut) -{ - sqr *o[4]; - o[0] = SWS(s,0,-1,ssize); - o[1] = SWS(s,0,1,ssize); - o[2] = SWS(s,1,0,ssize); - o[3] = SWS(s,-1,0,ssize); - wf = true; - uf = true; - if(SOLID(s)) - { - loopi(4) if(!SOLID(o[i])) - { - wf = false; - wt = s->wtex; - ut = s->utex; - return; - }; - } - else - { - loopi(4) if(!SOLID(o[i])) - { - if(o[i]->floorfloor) { wt = s->wtex; wf = false; }; - if(o[i]->ceil>s->ceil) { ut = s->utex; uf = false; }; - }; - }; -}; - -void toptimize() // FIXME: only does 2x2, make atleast for 4x4 also -{ - bool wf[4], uf[4]; - sqr *s[4]; - for(int x = 2; xwtex, ut = s[0]->utex; - topt(s[0], wf[0], uf[0], wt, ut); - topt(s[1] = SWS(s[0],0,1,ssize), wf[1], uf[1], wt, ut); - topt(s[2] = SWS(s[0],1,1,ssize), wf[2], uf[2], wt, ut); - topt(s[3] = SWS(s[0],1,0,ssize), wf[3], uf[3], wt, ut); - loopi(4) - { - if(wf[i]) s[i]->wtex = wt; - if(uf[i]) s[i]->utex = ut; - }; - }; -}; - -// these two are used by getmap/sendmap.. transfers compressed maps directly - -void writemap(char *mname, int msize, uchar *mdata) -{ - setnames(mname); - backup(cgzname, bakname); - FILE *f = fopen(cgzname, "wb"); - if(!f) { conoutf("could not write map to %s", cgzname); return; }; - fwrite(mdata, 1, msize, f); - fclose(f); - conoutf("wrote map %s as file %s", mname, cgzname); -} - -uchar *readmap(char *mname, int *msize) -{ - setnames(mname); - uchar *mdata = (uchar *)loadfile(cgzname, msize); - if(!mdata) { conoutf("could not read map %s", cgzname); return NULL; }; - return mdata; -} - -// save map as .cgz file. uses 2 layers of compression: first does simple run-length -// encoding and leaves out data for certain kinds of cubes, then zlib removes the -// last bits of redundancy. Both passes contribute greatly to the miniscule map sizes. - -void save_world(char *mname) -{ - resettagareas(); // wouldn't be able to reproduce tagged areas otherwise - voptimize(); - toptimize(); - if(!*mname) mname = getclientmap(); - setnames(mname); - backup(cgzname, bakname); - gzFile f = gzopen(cgzname, "wb9"); - if(!f) { conoutf("could not write map to %s", cgzname); return; }; - hdr.version = MAPVERSION; - hdr.numents = 0; - loopv(ents) if(ents[i].type!=NOTUSED) hdr.numents++; - header tmp = hdr; - endianswap(&tmp.version, sizeof(int), 4); - endianswap(&tmp.waterlevel, sizeof(int), 16); - gzwrite(f, &tmp, sizeof(header)); - loopv(ents) - { - if(ents[i].type!=NOTUSED) - { - entity tmp = ents[i]; - endianswap(&tmp, sizeof(short), 4); - gzwrite(f, &tmp, sizeof(persistent_entity)); - }; - }; - sqr *t = NULL; - int sc = 0; - #define spurge while(sc) { gzputc(f, 255); if(sc>255) { gzputc(f, 255); sc -= 255; } else { gzputc(f, sc); sc = 0; } }; - loopk(cubicsize) - { - sqr *s = &world[k]; - #define c(f) (s->f==t->f) - // 4 types of blocks, to compress a bit: - // 255 (2): same as previous block + count - // 254 (3): same as previous, except light // deprecated - // SOLID (5) - // anything else (9) - - if(SOLID(s)) - { - if(t && c(type) && c(wtex) && c(vdelta)) - { - sc++; - } - else - { - spurge; - gzputc(f, s->type); - gzputc(f, s->wtex); - gzputc(f, s->vdelta); - }; - } - else - { - if(t && c(type) && c(floor) && c(ceil) && c(ctex) && c(ftex) && c(utex) && c(wtex) && c(vdelta) && c(tag)) - { - sc++; - } - else - { - spurge; - gzputc(f, s->type); - gzputc(f, s->floor); - gzputc(f, s->ceil); - gzputc(f, s->wtex); - gzputc(f, s->ftex); - gzputc(f, s->ctex); - gzputc(f, s->vdelta); - gzputc(f, s->utex); - gzputc(f, s->tag); - }; - }; - t = s; - }; - spurge; - gzclose(f); - conoutf("wrote map file %s", cgzname); - settagareas(); -}; - -void load_world(char *mname) // still supports all map formats that have existed since the earliest cube betas! -{ - stopifrecording(); - cleardlights(); - pruneundos(); - setnames(mname); - gzFile f = gzopen(cgzname, "rb9"); - if(!f) { conoutf("could not read map %s", cgzname); return; }; - gzread(f, &hdr, sizeof(header)-sizeof(int)*16); - endianswap(&hdr.version, sizeof(int), 4); - if(strncmp(hdr.head, "CUBE", 4)!=0) fatal("while reading map: header malformatted"); - if(hdr.version>MAPVERSION) fatal("this map requires a newer version of cube"); - if(sfactorLARGEST_FACTOR) fatal("illegal map size"); - if(hdr.version>=4) - { - gzread(f, &hdr.waterlevel, sizeof(int)*16); - endianswap(&hdr.waterlevel, sizeof(int), 16); - } - else - { - hdr.waterlevel = -100000; - }; - ents.setsize(0); - loopi(hdr.numents) - { - entity &e = ents.add(); - gzread(f, &e, sizeof(persistent_entity)); - endianswap(&e, sizeof(short), 4); - e.spawned = false; - if(e.type==LIGHT) - { - if(!e.attr2) e.attr2 = 255; // needed for MAPVERSION<=2 - if(e.attr1>32) e.attr1 = 32; // 12_03 and below - }; - }; - free(world); - setupworld(hdr.sfactor); - char texuse[256]; - loopi(256) texuse[i] = 0; - sqr *t = NULL; - loopk(cubicsize) - { - sqr *s = &world[k]; - int type = gzgetc(f); - switch(type) - { - case 255: - { - int n = gzgetc(f); - for(int i = 0; ir = s->g = s->b = gzgetc(f); - gzgetc(f); - break; - }; - case SOLID: - { - s->type = SOLID; - s->wtex = gzgetc(f); - s->vdelta = gzgetc(f); - if(hdr.version<=2) { gzgetc(f); gzgetc(f); }; - s->ftex = DEFAULT_FLOOR; - s->ctex = DEFAULT_CEIL; - s->utex = s->wtex; - s->tag = 0; - s->floor = 0; - s->ceil = 16; - break; - }; - default: - { - if(type<0 || type>=MAXTYPE) - { - sprintf_sd(t)("%d @ %d", type, k); - fatal("while reading map: type out of range: ", t); - }; - s->type = type; - s->floor = gzgetc(f); - s->ceil = gzgetc(f); - if(s->floor>=s->ceil) s->floor = s->ceil-1; // for pre 12_13 - s->wtex = gzgetc(f); - s->ftex = gzgetc(f); - s->ctex = gzgetc(f); - if(hdr.version<=2) { gzgetc(f); gzgetc(f); }; - s->vdelta = gzgetc(f); - s->utex = (hdr.version>=2) ? gzgetc(f) : s->wtex; - s->tag = (hdr.version>=5) ? gzgetc(f) : 0; - s->type = type; - }; - }; - s->defer = 0; - t = s; - texuse[s->wtex] = 1; - if(!SOLID(s)) texuse[s->utex] = texuse[s->ftex] = texuse[s->ctex] = 1; - }; - gzclose(f); - calclight(); - settagareas(); - int xs, ys; - loopi(256) if(texuse) lookuptexture(i, xs, ys); - conoutf("read map %s (%d milliseconds)", cgzname, SDL_GetTicks()-lastmillis); - conoutf("%s", hdr.maptitle); - startmap(mname); - loopl(256) - { - sprintf_sd(aliasname)("level_trigger_%d", l); // can this be done smarter? - if(identexists(aliasname)) alias(aliasname, ""); - }; - execfile("data/default_map_settings.cfg"); - execfile(pcfname); - execfile(mcfname); -}; - -COMMANDN(savemap, save_world, ARG_1STR); - ADDED src/worldio.cxx Index: src/worldio.cxx ================================================================== --- /dev/null +++ src/worldio.cxx @@ -0,0 +1,328 @@ +// worldio.cpp: loading & saving of maps and savegames + +#include "cube.h" + +void backup(char *name, char *backupname) +{ + remove(backupname); + rename(name, backupname); +}; + +string cgzname, bakname, pcfname, mcfname; + +void setnames(char *name) +{ + string pakname, mapname; + char *slash = strpbrk(name, "/\\"); + if(slash) + { + strn0cpy(pakname, name, slash-name+1); + strcpy_s(mapname, slash+1); + } + else + { + strcpy_s(pakname, "base"); + strcpy_s(mapname, name); + }; + sprintf_s(cgzname)("packages/%s/%s.cgz", pakname, mapname); + sprintf_s(bakname)("packages/%s/%s_%d.BAK", pakname, mapname, lastmillis); + sprintf_s(pcfname)("packages/%s/package.cfg", pakname); + sprintf_s(mcfname)("packages/%s/%s.cfg", pakname, mapname); + + path(cgzname); + path(bakname); +}; + +// the optimize routines below are here to reduce the detrimental effects of messy mapping by +// setting certain properties (vdeltas and textures) to neighbouring values wherever there is no +// visible difference. This allows the mipmapper to generate more efficient mips. +// the reason it is done on save is to reduce the amount spend in the mipmapper (as that is done +// in realtime). + +inline bool nhf(sqr *s) { return s->type!=FHF && s->type!=CHF; }; + +void voptimize() // reset vdeltas on non-hf cubes +{ + loop(x, ssize) loop(y, ssize) + { + sqr *s = S(x, y); + if(x && y) { if(nhf(s) && nhf(S(x-1, y)) && nhf(S(x-1, y-1)) && nhf(S(x, y-1))) s->vdelta = 0; } + else s->vdelta = 0; + }; +}; + +void topt(sqr *s, bool &wf, bool &uf, int &wt, int &ut) +{ + sqr *o[4]; + o[0] = SWS(s,0,-1,ssize); + o[1] = SWS(s,0,1,ssize); + o[2] = SWS(s,1,0,ssize); + o[3] = SWS(s,-1,0,ssize); + wf = true; + uf = true; + if(SOLID(s)) + { + loopi(4) if(!SOLID(o[i])) + { + wf = false; + wt = s->wtex; + ut = s->utex; + return; + }; + } + else + { + loopi(4) if(!SOLID(o[i])) + { + if(o[i]->floorfloor) { wt = s->wtex; wf = false; }; + if(o[i]->ceil>s->ceil) { ut = s->utex; uf = false; }; + }; + }; +}; + +void toptimize() // FIXME: only does 2x2, make atleast for 4x4 also +{ + bool wf[4], uf[4]; + sqr *s[4]; + for(int x = 2; xwtex, ut = s[0]->utex; + topt(s[0], wf[0], uf[0], wt, ut); + topt(s[1] = SWS(s[0],0,1,ssize), wf[1], uf[1], wt, ut); + topt(s[2] = SWS(s[0],1,1,ssize), wf[2], uf[2], wt, ut); + topt(s[3] = SWS(s[0],1,0,ssize), wf[3], uf[3], wt, ut); + loopi(4) + { + if(wf[i]) s[i]->wtex = wt; + if(uf[i]) s[i]->utex = ut; + }; + }; +}; + +// these two are used by getmap/sendmap.. transfers compressed maps directly + +void writemap(char *mname, int msize, uchar *mdata) +{ + setnames(mname); + backup(cgzname, bakname); + FILE *f = fopen(cgzname, "wb"); + if(!f) { conoutf("could not write map to %s", cgzname); return; }; + fwrite(mdata, 1, msize, f); + fclose(f); + conoutf("wrote map %s as file %s", mname, cgzname); +} + +uchar *readmap(char *mname, int *msize) +{ + setnames(mname); + uchar *mdata = (uchar *)loadfile(cgzname, msize); + if(!mdata) { conoutf("could not read map %s", cgzname); return NULL; }; + return mdata; +} + +// save map as .cgz file. uses 2 layers of compression: first does simple run-length +// encoding and leaves out data for certain kinds of cubes, then zlib removes the +// last bits of redundancy. Both passes contribute greatly to the miniscule map sizes. + +void save_world(char *mname) +{ + resettagareas(); // wouldn't be able to reproduce tagged areas otherwise + voptimize(); + toptimize(); + if(!*mname) mname = getclientmap(); + setnames(mname); + backup(cgzname, bakname); + gzFile f = gzopen(cgzname, "wb9"); + if(!f) { conoutf("could not write map to %s", cgzname); return; }; + hdr.version = MAPVERSION; + hdr.numents = 0; + loopv(ents) if(ents[i].type!=NOTUSED) hdr.numents++; + header tmp = hdr; + endianswap(&tmp.version, sizeof(int), 4); + endianswap(&tmp.waterlevel, sizeof(int), 16); + gzwrite(f, &tmp, sizeof(header)); + loopv(ents) + { + if(ents[i].type!=NOTUSED) + { + entity tmp = ents[i]; + endianswap(&tmp, sizeof(short), 4); + gzwrite(f, &tmp, sizeof(persistent_entity)); + }; + }; + sqr *t = NULL; + int sc = 0; + #define spurge while(sc) { gzputc(f, 255); if(sc>255) { gzputc(f, 255); sc -= 255; } else { gzputc(f, sc); sc = 0; } }; + loopk(cubicsize) + { + sqr *s = &world[k]; + #define c(f) (s->f==t->f) + // 4 types of blocks, to compress a bit: + // 255 (2): same as previous block + count + // 254 (3): same as previous, except light // deprecated + // SOLID (5) + // anything else (9) + + if(SOLID(s)) + { + if(t && c(type) && c(wtex) && c(vdelta)) + { + sc++; + } + else + { + spurge; + gzputc(f, s->type); + gzputc(f, s->wtex); + gzputc(f, s->vdelta); + }; + } + else + { + if(t && c(type) && c(floor) && c(ceil) && c(ctex) && c(ftex) && c(utex) && c(wtex) && c(vdelta) && c(tag)) + { + sc++; + } + else + { + spurge; + gzputc(f, s->type); + gzputc(f, s->floor); + gzputc(f, s->ceil); + gzputc(f, s->wtex); + gzputc(f, s->ftex); + gzputc(f, s->ctex); + gzputc(f, s->vdelta); + gzputc(f, s->utex); + gzputc(f, s->tag); + }; + }; + t = s; + }; + spurge; + gzclose(f); + conoutf("wrote map file %s", cgzname); + settagareas(); +}; + +void load_world(char *mname) // still supports all map formats that have existed since the earliest cube betas! +{ + stopifrecording(); + cleardlights(); + pruneundos(); + setnames(mname); + gzFile f = gzopen(cgzname, "rb9"); + if(!f) { conoutf("could not read map %s", cgzname); return; }; + gzread(f, &hdr, sizeof(header)-sizeof(int)*16); + endianswap(&hdr.version, sizeof(int), 4); + if(strncmp(hdr.head, "CUBE", 4)!=0) fatal("while reading map: header malformatted"); + if(hdr.version>MAPVERSION) fatal("this map requires a newer version of cube"); + if(sfactorLARGEST_FACTOR) fatal("illegal map size"); + if(hdr.version>=4) + { + gzread(f, &hdr.waterlevel, sizeof(int)*16); + endianswap(&hdr.waterlevel, sizeof(int), 16); + } + else + { + hdr.waterlevel = -100000; + }; + ents.setsize(0); + loopi(hdr.numents) + { + entity &e = ents.add(); + gzread(f, &e, sizeof(persistent_entity)); + endianswap(&e, sizeof(short), 4); + e.spawned = false; + if(e.type==LIGHT) + { + if(!e.attr2) e.attr2 = 255; // needed for MAPVERSION<=2 + if(e.attr1>32) e.attr1 = 32; // 12_03 and below + }; + }; + free(world); + setupworld(hdr.sfactor); + char texuse[256]; + loopi(256) texuse[i] = 0; + sqr *t = NULL; + loopk(cubicsize) + { + sqr *s = &world[k]; + int type = gzgetc(f); + switch(type) + { + case 255: + { + int n = gzgetc(f); + for(int i = 0; ir = s->g = s->b = gzgetc(f); + gzgetc(f); + break; + }; + case SOLID: + { + s->type = SOLID; + s->wtex = gzgetc(f); + s->vdelta = gzgetc(f); + if(hdr.version<=2) { gzgetc(f); gzgetc(f); }; + s->ftex = DEFAULT_FLOOR; + s->ctex = DEFAULT_CEIL; + s->utex = s->wtex; + s->tag = 0; + s->floor = 0; + s->ceil = 16; + break; + }; + default: + { + if(type<0 || type>=MAXTYPE) + { + sprintf_sd(t)("%d @ %d", type, k); + fatal("while reading map: type out of range: ", t); + }; + s->type = type; + s->floor = gzgetc(f); + s->ceil = gzgetc(f); + if(s->floor>=s->ceil) s->floor = s->ceil-1; // for pre 12_13 + s->wtex = gzgetc(f); + s->ftex = gzgetc(f); + s->ctex = gzgetc(f); + if(hdr.version<=2) { gzgetc(f); gzgetc(f); }; + s->vdelta = gzgetc(f); + s->utex = (hdr.version>=2) ? gzgetc(f) : s->wtex; + s->tag = (hdr.version>=5) ? gzgetc(f) : 0; + s->type = type; + }; + }; + s->defer = 0; + t = s; + texuse[s->wtex] = 1; + if(!SOLID(s)) texuse[s->utex] = texuse[s->ftex] = texuse[s->ctex] = 1; + }; + gzclose(f); + calclight(); + settagareas(); + int xs, ys; + loopi(256) if(texuse) lookuptexture(i, xs, ys); + conoutf("read map %s (%d milliseconds)", cgzname, SDL_GetTicks()-lastmillis); + conoutf("%s", hdr.maptitle); + startmap(mname); + loopl(256) + { + sprintf_sd(aliasname)("level_trigger_%d", l); // can this be done smarter? + if(identexists(aliasname)) alias(aliasname, ""); + }; + execfile("data/default_map_settings.cfg"); + execfile(pcfname); + execfile(mcfname); +}; + +COMMANDN(savemap, save_world, ARG_1STR); + DELETED src/worldlight.cpp Index: src/worldlight.cpp ================================================================== --- src/worldlight.cpp +++ /dev/null @@ -1,214 +0,0 @@ -// worldlight.cpp - -#include "cube.h" - -extern bool hasoverbright; - -VAR(lightscale,1,4,100); - -void lightray(float bx, float by, persistent_entity &light) // done in realtime, needs to be fast -{ - float lx = light.x+(rnd(21)-10)*0.1f; - float ly = light.y+(rnd(21)-10)*0.1f; - float dx = bx-lx; - float dy = by-ly; - float dist = (float)sqrt(dx*dx+dy*dy); - if(dist<1.0f) return; - int reach = light.attr1; - int steps = (int)(reach*reach*1.6f/dist); // can change this for speedup/quality? - const int PRECBITS = 12; - const float PRECF = 4096.0f; - int x = (int)(lx*PRECF); - int y = (int)(ly*PRECF); - int l = light.attr2<>PRECBITS, y>>PRECBITS)) return; - - int g = light.attr3<>PRECBITS, y>>PRECBITS); - int tl = (l>>PRECBITS)+s->r; - s->r = tl>255 ? 255 : tl; - tl = (g>>PRECBITS)+s->g; - s->g = tl>255 ? 255 : tl; - tl = (b>>PRECBITS)+s->b; - s->b = tl>255 ? 255 : tl; - if(SOLID(s)) return; - x += stepx; - y += stepy; - l -= stepl; - g -= stepg; - b -= stepb; - stepl -= 25; - stepg -= 25; - stepb -= 25; - }; - } - else // white light, special optimized version - { - int dimness = rnd((255-light.attr2)/16+1); - x += stepx*dimness; - y += stepy*dimness; - - if(OUTBORD(x>>PRECBITS, y>>PRECBITS)) return; - - loopi(steps) - { - sqr *s = S(x>>PRECBITS, y>>PRECBITS); - int tl = (l>>PRECBITS)+s->r; - s->r = s->g = s->b = tl>255 ? 255 : tl; - if(SOLID(s)) return; - x += stepx; - y += stepy; - l -= stepl; - stepl -= 25; - }; - }; - } - else // the old (white) light code, here for the few people with old video cards that don't support overbright - { - loopi(steps) - { - sqr *s = S(x>>PRECBITS, y>>PRECBITS); - int light = l>>PRECBITS; - if(light>s->r) s->r = s->g = s->b = (uchar)light; - if(SOLID(s)) return; - x += stepx; - y += stepy; - l -= stepl; - }; - }; - -}; - -void calclightsource(persistent_entity &l) -{ - int reach = l.attr1; - int sx = l.x-reach; - int ex = l.x+reach; - int sy = l.y-reach; - int ey = l.y+reach; - - rndreset(); - - const float s = 0.8f; - - for(float sx2 = (float)sx; sx2<=ex; sx2+=s*2) { lightray(sx2, (float)sy, l); lightray(sx2, (float)ey, l); }; - for(float sy2 = sy+s; sy2<=ey-s; sy2+=s*2) { lightray((float)sx, sy2, l); lightray((float)ex, sy2, l); }; - - rndtime(); -}; - -void postlightarea(block &a) // median filter, smooths out random noise in light and makes it more mipable -{ - loop(x,a.xs) loop(y,a.ys) // assumes area not on edge of world - { - sqr *s = S(x+a.x,y+a.y); - #define median(m) s->m = (s->m*2 + SW(s,1,0)->m*2 + SW(s,0,1)->m*2 \ - + SW(s,-1,0)->m*2 + SW(s,0,-1)->m*2 \ - + SW(s,1,1)->m + SW(s,1,-1)->m \ - + SW(s,-1,1)->m + SW(s,-1,-1)->m)/14; // median is 4/2/1 instead - median(r); - median(g); - median(b); - }; - - remip(a); -}; - -void calclight() -{ - loop(x,ssize) loop(y,ssize) - { - sqr *s = S(x,y); - s->r = s->g = s->b = 10; - }; - - loopv(ents) - { - entity &e = ents[i]; - if(e.type==LIGHT) calclightsource(e); - }; - - block b = { 1, 1, ssize-2, ssize-2 }; - postlightarea(b); - setvar("fullbright", 0); -}; - -VARP(dynlight, 0, 16, 32); - -vector dlights; - -void cleardlights() -{ - while(!dlights.empty()) - { - block *backup = dlights.pop(); - blockpaste(*backup); - free(backup); - }; -}; - -void dodynlight(vec &vold, vec &v, int reach, int strength, dynent *owner) -{ - if(!reach) reach = dynlight; - if(owner->monsterstate) reach = reach/2; - if(!reach) return; - if(v.x<0 || v.y<0 || v.x>ssize || v.y>ssize) return; - - int creach = reach+16; // dependant on lightray random offsets! - block b = { (int)v.x-creach, (int)v.y-creach, creach*2+1, creach*2+1 }; - - if(b.x<1) b.x = 1; - if(b.y<1) b.y = 1; - if(b.xs+b.x>ssize-2) b.xs = ssize-2-b.x; - if(b.ys+b.y>ssize-2) b.ys = ssize-2-b.y; - - dlights.add(blockcopy(b)); // backup area before rendering in dynlight - - persistent_entity l = { (int)v.x, (int)v.y, (int)v.z, reach, LIGHT, strength, 0, 0 }; - calclightsource(l); - postlightarea(b); -}; - -// utility functions also used by editing code - -block *blockcopy(block &s) -{ - block *b = (block *)alloc(sizeof(block)+s.xs*s.ys*sizeof(sqr)); - *b = s; - sqr *q = (sqr *)(b+1); - for(int x = s.x; x>PRECBITS, y>>PRECBITS)) return; + + int g = light.attr3<>PRECBITS, y>>PRECBITS); + int tl = (l>>PRECBITS)+s->r; + s->r = tl>255 ? 255 : tl; + tl = (g>>PRECBITS)+s->g; + s->g = tl>255 ? 255 : tl; + tl = (b>>PRECBITS)+s->b; + s->b = tl>255 ? 255 : tl; + if(SOLID(s)) return; + x += stepx; + y += stepy; + l -= stepl; + g -= stepg; + b -= stepb; + stepl -= 25; + stepg -= 25; + stepb -= 25; + }; + } + else // white light, special optimized version + { + int dimness = rnd((255-light.attr2)/16+1); + x += stepx*dimness; + y += stepy*dimness; + + if(OUTBORD(x>>PRECBITS, y>>PRECBITS)) return; + + loopi(steps) + { + sqr *s = S(x>>PRECBITS, y>>PRECBITS); + int tl = (l>>PRECBITS)+s->r; + s->r = s->g = s->b = tl>255 ? 255 : tl; + if(SOLID(s)) return; + x += stepx; + y += stepy; + l -= stepl; + stepl -= 25; + }; + }; + } + else // the old (white) light code, here for the few people with old video cards that don't support overbright + { + loopi(steps) + { + sqr *s = S(x>>PRECBITS, y>>PRECBITS); + int light = l>>PRECBITS; + if(light>s->r) s->r = s->g = s->b = (uchar)light; + if(SOLID(s)) return; + x += stepx; + y += stepy; + l -= stepl; + }; + }; + +}; + +void calclightsource(persistent_entity &l) +{ + int reach = l.attr1; + int sx = l.x-reach; + int ex = l.x+reach; + int sy = l.y-reach; + int ey = l.y+reach; + + rndreset(); + + const float s = 0.8f; + + for(float sx2 = (float)sx; sx2<=ex; sx2+=s*2) { lightray(sx2, (float)sy, l); lightray(sx2, (float)ey, l); }; + for(float sy2 = sy+s; sy2<=ey-s; sy2+=s*2) { lightray((float)sx, sy2, l); lightray((float)ex, sy2, l); }; + + rndtime(); +}; + +void postlightarea(block &a) // median filter, smooths out random noise in light and makes it more mipable +{ + loop(x,a.xs) loop(y,a.ys) // assumes area not on edge of world + { + sqr *s = S(x+a.x,y+a.y); + #define median(m) s->m = (s->m*2 + SW(s,1,0)->m*2 + SW(s,0,1)->m*2 \ + + SW(s,-1,0)->m*2 + SW(s,0,-1)->m*2 \ + + SW(s,1,1)->m + SW(s,1,-1)->m \ + + SW(s,-1,1)->m + SW(s,-1,-1)->m)/14; // median is 4/2/1 instead + median(r); + median(g); + median(b); + }; + + remip(a); +}; + +void calclight() +{ + loop(x,ssize) loop(y,ssize) + { + sqr *s = S(x,y); + s->r = s->g = s->b = 10; + }; + + loopv(ents) + { + entity &e = ents[i]; + if(e.type==LIGHT) calclightsource(e); + }; + + block b = { 1, 1, ssize-2, ssize-2 }; + postlightarea(b); + setvar("fullbright", 0); +}; + +VARP(dynlight, 0, 16, 32); + +vector dlights; + +void cleardlights() +{ + while(!dlights.empty()) + { + block *backup = dlights.pop(); + blockpaste(*backup); + free(backup); + }; +}; + +void dodynlight(vec &vold, vec &v, int reach, int strength, dynent *owner) +{ + if(!reach) reach = dynlight; + if(owner->monsterstate) reach = reach/2; + if(!reach) return; + if(v.x<0 || v.y<0 || v.x>ssize || v.y>ssize) return; + + int creach = reach+16; // dependant on lightray random offsets! + block b = { (int)v.x-creach, (int)v.y-creach, creach*2+1, creach*2+1 }; + + if(b.x<1) b.x = 1; + if(b.y<1) b.y = 1; + if(b.xs+b.x>ssize-2) b.xs = ssize-2-b.x; + if(b.ys+b.y>ssize-2) b.ys = ssize-2-b.y; + + dlights.add(blockcopy(b)); // backup area before rendering in dynlight + + persistent_entity l = { (int)v.x, (int)v.y, (int)v.z, reach, LIGHT, strength, 0, 0 }; + calclightsource(l); + postlightarea(b); +}; + +// utility functions also used by editing code + +block *blockcopy(block &s) +{ + block *b = (block *)alloc(sizeof(block)+s.xs*s.ys*sizeof(sqr)); + *b = s; + sqr *q = (sqr *)(b+1); + for(int x = s.x; xpitch); - float af = getvar("fov")/2+apitch/1.5f+3; - float byaw = (player1->yaw-90+af)/360*PI2; - float syaw = (player1->yaw-90-af)/360*PI2; - - loopi(NUMRAYS) - { - float angle = i*PI2/NUMRAYS; - if((apitch>45 // must be bigger if fov>120 - || (anglesyaw) - || (anglesyaw-PI2) - || (anglesyaw+PI2)) - && !OUTBORD(vx, vy) - && !SOLID(S(fast_f2nat(vx), fast_f2nat(vy)))) // try to avoid tracing ray if outside of frustrum - { - float ray = i*8/(float)NUMRAYS; - float dx, dy; - if(ray>1 && ray<3) { dx = -(ray-2); dy = 1; } - else if(ray>=3 && ray<5) { dx = -1; dy = -(ray-4); } - else if(ray>=5 && ray<7) { dx = ray-6; dy = -1; } - else { dx = 1; dy = ray>4 ? ray-8 : ray; }; - float sx = vx; - float sy = vy; - for(;;) - { - sx += dx; - sy += dy; - if(SOLID(S(fast_f2nat(sx), fast_f2nat(sy)))) // 90% of time spend in this function is on this line - { - rdist[i] = (float)(fabs(sx-vx)+fabs(sy-vy)); - break; - }; - }; - } - else - { - rdist[i] = 2; - }; - }; -}; - -// test occlusion for a cube... one of the most computationally expensive functions in the engine -// as its done for every cube and entity, but its effect is more than worth it! - -inline float ca(float x, float y) { return x>y ? y/x : 2-x/y; }; -inline float ma(float x, float y) { return x==0 ? (y>0 ? 2 : -2) : y/x; }; - -int isoccluded(float vx, float vy, float cx, float cy, float csize) // v = viewer, c = cube to test -{ - if(!ocull) return 0; - - float nx = vx, ny = vy; // n = point on the border of the cube that is closest to v - if(nxcx+csize) nx = cx+csize; - if(nycy+csize) ny = cy+csize; - float xdist = (float)fabs(nx-vx); - float ydist = (float)fabs(ny-vy); - if(xdist>odist || ydist>odist) return 2; - float dist = xdist+ydist-1; // 1 needed? - - // ABC - // D E - // FGH - - // - check middle cube? BG - - // find highest and lowest angle in the occlusion map that this cube spans, based on its most left and right - // points on the border from the viewer pov... I see no easier way to do this than this silly code below - - float h, l; - if(cx<=vx) // ABDFG - { - if(cx+csizepitch); + float af = getvar("fov")/2+apitch/1.5f+3; + float byaw = (player1->yaw-90+af)/360*PI2; + float syaw = (player1->yaw-90-af)/360*PI2; + + loopi(NUMRAYS) + { + float angle = i*PI2/NUMRAYS; + if((apitch>45 // must be bigger if fov>120 + || (anglesyaw) + || (anglesyaw-PI2) + || (anglesyaw+PI2)) + && !OUTBORD(vx, vy) + && !SOLID(S(fast_f2nat(vx), fast_f2nat(vy)))) // try to avoid tracing ray if outside of frustrum + { + float ray = i*8/(float)NUMRAYS; + float dx, dy; + if(ray>1 && ray<3) { dx = -(ray-2); dy = 1; } + else if(ray>=3 && ray<5) { dx = -1; dy = -(ray-4); } + else if(ray>=5 && ray<7) { dx = ray-6; dy = -1; } + else { dx = 1; dy = ray>4 ? ray-8 : ray; }; + float sx = vx; + float sy = vy; + for(;;) + { + sx += dx; + sy += dy; + if(SOLID(S(fast_f2nat(sx), fast_f2nat(sy)))) // 90% of time spend in this function is on this line + { + rdist[i] = (float)(fabs(sx-vx)+fabs(sy-vy)); + break; + }; + }; + } + else + { + rdist[i] = 2; + }; + }; +}; + +// test occlusion for a cube... one of the most computationally expensive functions in the engine +// as its done for every cube and entity, but its effect is more than worth it! + +inline float ca(float x, float y) { return x>y ? y/x : 2-x/y; }; +inline float ma(float x, float y) { return x==0 ? (y>0 ? 2 : -2) : y/x; }; + +int isoccluded(float vx, float vy, float cx, float cy, float csize) // v = viewer, c = cube to test +{ + if(!ocull) return 0; + + float nx = vx, ny = vy; // n = point on the border of the cube that is closest to v + if(nxcx+csize) nx = cx+csize; + if(nycy+csize) ny = cy+csize; + float xdist = (float)fabs(nx-vx); + float ydist = (float)fabs(ny-vy); + if(xdist>odist || ydist>odist) return 2; + float dist = xdist+ydist-1; // 1 needed? + + // ABC + // D E + // FGH + + // - check middle cube? BG + + // find highest and lowest angle in the occlusion map that this cube spans, based on its most left and right + // points on the border from the viewer pov... I see no easier way to do this than this silly code below + + float h, l; + if(cx<=vx) // ABDFG + { + if(cx+csizetype==SEMISOLID) - { - float c1 = s->floor; - float c2 = s->floor; - if(s->type==FHF) { c1 -= d1->vdelta/4.0f; c2 -= d2->vdelta/4.0f; }; - float f1 = s->ceil; - float f2 = s->ceil; - if(s->type==CHF) { f1 += d1->vdelta/4.0f; f2 += d2->vdelta/4.0f; }; - //if(f1-c1<=0 && f2-c2<=0) return; - render_square(o->wtex, c1, c2, f1, f2, x1<floor; - float f2 = s->floor; - float c1 = o->floor; - float c2 = o->floor; - if(o->type==FHF && s->type!=FHF) - { - c1 -= d1->vdelta/4.0f; - c2 -= d2->vdelta/4.0f; - } - if(s->type==FHF && o->type!=FHF) - { - f1 -= d1->vdelta/4.0f; - f2 -= d2->vdelta/4.0f; - } - if(f1>=c1 && f2>=c2) goto skip; - render_square(o->wtex, f1, f2, c1, c2, x1<ceil; - float f2 = o->ceil; - float c1 = s->ceil; - float c2 = s->ceil; - if(o->type==CHF && s->type!=CHF) - { - f1 += d1->vdelta/4.0f; - f2 += d2->vdelta/4.0f; - } - else if(s->type==CHF && o->type!=CHF) - { - c1 += d1->vdelta/4.0f; - c2 += d2->vdelta/4.0f; - } - if(c1<=f1 && c2<=f2) return; - render_square(o->utex, f1, f2, c1, c2, x1<>mip; - x *= 2; - y *= 2; - switch(SWS(w, x+x1, y+y1, msize)->type) - { - case SEMISOLID: if(issemi(mip, x+x1, y+y1, x1, y1, x2, y2)) return true; - case CORNER: - case SOLID: break; - default: return true; - }; - switch(SWS(w, x+x2, y+y2, msize)->type) - { - case SEMISOLID: if(issemi(mip, x+x2, y+y2, x1, y1, x2, y2)) return true; - case CORNER: - case SOLID: break; - default: return true; - }; - return false; -}; - -bool render_floor, render_ceil; - -// the core recursive function, renders a rect of cubes at a certain mip level from a viewer perspective -// call itself for lower mip levels, on most modern machines however this function will use the higher -// mip levels only for perfect mips. - -void render_seg_new(float vx, float vy, float vh, int mip, int x, int y, int xs, int ys) -{ - sqr *w = wmip[mip]; - int sz = ssize>>mip; - int vxx = ((int)vx+(1<>mip; - int vyy = ((int)vy+(1<>mip; - int lx = vxx-lodleft; // these mark the rect inside the current rest that we want to render using a lower mip level - int ly = vyy-lodtop; - int rx = vxx+lodright; - int ry = vyy+lodbot; - - float fsize = (float)(1<occluded = isoccluded(player1->o.x, player1->o.y, (float)(ox<>mip; - int pvy = (int)vy>>mip; - if(pvx>=0 && pvy>=0 && pvxoccluded = 0; - SWS(w, pvx, pvy, sz)->occluded = 0; // player cell never occluded - }; - - #define df(x) s->floor-(x->vdelta/4.0f) - #define dc(x) s->ceil+(x->vdelta/4.0f) - - // loop through the rect 3 times (for floor/ceil/walls seperately, to facilitate dynamic stripify) - // for each we skip occluded cubes (occlusion at higher mip levels is a big time saver!). - // during the first loop (ceil) we collect cubes that lie within the lower mip rect and are - // also deferred, and render them recursively. Anything left (perfect mips and higher lods) we - // render here. - - #define LOOPH {for(int xx = x; xxoccluded==1) continue; \ - if(s->defer && !s->occluded && mip && xx>=lx && xx=ly && yydefer && !next->occluded) yy++; // collect 2xN rect of lower mip - render_seg_new(vx, vy, vh, mip-1, xx*2, start*2, xx*2+2, yy*2+2); - continue; - }; - stats[mip]++; - LOOPD - if((s->type==SPACE || s->type==FHF) && s->ceil>=vh && render_ceil) - render_flat(s->ctex, xx<ceil, s, t, u, v, true); - if(s->type==CHF) //if(s->ceil>=vh) - render_flatdelta(s->ctex, xx<type==SPACE || s->type==CHF) && s->floor<=vh && render_floor) - { - render_flat(s->ftex, xx<floor, s, t, u, v, false); - if(s->floortype==FHF) - { - render_flatdelta(s->ftex, xx<floor-s->vdelta/4.0ftype==CORNER) - { - // cull also - bool topleft = true; - sqr *h1 = NULL; - sqr *h2 = NULL; - if(SOLID(z)) - { - if(SOLID(w)) { render_wall(w, h2 = s, xx+1, yy, xx, yy+1, mip, t, v, false); topleft = false; } - else if(SOLID(v)) { render_wall(v, h2 = s, xx, yy, xx+1, yy+1, mip, s, u, false); }; - } - else if(SOLID(t)) - { - if(SOLID(w)) { render_wall(w, h1 = s, xx+1, yy+1, xx, yy, mip, u, s, false); } - else if(SOLID(v)) { render_wall(v, h1 = s, xx, yy+1, xx+1, yy, mip, v, t, false); topleft = false; }; - } - else - { - normalwall = false; - bool wv = w->ceil-w->floor < v->ceil-v->floor; - if(z->ceil-z->floor < t->ceil-t->floor) - { - if(wv) { render_wall(h1 = s, h2 = v, xx+1, yy, xx, yy+1, mip, t, v, false); topleft = false; } - else { render_wall(h1 = s, h2 = w, xx, yy, xx+1, yy+1, mip, s, u, false); }; - } - else - { - if(wv) { render_wall(h2 = s, h1 = v, xx+1, yy+1, xx, yy, mip, u, s, false); } - else { render_wall(h2 = s, h1 = w, xx, yy+1, xx+1, yy, mip, v, t, false); topleft = false; }; - }; - }; - render_tris(xx<=vxx && xx!=0 && yy!=sz-1 && !SOLID(z) && (!SOLID(s) || z->type!=CORNER) - && (z->type!=SEMISOLID || issemi(mip, xx-1, yy, 1, 0, 1, 1))) - render_wall(s, z, xx, yy, xx, yy+1, mip, s, v, true); - if(xx<=vxx && inner && !SOLID(t) && (!SOLID(s) || t->type!=CORNER) - && (t->type!=SEMISOLID || issemi(mip, xx+1, yy, 0, 0, 0, 1))) - render_wall(s, t, xx+1, yy, xx+1, yy+1, mip, t, u, false); - if(yy>=vyy && yy!=0 && xx!=sz-1 && !SOLID(w) && (!SOLID(s) || w->type!=CORNER) - && (w->type!=SEMISOLID || issemi(mip, xx, yy-1, 0, 1, 1, 1))) - render_wall(s, w, xx, yy, xx+1, yy, mip, s, t, false); - if(yy<=vyy && inner && !SOLID(v) && (!SOLID(s) || v->type!=CORNER) - && (v->type!=SEMISOLID || issemi(mip, xx, yy+1, 0, 0, 1, 0))) - render_wall(s, v, xx, yy+1, xx+1, yy+1, mip, v, u, true); - }; - }}; - -}; - -void distlod(int &low, int &high, int angle, float widef) -{ - float f = 90.0f/lod/widef; - low = (int)((90-angle)/f); - high = (int)(angle/f); - if(low45 && yaw<=135) - { - lodleft = lod; - distlod(lodtop, lodbot, yaw-45, widef); - } - else if(yaw>135 && yaw<=225) - { - lodbot = lod; - distlod(lodleft, lodright, yaw-135, widef); - } - else if(yaw>225 && yaw<=315) - { - lodright = lod; - distlod(lodbot, lodtop, yaw-225, widef); - } - else - { - lodtop = lod; - distlod(lodright, lodleft, yaw<=45 ? yaw+45 : yaw-315, widef); - }; - float hyfov = fov*h/w/2; - render_floor = pitch>MAX_MIP, ssize>>MAX_MIP); - mipstats(stats[0], stats[1], stats[2]); -}; - ADDED src/worldrender.cxx Index: src/worldrender.cxx ================================================================== --- /dev/null +++ src/worldrender.cxx @@ -0,0 +1,290 @@ +// worldrender.cpp: goes through all cubes in top down quad tree fashion, determines what has to +// be rendered and how (depending on neighbouring cubes), then calls functions in rendercubes.cpp + +#include "cube.h" + +void render_wall(sqr *o, sqr *s, int x1, int y1, int x2, int y2, int mip, sqr *d1, sqr *d2, bool topleft) +{ + if(SOLID(o) || o->type==SEMISOLID) + { + float c1 = s->floor; + float c2 = s->floor; + if(s->type==FHF) { c1 -= d1->vdelta/4.0f; c2 -= d2->vdelta/4.0f; }; + float f1 = s->ceil; + float f2 = s->ceil; + if(s->type==CHF) { f1 += d1->vdelta/4.0f; f2 += d2->vdelta/4.0f; }; + //if(f1-c1<=0 && f2-c2<=0) return; + render_square(o->wtex, c1, c2, f1, f2, x1<floor; + float f2 = s->floor; + float c1 = o->floor; + float c2 = o->floor; + if(o->type==FHF && s->type!=FHF) + { + c1 -= d1->vdelta/4.0f; + c2 -= d2->vdelta/4.0f; + } + if(s->type==FHF && o->type!=FHF) + { + f1 -= d1->vdelta/4.0f; + f2 -= d2->vdelta/4.0f; + } + if(f1>=c1 && f2>=c2) goto skip; + render_square(o->wtex, f1, f2, c1, c2, x1<ceil; + float f2 = o->ceil; + float c1 = s->ceil; + float c2 = s->ceil; + if(o->type==CHF && s->type!=CHF) + { + f1 += d1->vdelta/4.0f; + f2 += d2->vdelta/4.0f; + } + else if(s->type==CHF && o->type!=CHF) + { + c1 += d1->vdelta/4.0f; + c2 += d2->vdelta/4.0f; + } + if(c1<=f1 && c2<=f2) return; + render_square(o->utex, f1, f2, c1, c2, x1<>mip; + x *= 2; + y *= 2; + switch(SWS(w, x+x1, y+y1, msize)->type) + { + case SEMISOLID: if(issemi(mip, x+x1, y+y1, x1, y1, x2, y2)) return true; + case CORNER: + case SOLID: break; + default: return true; + }; + switch(SWS(w, x+x2, y+y2, msize)->type) + { + case SEMISOLID: if(issemi(mip, x+x2, y+y2, x1, y1, x2, y2)) return true; + case CORNER: + case SOLID: break; + default: return true; + }; + return false; +}; + +bool render_floor, render_ceil; + +// the core recursive function, renders a rect of cubes at a certain mip level from a viewer perspective +// call itself for lower mip levels, on most modern machines however this function will use the higher +// mip levels only for perfect mips. + +void render_seg_new(float vx, float vy, float vh, int mip, int x, int y, int xs, int ys) +{ + sqr *w = wmip[mip]; + int sz = ssize>>mip; + int vxx = ((int)vx+(1<>mip; + int vyy = ((int)vy+(1<>mip; + int lx = vxx-lodleft; // these mark the rect inside the current rest that we want to render using a lower mip level + int ly = vyy-lodtop; + int rx = vxx+lodright; + int ry = vyy+lodbot; + + float fsize = (float)(1<occluded = isoccluded(player1->o.x, player1->o.y, (float)(ox<>mip; + int pvy = (int)vy>>mip; + if(pvx>=0 && pvy>=0 && pvxoccluded = 0; + SWS(w, pvx, pvy, sz)->occluded = 0; // player cell never occluded + }; + + #define df(x) s->floor-(x->vdelta/4.0f) + #define dc(x) s->ceil+(x->vdelta/4.0f) + + // loop through the rect 3 times (for floor/ceil/walls seperately, to facilitate dynamic stripify) + // for each we skip occluded cubes (occlusion at higher mip levels is a big time saver!). + // during the first loop (ceil) we collect cubes that lie within the lower mip rect and are + // also deferred, and render them recursively. Anything left (perfect mips and higher lods) we + // render here. + + #define LOOPH {for(int xx = x; xxoccluded==1) continue; \ + if(s->defer && !s->occluded && mip && xx>=lx && xx=ly && yydefer && !next->occluded) yy++; // collect 2xN rect of lower mip + render_seg_new(vx, vy, vh, mip-1, xx*2, start*2, xx*2+2, yy*2+2); + continue; + }; + stats[mip]++; + LOOPD + if((s->type==SPACE || s->type==FHF) && s->ceil>=vh && render_ceil) + render_flat(s->ctex, xx<ceil, s, t, u, v, true); + if(s->type==CHF) //if(s->ceil>=vh) + render_flatdelta(s->ctex, xx<type==SPACE || s->type==CHF) && s->floor<=vh && render_floor) + { + render_flat(s->ftex, xx<floor, s, t, u, v, false); + if(s->floortype==FHF) + { + render_flatdelta(s->ftex, xx<floor-s->vdelta/4.0ftype==CORNER) + { + // cull also + bool topleft = true; + sqr *h1 = NULL; + sqr *h2 = NULL; + if(SOLID(z)) + { + if(SOLID(w)) { render_wall(w, h2 = s, xx+1, yy, xx, yy+1, mip, t, v, false); topleft = false; } + else if(SOLID(v)) { render_wall(v, h2 = s, xx, yy, xx+1, yy+1, mip, s, u, false); }; + } + else if(SOLID(t)) + { + if(SOLID(w)) { render_wall(w, h1 = s, xx+1, yy+1, xx, yy, mip, u, s, false); } + else if(SOLID(v)) { render_wall(v, h1 = s, xx, yy+1, xx+1, yy, mip, v, t, false); topleft = false; }; + } + else + { + normalwall = false; + bool wv = w->ceil-w->floor < v->ceil-v->floor; + if(z->ceil-z->floor < t->ceil-t->floor) + { + if(wv) { render_wall(h1 = s, h2 = v, xx+1, yy, xx, yy+1, mip, t, v, false); topleft = false; } + else { render_wall(h1 = s, h2 = w, xx, yy, xx+1, yy+1, mip, s, u, false); }; + } + else + { + if(wv) { render_wall(h2 = s, h1 = v, xx+1, yy+1, xx, yy, mip, u, s, false); } + else { render_wall(h2 = s, h1 = w, xx, yy+1, xx+1, yy, mip, v, t, false); topleft = false; }; + }; + }; + render_tris(xx<=vxx && xx!=0 && yy!=sz-1 && !SOLID(z) && (!SOLID(s) || z->type!=CORNER) + && (z->type!=SEMISOLID || issemi(mip, xx-1, yy, 1, 0, 1, 1))) + render_wall(s, z, xx, yy, xx, yy+1, mip, s, v, true); + if(xx<=vxx && inner && !SOLID(t) && (!SOLID(s) || t->type!=CORNER) + && (t->type!=SEMISOLID || issemi(mip, xx+1, yy, 0, 0, 0, 1))) + render_wall(s, t, xx+1, yy, xx+1, yy+1, mip, t, u, false); + if(yy>=vyy && yy!=0 && xx!=sz-1 && !SOLID(w) && (!SOLID(s) || w->type!=CORNER) + && (w->type!=SEMISOLID || issemi(mip, xx, yy-1, 0, 1, 1, 1))) + render_wall(s, w, xx, yy, xx+1, yy, mip, s, t, false); + if(yy<=vyy && inner && !SOLID(v) && (!SOLID(s) || v->type!=CORNER) + && (v->type!=SEMISOLID || issemi(mip, xx, yy+1, 0, 0, 1, 0))) + render_wall(s, v, xx, yy+1, xx+1, yy+1, mip, v, u, true); + }; + }}; + +}; + +void distlod(int &low, int &high, int angle, float widef) +{ + float f = 90.0f/lod/widef; + low = (int)((90-angle)/f); + high = (int)(angle/f); + if(low45 && yaw<=135) + { + lodleft = lod; + distlod(lodtop, lodbot, yaw-45, widef); + } + else if(yaw>135 && yaw<=225) + { + lodbot = lod; + distlod(lodleft, lodright, yaw-135, widef); + } + else if(yaw>225 && yaw<=315) + { + lodright = lod; + distlod(lodbot, lodtop, yaw-225, widef); + } + else + { + lodtop = lod; + distlod(lodright, lodleft, yaw<=45 ? yaw+45 : yaw-315, widef); + }; + float hyfov = fov*h/w/2; + render_floor = pitch>MAX_MIP, ssize>>MAX_MIP); + mipstats(stats[0], stats[1], stats[2]); +}; +