From 6c4bd13f0446b011a8b82cae5f156e57bee7f1a7 Mon Sep 17 00:00:00 2001 From: "T. J. Brumfield" Date: Fri, 15 Mar 2024 01:43:19 -0500 Subject: [PATCH] Interpolation, Emscripten, and SDL2 changes --- CMakeLists.txt | 34 +- defs.h | 1 + encoding.c | 3 + fileio.c | 10 +- lxlogic.c | 8 +- oshw.h | 1 + play.c | 4 + play.h | 2 + series.c | 4 + sets/CCLP2.dat-lynx.dac | 2 + sets/CCLP5-Lynx.dac | 4 + sets/CCLP5-MS.dac | 4 + sets/intro.dat-lynx.dac | 2 + sets/intro.dat-ms.dac | 2 + shell.html | 118 ++++++ solution.c | 6 +- state.h | 1 + tworld.c | 42 +- tworld.html | 864 ++++++++++++++++++++++++++++++++++++++++ ver.h | 2 +- 20 files changed, 1085 insertions(+), 29 deletions(-) create mode 100644 sets/CCLP2.dat-lynx.dac create mode 100644 sets/CCLP5-Lynx.dac create mode 100644 sets/CCLP5-MS.dac create mode 100644 sets/intro.dat-lynx.dac create mode 100644 sets/intro.dat-ms.dac create mode 100644 shell.html create mode 100644 tworld.html diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c89655..2a247cc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -3,13 +3,13 @@ # Copyright (C) 2020 by Michael Hansen, under the GNU General Public # License. No warranty. See COPYING for details. -cmake_minimum_required(VERSION 3.1) +cmake_minimum_required(VERSION 3.20) project(tworld) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED TRUE) -set(OSHW "qt" CACHE STRING "OS/HW Flavor to build (sdl or qt)") +set(OSHW "sdl" CACHE STRING "OS/HW Flavor to build (sdl or qt)") set_property(CACHE OSHW PROPERTY STRINGS qt sdl) if(OSHW STREQUAL "qt") @@ -21,7 +21,27 @@ else() endif() # We still require SDL even for the Qt build... -find_package(SDL REQUIRED) + +if(NOT ${CMAKE_SYSTEM_NAME} MATCHES "Emscripten") + find_package(SDL2 REQUIRED) +else() + set(USE_FLAGS "-fno-rtti -fno-exceptions -Wimplicit-function-declaration -O3 -sUSE_SDL=2") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${USE_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${USE_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${USE_FLAGS}\ + -s FORCE_FILESYSTEM=1 --preload-file data\ + --preload-file res --preload-file sets\ + --emit-symbol-map -s STB_IMAGE=1\ + -s ASYNCIFY -s ALLOW_MEMORY_GROWTH=1\ + -s EXIT_RUNTIME=1 --shell-file shell.html -flto -Wl,-u,filen") + set(CMAKE_EXECUTABLE_SUFFIX .html) + include_directories( + ${EMSCRIPTEN_ROOT_PATH}/system + ${CMAKE_SOURCE_DIR}/include + ${SDL2_INCLUDE_DIRS} + ) +endif() + if(OSHW STREQUAL "qt") set(CMAKE_AUTOUIC TRUE) set(CMAKE_AUTOMOC TRUE) @@ -65,9 +85,12 @@ if(WIN32) else() set(SHARE_DIR "${CMAKE_INSTALL_PREFIX}/share/tworld" CACHE STRING "Directory for shared files") - add_definitions(-DROOTDIR="${SHARE_DIR}") add_definitions(-Dstricmp=strcasecmp) + if(NOT CMAKE_BUILD_TYPE STREQUAL "Debug") + add_definitions(-DROOTDIR="${SHARE_DIR}") + endif() + install(FILES ${TW_SETS} DESTINATION "${SHARE_DIR}/sets") install(FILES ${TW_DATA} DESTINATION "${SHARE_DIR}/data") install(FILES ${TW_RES} DESTINATION "${SHARE_DIR}/res") @@ -126,12 +149,11 @@ if(OSHW STREQUAL "qt") target_sources(${TWORLD_EXE} PRIVATE tworld2.qrc) endif() - target_link_libraries(${TWORLD_EXE} PRIVATE oshw-${OSHW}) if(WIN32) target_link_libraries(${TWORLD_EXE} PRIVATE $<$:-mconsole>) endif() - + # Generate the current compile time if(WIN32) # TODO: It would be nice to use the format below on Win32 as well... diff --git a/defs.h b/defs.h index 75c8ad9..4905b18 100644 --- a/defs.h +++ b/defs.h @@ -197,6 +197,7 @@ typedef struct gamesetup { char const *unsolvable; /* why level is unsolvable, or NULL */ char name[256]; /* name of the level */ char passwd[256]; /* the level's password */ + char author[256]; /* the level's author */ } gamesetup; /* Flags associated with a saved game. diff --git a/encoding.c b/encoding.c index 0757072..4d23172 100644 --- a/encoding.c +++ b/encoding.c @@ -282,6 +282,9 @@ static int expandmsdatlevel(gamestate *state) case 8: /* field 8 passwd */ break; + case 9: + /* author field */ + break; case 10: if (size % 2) warn("level %d: ignoring extra byte at end of field 10", diff --git a/fileio.c b/fileio.c index 3c2a4d9..091363a 100644 --- a/fileio.c +++ b/fileio.c @@ -79,7 +79,7 @@ static FILE *FOPEN(char const *name, char const *mode) { FILE * file = NULL; if (!strcmp(mode, "wx")) { - int fd = open(name, O_WRONLY | O_CREAT | O_EXCL); + int fd = open(name, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); if (fd != -1) file = fdopen(fd, "w"); } @@ -185,12 +185,14 @@ void *filereadbuf(fileinfo *file, unsigned long size, char const *msg) { void *buf; + if (size == 0) { + return NULL; + } + if (!(buf = malloc(size))) { fileerr(file, msg); return NULL; } - if (!size) - return buf; errno = 0; if (fread(buf, size, 1, file->fp) != 1) { fileerr(file, msg); @@ -342,7 +344,7 @@ char *getpathbuffer(void) { char *buf; - if (!(buf = malloc(PATH_MAX + 1))) + if (!(buf = calloc(PATH_MAX + 1, 1))) memerrexit(); return buf; } diff --git a/lxlogic.c b/lxlogic.c index 99f374d..144de33 100644 --- a/lxlogic.c +++ b/lxlogic.c @@ -698,8 +698,10 @@ static int canpushblock(creature *block, int dir, int flags) _assert(dir != NIL); if (!canmakemove(block, dir, flags)) { - if (!block->moving && (flags & (CMM_PUSHBLOCKS | CMM_PUSHBLOCKSNOW))) + if (!block->moving && (flags & (CMM_PUSHBLOCKS | CMM_PUSHBLOCKSNOW))) { block->dir = dir; + block->tdir = dir; + } return FALSE; } if (flags & (CMM_PUSHBLOCKS | CMM_PUSHBLOCKSNOW)) { @@ -1029,7 +1031,7 @@ static int choosemove(creature *cr) } else { if (getforcedmove(cr)) cr->tdir = NIL; - else + else if (cr->id != Block) choosecreaturemove(cr); } @@ -1930,8 +1932,6 @@ static int advancegame(gamelogic *logic) initialhousekeeping(); for (cr = creaturelistend() ; cr >= creaturelist() ; --cr) { - setfdir(cr, NIL); - cr->tdir = NIL; if (cr != getchip() && cr->hidden) continue; if (isanimation(cr->id)) { diff --git a/oshw.h b/oshw.h index d33d7ad..e7a0b20 100644 --- a/oshw.h +++ b/oshw.h @@ -53,6 +53,7 @@ OSHW_EXTERN void settimer(int action); * zero selects the default of 1000 milliseconds. */ OSHW_EXTERN void settimersecond(int ms); +OSHW_EXTERN void setvisualtickrate(int ms); /* Return the number of ticks since the timer was last reset. */ diff --git a/play.c b/play.c index e044b90..0829cc5 100644 --- a/play.c +++ b/play.c @@ -507,3 +507,7 @@ int checksolution(void) state.game->besttime = currenttime; return FALSE; } + +void setinterpolation(float interpolation) { + state.movementinterpolation = interpolation; +} \ No newline at end of file diff --git a/play.h b/play.h index 3371f68..f3cfd1c 100644 --- a/play.h +++ b/play.h @@ -121,6 +121,8 @@ extern int setmudsuckingfactor(int mud); */ extern void toggleshowinitstate(void); +extern void setinterpolation(float interpolation); + #ifdef __cplusplus } #endif diff --git a/series.c b/series.c index d2d5cd4..a303c1d 100644 --- a/series.c +++ b/series.c @@ -260,6 +260,10 @@ static int readleveldata(fileinfo *file, gamesetup *game) game->passwd[n] = data[n] ^ 0x99; game->passwd[n] = '\0'; break; + case 9: + memcpy(game->author, data, size); + game->author[size] = '\0'; + break; case 8: warn("level %d: ignoring field 8 password", game->number); break; diff --git a/sets/CCLP2.dat-lynx.dac b/sets/CCLP2.dat-lynx.dac new file mode 100644 index 0000000..45400c1 --- /dev/null +++ b/sets/CCLP2.dat-lynx.dac @@ -0,0 +1,2 @@ +file=CCLP2.dat +ruleset=lynx diff --git a/sets/CCLP5-Lynx.dac b/sets/CCLP5-Lynx.dac new file mode 100644 index 0000000..886894e --- /dev/null +++ b/sets/CCLP5-Lynx.dac @@ -0,0 +1,4 @@ +file=CCLP5.dat +lastlevel=144 + +ruleset=lynx diff --git a/sets/CCLP5-MS.dac b/sets/CCLP5-MS.dac new file mode 100644 index 0000000..ac88728 --- /dev/null +++ b/sets/CCLP5-MS.dac @@ -0,0 +1,4 @@ +file=CCLP5.dat +lastlevel=144 + +ruleset=ms \ No newline at end of file diff --git a/sets/intro.dat-lynx.dac b/sets/intro.dat-lynx.dac new file mode 100644 index 0000000..a0e27a7 --- /dev/null +++ b/sets/intro.dat-lynx.dac @@ -0,0 +1,2 @@ +file=intro.dat +ruleset=lynx diff --git a/sets/intro.dat-ms.dac b/sets/intro.dat-ms.dac new file mode 100644 index 0000000..173f390 --- /dev/null +++ b/sets/intro.dat-ms.dac @@ -0,0 +1,2 @@ +file=intro.dat +ruleset=ms diff --git a/shell.html b/shell.html new file mode 100644 index 0000000..28f6d75 --- /dev/null +++ b/shell.html @@ -0,0 +1,118 @@ + + + + + + Tile World + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Downloading...
+ + + + + {{{ SCRIPT }}} + + diff --git a/solution.c b/solution.c index 4c2847a..ceb8391 100644 --- a/solution.c +++ b/solution.c @@ -612,11 +612,11 @@ int readsolutions(gameseries *series) break; if (gametmp.sgflags & SGF_SETNAME) { if (strcmp(gametmp.name, series->name)) { - errmsg(series->name, "ignoring solution file %s as it was" + warn("solution file %s as was" " recorded for a different level set: %s", series->savefile.name, gametmp.name); - series->gsflags |= GSF_NOSAVING; - return FALSE; + // series->gsflags |= GSF_NOSAVING; + // return FALSE; } continue; } diff --git a/state.h b/state.h index e27be33..ea34a6c 100644 --- a/state.h +++ b/state.h @@ -263,6 +263,7 @@ typedef struct gamestate { these are not large enough to make it worth it. */ struct msstate_ msstate; struct lxstate_ lxstate; + float movementinterpolation; } gamestate; /* General status flags. diff --git a/tworld.c b/tworld.c index 8330b04..4b81c18 100644 --- a/tworld.c +++ b/tworld.c @@ -51,6 +51,7 @@ typedef struct startupdata { int listscores; /* TRUE if the scores should be listed */ int listtimes; /* TRUE if the times should be listed */ int batchverify; /* TRUE to enter batch verification */ + int lynxmode; /* TRUE to use Lynx for the initial level */ } startupdata; /* History of levelsets in order of last used date/time. @@ -58,6 +59,8 @@ typedef struct startupdata { static history *historylist = NULL; static int historycount = 0; +static int noLevelMove = 0; + /* Structure used to hold the complete list of available series. */ typedef struct seriesdata { @@ -478,7 +481,7 @@ static int changecurrentgame(gamespec *gs, int offset) { int sign, m, n; - if (offset == 0) + if (offset == 0 || noLevelMove) return FALSE; m = gs->currentgame; @@ -1106,6 +1109,10 @@ static int playgame(gamespec *gs, int firstcmd) { int render, lastrendered; int cmd, n; + int visualtick = 0; + int visualtickrate = 6; + setvisualtickrate(visualtickrate); + settimersecond(1000 / visualtickrate); cmd = firstcmd; if (cmd == CmdProceed) @@ -1120,13 +1127,21 @@ static int playgame(gamespec *gs, int firstcmd) if (gamepaused) cmd = input(TRUE); else { - n = doturn(cmd); + if (visualtick == 0) { + n = doturn(cmd); + } + setinterpolation((float)visualtick / visualtickrate); drawscreen(render); + setinterpolation(0); lastrendered = render; if (n) break; render = waitfortick() || noframeskip; - cmd = input(FALSE); + if (visualtick == 0) { + cmd = input(FALSE); + } + visualtick += 1; + visualtick %= visualtickrate; } if (cmd == CmdQuitLevel) { quitgamestate(); @@ -1824,8 +1839,10 @@ static void initdirs(char const *series, char const *seriesdat, warn("Value of environment variable TWORLDDIR is too long"); } if (!root) { +#ifndef EMSCRIPTEN #ifdef ROOTDIR root = ROOTDIR; +#endif #else root = "."; #endif @@ -1855,9 +1872,9 @@ static void initdirs(char const *series, char const *seriesdat, #ifdef SAVEDIR save = SAVEDIR; #else - if ((dir = getenv("HOME")) && *dir && strlen(dir) < maxpath - 8) - combinepath(savedir, dir, ".tworld"); - else +// if ((dir = getenv("HOME")) && *dir && strlen(dir) < maxpath - 8) +// combinepath(savedir, dir, ".tworld"); +// else combinepath(savedir, root, "save"); #endif @@ -1895,7 +1912,7 @@ static int initoptionswithcmdline(int argc, char *argv[], startupdata *start) soundbufsize = 0; volumelevel = -1; - initoptions(&opts, argc - 1, argv + 1, "abD:dFfHhL:lm:n:PpqR:rS:stVv"); + initoptions(&opts, argc - 1, argv + 1, "abD:dFfHhL:lm:n:PxpqR:rS:stVvo"); while ((ch = readoption(&opts)) >= 0) { switch (ch) { case 0: @@ -1919,15 +1936,16 @@ static int initoptionswithcmdline(int argc, char *argv[], startupdata *start) case 'L': optseriesdir = opts.val; break; case 'R': optresdir = opts.val; break; case 'S': optsavedir = opts.val; break; - case 'H': showhistogram = !showhistogram; break; + case 'H': showhistogram = !showhistogram; break; case 'f': noframeskip = !noframeskip; break; case 'F': fullscreen = !fullscreen; break; case 'p': usepasswds = !usepasswds; break; - case 'q': silence = !silence; break; + case 'q': silence = !silence; break; case 'r': readonly = !readonly; break; case 'P': pedantic = !pedantic; break; - case 'a': ++soundbufsize; break; - case 'd': listdirs = TRUE; break; + case 'x': start->lynxmode = TRUE; break; + case 'a': ++soundbufsize; break; + case 'd': listdirs = TRUE; break; case 'l': start->listseries = TRUE; break; case 's': start->listscores = TRUE; break; case 't': start->listtimes = TRUE; break; @@ -1945,6 +1963,7 @@ static int initoptionswithcmdline(int argc, char *argv[], startupdata *start) fprintf(stderr, "unrecognized option: -%c\n", opts.opt); printtable(stderr, yowzitch); return FALSE; + case 'o': noLevelMove = TRUE; break; default: printtable(stderr, yowzitch); return FALSE; @@ -2057,6 +2076,7 @@ static int choosegameatstartup(gamespec *gs, char const *lastseries, errmsg(series.list[0].filebase, "cannot read level set"); return -1; } + if (start->lynxmode) series.list[0].ruleset = Ruleset_Lynx; if (start->batchverify) { n = batchverify(series.list, !silence && !start->listtimes && !start->listscores); diff --git a/tworld.html b/tworld.html new file mode 100644 index 0000000..a4c5e97 --- /dev/null +++ b/tworld.html @@ -0,0 +1,864 @@ + + + +Tile World + + +

+

+

Tile World

+
+

+

+

Contents

+

+

+

+ +

+ +

+Synopsis +

+

+Tile World is a reimplementation of the game "Chip's Challenge". The +player controls Chip, navigating him through his challenges. The +object of each level of the game is to find and reach the exit tile, +which takes you to the next level. The levels contain many different +kinds of obstacles, creatures both hostile and indifferent, tools, +protective gear -- and, of course, chips. +

+


+

+ +

+Overview of the Game +

+

+The main display shows Chip in the current level and his immediate +surroundings. To the right of this display is shown the basic +information about the current level. The most important data shown +here are how many seconds are left on the clock, and how many chips +still need to be collected. (On some level the clock does not show a +time. These levels have no time limit.) +

+The object of every level is to find and reach the exit before the +time runs out. The exit is frequently (but not always) guarded by a +chip socket. To move past the chip socket, Chip must collect a certain +number of computer chips; the amount needed is different in each +level. As you play a level, the information display on the right shows +the number of chips that still need to be collected in order to open +the socket. (Remember that getting enough chips to open the chip +socket is only a subgoal, not the main goal. Some levels do not +require any chips to be collected; some levels have no chip socket at +all.) +

+Also occupying many of the levels are other creatures. Most (but not +all) of them move about in simple, predictable patterns, and without +regard for Chip's presence. The creatures know enough to avoid running +into each other, but a collision with Chip is fatal. The complete +taxonomy of creatures is: tanks, balls, gliders, fireballs, walkers, +blobs, teeth, bugs, and paramecia. +

+In addition to the socket and the main exit, there are also four +different kinds of doors. These doors can be opened with the right +kind of key. The doors and the keys are color-coded -- red, green, +blue, and yellow -- so you can tell them apart. Like the chip socket, +a door that has been opened stays open. Keys are picked up simply by +stepping upon them. The key disappears from the map and appears in +your possession. Keys in your possession are displayed on the +right-hand side of the window. +

+Besides keys and chip, there are also four kinds of special footgear +that Chip can collect. Like keys, boots can be picked up simply by +walking over them. (There is no limit to the number of boots you can +have.) These boots permit Chip to walk across four different kinds of +surfaces, just as if they were normal flooring. Fire and water are two +kind of tiles that are normally fatal to Chip, but fire boots and +water boots will permit safe passage across these. Stepping onto ice +sends Chip sliding at high speed unless he has a pair of ice boots. +Finally, there are force floors that push Chip along in a specific +direction; these can be counteracted with force boots. +

+Two other types of surfaces are more useful to Chip, in that they keep +other creatures out. These are dirt and gravel, and they are special +in that Chip is the only one who can walk on them. However, when Chip +steps onto a dirt tile, it is cleared away and becomes normal +flooring. Gravel, on the other hand, is permanent. +

+There are numerous other objects scattered around the various levels, +which Chip can interact with, although he cannot pick them up. Bombs +are one kind of object which should always be avoided, as they explode +when stepped on. The thief tile should also generally be avoided; +entering this tile will cause Chip to lose any footgear he has +collected. +

+Dirt blocks are large, movable squares of dirt. Chip can push them +about, and use them to wall off areas or to safely detontate bombs. +Furthermore, if a block is pushed into water, the tile will turn into +dirt (which will become normal flooring when Chip steps on it). +Finally, note that blocks can sometimes be resting on top of other +objects, both helpful (such as a key) and harmful (such as a bomb). +

+Some levels have teleports. Entering a teleport causes Chip to vanish +and instantaneously reappear at another teleport. +

+Even some of the walls can demonstrate surprising behavior. The +so-called blue walls can either be actual walls, or empty mirages. +The only way for Chip to tell which is which is to attempt to walk +through one. There are also popup walls -- Chip can walk across these +only once, for they turn into walls as he walks over them. +

+There are four different types of pushbuttons. Like keys and boots, +they are color-coded. Stepping on a pushbutton activates it. +

+The green buttons control the toggle walls. Toggle walls have dotted +green outlines, and they change between being open (passable, like any +other floor) and open (unpassable, a wall). When a green button is +pressed, the closed toggle walls are opened and the open toggle walls +are closed. +

+Brown buttons control bear traps. Anything that wanders into a bear +trap will be stuck there until the brown button connected to it is +pushed. +

+Blue buttons exercise some control over the tanks. Normally, a tank +moves directly forward until it hits an obstacle, whereupon it stops. +But when a blue button is pressed, all tanks turn around 180 degrees +and begin moving again. +

+The objects with the most potential for help and hindrance are the +clone machines, which are controlled by red buttons. Every clone +machine contains a dirt block, a tank, or some other creature. When +the clone machine's red button is pressed, a duplicate of whatever the +clone machine contains is created and set loose. +

+Once in a while there will also be hint buttons. These have a question +mark displayed on them. When Chip steps onto a hint button, a short +bit of information will be displayed in the lower right-hand area of +the window. +

+Here are some general hints for successful play: +

+

+

+ +

+Passwords +

+

+Every level has a four-letter password. The password for a level is +shown in the information display at the upper-right of the window. The +obstensible purpose of passwords is to allow you to come back to a +level. Howver, normally you will never need to remember passwords, as +Tile World will automatically store the passwords for you. However, if +you somehow manage to learn the password of a level that you have yet +to achieve, you can use the password to gain early access to that +level. +

+ +

+Scoring +

+

+For each level in a set that you complete, the game awards 500 points +times the level's number. Furthermore, if the level is timed, an extra +10 points is added for every second left on the clock when you finish +the level. You can thus sometimes improve your score by returning to +already-completed levels and playing them again. +

+


+

+ +

+Key Commands +

+

+During game play, the arrows are the most important keys; they move +Chip through the level. The keys 2 4 6 8 on the numeric keypad +can also be used for the same purpose. Other keys have the following +functions: +

+ + + + + + + + + + + + + + + +
Bkspc pauses the game; press any key to resume play.
Ctrl-N stops the current game and moves forward to the next level.
Ctrl-P stops the current game and moves back to the previous level.
Esc quits the current level.
Ctrl-R starts over at the beginning of the current level.
V decreases the volume level. (If the volume level is reduced to zero, +then the program will display sound effects textually, as onomatopoeia.)
Shift-V increases the volume level.
+

+At the start of a level, before game play begins, the following key +commands are available: +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Esc returns to the list of available level sets.
Spc starts the current level without moving (i.e., standing still).
N moves to the next level.
P moves to the previous level.
PgUp moves ahead ten levels.
PgDn moves back ten levels.
G displays a prompt and accepts a password, then jumps to the level with +that password.
Tab plays back the best solution for that level.
Shift-Tab verifies the best solution for that level. If the solution is no +longer valid (e.g. because the level has been altered), the solution +will automatically be deprecated.
O toggles between even-step and odd-step offset.
Shift-O (Lynx-mode only) increments the stepping offset by one.
F (Lynx-mode only) changes the initial direction for the "random" force floors.
Ctrl-X deprecates the best solution for that level. If the level is then +succesfully completed again, the saved solution will be replaced with +the new one, whether or not it had a better time.
Shift-Ctrl-X deletes the saved solution for that level. If confirmed, the +solution will be immediately removed from the solution file.
S displays the list of known levels and the score for each, as well as +the overall score for the level set. The score list display also +permits changing the current level by moving the selection and +pressing Enter.
Ctrl-S displays the list of solution files in the save directory whose +names start with the name of the current level set. From here a +different solution file can be selected.
+

+A few commands are available only during solution playback: +

+ + + + + + + + + + + +
D toggles the display of the stepping and (in Lynx mode) the initial +direction of the "random" force floors.
PgUp goes back about 10 seconds.
PgDn goes forward about 10 seconds.
E advances the playback by a "tick". (1/10th second in MS, 1/20th second +in Lynx.)
Shift-E advances the playback by a "move". (1/5th second in MS)
+

+At every point in the program, the Esc key will abort the current +activity and return to the previous display. +

+


+

+ +

+Rulesets +

+

+Tile World contains emulators for two different versions of "Chip's +Challenge". They are referred to as the Lynx ruleset and the MS +ruleset. The Lynx ruleset recreates the original implementation of the +game, and the MS ruleset recreates the version that was implemented +for Microsoft Windows (cf History). +

+The most notable difference between the two rulesets is that in the MS +ruleset, movement between tiles is instantaneous, whereas under the +Lynx ruleset motion occurs across several "ticks". (This probably +reflects the fact that the latter ran on dedicated hardware, while the +former ran on 33 MHz PCs under a non-preemptive multitasking OS.) +Although the basic mechanics of the game are the same under both +rulesets, there are also a host of subtle differences between the two. +

+Each level set file includes a flag that indicates which ruleset it is +to be played under. Some level sets can be played under both rulesets +(most notably, the original set of levels), but this is the exception. +

+ +

+Adding New Level Sets +

+

+Level sets are defined by data files. By convention these file are +named with a .dat or .ccl extension. Classically, the name proper +contains the author's first name, last initial, and a single digit -- +for example, a set by someone named Bob Nomo could be called +BobN1.dat. (The digit is used to give the sequence in case the author, +for whatever reason, stores their creations in more than one file.) +Some more recent level sets do not follow this naming pattern, +however, so it is not always easy to find out the author from the +set's name. +

+When a new data file is obtained, it may simply be copied into the +level set directory (cf Directories), and Tile World will then make +it available for playing. +

+


+

+ +

+Command-Line Options +

+

+tworld2 is normally invoked without arguments. The program begins by +displaying a list of the available level sets. After a level set is +chosen, the program jumps to the first unsolved level to begin +play. +

+The available command-line options are enumerated in the following +table. +

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
-a Double the size of the audio buffer. This option can be repeated, so +for example -aaa would increase the audio buffer size eightfold.
-b Do a batch-mode verification of the existing solutions and exit. +Levels with invalid solutions are displayed on standard output. If +used with -q, then nothing is displayed, and the program's exit code +is the number of invalid solutions. Can also be used with -s or -t +to have solutions verified before the other option is applied. Note +that this options requires a level set file and/or a solution file be +named on the command line.
-D DIR Read level data files from DIR instead of the default directory.
-d Display the default directories used by the program on standard +output, and exit.
-F Run in full-screen mode.
-H Upon exit, display a histogram of idle time on standard output. (This +option is used for evaluating optimization efforts.)
-h Display a summary of the command-line syntax on standard output and +exit.
-L DIR Look for level sets in DIR instead of the default directory.
-l Write a list of available level sets to standard output and exit.
-n N Set the initial volume level to N, 0 being silence and 10 being +full volume.
-P Turn on pedantic mode, forcing the Lynx ruleset to emulate the +original game as closely as possible. (See the Tile World website for +more information on emulation of the Lynx ruleset.)
-p Turn off all password-checking. This option allows the normal sequence +of levels to be bypassed.
-q Run quietly. All sounds, including the ringing of the terminal bell, +are suppressed.
-r Run in read-only mode. This guarantees that no changes will be made +to the solution files.
-R DIR Read resource data from DIR instead of the default directory.
-S DIR Read and write solution files (and other saved data) under DIR instead of the default +directory.
-s Display the current scores for the selected level set on standard +output and exit. A level set must be named on the command line. If +used with -b, the solutions are verified beforehand, and invalid +solutions are indicated.
-t Display the best times for the selected level set on standard output +and exit. A level set must be named on the command line. If used with +-b, the solutions are verified beforehand, and invalid solutions are +indicated.
-V Display the program's version and license information on standard +output and exit.
-v Display the program's version number on standard output and exit.
+

+Besides the above options, tworld2 can accept up to three +command-line arguments: the name of a level set, the number of a level +to start on, and the name of an alternate solution file. If the name +of an installed level set is specified, then Tile World will start up +in that set, skipping the initial level set selection. +

+If the specified level set is not a simple name but is a pathname +(relative or absolute), then Tile World will use that level set only, +without requiring that it first be installed. No solutions will be +saved unless an explicit solution file is also supplied on the +command-line. (If the command-line only specifies a solution file, +then Tile World will look up the name of the level set in the solution +file.) +

+


+

+ +

+Configuration Files +

+

+Configuration files are used to override some of the settings in a +data file, or to set values not provided for by the data file format. +Starting in Tile World 2.2, they are generated automatically, so you +don't need to mess with them unless you want to change the defaults. +Configuration files are by convention named with a .dac +extension. A configuration file is stored in the sets directory. (cf +Directories). +

+The configuration file is a simple text file. The first line of a +configuration file must have the following form: +

+file = DATAFILE +

+where DATAFILE is the filename of the data file. (Arbitrary +whitespace is permitted around the equal sign, but there cannot be any +whitespace embedded at the beginning of the line.) After this initial +line, the configuration file can contain any of the following lines: +

+fileinsetsdir = y|n +

+This specifies that the level set is found in the sets directory, rather than +the data directory. The default is n. +

+usepasswords = y|n +

+This line permits password-checking to be enabled/disabled when +playing the levels in the set. The default is y. +

+ruleset = ms|lynx +

+This line allows the configuration file to override the ruleset +setting in the data file. This is mainly useful in the case where one +level set is playable under either ruleset (as is the case with the +original level set). The author can then provide one data file and two +configuration files to make both versions available. +

+lastlevel = levelnum +

+This line marks an arbitrary level as being the last level in the +set. The game will stop when this level is completed, instead of +proceeding to the next level. (Note that if the data file contains any +levels beyond this one, they will only be reachable via a password.) +

+fixlynx = y|n +

+This line is specifically for use with the original level set. It is +not generally useful, and is described here only for completeness. The +chips.dat file that MS distributed with their version of "Chip's +Challenge" contained a few minor differences from the original level +set as appeared on the Lynx. A positive value for this entry instructs +the program to undo those changes, so that the original Lynx level set +is obtained. (The changes made in the MS version were: an extra level +was added; four passwords were garbled; and four or five levels' maps +had minor alterations.) + +

CCX Files

+The data directory may contain a file with the same name as a level +set, but with a .ccx extension. This file can be used to specify +various data, such as ruleset compatibility and authorship (both for +the entire set and for individual levels. It is also possible to +specify text displayed when reaching or completing levels. This can be +used to give an ongoing story for a level set, or for other +purposes. Detailed documentation does not currently exist. One way to +produce a CCX file for a set is to emulate the structure of an +existing one (such as that for CCLP1 or CCLP3). You can also use the +"CCX File Editor" by Mike Lask. +

+Some people may wish not to view text associated with levels. This can +be controlled using the "Auto-display Level Text" menu under +"Options". Whether or not this option is enabled, level text can be +viewed using the "View Prologue" (for text displayed when visiting a +level) and "View Epilogue" (for text displayed when completing a +level). The epilogue is only available for levels that have been +completed. +

+ +

+Resources +

+

+Tile World loads various resources at runtime from its resource +directory (cf Directories). These resources include the program's +font, graphic images, and sound effects. The actual file names are +determined by the contents of a file named rc (short for "resource +configuration", not "runtime commands") in the same directory. +

+The rc file is a plain text file, and contains lines of the +form +

+resource = filename +

+where resource is a symbolic resource name, and filename is the +name of a file in the resource directory. +

+The resources can be set differently depending on the ruleset that the +program is using. A line in the rc file of the form +

+[ruleset] +

+indicates that the lines that follow only apply when that ruleset is +in effect (where ruleset is either MS or Lynx). Resources that +are defined before any such line apply to both rulesets, and are also +used as fallbacks if a ruleset-specific resource could not be +loaded. (The font and the text-color resources also need to have +ruleset-independent values, as these are needed when displaying the +initial file list, before a ruleset has been chosen.) +

+A line of the form +

+TileImages = FILENAME +

+identifies the file that provides the images used to draw the game. +These images are stored in a Windows bitmap. (See the Tile World +website for more information about this resource.) +

+A line of the form +

+Font = FILENAME +

+identifies the file that provides the program's font. The font is +stored as a Windows bitmap. (See the Tile World website for more +information about this resource.) +

+A line of the form +

+UnsolvableList = FILENAME +

+identifies the filename for the database of unsolvable levels. See +Database of Unsolvable Levels below for more information about this +file. Note that this resource must be defined independent of the +ruleset, or else it will be ignored. +

+A line of the form +

+EndMessages = FILENAME +

+identifies the filename for the messages displayed when losing or +winning a level. See End of Level Messages +below for more information about this file. Note that this resource +must be defined independent of the ruleset, or else it will be +ignored. +

+Four resources define the colors used in rendering text: +

+BackgroundColor = RRGGBB +
+TextColor = RRGGBB +
+BoldTextColor = RRGGBB +
+DimTextColor = RRGGBB +

+The value of RRGGBB is a string of six hexadecimal digits defining +the red, green, and blue values of the color (as with the color +specification used in HTML or X Windows, but without the preceding +octothorpe). +

+The remaining resources all define the game's sound effects. The +sounds are stored as Microsoft RIFF files (so-called wave files). +Unlike the tile images, each sound effect is defined as a separate +file. The complete list of symbolic resource names is as follows: +

+Sounds used in both rulesets +

+

+Sounds used only under the MS ruleset +

+

+Sounds used only under the Lynx ruleset +

+

+(Note that the symbolic names for the shared and MS-only sounds match +the names in the entpack.ini file used by the Microsoft program. +This makes it easy for someone with a copy of Microsoft's "Chip's +Challenge" to use the sound effects that were provided with that +version of the game.) +

+ +

+Database of Unsolvable Levels +

+

+Of the many thousands of user-created levels that are publicly +available, there are some that are not possible to complete. Some of +these are intentionally so (e.g. requiring the player to deduce the +password to the next level). The remainder, however, are simply due to +poor design, and there is typically no indication that attempting to +solve these levels is fruitless. +

+To help alleviate this, Tile World comes with a database of levels +that have been identified by the community to be definitely +unsolvable. When the player visits a level that appears in this +database, a warning is displayed, and the password to the next level +is automatically supplied. +

+The main database of unsolvable levels is stored in the resource +directory. In addition, a player can keep a separate database in a +file of the same name in the directory for solution files. If present, +Tile World will use the information from both of these files. +

+The offending levels are identified by content as well as by name and +number, so that updated versions will no longer be identified as +unsolvable. See the Tile World website for more information about the +format of this file, and to check for updates to the database. + +

End of Level Messages

+

When you win or lose a level, Tile World displays a message. These +messages can be customized using a file in the resource directory, +whose default name is messages.txt. This file can contain an umlimited +number of messages for Tile World to choose from. The file consists of +sections of the form +

+: messagetypes +
message +
message +

+Here, messagetypes +can contain any of die (for when the player dies), time (for when +the player runs out of time), or win (for when the player completes +the level. It is possible to specify more than one type. For example, +the file +

+:die time +
Message 1 +
Message 2 +
:die +
Message 3 +
:win +
Message 4 +

+specifies that either Message 1 or Message 2 can be displayed when +the player dies or runs out of time, Message 3 can only be displayed +upon death, and Message 4 can only be displayed when the level is +won. + +

+Directories +

+

+Tile World uses four different directories for storing external files. +The following list enumerates the directories and describes their +purpose. The default directories that the program uses can be +configured at compile time. The directories can also be changed at +runtime via command-line options and/or environment variables (see +below). +

+ + + + + + + + + +
Sets This directory is used to hold the configuration files. For backward +compatibility only, level sets can also be placed here. +(default for Linux: /usr/local/share/tworld/sets)
Data This directory is used to hold the level sets, as well as CCX files. +(default for Linux: /usr/local/share/tworld/data)
Res This directory stores the graphics and sound files used by the +program. (default for Linux: /usr/local/share/tworld/res)
Save This directory is used for saving solution files, and +settings. (default for +Linux: ~/.tworld)
+

+ +

+Environment Variables +

+

+Two environment variables can be used to override the program's +built-in defaults for which directories to use. They are as follows: +

+ + + + + +
TWORLDDIR Specifies a top-level directory, in which the program will look for +the resource, level set, and data file directories.
TWORLDSAVEDIR Specifies a directory for saving solution files.
+

+


+

+ +

+License +

+

+Tile World is copyright (C) 2001-2017 by Brian Raiter, Madhav +Shanbhag, and Eric Schmidt. +

+This program is free software; you can redistribute it and/or +modify it under the terms of the GNU General Public License as +published by the Free Software Foundation; either version 2 of +the License, or (at your option) any later version. +

+This program is distributed in the hope that it will be useful, but +without any warranty; without even the implied warranty of +merchantability or fitness for a particular purpose. See the GNU +General Public License for more details. +

+Please send bug reports to CrapulentCretin@Yahoo.com or eric41293@comcast.net. +

+


+

+ +

+History +

+

+"Chip's Challenge" was created by Chuck Sommerville, and was +originally written for the Atari Lynx handheld game console. ("Tile +World" was his working title for the game.) "Chip's Challenge" was +published by Epyx (the company who designed the Lynx before selling +the rights to Atari) in 1989, and was among the first set of games +made available for the Lynx. +

+"Chip's Challenge" was subsequently ported to several other platforms: +MS-DOS, Microsoft Windows (16-bit), Amiga, ZX Spectrum, Atari ST, and +the Commodore 64. (A NES port was also planned, but never completed.) +

+The Windows port was different from most (perhaps all?) of the others +in that it was not done by the original team at Epyx. Instead it was +done by Microsoft and sold as part of Windows Entertainment Pack 4 +(and later as part of Best of Windows Entertainment Pack). In the +process of recreating the game for the 16-bit Windows platform, +Microsoft introduced a surprising number of changes to the mechanics +of the game. Some were clearly intentional, some were done through +ignorance or indifference, and others were simply bugs in the program. +The programs in WEP4 came pre-installed on many PC computers sold +during the 1990s, which is part of the reason why this particular +version became the most popular. A small but fanatically loyal +community of adherents to the game connected via a MSN chatroom (and +later through the internet). A few members of this community managed +to decipher the format of the MS game's data file, and John K. Elion +subsequently created a freeware level editor, called ChipEdit. As a +result there are now dozens of new level sets, created by fans of the +game and all freely available. +

+Atari discontinued support for the Lynx in 1994. When Epyx went under, +the rights to their games were purchased by Bridgestone Multimedia. +Responding to the success of "Chip's Challenge", Chuck Sommerville +created a sequel ("Chip's Challenge 2"). The sequel included the +original game as a proper subset, and the company held the rights to +both games. Bridgestone Multimedia, who has now become Alpha Omega +Publications, unfortunately did not see fit to actually release +"Chip's Challenge 2". It was, however, finally released on Steam in 2015, +alongside a port of the original game. The other versions, including +the famous port by Microsoft, are no longer being sold and cannot be +obtained except by purchasing a used copy (or by downloading an illegal +copy). +

+In 2001, the author began writing "Tile World" with the intention of +recreating a version of the MS game for the Linux platform. At the +encouragement of Chuck Sommerville, this project was expanded to +include the goals of recreating the original Lynx game as well, and +also making the program work under MS Windows in addition to Linux. +

+In 2010, Madhav Shanbhag produced "Tile World 2", which had several +new features. In 2014, Eric Schmidt produced version 2.1. In 2017, +he produced version 2.2 +


+

+ +

+Appendix: Notes on Nomenclature +

+

+"Chip's Challenge" has seen several incarnations. Each had its own +graphical rendering, and thus many of the objects in the game are +known by more than one name. For example, the four types of boots in +the MS version of the game were known as fire boots, flippers (for +water), skates (for ice), and suction boots (for force floors). In the +original Lynx version, however, they were not even boots -- the four +tools were fire shields, water shields, cleats, and magnets, +respectively. +

+Several of the creatures have seen a variety of names. The list of +creatures given in Overview of the Game corresponds to the MS +version of the game. In the original Lynx version, the paramecia were +centipedes instead. In still other versions of the game, gliders were +referred to as ghosts or sharks, fireballs were flames, and teeth were +called frogs. (You will also occasionally see bugs referred to as +bees, and walkers referred to as dumbbells.) +

+Finally, the thief tile was called a spy in the MS version. +

+None of this information is needed in order to play the game, but it +helps to explain the titles of some of the user-created levels. + + diff --git a/ver.h b/ver.h index 96e98eb..97d2e44 100644 --- a/ver.h +++ b/ver.h @@ -1,5 +1,5 @@ #ifdef TWPLUSPLUS - #define VERSION "2.2.0" + #define VERSION "2.3.0" #else #define VERSION "1.4.0" #endif