aboutsummaryrefslogtreecommitdiff
path: root/towers.c (follow)
Commit message (Collapse)AuthorAge
* Refactor `button & ~MOD_MASK' as `STRIP_BUTTON_MODIFIERS(button)'.Franklin Wei2024-07-31
| | | | | | | | | This refactors all instances of bitwise-ANDs with `~MOD_MASK'. There is a handful of more complex instances I left unchanged (in cube.c, midend.c, and twiddle.c), since those AND with `~MOD_MASK | MOD_NUM_KEYPAD' or similar. I don't think it's worth writing a macro for those cases. Also document this new macro's usage in devel.but.
* Towers: use new move_cursor() featuresBen Harris2023-08-13
|
* Keen, Solo, Towers, Undead, Unequal, Group: new UI preference.Simon Tatham2023-08-13
| | | | | | | | | | | | | | | | | | | | | | | If you're using the mouse to change pencil marks, you have to right-click to pencil-highlight a square, then press a number or letter key to add or remove a highlight. That causes the highlight to vanish again. So adding or removing multiple pencil marks requires a right-click + keypress per mark. Chris's Android port reversed that decision, making the pencil highlight persist so that you could 'click' just once and then press multiple pencil keys. That makes it easier to add lots of highlights, but harder to just remove a single one (click + press + click to remove the highlight), unless you don't mind keeping the highlight around afterwards cluttering up your view. In other words, this is just the sort of thing users might reasonably disagree on. So now we have an organised preferences system, we can let them disagree, and each configure it whichever way they like! This only affects mouse-based play. The keyboard cursor has _always_ worked this way, because it doesn't disappear at all; its behaviour is unchanged, and independent of the new preference.
* move_cursor(): handle visible flag; return useful valueBen Harris2023-08-09
| | | | | | | | | | | | | | | | | This adds an extra parameter to move_cursor() that's an optional pointer to a bool indicating whether the cursor is visible. This allows for centralising the common idiom of having the keyboard cursor become visible when a cursor key is pressed. Consistently with the vast majority of existing puzzles, the cursor moves even if it was invisible before, and becomes visible even if it can't move. The function now also returns one of the special constants that can be returned by interpret_move(), so that the caller can correctly return MOVE_UI_UPDATE or MOVE_NO_EFFECT without needing to carefully check for changes itself. Callers are updated only to the extent that they all pass NULL as the new argument. Most of them could now be substantially simplified.
* Rename UI_UPDATE as MOVE_UI_UPDATEBen Harris2023-06-11
| | | | | | | | All the other constants named UI_* are special key names that can be passed to midend_process_key(), but UI_UPDATE is a special return value from the back-end interpret_move() function instead. This renaming makes the distinction clear and provides a naming convention for future special return values from interpret_move().
* New backend functions: get_prefs and set_prefs.Simon Tatham2023-04-23
| | | | | | | | | | | | | | | | | | | | | | | | These are similar to the existing pair configure() and custom_params() in that get_prefs() returns an array of config_item describing a set of dialog-box controls to present to the user, and set_prefs() receives the same array with answers filled in and implements the answers. But where configure() and custom_params() operate on a game_params structure, the new pair operate on a game_ui, and are intended to permit GUI configuration of all the settings I just moved into that structure. However, nothing actually _calls_ these routines yet. All I've done in this commit is to add them to 'struct game' and implement them for the functions that need them. Also, config_item has new fields, permitting each config option to define a machine-readable identifying keyword as well as the user-facing description. For options of type C_CHOICES, each choice also has a keyword. These keyword fields are only defined at all by the new get_prefs() function - they're left uninitialised in existing uses of the dialog system. The idea is to use them when writing out the user's preferences into a configuration file on disk, although I haven't actually done any of that work in this commit.
* Move per-puzzle ad-hoc getenv preferences into game_ui.Simon Tatham2023-04-23
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | Environment variables that set specific settings of particular puzzles, such as SLANT_SWAP_BUTTONS, LIGHTUP_LIT_BLOBS and LOOPY_AUTOFOLLOW, now all affect the game behaviour via fields in game_ui instead of being looked up by getenv in the individual functions that need to know them. The purpose of this refactoring is to put those config fields in a place where other more user-friendly configuration systems will also be able to access them, once I introduce one. However, for the moment, there's no functional change: I haven't _yet_ changed how the user sets those options. They're still set by environment variables alone. All I'm changing here is where the settings are stored inside the code, and exactly when they're read out of the environment to put into the game_ui. Specifically, the getenvs now happen during new_ui(). Or rather, in all the puzzles I've changed here, they happen in a subroutine legacy_prefs_override() called from within new_ui(), after it's set up the default values for the settings, and then gives the environment a chance to override them. Or rather, legacy_prefs_override() only actually calls getenv the first time, and after that, it's cached the answers it got. In order to make the override functions less wordy, I've altered the prototype of getenv_bool so that it returns an int rather than a bool, and takes its default return value in the same form. That way you can set the default to something other than 0 or 1, and find out whether a value was present at all. This commit only touches environment configuration specific to an individual puzzle. The midend also has some standard environment-based config options that apply to all puzzles, such as colour scheme and default presets and preset-menu extension. I haven't handled those yet.
* Pass a game_ui to compute_size, print_size and print.Simon Tatham2023-04-21
| | | | | | | I'm about to move some of the bodgy getenv-based options so that they become fields in game_ui. So these functions, which could previously access those options directly via getenv, will now need to be given a game_ui where they can look them up.
* Make encode_ui() and decode_ui() optional in back-endsBen Harris2023-04-08
| | | | | | | | | | | | The majority of back-ends define encode_ui() to return NULL and decode_ui() to do nothing. This commit allows them to instead specify the relevant function pointers as NULL, in which case the mid-end won't try to call them. I'm planning to add a parameter to decode_ui(), and if I'm going to have to touch every back-end's version of decode_ui(), I may as well ensure that most of them never need to be touched again. And obviously encode_ui() should go the same way for symmetry.
* Fall back to <math.h> if <tgmath.h> doesn't work.Simon Tatham2023-04-06
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This fixes a build failure introduced by commit 2e48ce132e011e8 yesterday. When I saw that commit I expected the most likely problem would be in the NestedVM build, which is currently the thing with the most most out-of-date C implementation. And indeed the NestedVM toolchain doesn't have <tgmath.h> - but much more surprisingly, our _Windows_ builds failed too, with a compile error inside <tgmath.h> itself! I haven't looked closely into the problem yet. Our Windows builds are done with clang, which comes with its own <tgmath.h> superseding the standard Windows one. So you'd _hope_ that clang could make sense of its own header! But perhaps the problem is that this is an unusual compile mode and hasn't been tested. My fix is to simply add a cmake check for <tgmath.h> - which doesn't just check the file's existence, it actually tries compiling a file that #includes it, so it will detect 'file exists but is mysteriously broken' just as easily as 'not there at all'. So this makes the builds start working again, precisely on Ben's theory of opportunistically using <tgmath.h> where possible and falling back to <math.h> otherwise. It looks ugly, though! I'm half tempted to make a new header file whose job is to include a standard set of system headers, just so that that nasty #ifdef doesn't have to sit at the top of almost all the source files. But for the moment this at least gets the build working again.
* Replace <math.h> with <tgmath.h> throughoutBen Harris2023-04-04
| | | | | | | | | | | | | | | C89 provided only double-precision mathematical functions (sin() etc), and so despite using single-precision elsewhere, those are what Puzzles has traditionally used. C99 introduced single-precision equivalents (sinf() etc), and I hope it's been long enough that we can safely use them. Maybe they'll even be faster. Rather than directly use the single-precision functions, though, we use the magic macros from <tgmath.h> that automatically choose the precision of mathematical functions based on their arguments. This has the advantage that we only need to change which header we include, and thus that we can switch back again if some platform has trouble with the new header.
* Add an environment variable to control initial cursor visibilityBen Harris2023-03-22
| | | | | | | | | | | | | If you define PUZZLES_INITIAL_CURSOR=y, puzzles that have a keyboard cursor will default to making it visible rather than invisible at the start of a new game. Behaviour is otherwise the same, so mouse actions will cause the cursor to vanish and keyboard actions will cause it to appear. It's just the default that has changed. The purpose of this is for use on devices and platforms where the primary or only means of interaction is keyboard-based. In those cases, starting with the keyboard cursor invisible is weird and a bit confusing.
* New shared function, getenv_bool()Ben Harris2023-03-22
| | | | | | | | | | | | | This provides a standard way to get a boolean from an environment variable. It treats the variable as true iff its value begins with 'y' or 'Y', like most of the current implementations. The function takes a default value which it returns if the environment variable is undefined. This replaces the various ad-hoc tests of environment variable scattered around and mostly doesn't change their behaviour. The exceptions are TOWERS_2D in Towers and DEBUG_PUZZLES in the Windows front end. Both of those were treated as true if they were defined at all, but now follow the same rules as other boolean environment variables.
* Remove various unused game functionsBen Harris2023-01-31
| | | | | | | | | | | | | | | | | | | | If can_configure is false, then the game's configure() and custom_params() functions will never be called. If can_solve is false, solve() will never be called. If can_format_as_text_ever is false, can_format_as_text_now() and text_format() will never be called. If can_print is false, print_size() and print() will never be called. If is_timed is false, timing_state() will never be called. In each case, almost all puzzles provided a function nonetheless. I think this is because in Puzzles' early history there was no "game" structure, so the functions had to be present for linking to work. But now that everything indirects through the "game" structure, unused functions can be left unimplemented and the corresponding pointers set to NULL. So now where the flags mentioned above are false, the corresponding functions are omitted and the function pointers in the "game" structures are NULL.
* Towers: reject descriptions with odd characters at the endBen Harris2023-01-15
| | | | | | | | | Towers' new_game() causes an assertion failure on game description strings that contain spurious characters after a valid description, so validate_desc() should also refuse such a description. The problem could be demonstrated by editing the game description in the "Specific" dialogue box to add a '!' at the end, which caused "new_game: Assertion `!*p' failed.".
* When filling in or blanking a square, don't generate null movesBen Harris2022-12-27
| | | | | | | | | | | | | | | | This applies to various square-filling games: Keen, Solo, Towers, Undead, and Unequal. In all cases, selecting a square and pressing the number that was already in it, or selecting an empty square and pressing Backspace, would add a move to the undo chain that did nothing. This also meant that the convention where Backspace from the top level of an application in KaiOS leaves the application didn't work. Now the various interpret_move() functions check the current state of the grid, and return NULL or UI_UPDATE where a move wouldn't change the board. UI_UPDATE is returned in the case where the cursor was put in place using the mouse, because in those cases I think the cursor should still be hidden again. NULL is returned when the cursor was put in place by keyboard, because then there's really nothing to do.
* New backend function: current_key_label()Ben Harris2022-12-09
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | This provides a way for the front end to ask how a particular key should be labelled right now (specifically, for a given game_state and game_ui). This is useful on feature phones where it's conventional to put a small caption above each soft key indicating what it currently does. The function currently provides labels only for CURSOR_SELECT and CURSOR_SELECT2. This is because these are the only keys that need labelling on KaiOS. The concept of labelling keys also turns up in the request_keys() call, but there are quite a few differences. The labels returned by current_key_label() are dynamic and likely to vary with each move, while the labels provided by request_keys() are constant for a given game_params. Also, the keys returned by request_keys() don't generally include CURSOR_SELECT and CURSOR_SELECT2, because those aren't necessary on platforms with pointing devices. It might be possible to provide a unified API covering both of this, but I think it would be quite difficult to work with. Where a key is to be unlabelled, current_key_label() is expected to return an empty string. This leaves open the possibility of NULL indicating a fallback to button2label or the label specified by request_keys() in the future. It's tempting to try to implement current_key_label() by calling interpret_move() and parsing its output. This doesn't work for two reasons. One is that interpret_move() is entitled to modify the game_ui, and there isn't really a practical way to back those changes out. The other is that the information returned by interpret_move() isn't sufficient to generate a label. For instance, in many puzzles it generates moves that toggle the state of a square, but we want the label to reflect which state the square will be toggled to. The result is that I've generally ended up pulling bits of code from interpret_move() and execute_move() together to implement current_key_label(). Alongside the back-end function, there's a midend_current_key_label() that's a thin wrapper around the back-end function. It just adds an assertion about which key's being requested and a default null implementation so that back-ends can avoid defining the function if it will do nothing useful.
* Towers: Fix indentation of some solver codeBen Hutchings2022-07-31
| | | | | | | | gcc 6 warns about statements that are indented as if they were meant to be part of a preceding conditional block. In this case I don't think that was intended, so shift it left. References: https://bugs.debian.org/811577
* Centralise initial clearing of the puzzle window.Simon Tatham2021-04-25
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | I don't know how I've never thought of this before! Pretty much every game in this collection has to have a mechanism for noticing when game_redraw is called for the first time on a new drawstate, and if so, start by covering the whole window with a filled rectangle of the background colour. This is a pain for implementers, and also awkward because the drawstate often has to _work out_ its own pixel size (or else remember it from when its size method was called). The backends all do that so that the frontends don't have to guarantee anything about the initial window contents. But that's a silly tradeoff to begin with (there are way more backends than frontends, so this _adds_ work rather than saving it), and also, in this code base there's a standard way to handle things you don't want to have to do in every backend _or_ every frontend: do them just once in the midend! So now that rectangle-drawing operation happens in midend_redraw, and I've been able to remove it from almost every puzzle. (A couple of puzzles have other approaches: Slant didn't have a rectangle-draw because it handles even the game borders using its per-tile redraw function, and Untangle clears the whole window on every redraw _anyway_ because it would just be too confusing not to.) In some cases I've also been able to remove the 'started' flag from the drawstate. But in many cases that has to stay because it also triggers drawing of static display furniture other than the background.
* Add method for frontends to query the backend's cursor location.Franklin Wei2020-12-07
| | | | | | | | | | | | | | | | The Rockbox frontend allows games to be displayed in a "zoomed-in" state targets with small displays. Currently we use a modal interface -- a "viewing" mode in which the cursor keys are used to pan around the rendered bitmap; and an "interaction" mode that actually sends keys to the game. This commit adds a midend_get_cursor_location() function to allow the frontend to retrieve the backend's cursor location or other "region of interest" -- such as the player location in Cube or Inertia. With this information, the Rockbox frontend can now intelligently follow the cursor around in the zoomed-in state, eliminating the need for a modal interface.
* Towers: fill in the latin.c validator function.Simon Tatham2020-05-23
| | | | | Again, this seems to have made no difference in a test generation run with the command "towers --generate 100 6du#12345".
* latin.c: call a user-provided validator function. [NFC]Simon Tatham2020-05-23
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | I've only just realised that there's a false-positive bug in the latin.c solver framework. It's designed to solve puzzles in which the solution is a latin square but with some additional constraints provided by the individual puzzle, and so during solving, it runs a mixture of its own standard deduction functions that apply to any latin-square puzzle and extra functions provided by the client puzzle to do deductions based on the extra clues or constraints. But what happens if the _last_ move in the solving process is performed by one of the latin.c built-in methods, and it causes a violation of the client puzzle's extra constraints? Nothing will ever notice, and so the solver will report that the puzzle has a solution when it actually has none. An example is the Group game id 12i:m12b9a1zd9i6d10c3y2l11q4r . This was reported by 'groupsolver -g' as being ambiguous. But if you look at the two 'solutions' reported in the verbose diagnostics, one of them is arrant nonsense: it has no identity element at all, and therefore, it fails associativity all over the place. Actually that puzzle _does_ have a unique solution. This bug has been around for ages, and nobody has reported a problem. For recursive solving, that's not much of a surprise, because it would cause a spurious accusation of ambiguity, so that at generation time some valid puzzles would be wrongly discarded, and you'd never see them. But at non-recursive levels, I can't see a reason why this bug _couldn't_ have led one of the games to present an actually impossible puzzle believing it to be soluble. Possibly this never came up because the other clients of latin.c are more forgiving of this error in some way. For example, they might all be very likely to use their extra clues early in the solving process, so that the requirements are already baked in by the time the final grid square is filled. I don't know! Anyway. The fix is to introduce last-minute client-side validation: whenever the centralised latin_solver thinks it's come up with a filled grid, it should present it to a puzzle-specific validator function and check that it's _really_ a legal solution. This commit does the plumbing for all of that: it introduces the new validator function as one of the many parameters to latin_solver, and arranges to call it in an appropriate way during the solving process. But all the per-puzzle validation functions are empty, for the moment.
* Use C99 bool within source modules.Simon Tatham2018-11-13
| | | | | | | | | | This is the main bulk of this boolification work, but although it's making the largest actual change, it should also be the least disruptive to anyone interacting with this code base downstream of me, because it doesn't modify any interface between modules: all the inter-module APIs were updated one by one in the previous commits. This just cleans up the code within each individual source file to use bool in place of int where I think that makes things clearer.
* Replace TRUE/FALSE with C99 true/false throughout.Simon Tatham2018-11-13
| | | | | | This commit removes the old #defines of TRUE and FALSE from puzzles.h, and does a mechanical search-and-replace throughout the code to replace them with the C99 standard lowercase spellings.
* Adopt C99 bool in the game backend API.Simon Tatham2018-11-13
| | | | | | | | | | | encode_params, validate_params and new_desc now take a bool parameter; fetch_preset, can_format_as_text_now and timing_state all return bool; and the data fields is_timed, wants_statusbar and can_* are all bool. All of those were previously typed as int, but semantically boolean. This commit changes the API declarations in puzzles.h, updates all the games to match (including the unfinisheds), and updates the developer docs as well.
* Make static keyword come first everywhere.Franklin Wei2018-04-25
| | | | I somehow missed these instances as well. Oh well.
* Add a request_keys() function with a midend wrapper.Franklin Wei2018-04-22
| | | | | | | | This function gives the front end a way to find out what keys the back end requires; and as such it is mostly useful for ports without a keyboard. It is based on changes originally found in Chris Boyle's Android port, though some modifications were needed to make it more flexible.
* towerssolver: always print solver diagnostics in -v mode.Simon Tatham2018-02-26
| | | | | | | The branch of the code that claimed the puzzle to be ambiguous was not also re-running the solver with diagnostics enabled, so that if a user tries to use this tool to hand-design a puzzle, they do not get feedback on what the multiple legal solutions actually are.
* Return error messages as 'const char *', not 'char *'.Simon Tatham2017-10-01
| | | | | They're never dynamically allocated, and are almost always string literals, so const is more appropriate.
* Use a proper union in struct config_item.Simon Tatham2017-10-01
| | | | | | This allows me to use different types for the mutable, dynamically allocated string value in a C_STRING control and the fixed constant list of option names in a C_CHOICES.
* New name UI_UPDATE for interpret_move's return "".Simon Tatham2017-10-01
| | | | | | | | | | | | | Now midend.c directly tests the returned pointer for equality to this value, instead of checking whether it's the empty string. A minor effect of this is that games may now return a dynamically allocated empty string from interpret_move() and treat it as just another legal move description. But I don't expect anyone to be perverse enough to actually do that! The main purpose is that it avoids returning a string literal from a function whose return type is a pointer to _non-const_ char, i.e. we are now one step closer to being able to make this code base clean under -Wwrite-strings.
* Rework the preset menu system to permit submenus.Simon Tatham2017-04-26
| | | | | | | | | | | | | | | | | | | | To do this, I've completely replaced the API between mid-end and front end, so any downstream front end maintainers will have to do some rewriting of their own (sorry). I've done the necessary work in all five of the front ends I keep in-tree here - Windows, GTK, OS X, Javascript/Emscripten, and Java/NestedVM - and I've done it in various different styles (as each front end found most convenient), so that should provide a variety of sample code to show downstreams how, if they should need it. I've left in the old puzzle back-end API function to return a flat list of presets, so for the moment, all the puzzle backends are unchanged apart from an extra null pointer appearing in their top-level game structure. In a future commit I'll actually use the new feature in a puzzle; perhaps in the further future it might make sense to migrate all the puzzles to the new API and stop providing back ends with two alternative ways of doing things, but this seemed like enough upheaval for one day.
* Improve Towers error highlighting.Jonas Kölker2015-10-03
| | | | | | | Highlight clues of value n in Towers if its row/column contains an increasing sequence of length n, the last number of which is not equal to the number of rows/columns (i.e. such that the sequence will have to be extended, in violation of the clue).
* Towers: allow marking of clues as done with the keyboardJonas Kölker2015-10-03
|
* Updated name/size of clues_done in TowersKevin Lyles2015-05-26
|
* Towers: you can now mark clues doneKevin Lyles2015-05-22
|
* Split out the text color portion of draw_tileKevin Lyles2015-05-22
|
* Cleaned up execute_move a littleKevin Lyles2015-05-22
|
* Stop using a zero precision specifier with sprintf ("%.0d") to causeSimon Tatham2013-07-05
| | | | | | | | | zero to be generated as the empty string rather than "0". Instead, do the job by the obvious approach of not calling sprintf at all if the number is zero. Works around a bug in Emscripten's C library, whose sprintf doesn't correctly handle that corner case. [originally from svn r9893]
* Giant const patch of doom: add a 'const' to every parameter in everySimon Tatham2013-04-13
| | | | | | | | | | | | | | puzzle backend function which ought to have it, and propagate those consts through to per-puzzle subroutines as needed. I've recently had to do that to a few specific parameters which were being misused by particular puzzles (r9657, r9830), which suggests that it's probably a good idea to do the whole lot pre-emptively before the next such problem shows up. [originally from svn r9832] [r9657 == 3b250baa02a7332510685948bf17576c397b8ceb] [r9830 == 0b93de904a98f119b1a95d3a53029f1ed4bfb9b3]
* Add 'const' to the game_params arguments in validate_desc andSimon Tatham2013-04-12
| | | | | | | | | | | | new_desc. Oddities in the 'make test' output brought to my attention that a few puzzles have been modifying their input game_params for various reasons; they shouldn't do that, because that's the game_params held permanently by the midend and it will affect subsequent game generations if they modify it. So now those arguments are const, and all the games which previously modified their game_params now take a copy and modify that instead. [originally from svn r9830]
* New rule: interpret_move() is passed a pointer to the game_drawstateSimon Tatham2012-09-09
| | | | | | | | | | | | | | | | basically just so that it can divide mouse coordinates by the tile size, but is definitely not expected to _write_ to it, and it hadn't previously occurred to me that anyone might try. Therefore, interpret_move() now gets a pointer to a _const_ game_drawstate instead of a writable one. All existing puzzles cope fine with this API change (as long as the new const qualifier is also added to a couple of subfunctions to which interpret_move delegates work), except for the just-committed Undead, which somehow had ds->ascii and ui->ascii the wrong way round but is otherwise unproblematic. [originally from svn r9657]
* Changed my mind about midend_is_solved: I've now reprototyped it asSimon Tatham2011-06-19
| | | | | | | | | | | | | | | | midend_status(), and given it three return codes for win, (permanent) loss and game-still-in-play. Depending on what the front end wants to use it for, it may find any or all of these three states worth distinguishing from each other. (I suppose a further enhancement might be to add _non_-permanent loss as a fourth distinct status, to describe situations in which you can't play further without pressing Undo but doing so is not completely pointless. That might reasonably include dead-end situations in Same Game and Pegs, and blown-self-up situations in Mines and Inertia. However, I haven't done this at present.) [originally from svn r9179]
* Fix warnings generated by gcc 4.6.0 about variables set but notSimon Tatham2011-05-04
| | | | | | | | | | thereafter read. Most of these changes are just removal of pointless stuff or trivial reorganisations; one change is actually substantive, and fixes a bug in Keen's clue selection (the variable 'bad' was unreferenced not because I shouldn't have set it, but because I _should_ have referenced it!). [originally from svn r9164]
* Add a function to every game backend which indicates whether a gameSimon Tatham2011-04-02
| | | | | | | | | | | state is in a solved position, and a midend function wrapping it. (Or, at least, a situation in which further play is pointless. The point is, given that game state, would it be a good idea for a front end that does that sort of thing to proactively provide the option to start a fresh game?) [originally from svn r9140]
* Highlight the sides as well as the top of a tower when it'sSimon Tatham2010-01-16
| | | | | | | | | selected. I think this is a more or less aesthetically neutral change for the actual selection, but the selection code is reused to draw the victory flash and that's greatly improved by not having the tower sides remain unflashed. [originally from svn r8843]
* Add a couple more checks to the 3d collision detection, withoutSimon Tatham2010-01-12
| | | | | | | which a click miles above the invisible RH surface of a tower would sometimes select that tower. [originally from svn r8833]
* The 3-D graphics in Towers need a corresponding change on the inputSimon Tatham2010-01-11
| | | | | | | | | | side: instead of locating a mouse click in whichever base grid square contained the click location, we should check to see if a tower protrudes on to that location from a neighbouring square. That way, a click should reliably activate the tower that was actually drawn under the mouse pointer. [originally from svn r8826]
* Oops! Remove evidence of blatant clone-and-hack from Keen. :-)Simon Tatham2010-01-11
| | | | [originally from svn r8825]
* Upgrade the graphics in Towers to include a thematically appropriateSimon Tatham2010-01-07
| | | | | | | | | 3D effect. (I only committed it without so that I could preserve a record of how the code looked before I added it.) The 3D effect can be disabled by setting TOWERS_2D in the environment, should anybody seriously dislike it. [originally from svn r8817]